2024-10-10 19:35:10 +02:00

718 lines
32 KiB
C

/*
* Fichier : cache_fifo.c
* Auteur : Moncef STITI
* Date : 06/11/2024
* Version : 1.0
* Description : Ce programme permet de simuler une mémoire cache avec comme algorithme d'éviction d'une ligne "FIFO" (First In First Out).
* Compilation : Pour compiler le programme, on utilise la commande suivante : gcc cache_fifo.c -o cache_simulator -lm -Wall ou make
*/
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <time.h>
#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 fifo_position; // Position dans la file FIFO
};
// 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.
unsigned int fifo_head; // Tête de la file (indique la ligne à remplacer en premier).
unsigned int fifo_tail; // Queue de la file (indique la prochaine position à remplir).
};
// 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;i<nombre_sets;i++)
{
val_ret->sets[i].lines = (struct line *)malloc(associativite*sizeof(struct line));
for(j=0;j<associativite;j++)
{
val_ret->sets[i].lines[j].valid=0;
val_ret->sets[i].lines[j].dirty=0;
val_ret->sets[i].lines[j].tag=0;
}
val_ret->sets[i].fifo_head = 0; // Initialisation de la tête FIFO
val_ret->sets[i].fifo_tail = 0; // Initialisation de la queue FIFO
}
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_fifo
* Description : Cette fonction gère la politique de remplacement FIFO.
*
* 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 mémoire cache où se
* trouve la ligne à mettre à jour.
* - struct cache* c : Pointeur vers la structure de la mémoire cache qui contient
* les informations sur les lignes et les valeurs FIFO
* de chaque ensemble.
*
* Retour :
* - La fonction ne retourne aucune valeur. Elle modifie les
* informations FIFO directement dans la structure de cache.
*/
void maj_info_fifo(unsigned int tag, unsigned int index, struct cache* c)
{
struct set *current_set = &c->sets[index];
unsigned int pos_remplacement = current_set->fifo_head; // La position de remplacement est celle de la tête FIFO
// Remplacer l'ancienne ligne par la nouvelle
current_set->lines[pos_remplacement].tag = tag;
current_set->lines[pos_remplacement].valid = 1; // Ligne maintenant valide
current_set->lines[pos_remplacement].dirty = 0; // Par défaut, pas encore "salie"
// Mise à jour de la position FIFO (circular buffer)
current_set->fifo_head = (current_set->fifo_head + 1) % c->associativite; // Avancer la tête
}
/*
* 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) {
unsigned int index = recuperer_index(m.adresse, c);
unsigned int tag = recuperer_tag(m.adresse, c);
struct set* current_set = &c->sets[index];
clock_t start_time = clock(); // Démarrer le chronomètre
// Logique pour les accès en lecture
if (m.adresse_type == 0) { // 0 pour lecture
c->action.data_read_reference_count++;
// Vérification des hits
for (unsigned int i = 0; i < c->associativite; i++) {
if (current_set->lines[i].valid && current_set->lines[i].tag == tag) {
// Hit
c->action.data_read_hit_count++;
// 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
return;
}
}
// Miss
c->action.data_read_miss_count++;
// Incrémenter le compteur total d'accès
c->action.memory_accesses++;
// Gérer l'insertion dans la cache (FIFO)
if (current_set->fifo_tail < c->associativite) {
// Insérer dans une ligne vide
current_set->lines[current_set->fifo_tail].valid = 1;
current_set->lines[current_set->fifo_tail].tag = tag;
current_set->fifo_tail++;
} else {
// Remplacer selon FIFO
maj_info_fifo(tag, index, c); // Appel à la fonction de mise à jour
}
}
// Logique pour les écritures
else if (m.adresse_type == 1) { // 1 pour écriture
c->action.data_write_reference_count++;
// Vérification des hits
for (unsigned int i = 0; i < c->associativite; i++) {
if (current_set->lines[i].valid && current_set->lines[i].tag == tag) {
// Hit
c->action.data_write_hit_count++;
current_set->lines[i].dirty = 1; // Marquer comme "dirty"
// 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
return;
}
}
// Miss
c->action.data_write_miss_count++;
// Incrémenter le compteur total d'accès
c->action.memory_accesses++;
// Gérer l'insertion dans la cache (FIFO)
if (current_set->fifo_tail < c->associativite) {
// Insérer dans une ligne vide
current_set->lines[current_set->fifo_tail].valid = 1;
current_set->lines[current_set->fifo_tail].tag = tag;
current_set->lines[current_set->fifo_tail].dirty = 1; // Marquer comme "dirty"
current_set->fifo_tail++;
} else {
// Remplacer selon FIFO
maj_info_fifo(tag, index, c); // Appel à la fonction de mise à jour
}
}
// Logique pour les instructions
else if (m.adresse_type == 2) { // 2 pour instruction
c->action.instruction_reference_count++;
// Vérification des hits
for (unsigned int i = 0; i < c->associativite; i++) {
if (current_set->lines[i].valid && current_set->lines[i].tag == tag) {
// Hit
c->action.instruction_hit_count++;
// 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
return;
}
}
// Miss
c->action.instruction_miss_count++;
// Incrémenter le compteur total d'accès
c->action.memory_accesses++;
// Gérer l'insertion dans la cache (FIFO)
if (current_set->fifo_tail < c->associativite) {
// Insérer dans une ligne vide
current_set->lines[current_set->fifo_tail].valid = 1;
current_set->lines[current_set->fifo_tail].tag = tag;
current_set->fifo_tail++;
} else {
// Remplacer selon FIFO
maj_info_fifo(tag, index, c); // Appel à la fonction de mise à jour
}
}
// Fin de la mesure de temps pour les cas de misses
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
}
/*
* 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 <taille_cache> : Spécifie la taille du cache en Ko\n");
printf(" -l <taille_ligne> : Spécifie la taille d'une ligne de cache en octets\n");
printf(" -a <associativite>: 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;
}