/* * Fichier : cache_lru.c * Auteur : Moncef STITI * Date : 10/11/2024 * Version : 1.0 * Description : Ce programme permet de simuler une mémoire cache avec comme algorithme d'éviction d'une ligne "LRU" (Least Recently Used). * Compilation : Pour compiler le programme, on utilise la commande suivante : gcc cache_lru.c -o cache_simulator -lm -Wall ou simplement make */ #include #include #include #include #include #include #include #include // Pour les mesures de temps #define TAILLE_INITIALE 1000 // Taille initiale du tableau d'adresses #define INCREMENT_TAILLE 1000 // Incrément pour realloc // Différentes couleurs pour l'affichage des résultats #define RESET "\x1b[0m" // Couleur de base du terminal #define RED "\033[31m" // Rouge #define GREEN "\033[32m" // Vert #define YELLOW "\033[33m" // Jaune #define MAGENTA "\033[35m" // Violet/Magenta #define CYAN "\033[36m" // Cyan #define WHITE "\033[37m" // Blanc #define COULEUR_BORDURE CYAN // Couleur des bordures // Structure représentant une ligne de cache. struct line { unsigned int valid; // Indicateur de validité : 1 si la ligne est valide, 0 sinon. unsigned int dirty; // Indicateur de "saleté" (dirty) : 1 si la ligne a été modifiée, 0 sinon. unsigned int tag; // Tag de la ligne de cache utilisé pour identifier les données stockées. unsigned int valeur_lru; // Valeur pour la gestion du remplacement de ligne (Least Recently Used). }; // Structure représentant un ensemble de lignes de cache. struct set { struct line * lines; // Pointeur vers un tableau de lignes de cache dans cet ensemble. }; // Structure contenant les tailles des différents composants d'une adresse mémoire. struct adresse_info { unsigned int taille_adresse; // Taille totale de l'adresse mémoire (en bits). unsigned int taille_offset; // Taille de l'offset (en bits). unsigned int taille_index; // Taille de l'index (en bits). unsigned int taille_tag; // Taille du tag (en bits). }; // Structure contenant les comptes des actions de cache (hits et misses). struct cache_action { unsigned int instruction_reference_count; // Compte des références d'instructions (total). unsigned int instruction_hit_count; // Compte des hits pour les instructions. unsigned int instruction_miss_count; // Compte des misses pour les instructions. unsigned int data_read_reference_count; // Compte des références de lecture de données (total). unsigned int data_read_hit_count; // Compte des hits pour les lectures de données. unsigned int data_read_miss_count; // Compte des misses pour les lectures de données. unsigned int data_write_reference_count; // Compte des références d'écriture de données (total). unsigned int data_write_hit_count; // Compte des hits pour les écritures de données. unsigned int data_write_miss_count; // Compte des misses pour les écritures de données. unsigned int memory_accesses; // Compte total des accès à la mémoire (tous types). }; // Structure principale représentant une cache. struct cache { unsigned int taille_cache; // Taille totale de la mémoire cache (en octets). unsigned int taille_ligne; // Taille d'une ligne de cache (en octets). unsigned int associativite; // Niveau d'associativité de la mémoire cache (directement mappée, associée par ensembles, etc.). unsigned int nombre_sets; // Nombre total d'ensembles dans la cache. unsigned int write_policy; // Politique d'écriture : 0 pour Write Through, 1 pour Write Back. unsigned int write_allocate; // Politique d'allocation lors des écritures : 0 pour pas d'allocation, 1 pour Write Allocate. struct adresse_info addr_info; // Informations sur les tailles d'adresse dans la cache. struct set * sets; // Pointeur vers un tableau d'ensembles de lignes de cache. struct cache_action action; // Comptes des actions réalisées sur la cache (hits/misses). double total_access_time; // Temps total d'accès au cache double cache_hit_time; // Temps pour un hit double cache_miss_time; // Temps pour un miss }; // Structure représentant une référence mémoire, incluant son adresse (en hexadécimal) et son type d'accès (0, 1 ou 2). struct reference_memoire { unsigned int adresse; // Adresse mémoire en hexadécimal. unsigned int adresse_type; // Type d'accès à l'adresse. }; /* * Fonction : calculer_tailles_adresse * Description : Cette fonction calcule la taille de l'offset, de l'index et du tag en nombre de bits * à partir de la taille de ligne de cache et du nombre de sets. Elle met à jour * les informations d'adresse dans la structure de cache. * * Paramètre : * - struct cache* c : Un pointeur vers la structure de cache dont les informations d'adresse doivent être calculées. * * Détails des calculs : * - La taille totale de l'adresse mémoire est fixée à 32 bits. * - La taille de l'offset est calculée comme le logarithme en base 2 de la taille de ligne, * ce qui détermine combien de bits sont nécessaires pour adresser chaque octet dans une ligne. * - La taille de l'index est calculée de la même manière, en utilisant le nombre de sets, * pour déterminer combien de bits sont nécessaires pour identifier un set particulier dans la cache. * - La taille du tag est ensuite déterminée en soustrayant les tailles d'offset et d'index * de la taille totale de l'adresse. * * Erreurs : * - Si la taille de l'offset ou de l'index est égale à -1, cela signifie que la taille de ligne ou * le nombre de sets ne sont pas des puissances de 2, ce qui est requis pour le bon fonctionnement * de la cache. Dans ce cas, un message d'erreur est affiché. */ void calculer_tailles_adresse (struct cache* c) { // Taille totale de l'adresse (fixée à 32 bits) unsigned int taille_adresse = 32; // Calcul de la taille de l'offset en bits. // On utilise log2 pour déterminer combien de bits sont nécessaires pour adresser chaque octet dans une ligne. unsigned int taille_offset = (int) ceil(log2(c->taille_ligne)); unsigned int taille_index = (int) ceil(log2(c->nombre_sets)); unsigned int taille_tag = taille_adresse - taille_offset - taille_index; // Vérification des puissances de 2 if (taille_offset == -1) { printf("Erreur : Taille de ligne de cache invalide (%d). La valeur doit être une puissance de 2.\n", c->taille_ligne); } else c->addr_info.taille_offset = taille_offset; if (taille_index == -1) { printf("Erreur : Nombre de sets invalide (%d). La valeur doit être une puissance de 2.\n", c->nombre_sets); } else c->addr_info.taille_index = taille_index; if (taille_offset != -1 && taille_index != -1) { c->addr_info.taille_adresse = taille_adresse; c->addr_info.taille_tag = taille_tag; } } /* * Fonction : creer_struct_cache * Description : Cette fonction crée une structure de type cache en allouant dynamiquement * la mémoire nécessaire pour les ensembles et les lignes en fonction des * paramètres fournis, notamment la taille de la cache, la taille des lignes, * la politique d'écriture, et l'associativité. * * Paramètre : * - unsigned int taille_cache : Taille de la cache en Ko. * - unsigned int taille_ligne : Taille de chaque ligne de cache en octets. * - unsigned int write_policy : Politique d'écriture (0 pour Write Through, * 1 pour Write Back). * - unsigned int write_allocate : Stratégie d'allocation d'écriture * (0 pour pas d'allocation, 1 pour * allocation d'écriture). * - unsigned int associativite : Niveau d'associativité de la mémoire cache. * * Retour : * - struct cache* : Pointeur vers la structure de cache créée. */ struct cache * creer_struct_cache(unsigned int taille_cache,unsigned int taille_ligne,unsigned int write_policy,unsigned int write_allocate,unsigned int associativite) { unsigned int nbr_lignes = (taille_cache * 1024)/taille_ligne; unsigned int nombre_sets = nbr_lignes/associativite; struct cache* val_ret = malloc(sizeof(struct cache)); val_ret->sets = (struct set*)malloc(nombre_sets*sizeof(struct set)); unsigned int i = 0,j; for(i=0;isets[i].lines = (struct line *)malloc(associativite*sizeof(struct line)); for(j=0;jsets[i].lines[j].valid=0; val_ret->sets[i].lines[j].dirty=0; val_ret->sets[i].lines[j].tag=0; } } val_ret->taille_cache = taille_cache; val_ret->associativite = associativite; val_ret->taille_ligne = taille_ligne; val_ret->nombre_sets = nombre_sets; val_ret->write_policy = write_policy; val_ret->write_allocate = write_allocate; val_ret->total_access_time = 0.0; val_ret->cache_hit_time = 0.1; // Exemple : 0.1 ms par hit val_ret->cache_miss_time = 0.5; // Exemple : 0.5 ms par miss // Initialiser tous les comptes calculer_tailles_adresse(val_ret); val_ret->action.instruction_reference_count = 0; val_ret->action.instruction_hit_count = 0; val_ret->action.instruction_miss_count = 0; val_ret->action.data_read_reference_count= 0; val_ret->action.data_read_hit_count= 0; val_ret->action.data_read_miss_count= 0; val_ret->action.data_write_reference_count = 0; val_ret->action.data_write_hit_count = 0; val_ret->action.data_write_miss_count= 0; val_ret->action.memory_accesses = 0; return val_ret; } /* * Fonction : recuperer_index * Description : Cette fonction calcule l'index d'un ensemble dans la cache à partir d'une * adresse mémoire donnée. L'index est déterminé en extrayant des bits * spécifiques de l'adresse mémoire selon la structure de la cache. * * Paramètre : * - unsigned int adresse : Adresse mémoire * - struct cache* c : Pointeur vers la structure de la mémoire cache qui contient * les informations nécessaires pour le calcul de l'index, * notamment la taille de l'index et la taille de l'offset. * * Retour : * - unsigned int : La valeur entière représentant l'index de l'ensemble * dans la cache correspondant à l'adresse mémoire fournie. */ unsigned int recuperer_index(unsigned int adresse, struct cache* c) { unsigned int mask = (1 << c->addr_info.taille_index) - 1; return (adresse >> c->addr_info.taille_offset) & mask; } /* * Fonction : recuperer_tag * Description : Cette fonction extrait le tag d'une adresse mémoire donnée en * décalant l'adresse à droite d'un certain nombre de bits, qui est la * somme de la taille de l'offset et de la taille de l'index. * Le tag est utilisé pour identifier de manière unique une ligne de * cache dans un ensemble donné. * * Paramètre : * - unsigned int adresse : Adresse mémoire à partir de laquelle le tag * doit être extrait. * - struct cache* c : Pointeur vers la structure de la mémoire cache qui contient * les informations nécessaires pour le calcul du tag, * notamment la taille de l'offset et la taille de l'index. * * Retour : * - unsigned int : La valeur entière représentant le tag extrait de * l'adresse mémoire fournie. */ unsigned int recuperer_tag( unsigned int adresse, struct cache* c) { unsigned int bits_deplacer; bits_deplacer = c->addr_info.taille_offset + c->addr_info.taille_index; return adresse>> bits_deplacer; } /* * Fonction : maj_info_lru * Description : Cette fonction met à jour les informations LRU pour les lignes * d'un ensemble dans la cache lors de l'accès à une * ligne spécifique. Elle incrémente la valeur LRU pour les lignes * non accédées et réinitialise la valeur LRU de la ligne récemment * utilisée à zéro, indiquant qu'elle est la plus récemment utilisée. * * Paramètre : * - unsigned int tag : Tag de l'adresse mémoire pour identifier la ligne * dans la cache. * - unsigned int index : Index de l'ensemble dans la cache où se * trouve la ligne à mettre à jour. * - struct cache* c : Pointeur vers la structure de la cache qui contient * les informations sur les lignes et les valeurs LRU * de chaque ensemble. * * Retour : * - La fonction ne retourne aucune valeur. Elle modifie les * informations LRU directement dans la structure de cache. */ void maj_info_lru( unsigned int tag, unsigned int index,struct cache* c) { // Si la valeur de LRU = 0, cela signifie que c'est le set qui a été récemment utilisé. // Plus la valeur LRU est grande, plus le temps écoulé depuis le dernier accès à ce set est important. unsigned int i = 0; // Index pour parcourir les lignes du set unsigned int set_index = 0; // Index de la ligne récemment utilisée // Parcours de toutes les lignes de l'ensemble for(i=0;iassociativite;i++) { // Vérifie si la ligne est valide et si le tag correspond à celui de l'adresse if (c->sets[index].lines[i].valid && c->sets[index].lines[i].tag == tag) { // Si la valeur LRU de la ligne est déjà 0, cela signifie qu'elle a été récemment utilisée ; // nous n'avons donc pas besoin de mettre à jour if (!c->sets[index].lines[i].valeur_lru) return; // Enregistre l'index de la ligne récemment utilisée set_index = i; } } // Incrémente ou réinitialise les valeurs LRU pour toutes les lignes for(i = 0; i < c->associativite; i++) { if (i == set_index) { // Réinitialise la valeur LRU de la ligne récemment utilisée à 0 c->sets[index].lines[i].valeur_lru = 0; } else { // Incrémente la valeur LRU des autres lignes, indiquant qu'elles ont été le moins récemment utilisées c->sets[index].lines[i].valeur_lru++; } } } /* * Fonction : calcul_hits_miss * Description : Cette fonction calcule les compteurs de hits et de misses pour une * référence mémoire donnée. Elle vérifie si l'adresse est présente * dans la cache, met à jour les compteurs en conséquence, et enregistre * les informations nécessaires dans la structure de cache. * * Paramètre : * - struct reference_memoire m : Structure contenant l'adresse mémoire * et le type d'accès (lecture/écriture). * - struct cache* c : Pointeur vers la structure de la mémoire cache. * * Retour : * - void : La fonction ne retourne aucune valeur, mais met à jour les * compteurs et les informations de la mémoire cache directement. */ void calcul_hits_miss( struct reference_memoire m, struct cache* c) { // Récupère le tag et l'index de l'adresse mémoire unsigned int tag = recuperer_tag(m.adresse,c); unsigned int index = recuperer_index(m.adresse,c); unsigned int i = 0; // Mesurer les temps d'accés clock_t start_time = clock(); // Démarrer le chronomètre // Vérifier si la référence entraîne un miss bool miss = true; // Initialement, on suppose qu'il y a un miss for (i=0;iassociativite;i++) { struct line temp = c->sets[index].lines[i]; // Vérifie si la ligne est valide et si le tag correspond if (temp.tag == tag && temp.valid) { miss = false; // Hit trouvé break; } } // Charger la valeur dans la cache si c'est un miss if ( miss ) { c->action.memory_accesses++; // Compte l'accès mémoire // Gestion de la politique d'écriture pour Write Back if (c->write_policy==1 && c->sets[index].lines[0].dirty == 1) // Write Back et set dirty { c->action.memory_accesses++; // Incrémente l'accès mémoire c->sets[index].lines[0].dirty = 0; // Réinitialise le bit dirty } // Met à jour les lignes du set for(i=0;iassociativite;i++) { c->sets[index].lines[i].valid = 1; // Marque la ligne comme valide if (m.adresse_type == 1) c->sets[index].lines[i].dirty = 1; // Si c'est une écriture, marque dirty à 1 c->sets[index].lines[i].tag = tag; // Met à jour le tag } // Incrémente le compteur de miss selon le type d'adresse switch (m.adresse_type) { case 0: c->action.data_read_miss_count++; break; case 1: c->action.data_write_miss_count++; break; case 2: c->action.instruction_miss_count++; break; default: break; } } else // C'est un hit et non pas un miss { // Incrémente le compteur de hit selon le type d'adresse switch (m.adresse_type) { case 0: c->action.data_read_hit_count++; break; case 1: c->action.data_write_hit_count++; break; case 2: c->action.instruction_hit_count++; break; default: break; } } // Met à jour les informations LRU pour le tag et l'index maj_info_lru(tag,index,c); // Fin de la mesure de temps clock_t end_time = clock(); // Arrêter le chronomètre double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC; // Temps écoulé en secondes c->total_access_time += elapsed_time; // Ajouter au temps total d'accès // Incrémente le compteur de références selon le type d'adresse switch (m.adresse_type) { case 0: c->action.data_read_reference_count++; break; case 1: c->action.data_write_reference_count++; break; case 2: c->action.instruction_reference_count++; break; default: break; } } /* * Fonction : get_hits_color * Description : Cette fonction détermine la couleur d'affichage basée sur le pourcentage * de hits pour une performance de cache donnée. Elle renvoie une constante * représentant une couleur en fonction du niveau de performance. * * Paramètre : * - float percentage : Pourcentage de hits, exprimé en tant que valeur * flottante. Ce pourcentage est utilisé pour évaluer * la performance de la mémoire cache. * * Retour : * - const char* : Retourne une constante de chaîne de caractères représentant * la couleur correspondant à la performance de la mémoire cache : * - GREEN pour une bonne performance (≥ 80 %) * - YELLOW pour une performance moyenne (≥ 50 % et < 80 %) * - RED pour une mauvaise performance (< 50 %) * * Potentiel erreur : * - La fonction ne gère pas les valeurs de pourcentage en dehors de la * plage normale (0 à 100). Les entrées négatives ou supérieures à 100 * pourraient donner des résultats inattendus. */ const char* get_hits_color(float percentage) { if (percentage >= 80.0) { return GREEN; // Bonne performance } else if (percentage >= 50.0) { return YELLOW; // Moyenne performance } else { return RED; // Mauvais performance } } /* * Fonction : get_misses_color * Description : Cette fonction détermine la couleur d'affichage basée sur le pourcentage * de misses pour une performance de cache donnée. Elle renvoie une constante * représentant une couleur en fonction du niveau de performance. * * Paramètre : * - float percentage : Pourcentage de misses, exprimé en tant que valeur * flottante. Ce pourcentage est utilisé pour évaluer * la performance de la mémoire cache. * * Retour : * - const char* : Retourne une constante de chaîne de caractères représentant * la couleur correspondant à la performance de la mémoire cache : * - GREEN pour une bonne performance (< 8 % de misses) * - YELLOW pour une performance acceptable (≥ 8 % et < 16 %) * - RED pour une mauvaise performance (≥ 16 % de misses) * * Potentiel erreur : * - La fonction ne gère pas les valeurs de pourcentage en dehors de la * plage normale (0 à 100). Les entrées négatives ou supérieures à 100 * pourraient donner des résultats inattendus. */ const char* get_misses_color(float percentage) { if (percentage < 8.0) { return GREEN; // Bonne performance } else if (percentage < 16.0) { return YELLOW; // Acceptable } else { return RED; // Mauvais performance } } /* * Fonction : affiche_hits_miss * Description : Cette fonction affiche les statistiques de performance de la mémoire cache, * y compris le nombre total d'accès, les hits et les misses, ainsi que * les pourcentages correspondants pour les lectures et les écritures. * Les résultats sont formatés et colorés pour une meilleure lisibilité. * * Paramètre : * - struct cache * c : Pointeur vers une structure de cache contenant * les compteurs d'accès et les statistiques * de hits et misses. * * Calculs : * - Total des accès : Somme des accès en lecture, écriture et * instructions. * - Accès en lecture et en écriture : Calcul des accès respectifs * et de leurs pourcentages par rapport au total. * - Hits et misses : Somme des hits et misses en lecture, écriture * et instructions, avec calcul des pourcentages. * - Conversion des accès mémoire en nombre de mots (en divisant * par 4). * * Affichage : * - Les statistiques sont affichées dans un format tabulaire * avec des couleurs indiquant la performance : * - Hits en vert, jaune ou rouge selon le pourcentage. * - Misses en vert, jaune ou rouge selon le pourcentage. * * Potentiel erreur : * - La fonction ne gère pas les cas où le nombre total d'accès * est égal à zéro, ce qui pourrait entraîner une division par zéro * lors du calcul des pourcentages. */ void affiche_hits_miss(struct cache * c) { int accesses = c->action.data_read_reference_count + c->action.data_write_reference_count + c->action.instruction_reference_count; int access_reads = c->action.data_read_reference_count + c->action.instruction_reference_count; float pourcent_acc_reads = ((float)access_reads / (float)accesses) * 100; int access_writes = c->action.data_write_reference_count; float pourcent_acc_writes = ((float)access_writes / (float)accesses) * 100; int hits = c->action.data_read_hit_count + c->action.data_write_hit_count + c->action.instruction_hit_count; float pourcent_hits = ((float)hits / (float)accesses) * 100; int hits_reads = c->action.data_read_hit_count + c->action.instruction_hit_count; int hits_writes = c->action.data_write_hit_count; float pourcent_hits_reads = ((float)hits_reads / (float)accesses) * 100; float pourcent_hits_writes = ((float)hits_writes / (float)accesses) * 100; int miss = c->action.data_read_miss_count + c->action.data_write_miss_count + c->action.instruction_miss_count; float pourcent_miss = ((float)miss / (float)accesses) * 100; int miss_reads = c->action.data_read_miss_count + c->action.instruction_miss_count; int miss_writes = c->action.data_write_miss_count; float pourcent_miss_reads = ((float)miss_reads / (float)accesses) * 100; float pourcent_miss_writes = ((float)miss_writes / (float)accesses) * 100; c->action.memory_accesses = c->action.memory_accesses * (c->taille_ligne / 4); // Convertit en nombre de mots // Afficher les hits et misses de manière formatée avec couleurs printf("%s----------------------------------------------------\n", COULEUR_BORDURE); printf("| %sStatistiques de Cache%s |\n", WHITE, COULEUR_BORDURE); printf("----------------------------------------------------\n"); printf("| %sTotal Accès%s | %s%6d %s|\n", MAGENTA,COULEUR_BORDURE,WHITE, accesses, COULEUR_BORDURE); printf("----------------------------------------------------\n"); printf("| %sAccès en Lecture%s | %s%6d (%.2f%%)%s |\n", WHITE, COULEUR_BORDURE,WHITE, access_reads, pourcent_acc_reads, COULEUR_BORDURE); printf("| %sAccès en Écriture%s | %s%6d (%.2f%%)%s |\n", WHITE, COULEUR_BORDURE,WHITE, access_writes, pourcent_acc_writes, COULEUR_BORDURE); printf("----------------------------------------------------\n"); printf("| %sTotal Hits :%s | %s%6d (%.2f%%) %s|\n", MAGENTA,COULEUR_BORDURE, get_hits_color(pourcent_hits),hits, pourcent_hits, COULEUR_BORDURE); printf("|%s - Hits en Lecture %s | %s%6d (%.2f%%)%s |\n", WHITE,COULEUR_BORDURE,get_hits_color(pourcent_hits), hits_reads, pourcent_hits_reads, COULEUR_BORDURE); printf("|%s - Hits en Écriture %s | %s%6d (%.2f%%)%s |\n", WHITE,COULEUR_BORDURE,get_hits_color(pourcent_hits), hits_writes, pourcent_hits_writes, COULEUR_BORDURE); printf("----------------------------------------------------\n"); printf("| %sTotal Misses :%s | %s%6d (%.2f%%) %s|\n", MAGENTA,COULEUR_BORDURE,get_misses_color(pourcent_miss), miss, pourcent_miss, COULEUR_BORDURE); printf("|%s - Misses en Lecture%s | %s%6d (%.2f%%) %s|\n", WHITE,COULEUR_BORDURE, get_misses_color(pourcent_miss), miss_reads, pourcent_miss_reads, COULEUR_BORDURE); printf("|%s - Misses en Écriture%s | %s%6d (%.2f%%) %s|\n", WHITE,COULEUR_BORDURE, get_misses_color(pourcent_miss), miss_writes, pourcent_miss_writes, COULEUR_BORDURE); printf("----------------------------------------------------\n"); printf("|%s Accès Mémoire%s | %s%6d %s|\n", WHITE, COULEUR_BORDURE, WHITE, c->action.memory_accesses, COULEUR_BORDURE); printf("----------------------------------------------------\n"); printf("| %sTemps total d'accès au cache : %.6f secondes%s |\n", MAGENTA, c->total_access_time, COULEUR_BORDURE); printf("----------------------------------------------------%s\n", RESET); } /* * Fonction : afficher_aide * Description : Cette fonction affiche un message d'aide détaillant comment utiliser le programme * de simulation de cache. Elle fournit des informations sur les options disponibles * et la syntaxe correcte pour exécuter le programme. * * Fonctionnalités : * - Indique l'utilisation générale du programme. * - Liste les options possibles avec des descriptions pour chacune, y compris * les paramètres de taille de cache, de taille de ligne, d'associativité, * et les options de politique d'écriture. * - Fournit un exemple de ligne de commande illustrant l'utilisation des options. * * Comportement : * - Appelle `printf` pour afficher chaque ligne d'information à l'utilisateur. * - Utilise `exit(EXIT_SUCCESS)` pour quitter le programme après l'affichage de l'aide, * garantissant que le programme ne continue pas à s'exécuter après avoir fourni * les informations nécessaires. */ void afficher_aide() { printf("Usage: ./cache [options] < fichier\n"); printf("Options :\n"); printf(" -h : Affiche cette aide et quitte le programme\n"); printf(" -s : Spécifie la taille du cache en Ko\n"); printf(" -l : Spécifie la taille d'une ligne de cache en octets\n"); printf(" -a : Spécifie l'associativité de la mémoire cache (ex: 1, 2, 4, 8...)\n"); printf(" -c : Active le Write Back (par défaut, Write Through est activé)\n"); printf(" -W : Active le Write Allocate (par défaut, No Write Allocate est activé)\n"); printf("\nExemple :\n"); printf(" ./cache -s 1 -l 8 -W -c -a 1 < sample\n"); printf("\n"); exit(EXIT_SUCCESS); } //Fonction pricipale qui utilise getopt pour les options et lit les informations du fichier trace int main(int argc, char * argv[]) { if (argc < 7 || argc > 9) { afficher_aide(); } char c; unsigned int i = 0, j = 0; unsigned int taille_actuelle = TAILLE_INITIALE; // Taille actuelle du tableau d'adresses struct reference_memoire* adresses = malloc(taille_actuelle * sizeof(struct reference_memoire)); // Allocation dynamique if (adresses == NULL) { fprintf(stderr, "Erreur : Allocation de mémoire échouée.\n"); exit(EXIT_FAILURE); } int taille_c = 0, taille_l = 0, write_policy = 0, write_allocate = 0, asso = 0; // Récupération des options via getopt while ((c = getopt(argc, argv, "hs:l:Wca:")) != -1) { switch (c) { case 's': taille_c = atoi(optarg); break; case 'h': afficher_aide(); break; case 'l': taille_l = atoi(optarg); break; case 'W': write_allocate = 1; break; case 'c': write_policy = 1; break; case 'a': asso = atoi(optarg); break; default: fprintf(stderr, "Usage: [-s valeur] [-l valeur] [-W] [-c] [-a valeur] < fichier\n"); free(adresses); // Libérer la mémoire allouée avant de quitter exit(EXIT_FAILURE); } } // Vérifier s'il n'y a pas de fichier donné en entrée standard if (optind >= argc && isatty(fileno(stdin))) { fprintf(stderr, "Erreur : Aucun fichier donné en entrée.\n"); free(adresses); // Libérer la mémoire allouée exit(EXIT_FAILURE); } // Lecture des adresses depuis l'entrée standard while (scanf("%d %x", &adresses[i].adresse_type, &adresses[i].adresse) == 2) { i++; // Si on atteint la taille limite actuelle du tableau, on la double avec realloc if (i >= taille_actuelle) { taille_actuelle += INCREMENT_TAILLE; adresses = realloc(adresses, taille_actuelle * sizeof(struct reference_memoire)); if (adresses == NULL) { fprintf(stderr, "Erreur : Reallocation de mémoire échouée.\n"); exit(EXIT_FAILURE); } } } // Vérifier si aucune adresse n'a été lue (fichier vide) if (i == 0) { fprintf(stderr, "Erreur : Fichier vide ou données d'entrée incorrectes.\n"); free(adresses); // Libérer la mémoire allouée exit(EXIT_FAILURE); } // Créer la structure du cache et exécuter le calcul des hits/miss struct cache* ch; ch = creer_struct_cache(taille_c, taille_l, write_policy, write_allocate, asso); for (j = 0; j < i; j++) { calcul_hits_miss(adresses[j], ch); } // Afficher les statistiques de hits et miss affiche_hits_miss(ch); // Libération de la mémoire allouée dynamiquement free(adresses); free(ch->sets); // Libérer la mémoire allouée pour les sets et lignes free(ch); return 0; }