/* -*- c++ -*- * * SOCLIB_LGPL_HEADER_BEGIN * * This file is part of SoCLib, GNU LGPLv2.1. * * SoCLib is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 of the License. * * SoCLib is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with SoCLib; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * SOCLIB_LGPL_HEADER_END * * Copyright (c) UPMC, Lip6 * Alain Greiner July 2008 * * Maintainers: alain yang */ ////////////////////////////////////////////////////////////////////////////////// // This object is a generic, set associative, cache providing read(), write(), // inval() and update() access primitives. The replacement policy is pseudo-LRU. // The DATA part is implemented as an int32_t array. // The DIRECTORY part is implemented as an uint32_t array. // // Constructor parameters are : // - std::string &name // - size_t nways : number of associativity levels // - size_t nsets : number of sets // - size_t nwords : number of words in a cache line // The nways, nsets, nwords parameters must be power of 2 // The nsets parameter cannot be larger than 1024 // The nways parameter cannot be larger than 16 // The nwords parameter cannot be larger than 64 // // Template parameter is : // - addr_t : address format to access the cache // The address can be larger than 32 bits, but the TAG field // cannot be larger than 32 bits. ///////////////////////////////////////////////////////////////////////////////// // History : // 05/01/2010 // Two new methods select_before_update(), and update_after_select() have // been introduced to support update in two cycles, when the cache directory // is implemented as a simple port RAM. ///////////////////////////////////////////////////////////////////////////////// #ifndef SOCLIB_GENERIC_CACHE_H #define SOCLIB_GENERIC_CACHE_H #include #include #include "arithmetics.h" #include "static_assert.h" #include "mapping_table.h" #include namespace soclib { ////////////////////////// template class GenericCache ////////////////////////// { typedef uint32_t data_t; typedef uint32_t tag_t; data_t *r_data ; tag_t *r_tag ; bool *r_val ; bool *r_lru ; size_t m_ways; size_t m_sets; size_t m_words; size_t m_next_way; const soclib::common::AddressMaskingTable m_x ; const soclib::common::AddressMaskingTable m_y ; const soclib::common::AddressMaskingTable m_z ; ////////////////////////////////////////////////////////////// inline data_t &cache_data(size_t way, size_t set, size_t word) { return r_data[(way*m_sets*m_words)+(set*m_words)+word]; } ////////////////////////////////////////////// inline tag_t &cache_tag(size_t way, size_t set) { return r_tag[(way*m_sets)+set]; } ////////////////////////////////////////////// inline bool &cache_val(size_t way, size_t set) { return r_val[(way*m_sets)+set]; } ////////////////////////////////////////////// inline bool &cache_lru(size_t way, size_t set) { return r_lru[(way*m_sets)+set]; } ////////////////////////////////////////////// inline void cache_set_lru(size_t way, size_t set) { size_t way2; cache_lru(way, set) = true; for (way2 = 0; way2 < m_ways; way2++ ) { if (cache_lru(way2, set) == false) return; } /* all lines are new -> they all become old */ for (way2 = 0; way2 < m_ways; way2++ ) { cache_lru(way2, set) = false; } } public: ////////////////////////////////////////////// GenericCache( const std::string &name, size_t nways, size_t nsets, size_t nwords) : m_ways(nways), m_sets(nsets), m_words(nwords), #define l2 soclib::common::uint32_log2 m_x( l2(nwords), l2(sizeof(data_t))), m_y( l2(nsets), l2(nwords) + l2(sizeof(data_t))), m_z( 8*sizeof(addr_t) - l2(nsets) - l2(nwords) - l2(sizeof(data_t)), l2(nsets) + l2(nwords) + l2(sizeof(data_t))) #undef l2 { assert(IS_POW_OF_2(nways)); assert(IS_POW_OF_2(nsets)); assert(IS_POW_OF_2(nwords)); assert(nwords); assert(nsets); assert(nways); assert(nwords <= 64); assert(nsets <= 1024); assert(nways <= 16); #ifdef GENERIC_CACHE_DEBUG std::cout << " m_x: " << m_x << " m_y: " << m_y << " m_z: " << m_z << std::endl; #endif r_data = new data_t[nways*nsets*nwords]; r_tag = new tag_t[nways*nsets]; r_val = new bool[nways*nsets]; r_lru = new bool[nways*nsets]; m_next_way = 0; } //////////////// ~GenericCache() { delete [] r_data; delete [] r_tag; delete [] r_val; delete [] r_lru; } //////////////////// inline void reset( ) { std::memset(r_data, 0, sizeof(*r_data)*m_ways*m_sets*m_words); std::memset(r_tag, 0, sizeof(*r_tag)*m_ways*m_sets); std::memset(r_val, 0, sizeof(*r_val)*m_ways*m_sets); std::memset(r_lru, 0, sizeof(*r_lru)*m_ways*m_sets); } //////////////////////////////////////////////////////// inline bool flush(size_t way, size_t set, addr_t* nline) { if ( cache_val(way,set) ) { cache_val(way,set) = false; *nline = (data_t)cache_tag(way,set)* m_sets + set; return true; } return false; } ///////////////////////////////////////// inline bool read( addr_t ad, data_t* dt) { const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; #ifdef GENERIC_CACHE_DEBUG std::cout << "Reading data at " << ad << ", " << " s/t=" << set << '/' << tag << ", "; #endif for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { *dt = cache_data(way, set, word); cache_set_lru(way, set); #ifdef GENERIC_CACHE_DEBUG std::cout << "hit" << " w/s/t=" << way << '/' << set << '/' << tag << " = " << *dt << std::endl; #endif return true; } } #ifdef GENERIC_CACHE_DEBUG std::cout << "miss" << std::endl; #endif return false; } ///////////////////////////////////////////////////////////////////// inline bool read( addr_t ad, data_t* dt, size_t* n_way, size_t* n_set) { const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; #ifdef GENERIC_CACHE_DEBUG std::cout << "Reading data at " << ad << ", " << " s/t=" << set << '/' << tag << ", "; #endif for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { *dt = cache_data(way, set, word); cache_set_lru(way, set); *n_way = way; *n_set = set; #ifdef GENERIC_CACHE_DEBUG std::cout << "hit" << " w/s/t=" << way << '/' << set << '/' << tag << " = " << *dt << std::endl; #endif return true; } } #ifdef GENERIC_CACHE_DEBUG std::cout << "miss" << std::endl; #endif return false; } ////////////////////////////////////////////////////// inline bool setinbit( addr_t ad, bool* buf, bool val ) { const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { buf[m_sets*way+set] = val; #ifdef GENERIC_CACHE_DEBUG std::cout << "hit" << " w/s=" << way << '/' << set << std::endl; #endif return true; } } #ifdef GENERIC_CACHE_DEBUG std::cout << "miss" << std::endl; #endif return false; } //////////////////////////////////////////// inline tag_t get_tag(size_t way, size_t set) { return cache_tag(way, set); } ///////////////////////////////////////// inline bool write( addr_t ad, data_t dt ) { const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; #ifdef GENERIC_CACHE_DEBUG std::cout << "Writing data at " << ad << ", " << " s/t=" << set << '/' << tag << ", "; #endif for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { cache_data(way, set, word) = dt; cache_set_lru(way, set); #ifdef GENERIC_CACHE_DEBUG std::cout << "hit" << " w/s/t=" << way << '/' << set << '/' << tag << std::endl; #endif return true; } } #ifdef GENERIC_CACHE_DEBUG std::cout << "miss" << std::endl; #endif return false; } ///////////////////////////////////////////////////////////////////// inline bool write( addr_t ad, data_t dt, size_t* nway, size_t* nset ) { const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; #ifdef GENERIC_CACHE_DEBUG std::cout << "Writing data at " << ad << ", " << " s/t=" << set << '/' << tag << ", "; #endif for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { cache_data(way, set, word) = dt; cache_set_lru(way, set); *nway = way; *nset = set; #ifdef GENERIC_CACHE_DEBUG std::cout << "hit" << " w/s/t=" << way << '/' << set << '/' << tag << std::endl; #endif return true; } } #ifdef GENERIC_CACHE_DEBUG std::cout << "miss" << std::endl; #endif return false; } inline void write(size_t way, size_t set, size_t word, data_t data) { cache_data(way, set, word) = data; } ////////////////////////////// inline bool inval( addr_t ad ) { bool hit = false; const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; #ifdef GENERIC_CACHE_DEBUG std::cout << "Invalidating data at " << ad << ", " << " s/t=" << set << '/' << tag << ", "; #endif for ( size_t way = 0 ; way < m_ways && !hit ; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { hit = true; cache_val(way, set) = false; cache_lru(way, set) = false; #ifdef GENERIC_CACHE_DEBUG std::cout << "hit" << " w/s/t=" << way << '/' << set << '/' << tag << " "; #endif } } #ifdef GENERIC_CACHE_DEBUG std::cout << std::endl; #endif return hit; } //////////////////////////////////////////////////////////// inline bool inval( addr_t ad, size_t* n_way, size_t* n_set ) { bool hit = false; const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; #ifdef GENERIC_CACHE_DEBUG std::cout << "Invalidating data at " << ad << ", " << " s/t=" << set << '/' << tag << ", "; #endif for ( size_t way = 0 ; way < m_ways && !hit ; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { hit = true; cache_val(way, set) = false; cache_lru(way, set) = false; *n_way = way; *n_set = set; #ifdef GENERIC_CACHE_DEBUG std::cout << "hit" << " w/s/t=" << way << '/' << set << '/' << tag << " "; #endif } } #ifdef GENERIC_CACHE_DEBUG std::cout << std::endl; #endif return hit; } /////////////////////////////////////////////////////////////////// // This function implements a pseudo LRU policy for a one cycle // line replacement. It can be used if the directory is implemented // as a dual port RAM. // 1 - First we search an invalid way // 2 - Il all ways are valid, we search the first old way // 3 - If all ways are recent, they are all transformed to old. // and we select the way with index 0. // The victim line index is returned in the victim parameter. // This function returns true, if the victim line is valid. /////////////////////////////////////////////////////////////////// inline bool update( addr_t ad, data_t* buf, addr_t* victim ) { size_t set, way; bool cleanup = victim_select(ad, victim, &way, &set); victim_update_tag (ad, way, set); for ( size_t word = 0 ; word < m_words ; word++ ) { cache_data(way, set, word) = buf[word] ; } return cleanup; } inline bool victim_select( addr_t ad, addr_t* victim, size_t * way, size_t * set ) { bool found = false; bool cleanup = false; *set = m_y[ad]; *way = 0; // Schearch and invalid slot for ( size_t _way = 0 ; _way < m_ways && !found ; _way++ ) { if ( !cache_val(_way, *set) ) { found = true; cleanup = false; *way = _way; } } // No invalid way, scearch the lru if ( !found ) { for ( size_t _way = 0 ; _way < m_ways && !found ; _way++ ) { if ( !cache_lru(_way, *set) ) { cache_val (_way, *set) = false; found = true; cleanup = true; *way = _way; } } } assert(found && "all ways can't be new at the same time"); *victim = (addr_t)((cache_tag(*way,*set) * m_sets) + *set); return cleanup; } inline void victim_update_tag( addr_t ad, size_t way, size_t set ) { tag_t tag = m_z[ad]; cache_tag (way, set) = tag; cache_val (way, set) = true; cache_set_lru(way, set); } /* ////////////////////////////////////////////////////////////////////////////// // The two functions select_before_update() & update_after_select() // can be used to perform a line replacement in two cycles // (when the directory is implemented as a single port RAM) // // The select_before_update function implements a pseudo LRU // policy and and returns the victim line index and the selected // way in the return arguments. The selected cache line is invalidated. // This function returns true, il the victim line is valid, //////////////////////////////////////////////////////////////////////////////// inline bool select_before_update( addr_t ad, size_t* selway, addr_t* victim ) { size_t set = m_y[ad]; // search an empty slot (using valid bit) for ( size_t way = 0 ; way < m_ways ; way++ ) { if ( !cache_val(way, set) ) { *selway = way; *victim = (addr_t)((cache_tag(way, set) * m_sets) + set); return false; } } // search an old line (using lru bit) for ( size_t way = 0 ; way < m_ways ; way++ ) { if ( !cache_lru(way, set) ) { cache_val(way, set) = false; *selway = way; *victim = (addr_t)((cache_tag(way, set) * m_sets) + set); return true; } } assert("all lines can't be new at the same time"); return true; } ///////////////////////////////////////////////////////////////////// // The two functions select_before_update() & update_after_select() // can be used to perform a line replacement in two cycles // (when the directory is implemented as a single port RAM) // // The update_after select() function performs the actual // update of the cache line. ///////////////////////////////////////////////////////////////////// inline void update_after_select( data_t* buf, size_t way, addr_t ad) { tag_t tag = m_z[ad]; size_t set = m_y[ad]; cache_tag(way, set) = tag; cache_val(way, set) = true; cache_set_lru(way, set); for ( size_t word = 0 ; word < m_words ; word++ ) cache_data(way, set, word) = buf[word] ; } */ /////////////////////////// inline bool find( addr_t ad, bool* itlb_buf, bool* dtlb_buf, size_t* n_way, size_t* n_set, addr_t* victim ) { size_t set = m_y[ad]; size_t selway = 0; bool found = false; bool cleanup = false; for ( size_t way = 0 ; way < m_ways && !found ; way++ ) { if ( !cache_val(way, set) ) { found = true; cleanup = false; selway = way; } } if ( !found ) { /* No invalid way, look for an old way, in priotity order: * an old way which is not refereced by any tlb * an old way which is not referenced by the itlb * an old way which is not referenced by the dtlb * an old way */ for ( size_t way = 0 ; way < m_ways && !found ; way++ ) { if ( !cache_lru(way, set) && !itlb_buf[m_sets*way+set] && !dtlb_buf[m_sets*way+set]) { found = true; cleanup = true; selway = way; } } for ( size_t way = 0 ; way < m_ways && !found ; way++ ) { if ( !cache_lru(way, set) && !itlb_buf[m_sets*way+set]) { found = true; cleanup = true; selway = way; } } for ( size_t way = 0 ; way < m_ways && !found ; way++ ) { if ( !cache_lru(way, set) && !dtlb_buf[m_sets*way+set]) { found = true; cleanup = true; selway = way; } } for ( size_t way = 0 ; way < m_ways && !found ; way++ ) { if ( !cache_lru(way, set)) { found = true; cleanup = true; selway = way; } } } assert(found && "all ways can't be new at the same time"); *victim = (addr_t)((cache_tag(selway, set) * m_sets) + set); cache_val(selway, set) = false; *n_way = selway; *n_set = set; return cleanup; } ///////////////////////////////////////////////////////////////////////// inline void update( addr_t ad, size_t n_way, size_t n_set, data_t* buf ) { tag_t tag = m_z[ad]; cache_tag(n_way, n_set) = tag; cache_val(n_way, n_set) = true; cache_set_lru(n_way, n_set); for ( size_t word = 0 ; word < m_words ; word++ ) { cache_data(n_way, n_set, word) = buf[word] ; } } /////////////////////////////////////////////////////////////////// // cleanupcheck function is used for checking whether a line exists inline bool cleanupcheck( addr_t ad ) { const tag_t tag = m_z[ad]; const size_t set = m_y[ad]; for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && cache_val(way, set) ) { return true; } } return false; } /////////////////////////// void fileTrace(FILE* file) { for( size_t nway = 0 ; nway < m_ways ; nway++) { for( size_t nset = 0 ; nset < m_sets ; nset++) { if( cache_val(nway, nset) ) fprintf(file, " V / "); else fprintf(file, " / "); fprintf(file, "way %d / ", (int)nway); fprintf(file, "set %d / ", (int)nset); fprintf(file, "@ = %08zX / ", ((cache_tag(nway, nset)*m_sets+nset)*m_words*4)); for( size_t nword = m_words ; nword > 0 ; nword--) { unsigned int data = cache_data(nway, nset, nword-1); fprintf(file, "%08X ", data ); } fprintf(file, "\n"); } } } //////////////////////// inline void printTrace() { for ( size_t way = 0; way < m_ways ; way++ ) { for ( size_t set = 0 ; set < m_sets ; set++ ) { if ( cache_val(way,set) ) std::cout << " * " ; else std::cout << " " ; std::cout << std::dec << "way " << way << std::hex << " | " ; std::cout << "@ " << (cache_tag(way,set)*m_words*m_sets+m_words*set)*4 ; for ( size_t word = 0 ; word < m_words ; word++ ) { std::cout << " | " << cache_data(way,set,word) ; } std::cout << std::endl ; } } } }; } // namespace soclib #endif // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4