From 5a30fed6968dbf8570d89069795f8787294f06ae Mon Sep 17 00:00:00 2001 From: yangminz Date: Mon, 24 May 2021 22:05:55 +0800 Subject: [PATCH] mesi for video --- src/hardware/cpu/mesi.c | 456 ++++++++++++++++++++-------------------- 1 file changed, 231 insertions(+), 225 deletions(-) diff --git a/src/hardware/cpu/mesi.c b/src/hardware/cpu/mesi.c index a36b447..e6b5781 100644 --- a/src/hardware/cpu/mesi.c +++ b/src/hardware/cpu/mesi.c @@ -1,205 +1,212 @@ #include #include -#include #include +#include -// states of MESI typedef enum { - // exclusive modified: the only dirty copy - MODIFIED, - // exclusive clean: the only copy - EXCLUSIVE, - // shared clean: multiple processors may hold this exact same copy - SHARED, - // invalid copy of the physical address - // the cache line may hold data of other physical address - INVALID -} cachestate_t; - -// struct of a MESI cache line -typedef struct + MODIFIED, // exclusive dirty, global 1 + EXCLUSIVE, // exclusive clean, global 1 + SHARED, // shared clean, global >= 2 + INVALID, // invalid +} state_t; + +typedef struct { - cachestate_t state; - int value; -} cacheline_t; + state_t state; + int value; +} line_t; -#ifndef NUM_PROCESSOR -#define NUM_PROCESSOR (128) -#endif +#define NUM_PROCESSOR (1000) -cacheline_t cache[NUM_PROCESSOR]; +line_t cache[NUM_PROCESSOR]; -// the value on mem shared cache int mem_value = 15213; -// count the number of each state: MESI -int state_count[4]; - -int check_states() +int check_state() { - // initialize - state_count[0] = 0; // M - state_count[1] = 0; // E - state_count[2] = 0; // S - state_count[3] = 0; // I + int m_count = 0; + int e_count = 0; + int s_count = 0; + int i_count = 0; - // counting for (int i = 0; i < NUM_PROCESSOR; ++ i) { - state_count[(int)cache[i].state] += 1; + if (cache[i].state == MODIFIED) + { + m_count += 1; + } + else if (cache[i].state == EXCLUSIVE) + { + e_count += 1; + } + else if (cache[i].state == SHARED) + { + s_count += 1; + } + else if (cache[i].state == INVALID) + { + i_count += 1; + } } - /* The legal states: - +----+----+----+----+----+ - | | M | E | S | I | - +----+----+----+----+----+ - | M | X | X | X | O | - | E | X | X | X | O | - | S | X | X | O | O | - | I | O | O | O | O | - +----+----+----+----+----+ + /* + M E S I + M X X X O + E X X X O + S X X O O + I O O O O */ - if ( (state_count[MODIFIED] == 1 && state_count[INVALID] == NUM_PROCESSOR - 1) || - (state_count[EXCLUSIVE] == 1 && state_count[INVALID] == NUM_PROCESSOR - 1) || - (state_count[SHARED] + state_count[INVALID] == NUM_PROCESSOR) - ) + + #ifdef DEBUG + printf("M %d\t E %d\t S %d\t I %d\n", m_count, e_count, s_count, i_count); + #endif + + if ((m_count == 1 && i_count == (NUM_PROCESSOR - 1)) || + (e_count == 1 && i_count == (NUM_PROCESSOR - 1)) || + (s_count >= 2 && i_count == (NUM_PROCESSOR - s_count))) { return 1; } - -#ifdef DEBUG - printf("statistics: M <%d> E <%d> S <%d> I <%d> \n", - state_count[0], state_count[1], state_count[2], state_count[3]); - exit(0); -#endif - // illegal return 0; } -// id - the index of processor -int read_cacheline(int i) +// i - the index of processor +int read_cacheline(int i, int *read_value) { if (cache[i].state == MODIFIED) { -#ifdef DEBUG - printf("[%d] read hit; value: %d\n", i, cache[i].value); -#endif + // read hit + #ifdef DEBUG + printf("[%d] read hit; dirty value %d\n", i, cache[i].value); + #endif + *read_value = cache[i].value; return 1; } else if (cache[i].state == EXCLUSIVE) { -#ifdef DEBUG - printf("[%d] read hit; value: %d\n", i, cache[i].value); -#endif + // read hit + #ifdef DEBUG + printf("[%d] read hit; exclusive clean value %d\n", i, cache[i].value); + #endif + *read_value = cache[i].value; return 1; } else if (cache[i].state == SHARED) { -#ifdef DEBUG - printf("[%d] read hit; value: %d\n", i, cache[i].value); -#endif + // read hit + #ifdef DEBUG + printf("[%d] read hit; shared clean value %d\n", i, cache[i].value); + #endif + *read_value = cache[i].value; return 1; } - else if (cache[i].state == INVALID) + else { - // need to sniff other processors' caches first -#ifdef DEBUG - printf("\tbus boardcast: [%d] read", i); -#endif + // read miss + // bus boardcast read miss for (int j = 0; j < NUM_PROCESSOR; ++ j) { - if (j != i) + if (i != j) { - // another cache - if (cache[j].state == SHARED) + if (cache[j].state == MODIFIED) { - // there exists multiple copies in the chip - // copy from here to avoid I/O to mem + // write back + // there are eaxctly 2 copies in processors + mem_value = cache[j].value; + cache[j].state = SHARED; + + // update read miss cache cache[i].state = SHARED; cache[i].value = cache[j].value; -#ifdef DEBUG - printf("[%d] read miss; [%d] supplies data %d\n", i, j, cache[j].value); -#endif + + *read_value = cache[i].value; + + #ifdef DEBUG + printf("[%d] read miss; [%d] supplies dirty value %d; write back; s_count == 2\n", i, j, cache[i].value); + #endif + return 1; } else if (cache[j].state == EXCLUSIVE) { - // there exists only one copy in the chip here - // copy from here to avoid I/O to mem + // no memory transaction cache[i].state = SHARED; cache[i].value = cache[j].value; - cache[j].state = SHARED; -#ifdef DEBUG - printf("[%d] read miss; [%d] supplies data %d\n", i, j, cache[j].value); -#endif + // there are eaxctly 2 copies in processors + cache[i].state = SHARED; + + *read_value = cache[i].value; + + #ifdef DEBUG + printf("[%d] read miss; [%d] supplies clean value %d; s_count == 2\n", i, j, cache[i].value); + #endif + return 1; } - else if (cache[j].state == MODIFIED) + else if (cache[j].state == SHARED) { - // there exists only one copy in the chip here - // but it's DIRTY! write it back to mem - // do I/O transaction over the bus to mem - // copy from here to avoid I/O to mem - - // write back the dirty data - mem_value = cache[j].value; - cache[j].state = SHARED; - - // copy from the NOW shared cache line + // >= 3 cache[i].state = SHARED; cache[i].value = cache[j].value; -#ifdef DEBUG - printf("[%d] read miss; [%d] supplies data %d; write back ** BUS WRITE **\n", i, j, cache[j].value); -#endif + + *read_value = cache[i].value; + + #ifdef DEBUG + printf("[%d] read miss; [%d] supplies clean value %d; s_count >= 3\n", i, j, cache[i].value); + #endif + return 1; } } } - // no return before means: - // all other cache lines are invalid - cache[i].state = EXCLUSIVE; // so I am the only copy among all processors - cache[i].value = mem_value; // do one I/O transaction over bus to mem -#ifdef DEBUG - printf("[%d] read miss; mem shared cache supplies data %d ** BUS READ **\n", i, mem_value); -#endif + // all others are invalid + cache[i].state = EXCLUSIVE; + cache[i].value = mem_value; + + *read_value = cache[i].value; + + #ifdef DEBUG + printf("[%d] read miss; mem supplies clean value %d; e_count == 1\n", i, cache[i].value); + #endif + return 1; } return 0; } -// i - the index of this processor -// data - the data to be written to physical address on cache -int write_cacheline(int i, int value) +int write_cacheline(int i, int write_value) { if (cache[i].state == MODIFIED) { - // write hit, directly update the dirty value - cache[i].value = value; -#ifdef DEBUG - printf("[%d] write hit; update data to %d\n", i, value); -#endif + // write hit + cache[i].value = write_value; + + #ifdef DEBUG + printf("[%d] write hit; update to value %d\n", i, cache[i].value); + #endif + return 1; } else if (cache[i].state == EXCLUSIVE) { - // in MESI, we do not need to boardcast the write when Exclusive clean cache[i].state = MODIFIED; - cache[i].value = value; -#ifdef DEBUG - printf("[%d] write hit; update data to %d; dirty this cache\n", i, value); -#endif + cache[i].value = write_value; + + #ifdef DEBUG + printf("[%d] write hit; update to value %d\n", i, cache[i].value); + #endif + return 1; } else if (cache[i].state == SHARED) { - // multiple copies among the processors - // invalid them through the boardcast on the shared bus - for (int j = 0; j < NUM_PROCESSOR; ++ j) + // boardcast write invalid + for (int j = 0; j < NUM_PROCESSOR; j ++) { if (j != i) { @@ -207,161 +214,153 @@ int write_cacheline(int i, int value) cache[j].value = 0; } } - // update this cache line + cache[i].state = MODIFIED; - cache[i].value = value; -#ifdef DEBUG - printf("[%d] write hit; boardcast writing %d\n", i, value); -#endif + cache[i].value = write_value; + + #ifdef DEBUG + printf("[%d] write hit; boardcast invalid; update to value %d\n", i, cache[i].value); + #endif + return 1; } else if (cache[i].state == INVALID) { - // check the cache line in other processors -#ifdef DEBUG - printf("\tbus boardcast: [%d] write", i); -#endif for (int j = 0; j < NUM_PROCESSOR; ++ j) { - if (j != i) + if (i != j) { if (cache[j].state == MODIFIED) { - // RWITM: read with intent to modify - cache[i].value = mem_value; - // existing only one modified copy, just invalid it - // no bus transaction here, no I/O to mem + mem_value = cache[j].value; cache[j].state = INVALID; cache[j].value = 0; - // update the current cache - // re-issue RWITM + cache[i].state = MODIFIED; - cache[i].value = value; -#ifdef DEBUG - printf("[%d] write miss; invalid the Modified [%d]\n", i, j); -#endif + cache[i].value = write_value; + + #ifdef DEBUG + printf("[%d] write miss; boardcast invalid to M; update to value %d\n", i, cache[i].value); + #endif return 1; } else if (cache[j].state == EXCLUSIVE) { - // RWITM: read with intent to modify - cache[i].value = mem_value; - // existing only one Exclusive copy, just invalid it - // no bus transaction here, no I/O to mem cache[j].state = INVALID; cache[j].value = 0; - // update the current cache + cache[i].state = MODIFIED; - cache[i].value = value; -#ifdef DEBUG - printf("[%d] write miss; invalid the Exclusive [%d]\n", i, j); -#endif + cache[i].value = write_value; + + #ifdef DEBUG + printf("[%d] write miss; boardcast invalid to E; update to value %d\n", i, cache[i].value); + #endif return 1; } else if (cache[j].state == SHARED) { - // RWITM: read with intent to modify - cache[i].value = mem_value; - // existing multiple clean copies, must invalid all of them for (int k = 0; k < NUM_PROCESSOR; ++ k) { - if (k != i) + if (i != k) { cache[k].state = INVALID; cache[k].value = 0; } - } - // update the current cache + } + cache[i].state = MODIFIED; - cache[i].value = value; -#ifdef DEBUG - printf("[%d] write miss; boardcast writing %d\n", i, value); -#endif + cache[i].value = write_value; + + #ifdef DEBUG + printf("[%d] write miss; boardcast invalid to S; update to value %d\n", i, cache[i].value); + #endif return 1; } } } - // all other processors are not holding any copy - // why protocol needs to load from mem in this case? + // all other are invalid + // write allcoate cache[i].value = mem_value; - // then update to modified with value cache[i].state = MODIFIED; - cache[i].value = value; -#ifdef DEBUG - printf("[%d] write miss; no copies in chip; update value in place %d\n", i, value); -#endif + cache[i].value = write_value; + + #ifdef DEBUG + printf("[%d] write miss; all invalid; update to value %d\n", i, cache[i].value); + #endif + return 1; } return 0; } -// i - the index of current processor int evict_cacheline(int i) { if (cache[i].state == MODIFIED) { - // write back to mem and transit to invalid + // write back mem_value = cache[i].value; - - // invalid this cache line since the physical address is no longer in the cache cache[i].state = INVALID; cache[i].value = 0; -#ifdef DEBUG - printf("[%d] evict; write back value %d ** BUS WRITE **\n", i, mem_value); -#endif + + #ifdef DEBUG + printf("[%d] evict; write back value %d\n", i, cache[i].value); + #endif + return 1; } - else if (cache[i].state != INVALID) + else if (cache[i].state == EXCLUSIVE || cache[i].state == SHARED) { - // we do not consider invalid evict since the paddr is already detached - // for other states: invalid, exclusive, shared - // they are all clean, so no bus I/O transaction cache[i].state = INVALID; cache[i].value = 0; -#ifdef DEBUG - printf("[%d] evict in place\n", i); -#endif + + #ifdef DEBUG + printf("[%d] evict\n", i); + #endif + return 1; } - + return 0; } -#ifdef DEBUG -void print_cache() + +void print_cacheline() { for (int i = 0; i < NUM_PROCESSOR; ++ i) { char c; + switch (cache[i].state) { - case MODIFIED: - c = 'M'; - break; - case EXCLUSIVE: - c = 'E'; - break; - case SHARED: - c = 'S'; - break; - case INVALID: - c = 'I'; - break; - default: - break; + case MODIFIED: + c = 'M'; + break; + case EXCLUSIVE: + c = 'E'; + break; + case SHARED: + c = 'S'; + break; + case INVALID: + c = 'I'; + break; + default: + c = '?'; } - - printf("\t[%4d] state %c value %d\n", i, c, cache[i].value); + + printf("\t[%d] state %c value %d\n", i, c, cache[i].value); } - printf(" mem Shared cache copy: %d\n", mem_value); + printf("\t mem value %d\n", mem_value); } -#endif int main() { - srand(12345); + srand(123456); + + + int read_value; for (int i = 0; i < NUM_PROCESSOR; ++ i) { @@ -369,43 +368,50 @@ int main() cache[i].value = 0; } -#ifdef DEBUG - print_cache(); -#endif + #ifdef DEBUG + print_cacheline(); + #endif - int pc = 0; - for (int i = 0; i < 200000; ++ i) + for (int i = 0; i < 10000; ++ i) { - int op_case = rand() % 3; - int index_proc = rand() % NUM_PROCESSOR; + int core_index = rand() % NUM_PROCESSOR; + int op = rand() % 3; + + int do_print = 0; - if (op_case == 0) + if (op == 0) { - pc = read_cacheline(index_proc); + // printf("read [%d]\n", core_index); + do_print = read_cacheline(core_index, &read_value); } - else if (op_case == 1) + else if (op == 1) { - pc = write_cacheline(index_proc, rand()); + // printf("write [%d]\n", core_index); + do_print = write_cacheline(core_index, rand() % 1000); } - else if (op_case == 2) + else if (op == 2) { - pc = evict_cacheline(index_proc); + // printf("evict [%d]\n", core_index); + do_print = evict_cacheline(core_index); } - -#ifdef DEBUG - if (pc == 1) + + #ifdef DEBUG + if (do_print) { - print_cache(); + print_cacheline(); } -#endif + #endif - if (check_states() == 0) + if (check_state() == 0) { - printf("Failed\n"); + printf("failed\n"); + return 0; } } - printf("Pass\n"); + printf("pass\n"); + return 0; -} + +} \ No newline at end of file