/* -*- 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 */ //////////////////////////////////////////////////////////////////////////////// // File : generic_cache.h // Date : 07/01/2012 // Authors : Alain Greiner ///////////////////////////////////////////////////////////////////////////////// // This object is a generic, set associative, cache. // Each slot can be in three states: VALID, EMPTY or ZOMBI. // The ZOMBI state is used by cache coherence protocols to indicate // a pending cleanup request. // Hit if ( (matching tag) and (state == VALID). // The replacement policy is pseudo-LRU. The victim selection process cannot // fail if the ZOMBI state is not used. // But it can fail if all ways are in ZOMBI state. ///////////////////////////////////////////////////////////////////////////////// // Implementation note // The DATA part is implemented as an uint32_t array[nways*nsets*nwords]. // The DIRECTORY part is implemented as an uint32_t array[nways*nsets]. // All methods requiring a dual port RAM or cache modification using // an associative search have been deprecated. ///////////////////////////////////////////////////////////////////////////////// // 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 ///////////////////////////////////////////////////////////////////////////////// #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 { enum cache_slot_state_e { CACHE_SLOT_STATE_INVALID, CACHE_SLOT_STATE_EXCLUSIVE, CACHE_SLOT_STATE_SHARED, CACHE_SLOT_STATE_MODIFIED, CACHE_SLOT_STATE_ZOMBI }; ////////////////////////// template class GenericCache ////////////////////////// { typedef uint32_t data_t; typedef uint32_t be_t; data_t *r_data ; addr_t *r_tag ; int *r_state; bool *r_lru ; size_t m_ways; size_t m_sets; size_t m_words; 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 addr_t &cache_tag(size_t way, size_t set) { return r_tag[(way*m_sets)+set]; } ////////////////////////////////////////////// inline bool &cache_lru(size_t way, size_t set) { return r_lru[(way*m_sets)+set]; } ////////////////////////////////////////////// inline int &cache_state(size_t way, size_t set) { return r_state[(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; } } //////////////////////////////// inline data_t be2mask( be_t be ) { data_t mask = 0; if ( (be & 0x1) == 0x1 ) mask = mask | 0x000000FF; if ( (be & 0x2) == 0x2 ) mask = mask | 0x0000FF00; if ( (be & 0x4) == 0x4 ) mask = mask | 0x00FF0000; if ( (be & 0x8) == 0x8 ) mask = mask | 0xFF000000; return mask; } 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 << "constructing " << name << std::endl << "- nways = " << nways << std::endl << "- nsets = " << nsets << std::endl << "- nwords = " << nwords << std::endl << " 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 addr_t[nways*nsets]; r_state = new int[nways*nsets]; r_lru = new bool[nways*nsets]; } //////////////// ~GenericCache() { delete [] r_data; delete [] r_tag; delete [] r_state; 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_state, CACHE_SLOT_STATE_INVALID, sizeof(*r_state)*m_ways*m_sets); std::memset(r_lru, 0, sizeof(*r_lru)*m_ways*m_sets); } inline int get_cache_state(int way, int set) { return cache_state(way,set); } ///////////////////////////////////////////////////////////////////// // Read a single 32 bits word. // returns true if (matching tag) and (state == VALID) // Both data & directory are accessed. ///////////////////////////////////////////////////////////////////// inline bool read( addr_t ad, data_t* dt) { const addr_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && ( (cache_state(way, set) == CACHE_SLOT_STATE_EXCLUSIVE) or (cache_state(way, set) == CACHE_SLOT_STATE_MODIFIED) or (cache_state(way, set) == CACHE_SLOT_STATE_SHARED)) ) { *dt = cache_data(way, set, word); cache_set_lru(way, set); return true; } } return false; } //////////////////////////////////////////////////////////////////// // Read a single 32 bits word. // returns true if (matching tag) and (state == VALID) // Both data & directory are accessed. // The selected way, set and word index are returned in case of hit. ///////////////////////////////////////////////////////////////////// inline bool read( addr_t ad, data_t* dt, size_t* selway, size_t* selset, size_t* selword) { const addr_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) and ( (cache_state(way, set) == CACHE_SLOT_STATE_EXCLUSIVE) or (cache_state(way, set) == CACHE_SLOT_STATE_MODIFIED) or (cache_state(way, set) == CACHE_SLOT_STATE_SHARED)) ) { *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); cache_set_lru(way, set); return true; } } return false; } //////////////////////////////////////////////////////////////////// // Read a single 32 bits word when the ZOMBI state is used. // Both data and directory are accessed. // returns the access status in the state argument: // - VALID : (matching tag) and (state == VALID) // - ZOMBI : (matching tag) and (state == ZOMBI) // - MISS : no matching tag or EMPTY state // If VALID or ZOMBI, the data, the way, set and word index are // returned in the other arguments. //////////////////////////////////////////////////////////////////// inline void read( addr_t ad, data_t* dt, size_t* selway, size_t* selset, size_t* selword, int* state ) { const addr_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; // default return values *state = CACHE_SLOT_STATE_INVALID; *selway = 0; *selset = 0; *selword = 0; *dt = 0; for ( size_t way = 0; way < m_ways; way++ ) { if ( tag == cache_tag(way, set) ) // matching tag { if ( cache_state(way, set) == CACHE_SLOT_STATE_EXCLUSIVE ) { *state = CACHE_SLOT_STATE_EXCLUSIVE; *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); cache_set_lru(way, set); } else if ( cache_state(way, set) == CACHE_SLOT_STATE_SHARED ) { *state = CACHE_SLOT_STATE_SHARED; *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); cache_set_lru(way, set); } else if ( cache_state(way, set) == CACHE_SLOT_STATE_MODIFIED ) { *state = CACHE_SLOT_STATE_MODIFIED; *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); cache_set_lru(way, set); } else if ( cache_state(way, set) == CACHE_SLOT_STATE_ZOMBI ) { *state = CACHE_SLOT_STATE_ZOMBI; *selway = way; *selset = set; *selword = word; } } } } //////////////////////////////////////////////////////////////////// // Read a single 32 bits word, without LRU update. // returns true if (matching tag) and (state == VALID) // Both data & directory are accessed. // The selected way, set and word index are returned in case of hit. ///////////////////////////////////////////////////////////////////// inline bool read_neutral( addr_t ad, data_t* dt, size_t* selway, size_t* selset, size_t* selword) { const addr_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && ( (cache_state(way, set) == CACHE_SLOT_STATE_EXCLUSIVE) or (cache_state(way, set) == CACHE_SLOT_STATE_MODIFIED) or (cache_state(way, set) == CACHE_SLOT_STATE_SHARED)) ) { *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); return true; } } return false; } ///////////////////////////////////////////////////////////////////////////// // Read one or two 32 bits word. // Both data & directory are accessed. // Hit if (matching tag) and (valid == true) and (zombi == false) // If the addressed word is not the last in the cache line, // two successive words are returned. // The selected way, set and first word index are returned in case of hit. // This function is used by the cc_vcache to get a 64 bits page table entry. ///////////////////////////////////////////////////////////////////////////// inline bool read( addr_t ad, data_t* dt, data_t* dt_next, size_t* selway, size_t* selset, size_t* selword) { const addr_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && ( (cache_state(way, set) == CACHE_SLOT_STATE_EXCLUSIVE) or (cache_state(way, set) == CACHE_SLOT_STATE_MODIFIED) or (cache_state(way, set) == CACHE_SLOT_STATE_SHARED)) ) { *dt = cache_data(way, set, word); if ( word+1 < m_words) { *dt_next = cache_data(way, set, word+1); } *selway = way; *selset = set; *selword = word; cache_set_lru(way, set); return true; } } return false; } //////////////////////////////////////////////////////////////////// // Read one or two 32 bits word. // Both data and directory are accessed. // returns the access status in the state argument: // - VALID : (matching tag) and (state == VALID) // - ZOMBI : (matching tag) and (state == ZOMBI) // - MISS : no matching tag or EMPTY state // If VALID or ZOMBI, the data, the way, set and word index are // returned in the other arguments. //////////////////////////////////////////////////////////////////// inline void read( addr_t ad, data_t* dt, data_t* dt_next, size_t* selway, size_t* selset, size_t* selword, int* state ) { const addr_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; // default return values *state = CACHE_SLOT_STATE_INVALID; *selway = 0; *selset = 0; *selword = 0; *dt = 0; for ( size_t way = 0; way < m_ways; way++ ) { if ( tag == cache_tag(way, set) ) // matching tag { if ( cache_state(way, set) == CACHE_SLOT_STATE_EXCLUSIVE ) { *state = CACHE_SLOT_STATE_EXCLUSIVE; *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); if ( word+1 < m_words) { *dt_next = cache_data(way, set, word+1); } cache_set_lru(way, set); } else if ( cache_state(way, set) == CACHE_SLOT_STATE_SHARED ) { *state = CACHE_SLOT_STATE_SHARED; *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); if ( word+1 < m_words) { *dt_next = cache_data(way, set, word+1); } cache_set_lru(way, set); } else if ( cache_state(way, set) == CACHE_SLOT_STATE_MODIFIED ) { *state = CACHE_SLOT_STATE_MODIFIED; *selway = way; *selset = set; *selword = word; *dt = cache_data(way, set, word); if ( word+1 < m_words) { *dt_next = cache_data(way, set, word+1); } cache_set_lru(way, set); } else if ( cache_state(way, set) == CACHE_SLOT_STATE_ZOMBI ) { *state = CACHE_SLOT_STATE_ZOMBI; *selway = way; *selset = set; *selword = word; } } } } /////////////////////////////////////////////////////////////////////////////// // Checks the cache state for a given address. // Only the directory is accessed. // returns true if (matching tag) and (state == VALID) // The selected way, set and first word index are returned in case of hit. // This function can be used when we need to access the directory // while we write in the data part with a different address in the same cycle. /////////////////////////////////////////////////////////////////////////////// inline bool hit( addr_t ad, size_t* selway, size_t* selset, size_t* selword) { const addr_t tag = m_z[ad]; const size_t set = m_y[ad]; const size_t word = m_x[ad]; for ( size_t way = 0; way < m_ways; way++ ) { if ( (tag == cache_tag(way, set)) && ( (cache_state(way, set) == CACHE_SLOT_STATE_EXCLUSIVE) or (cache_state(way, set) == CACHE_SLOT_STATE_MODIFIED) or (cache_state(way, set) == CACHE_SLOT_STATE_SHARED)) ) { *selway = way; *selset = set; *selword = word; cache_set_lru(way, set); return true; } } return false; } /////////////////////////////////////////////////////////////////////////////// // Checks the cache state for a given address, when the ZOMBI state is used. // Only the directory is accessed. // Returns the access status in the state argument: // - VALID if (matching tag) and (state == VALID) // - ZOMBI if (matching tag) and (state == ZOMBI) // - EMPTY if no match or (state == EMPTY) // The selected way, set and first word index are returned if not empty. // This function can be used when we need to access the directory // while we write in the data part with a different address in the same cycle. /////////////////////////////////////////////////////////////////////////////// inline void read_dir( addr_t ad, int* state, size_t* way, size_t* set, size_t* word) { const addr_t ad_tag = m_z[ad]; const size_t ad_set = m_y[ad]; const size_t ad_word = m_x[ad]; for ( size_t _way = 0; _way < m_ways; _way++ ) { if ( (ad_tag == cache_tag(_way, ad_set) ) and (cache_state(_way, ad_set) != CACHE_SLOT_STATE_INVALID) ) { *state = cache_state(_way, ad_set); *way = _way; *set = ad_set; *word = ad_word; return; } } // return value if not (VALID or ZOMBI) *state = CACHE_SLOT_STATE_INVALID; } /////////////////////////////////////////////////////////////////////////////// // Checks the cache state for a slot (set,way), when the ZOMBI state is used. // Only the directory is accessed. // Returns the access status and the tag value in the state and tag argument. /////////////////////////////////////////////////////////////////////////////// inline void read_dir( size_t way, size_t set, addr_t* tag, int* state ) { *state = cache_state(way, set); *tag = cache_tag(way, set); } //////////////////////////////////////////// inline addr_t get_tag(size_t way, size_t set) { return cache_tag(way, set); } /////////////////////////////////////////////////////////////////// // This function writes a complete 32 bits word // It does not use the directory and cannot miss. ////////////////////////////////////////////////////////////////// inline void write(size_t way, size_t set, size_t word, data_t data) { /**/ //std::cout << "write cache : way = "<