#ifndef CACHE_ONELEVEL_H #define CACHE_ONELEVEL_H #include #include #include #include #include "../file/sort_file_dynamic.h" #include "type_req_cache.h" #include "tag.h" #include "address.h" #include "access_port.h" #include "write_buffer.h" namespace hierarchy_memory { namespace cache { namespace cache_multilevel { namespace cache_onelevel { class param_cache_t { public : const char * name ; public : uint32_t nb_line ; public : uint32_t size_line ; public : uint32_t size_word ; public : uint32_t associativity; public : uint32_t hit_latence ; public : uint32_t miss_penality; public : param_cache_t () {}; public : param_cache_t (const char * name , uint32_t nb_line , uint32_t size_line , uint32_t size_word , uint32_t associativity, uint32_t hit_latence , uint32_t miss_penality ) : name (name) { //this->name = name ; this->nb_line = nb_line ; this->size_line = size_line ; this->size_word = size_word ; this->associativity = associativity; this->hit_latence = hit_latence ; this->miss_penality = miss_penality; } friend ostream& operator<< (ostream& output_stream, const param_cache_t & x) { output_stream << "<" << x.name << "> " << x.nb_line << " " << x.size_line << " " << x.size_word << " " << x.associativity << " " << x.hit_latence << " " << x.miss_penality; return output_stream; } };//end param_cache_t class param_t { public : uint32_t nb_port ; public : param_cache_t param_cache; public : param_t (uint32_t nb_port , param_cache_t param_cache) { this->nb_port = nb_port ; this->param_cache = param_cache ; } friend ostream& operator<< (ostream& output_stream, const param_t & x) { output_stream << x.nb_port << " " << x.param_cache; return output_stream; } };//end param_t class Cache_OneLevel { protected : const uint32_t nb_port ; protected : const uint32_t nb_line ; protected : const uint32_t size_line ; protected : const uint32_t size_word ; protected : const uint32_t associativity ; protected : const uint32_t hit_latence ; protected : const uint32_t miss_penality ; private : char * name; private : tag_t ** tag; private : access_port_t * access_port; private : address_t size_address; private : Sort_File_Dynamic * write_buffer; //*****[ constructor ]***** public : Cache_OneLevel (param_t param): nb_port (param.nb_port ), nb_line (param.param_cache.nb_line ), size_line (param.param_cache.size_line ), size_word (param.param_cache.size_word ), associativity (param.param_cache.associativity), hit_latence (param.param_cache.hit_latence ), miss_penality (param.param_cache.miss_penality) { uint32_t size_name = strlen(param.param_cache.name)+1; name = new char [size_name]; strncpy(name,param.param_cache.name,size_name); if ((nb_line * size_line * associativity) == 0) { cerr << "<" << name << ".{Cache_OneLevel}> all parameter must be greater that 0" << endl; cerr << " nb_line : " << nb_line << endl; cerr << " size_line : " << size_line << endl; cerr << " associativity : " << associativity << endl; exit(1); } if ((nb_line % associativity) != 0) { cerr << "<" << name << ".{Cache_OneLevel}> nb_line must be a mutiple of associativity" << endl; cerr << " * nb_line : " << nb_line << endl; cerr << " * associativity : " << associativity << endl; exit(1); } if ((double)nb_line != ::pow(2,::log2(nb_line))) { cerr << "<" << name << ".{Cache_OneLevel}> nb_line must be a mutiple of 2^n" << endl; cerr << " * nb_line : " << nb_line << endl; exit(1); } if ((double)size_line != ::pow(2,::log2(size_line))) { cerr << "<" << name << ".{Cache_OneLevel}> size_line must be a mutiple of 2^n" << endl; cerr << " * size_line : " << size_line << endl; exit(1); } if ((double)size_word != ::pow(2,::log2(size_word))) { cerr << "<" << name << ".{Cache_OneLevel}> size_word must be a mutiple of 2^n" << endl; cerr << " * size_word : " << size_word << endl; exit(1); } write_buffer= new Sort_File_Dynamic (sort_file::param_t("write_buffer",5)); access_port = new access_port_t [nb_port]; tag = new tag_t * [nb_line/associativity]; for (uint32_t it = 0; it < nb_line/associativity; it ++) tag [it] = new tag_t [associativity]; size_address.offset = (uint32_t) log2(size_line * size_word); size_address.familly = (uint32_t) log2(nb_line/associativity); size_address.tag = 32 - size_address.familly - size_address.offset; } //*****[ destructor ]***** public : ~Cache_OneLevel () { // delete tag; // delete access_port; }; //*****[ reset ]***** public : void reset () { for (uint32_t x = 0; x < nb_port; x ++) access_port[x].valid = false; for (uint32_t x = 0; x < nb_line/associativity; x ++) for (uint32_t y = 0; y < associativity; y ++) { tag[x][y].valid = false; tag[x][y].index_lru = y; } write_buffer->reset(); } //*****[ transition ]***** public : void transition () { // scan all port - test if have a transaction for (int32_t x = nb_port-1; x >= 0; x --) { if (access_port[x].valid == true) { access_port[x].valid = false; // Update LRU // * hit : now // * miss : after the return of next cache if (access_port[x].hit == HIT_CACHE) {// Hit update_lru(access_port[x].address.familly,access_port[x].num_associativity); } if (access_port[x].hit == MISS) {// Miss // Write in write_buffer write_buffer->push(access_port[x].latence,write_buffer_t(access_port[x].address,access_port[x].trdid)); } } } // Test if a write_buffer have the result while ((write_buffer->empty() == false) && (write_buffer->read(0).delay == 0)) { // Save in the cache write_buffer_t val = write_buffer->pop(); uint32_t num_tag = val.address.tag; uint32_t num_familly = val.address.familly; uint32_t num_associativity = index_victim(num_familly); tag [num_familly][num_associativity].valid = true; tag [num_familly][num_associativity].address = num_tag; tag [num_familly][num_associativity].trdid = val.trdid; update_lru(num_familly,num_associativity); } write_buffer->transition(); } //*****[ update_lru ]***** private : void update_lru (uint32_t familly, uint32_t num_associativity) { uint32_t current_lru = tag [familly][num_associativity].index_lru; for (uint32_t k = 0; k < associativity; k ++) if (tag [familly][k].index_lru < current_lru) tag [familly][k].index_lru ++; tag [familly][num_associativity].index_lru = 0; } //*****[ index_victim ]***** // return the index of the nex victim public : uint32_t index_victim (uint32_t familly) { uint32_t victim = 0; while (tag [familly][victim].index_lru != (associativity-1)) { victim ++; } return victim; } //*****[ translate_address ]***** private : address_t translate_address (uint32_t address) { address_t address_translated; uint32_t shift; address_translated.offset = (address & ((uint32_t)-1 >> (32-(size_address.offset )))); address -= address_translated.offset; shift = size_address.offset; address_translated.familly = (address & ((uint32_t)-1 >> (32-(size_address.familly + shift))))>>shift; address -= address_translated.familly; shift += size_address.familly; address_translated.tag = (address & ((uint32_t)-1 >> (32-(size_address.tag + shift))))>>shift; return address_translated; } //******************** //******************** //*****[ access ]***** // Return hit (true) // uncache is to stocke the address on the cache public : type_rsp_cache_t access (uint32_t nb_port, uint32_t address, uint32_t trdid, type_req_cache_t type, direction_req_cache_t dir) { switch (type) { case UNCACHED : return access_uncached (nb_port,address,trdid ); break; case INVALIDATE : return access_invalidate (nb_port,address,trdid ); break; case FLUSH : return access_flush (nb_port,address,trdid ); break; case PREFETCH : // no difference with the simple read cached (have no add a prefetch cache) case CACHED : return access_cached (nb_port,address,trdid,dir); break; default : cout << " unkonow type : " << endl; exit(1); break; } } // *****[ access_cached ]***** public : type_rsp_cache_t access_cached (uint32_t nb_port, uint32_t address, uint32_t trdid, direction_req_cache_t dir) { address_t address_translate = translate_address(address); uint32_t num_associativity = hit_cache (trdid, address_translate); uint32_t num_port = hit_access_port (trdid, address_translate); uint32_t num_write_buffer = hit_write_buffer (trdid, address_translate); if (num_port == this->nb_port) num_port = nb_port; uint32_t latence ; type_rsp_cache_t res = MISS; bool is_in_cache = (num_associativity != associativity); bool is_in_access_port = (num_port != nb_port); bool is_in_write_buffer = false; if (is_in_access_port == true) { res = HIT_BYPASS; latence = access_port[num_port].latence; //already compute } else if (is_in_cache == true) { res = HIT_CACHE; latence = 0; // Hit !!! } else { // Search in the write buffer, and test if have a miss if ( num_write_buffer == write_buffer->nb_slot_use()) { res = MISS; latence = miss_penality; // miss -> access at down of cache, + respons at the up of cache } else { res = HIT_WRITE_BUFFER; is_in_write_buffer = true; latence = write_buffer->read(num_write_buffer).delay; } } // access_port valid = there are a new request to update // -> no previous request in the same cycle (hit in a access port) // -> no previous request in the previous cycle (hit in the write buffer) access_port[nb_port].valid = ((is_in_access_port || is_in_write_buffer) == false); access_port[nb_port].address = address_translate; access_port[nb_port].trdid = trdid; access_port[nb_port].hit = res; access_port[nb_port].num_associativity = num_associativity; access_port[nb_port].latence = latence; return res; } // *****[ access_uncached ]***** public : type_rsp_cache_t access_uncached (uint32_t nb_port, uint32_t address, uint32_t trdid) { access_port[nb_port].valid = false; access_port[nb_port].trdid = trdid; access_port[nb_port].hit = MISS; access_port[nb_port].latence = miss_penality; return MISS; } // *****[ access_invalidate ]***** public : type_rsp_cache_t access_invalidate (uint32_t nb_port, uint32_t address, uint32_t trdid) { cerr << "<" << name << ".{Cache_OneLevel}.access_invalidate> Not implemented" << endl; exit (0); return MISS; } // *****[ access_flush ]***** public : type_rsp_cache_t access_flush (uint32_t nb_port, uint32_t address, uint32_t trdid) { cerr << "<" << name << ".{Cache_OneLevel}.access_flush> Not implemented" << endl; exit (0); return MISS; } //****************************** //****************************** //*****[ hit_write_buffer ]***** // If a instruction is in the write_buffer, return the position // else, return the number of elt in the write_buffer public : uint32_t hit_write_buffer (uint32_t trdid, address_t address) { uint32_t num_write_buffer = 0; // Scan the write_buffer for (num_write_buffer = 0; num_write_buffer < write_buffer->nb_slot_use(); num_write_buffer ++) { slot_t val = write_buffer->read(num_write_buffer); if ( (val.data.trdid == trdid ) && (val.data.address.tag == address.tag ) && (val.data.address.familly == address.familly)) break; } return num_write_buffer; } //*****[ hit_access_port ]***** // return the number of associativity if hit // return nb_accosiativity if miss public : uint32_t hit_cache (uint32_t trdid, address_t address) { uint32_t num_associativity; for (num_associativity = 0; num_associativity < associativity; num_associativity ++) // Hit if : // * in the line // * in a way associative // -> there are the same tag // the same trdid // is valid if ( (tag [address.familly][num_associativity].address == address.tag) && (tag [address.familly][num_associativity].valid == true ) && (tag [address.familly][num_associativity].trdid == trdid ) ) break; return num_associativity; } //*****[ hit_access_port ]***** // Return the number of port if hit // Return nb_port if miss public : uint32_t hit_access_port (uint32_t trdid, address_t address) { uint32_t it_num_port = 0; // scan all port - test if have a transaction for (it_num_port = 0; it_num_port < nb_port; it_num_port ++) if ( (access_port[it_num_port].valid == true ) && (access_port[it_num_port].trdid == trdid ) && (access_port[it_num_port].address.tag == address.tag ) && (access_port[it_num_port].address.familly == address.familly)) break; return it_num_port; } //*****[ need_slot ]***** public : uint32_t need_slot () { uint32_t res = 0; // scan all port - test if have a transaction for (uint32_t x = 0; x < nb_port; x ++) if (access_port[x].valid == true) res ++; return res; } //*****[ latence ]***** // Return the time to have the data // uncache is to stocke the address on the cache public : uint32_t latence (uint32_t num_port) { uint32_t res = hit_latence + access_port[num_port].latence; return res; } //*****[ update_latence ]***** // Return the time to have the data // uncache is to stocke the address on the cache public : bool update_latence (uint32_t nb_port, uint32_t latence) { access_port[nb_port].latence = latence-hit_latence; return access_port[nb_port].valid; } //*****[ information ]***** public : void information (void) { cout << "<" << name << ">" << endl << " * Information" << endl << " * Capacity : " << nb_line * size_line * size_word << " bytes" << endl << " * Nb line : " << nb_line << endl << " * Size line : " << size_line << endl << " * Size word : " << size_word << endl << " * Associativity : " << associativity << endl << " * Timing : " << endl << " * Hit latence : " << hit_latence << endl << " * Miss penality : " << miss_penality << endl << " * Nb port : " << nb_port << endl << endl; } friend ostream& operator<< (ostream& output_stream, const Cache_OneLevel & x) { output_stream << "<" << x.name << ">" << endl << " * Capacity : " << x.nb_line * x.size_line * x.size_word << " bytes" << endl << " * Nb line : " << x.nb_line << endl << " * Size line : " << x.size_line << endl << " * Size word : " << x.size_word << endl << " * Associativity : " << x.associativity << endl << " * Timing : " << endl << " * Hit latence : " << x.hit_latence << endl << " * Miss penality : " << x.miss_penality << endl << " * Nb port : " << x.nb_port << endl << endl; output_stream << " * Tag" << endl; for (uint32_t i = 0; i < x.nb_line / x.associativity; i ++) { for (uint32_t j = 0; j < x.associativity; j ++) output_stream << x.tag [i][j] << " | "; output_stream << endl; } output_stream << endl; output_stream << *x.write_buffer << endl; return output_stream; } }; };};};}; #endif //!CACHE_ONELEVEL_H