/* -*- 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 * * Alexandre JOANNOU * */ #ifndef SOCLIB_GENERIC_LLSC_GLOBAL_TABLE_H #define SOCLIB_GENERIC_LLSC_GLOBAL_TABLE_H #include #include #include #include #include #include #include namespace soclib { ////////////////////////// //TODO switch to this /* template < size_t nb_slots, // max number of concerned shared resources typename key_t, // key type => max number of key; TODO wich one ? unsigned int t_network, // max number of cycle spent in the network when responding to a client (or more) unsigned int t_inter_op, // min number of cycle between 2 reservation operation (or less but > 0) typename addr_t // ressource identifier type > */ template < size_t nb_slots, // desired number of slots unsigned int nb_procs, // number of processors in the system unsigned int life_span, // registratioçn life span (in # of LL operations) typename addr_t // address type > class GenericLLSCGlobalTable //////////////////////////// { private : const std::string name ; // component name uint32_t r_key [nb_slots] ; // array of key addr_t r_addr [nb_slots] ; // array of addresses bool r_val [nb_slots] ; // array of valid bits uint32_t r_next_key ; // value of the next key sc_dt::sc_uint r_block_mask ; // mask for the slots blocks sc_dt::sc_uint r_last_counter ; // mask for the slots blocks size_t r_write_ptr ; // index of next slot to replace size_t r_last_empty ; // index of last empty slot used uint32_t m_cpt_evic ; // number of eviction in the table uint32_t m_cpt_ll ; // number of ll accesses to the table uint32_t m_cpt_ll_update ; // number of ll accesses to the table that trigger an update TODO check that uint32_t m_cpt_sc ; // number of sc accesses to the table uint32_t m_cpt_sc_success ; // number of sc accesses to the table that are successful uint32_t m_cpt_sw ; // number of sw accesses to the table //////////////////////////////////////////////////////////////////////////// inline void upNextKey() // This function generates a new value for the next key { // generating a new key in r_next_key r_next_key++; } //////////////////////////////////////////////////////////////////////////// /* inline void updateVictimSlot() // This function selects the next slot to be evicted // This is done by updating the value of r_write_ptr { // updates the position of the next slot to be replaced static unsigned int count = 0; // for each slot, check if it actually is the slot to replace // this is done by checking count % 2^(i+1) == (2^i)-1 // 2^(i+1) being the period // (2^i)-1 being the first apparition // NB : the -1 in (2^i)-1 is here because of the 0 indexed array for (size_t i = 0 ; i < nb_slots; i++) if (count % (int)pow(2,i+1) == pow(2,i)-1) r_write_ptr = i; count = (count + 1) % (int) pow(2,nb_slots); // mustn't go further than 2^nb_slots // or (2^nb_slots)+1 for a 1 indexed array // 2^32 = periodicity of slot #31 } */ //////////////////////////////////////////////////////////////////////////// inline void updateVictimSlot() // This function selects the next slot to be evicted // This is done by updating the value of r_write_ptr { sc_dt::sc_uint new_counter; sc_dt::sc_uint xor_counter; new_counter = newCounter(r_block_mask, r_last_counter); xor_counter = new_counter ^ r_last_counter; for (size_t i = nb_slots - 1; i >= 0; --i) { if(xor_counter[i]) { r_write_ptr = i; break; } } r_last_counter = new_counter; } //////////////////////////////////////////////////////////////////////////// inline sc_dt::sc_uint newCounter(const sc_dt::sc_uint& mask, const sc_dt::sc_uint& counter) // This function generates the new counter //TODO comment more { // return ((((~counter) & (counter << 1)) & mask) | (counter + 1)); } //////////////////////////////////////////////////////////////////////////// inline void init_block_mask() //TODO //This function selects the block mask to be used //Need to provide another way to do that ? { /* //try to dynamically compute the block mask ... #define L2 soclib::common::uint32_log2 unsigned int budget = nb_slots - (L2(nb_procs) + 1); //TODO +1? #undef L2 */ switch(nb_slots) { case 12: r_block_mask = sc_dt::sc_uint("0x000"); break; case 16 : r_block_mask = sc_dt::sc_uint("0xA800"); break; case 20 : r_block_mask = sc_dt::sc_uint("0xD5500"); break; case 24 : r_block_mask = sc_dt::sc_uint("0xDB5540"); break; case 28 : r_block_mask = sc_dt::sc_uint("0xEEDAAA0"); break; case 32 : r_block_mask = sc_dt::sc_uint("0xF776D550"); break; case 36 : r_block_mask = sc_dt::sc_uint("0xFBDDDB550"); break; case 40 : r_block_mask = sc_dt::sc_uint("0xFDF7BB6D50"); break; case 44 : r_block_mask = sc_dt::sc_uint("0xFEFBDEEDAA8"); break; case 48 : r_block_mask = sc_dt::sc_uint("0xFF7EFBDDDAA8"); break; case 52 : r_block_mask = sc_dt::sc_uint("0xFFBFBF7BBB6A8"); break; case 56 : r_block_mask = sc_dt::sc_uint("0xFFDFEFDF7BB6A8"); break; case 60 : r_block_mask = sc_dt::sc_uint("0xFFF7FDFDF7BB6A8"); break; case 64 : r_block_mask = sc_dt::sc_uint("0xFFFBFF7FBF7BB6A8"); break; default: assert(false && "nb_slots must be either 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60 or 64"); } } //////////////////////////////////////////////////////////////////////////// inline int nextEmptySlot() // This function returns : // - the position of the first next empty slot in the table // (starting from the last empty slot used) // and updates the r_last_empty_slot register // - -1 if the table is full { size_t i = r_last_empty; do { // checking if current slot is empty if(!r_val[i]) { // updating last empty slot and returning its position r_last_empty = i; return i; } // selecting next slot i = (i+1) % nb_slots; } // stop if all slots have been tested while(i != r_last_empty); // the table is full return -1; } //////////////////////////////////////////////////////////////////////////// inline int hitAddr(const addr_t ad) // HIT on the address only // This function takes an addr_t ad // It returns : // - the position of the first HIT in the table // - -1 in case of MISS // NB : HIT = (slot addr == ad) AND (slot is valid) { // checking all slots for (size_t i = 0; i < nb_slots; i++) { // if HIT, returning its position if(ad == r_addr[i] && r_val[i]) return i; } // MISS return -1; } //////////////////////////////////////////////////////////////////////////// inline int hitAddrKey(const addr_t ad, const uint32_t key) // HIT on the address AND the on the signature // This function takes an addr_t ad and a uint32_t key // It returns : // - the position of the first HIT in the table // - -1 in case of MISS // NB : HIT = (slot addr == ad) AND (slot key == key) // AND (slot is valid) { // checking all slots for (size_t i = 0; i < nb_slots; i++) { // if HIT, returning its position if(ad == r_addr[i] && key == r_key[i] && r_val[i]) return i; } // MISS return -1; } //////////////////////////////////////////////////////////////////////////// inline void reset() { // init the table init(); // init stat counters m_cpt_evic = 0; m_cpt_ll = 0; m_cpt_ll_update = 0; m_cpt_sc = 0; m_cpt_sc_success = 0; m_cpt_sw = 0; } public: //////////////////////////////////////////////////////////////////////////// GenericLLSCGlobalTable( const std::string &n = "llsc_global_table" ) : name(n) { #define L2 soclib::common::uint32_log2 assert(nb_procs > 1); assert((int)nb_slots >= L2(nb_procs)); #undef L2 init(); init_block_mask(); } //////////////////////////////////////////////////////////////////////////// ~GenericLLSCGlobalTable() { } //////////////////////////////////////////////////////////////////////////// // This function initializes the table (all slots empty) inline void init() { // making all slots available by reseting all valid bits std::memset(r_val, 0, sizeof(*r_val)*nb_slots); // init registers r_next_key = 0; r_last_counter = 0; //TODO static in updateVictimSlot() ? r_write_ptr = 0; r_last_empty = 0; } //////////////////////////////////////////////////////////////////////////// inline uint32_t ll(const addr_t ad) // This method registers an LL in the table and returns the key associated // with the registration { // increment the ll access counter (for stats) m_cpt_ll++; // hit addr ? // YES // enough time left ? // YES // use this registration, return key // NO // update this registration with a new key, return new key // NO // table has an empty slot ? // YES // select empty slot // NO // select victim slot (r_write_ptr) // update next victim // open registration on selected slot // update next key // return the registration key // Is the address found in the table ? int pos = hitAddr(ad); // Yes, then return the associated key if (pos >= 0) { if(r_key[pos] - r_next_key > life_span) return r_key[pos]; r_key[pos] = r_next_key; upNextKey(); m_cpt_ll_update++; return r_key[pos]; } // No, then try to find an empty slot pos = nextEmptySlot(); // If there is no empty slot, // evict an existing registration if (pos == -1) { // get the position of the evicted registration pos = r_write_ptr; // update the victim slot for the next eviction updateVictimSlot(); // increment the eviction counter (for stats) m_cpt_evic++; } // get the key for the new registration uint32_t key = r_next_key; // update the registration slot r_key[pos] = key ; r_addr[pos] = ad ; r_val[pos] = true ; // compute the next key upNextKey(); // return the key of the new registration return key; } //////////////////////////////////////////////////////////////////////////// inline bool sc(const addr_t ad, const uint32_t key) // This method checks if there is a valid registration for the SC (ad && // key) and, in case of hit,invalidates the registration and returns true // (returns false otherwise) // // The return value can be used to tell if the SC is atomic { // increment the sc access counter (for stats) m_cpt_sc++; // hit addr && hit key ? // NO // return miss // YES // inval registration and return hit // Is there a valid registration in the table ? int pos = hitAddrKey(ad, key); if(pos >= 0) { // increment the sc success counter (for stats) m_cpt_sc_success++; // invalidate the registration r_val[pos] = false; // return the success of the sc operation return true; } else { // return the failure of the sc operation return false; } } //////////////////////////////////////////////////////////////////////////// inline void sw(const addr_t ad) // This method checks if there is a valid registration for the given // address and, in case of hit, invalidates the registration { // increment the sw access counter (for stats) m_cpt_sw++; // hit addr ? // YES // inval registration // NO // nothing // Is there a registration for the given address ? int pos = hitAddr(ad); // If there is one, invalidate it if(pos >= 0) r_val[pos] = false; } //////////////////////////////////////////////////////////////////////////// /* void fileTrace(FILE* file) { } */ //////////////////////////////////////////////////////////////////////////// inline void print_trace(std::ostream& out = std::cout) { out << " ___________________________________" << std::endl << "| " << std::setw(33) << "generic_llsc_global_table" << " |" << std::endl << "| " << std::setw(33) << name << " |" << std::endl << " ===================================" << std::endl << "| " << std::setw(11) << "addr" << " | " << std::setw(11) << "key" << " | " << std::setw(5) << "val" << " |" << std::endl << " -----------------------------------" << std::endl; for ( size_t i = 0; i < nb_slots ; i++ ) { out << "| " << std::showbase << std::setw(11) << std::setfill('0') << std::hex << r_addr[i] << " | " << std::noshowbase << std::setw(11) << std::setfill('0') << std::dec << r_key[i] << " | " << std::setw(5) << std::setfill(' ') << std::boolalpha << r_val[i] << " |" << std::endl ; } out << " -----------------------------------" << std::endl << std::noshowbase << std::dec << std::endl ; } //////////////////////////////////////////////////////////////////////////// inline void print_stats(std::ostream& out = std::cout) { out << "# of ll accesses : " << m_cpt_ll << std::endl << "# of ll updates : " << m_cpt_ll_update << std::endl << "# of sc accesses : " << m_cpt_sc << std::endl << "# of sc success : " << m_cpt_sc_success << std::endl << "# of sw accesses : " << m_cpt_sw << std::endl << "# of evictions : " << m_cpt_evic << std::endl ; } }; } // end namespace soclib #endif // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4