CacheMemory.cc revision 7056
1/* 2 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "base/intmath.hh" 30#include "mem/ruby/system/CacheMemory.hh" 31 32using namespace std; 33 34ostream& 35operator<<(ostream& out, const CacheMemory& obj) 36{ 37 obj.print(out); 38 out << flush; 39 return out; 40} 41 42CacheMemory * 43RubyCacheParams::create() 44{ 45 return new CacheMemory(this); 46} 47 48CacheMemory::CacheMemory(const Params *p) 49 : SimObject(p) 50{ 51 m_cache_size = p->size; 52 m_latency = p->latency; 53 m_cache_assoc = p->assoc; 54 m_policy = p->replacement_policy; 55 m_profiler_ptr = new CacheProfiler(name()); 56} 57 58void 59CacheMemory::init() 60{ 61 m_cache_num_sets = (m_cache_size / m_cache_assoc) / 62 RubySystem::getBlockSizeBytes(); 63 assert(m_cache_num_sets > 1); 64 m_cache_num_set_bits = floorLog2(m_cache_num_sets); 65 assert(m_cache_num_set_bits > 0); 66 67 if (m_policy == "PSEUDO_LRU") 68 m_replacementPolicy_ptr = 69 new PseudoLRUPolicy(m_cache_num_sets, m_cache_assoc); 70 else if (m_policy == "LRU") 71 m_replacementPolicy_ptr = 72 new LRUPolicy(m_cache_num_sets, m_cache_assoc); 73 else 74 assert(false); 75 76 m_cache.setSize(m_cache_num_sets); 77 m_locked.setSize(m_cache_num_sets); 78 for (int i = 0; i < m_cache_num_sets; i++) { 79 m_cache[i].setSize(m_cache_assoc); 80 m_locked[i].setSize(m_cache_assoc); 81 for (int j = 0; j < m_cache_assoc; j++) { 82 m_cache[i][j] = NULL; 83 m_locked[i][j] = -1; 84 } 85 } 86} 87 88CacheMemory::~CacheMemory() 89{ 90 if (m_replacementPolicy_ptr != NULL) 91 delete m_replacementPolicy_ptr; 92 delete m_profiler_ptr; 93 for (int i = 0; i < m_cache_num_sets; i++) { 94 for (int j = 0; j < m_cache_assoc; j++) { 95 delete m_cache[i][j]; 96 } 97 } 98} 99 100void 101CacheMemory::printConfig(ostream& out) 102{ 103 int block_size = RubySystem::getBlockSizeBytes(); 104 105 out << "Cache config: " << m_cache_name << endl; 106 out << " cache_associativity: " << m_cache_assoc << endl; 107 out << " num_cache_sets_bits: " << m_cache_num_set_bits << endl; 108 const int cache_num_sets = 1 << m_cache_num_set_bits; 109 out << " num_cache_sets: " << cache_num_sets << endl; 110 out << " cache_set_size_bytes: " << cache_num_sets * block_size << endl; 111 out << " cache_set_size_Kbytes: " 112 << double(cache_num_sets * block_size) / (1<<10) << endl; 113 out << " cache_set_size_Mbytes: " 114 << double(cache_num_sets * block_size) / (1<<20) << endl; 115 out << " cache_size_bytes: " 116 << cache_num_sets * block_size * m_cache_assoc << endl; 117 out << " cache_size_Kbytes: " 118 << double(cache_num_sets * block_size * m_cache_assoc) / (1<<10) 119 << endl; 120 out << " cache_size_Mbytes: " 121 << double(cache_num_sets * block_size * m_cache_assoc) / (1<<20) 122 << endl; 123} 124 125// convert a Address to its location in the cache 126Index 127CacheMemory::addressToCacheSet(const Address& address) const 128{ 129 assert(address == line_address(address)); 130 return address.bitSelect(RubySystem::getBlockSizeBits(), 131 RubySystem::getBlockSizeBits() + m_cache_num_set_bits - 1); 132} 133 134// Given a cache index: returns the index of the tag in a set. 135// returns -1 if the tag is not found. 136int 137CacheMemory::findTagInSet(Index cacheSet, const Address& tag) const 138{ 139 assert(tag == line_address(tag)); 140 // search the set for the tags 141 m5::hash_map<Address, int>::const_iterator it = m_tag_index.find(tag); 142 if (it != m_tag_index.end()) 143 if (m_cache[cacheSet][it->second]->m_Permission != 144 AccessPermission_NotPresent) 145 return it->second; 146 return -1; // Not found 147} 148 149// Given a cache index: returns the index of the tag in a set. 150// returns -1 if the tag is not found. 151int 152CacheMemory::findTagInSetIgnorePermissions(Index cacheSet, 153 const Address& tag) const 154{ 155 assert(tag == line_address(tag)); 156 // search the set for the tags 157 m5::hash_map<Address, int>::const_iterator it = m_tag_index.find(tag); 158 if (it != m_tag_index.end()) 159 return it->second; 160 return -1; // Not found 161} 162 163bool 164CacheMemory::tryCacheAccess(const Address& address, CacheRequestType type, 165 DataBlock*& data_ptr) 166{ 167 assert(address == line_address(address)); 168 DEBUG_EXPR(CACHE_COMP, HighPrio, address); 169 Index cacheSet = addressToCacheSet(address); 170 int loc = findTagInSet(cacheSet, address); 171 if (loc != -1) { 172 // Do we even have a tag match? 173 AbstractCacheEntry* entry = m_cache[cacheSet][loc]; 174 m_replacementPolicy_ptr-> 175 touch(cacheSet, loc, g_eventQueue_ptr->getTime()); 176 data_ptr = &(entry->getDataBlk()); 177 178 if (entry->m_Permission == AccessPermission_Read_Write) { 179 return true; 180 } 181 if ((entry->m_Permission == AccessPermission_Read_Only) && 182 (type == CacheRequestType_LD || type == CacheRequestType_IFETCH)) { 183 return true; 184 } 185 // The line must not be accessible 186 } 187 data_ptr = NULL; 188 return false; 189} 190 191bool 192CacheMemory::testCacheAccess(const Address& address, CacheRequestType type, 193 DataBlock*& data_ptr) 194{ 195 assert(address == line_address(address)); 196 DEBUG_EXPR(CACHE_COMP, HighPrio, address); 197 Index cacheSet = addressToCacheSet(address); 198 int loc = findTagInSet(cacheSet, address); 199 200 if (loc != -1) { 201 // Do we even have a tag match? 202 AbstractCacheEntry* entry = m_cache[cacheSet][loc]; 203 m_replacementPolicy_ptr-> 204 touch(cacheSet, loc, g_eventQueue_ptr->getTime()); 205 data_ptr = &(entry->getDataBlk()); 206 207 return m_cache[cacheSet][loc]->m_Permission != 208 AccessPermission_NotPresent; 209 } 210 211 data_ptr = NULL; 212 return false; 213} 214 215// tests to see if an address is present in the cache 216bool 217CacheMemory::isTagPresent(const Address& address) const 218{ 219 assert(address == line_address(address)); 220 Index cacheSet = addressToCacheSet(address); 221 int loc = findTagInSet(cacheSet, address); 222 223 if (loc == -1) { 224 // We didn't find the tag 225 DEBUG_EXPR(CACHE_COMP, LowPrio, address); 226 DEBUG_MSG(CACHE_COMP, LowPrio, "No tag match"); 227 return false; 228 } 229 DEBUG_EXPR(CACHE_COMP, LowPrio, address); 230 DEBUG_MSG(CACHE_COMP, LowPrio, "found"); 231 return true; 232} 233 234// Returns true if there is: 235// a) a tag match on this address or there is 236// b) an unused line in the same cache "way" 237bool 238CacheMemory::cacheAvail(const Address& address) const 239{ 240 assert(address == line_address(address)); 241 242 Index cacheSet = addressToCacheSet(address); 243 244 for (int i = 0; i < m_cache_assoc; i++) { 245 AbstractCacheEntry* entry = m_cache[cacheSet][i]; 246 if (entry != NULL) { 247 if (entry->m_Address == address || 248 entry->m_Permission == AccessPermission_NotPresent) { 249 // Already in the cache or we found an empty entry 250 return true; 251 } 252 } else { 253 return true; 254 } 255 } 256 return false; 257} 258 259void 260CacheMemory::allocate(const Address& address, AbstractCacheEntry* entry) 261{ 262 assert(address == line_address(address)); 263 assert(!isTagPresent(address)); 264 assert(cacheAvail(address)); 265 DEBUG_EXPR(CACHE_COMP, HighPrio, address); 266 267 // Find the first open slot 268 Index cacheSet = addressToCacheSet(address); 269 Vector<AbstractCacheEntry*> &set = m_cache[cacheSet]; 270 for (int i = 0; i < m_cache_assoc; i++) { 271 if (!set[i] || set[i]->m_Permission == AccessPermission_NotPresent) { 272 set[i] = entry; // Init entry 273 set[i]->m_Address = address; 274 set[i]->m_Permission = AccessPermission_Invalid; 275 DPRINTF(RubyCache, "Allocate clearing lock for addr: %x\n", 276 address); 277 m_locked[cacheSet][i] = -1; 278 m_tag_index[address] = i; 279 280 m_replacementPolicy_ptr-> 281 touch(cacheSet, i, g_eventQueue_ptr->getTime()); 282 283 return; 284 } 285 } 286 ERROR_MSG("Allocate didn't find an available entry"); 287} 288 289void 290CacheMemory::deallocate(const Address& address) 291{ 292 assert(address == line_address(address)); 293 assert(isTagPresent(address)); 294 DEBUG_EXPR(CACHE_COMP, HighPrio, address); 295 Index cacheSet = addressToCacheSet(address); 296 int loc = findTagInSet(cacheSet, address); 297 if (loc != -1) { 298 delete m_cache[cacheSet][loc]; 299 m_cache[cacheSet][loc] = NULL; 300 DPRINTF(RubyCache, "Deallocate clearing lock for addr: %x\n", 301 address); 302 m_locked[cacheSet][loc] = -1; 303 m_tag_index.erase(address); 304 } 305} 306 307// Returns with the physical address of the conflicting cache line 308Address 309CacheMemory::cacheProbe(const Address& address) const 310{ 311 assert(address == line_address(address)); 312 assert(!cacheAvail(address)); 313 314 Index cacheSet = addressToCacheSet(address); 315 return m_cache[cacheSet][m_replacementPolicy_ptr->getVictim(cacheSet)]-> 316 m_Address; 317} 318 319// looks an address up in the cache 320AbstractCacheEntry& 321CacheMemory::lookup(const Address& address) 322{ 323 assert(address == line_address(address)); 324 Index cacheSet = addressToCacheSet(address); 325 int loc = findTagInSet(cacheSet, address); 326 assert(loc != -1); 327 return *m_cache[cacheSet][loc]; 328} 329 330// looks an address up in the cache 331const AbstractCacheEntry& 332CacheMemory::lookup(const Address& address) const 333{ 334 assert(address == line_address(address)); 335 Index cacheSet = addressToCacheSet(address); 336 int loc = findTagInSet(cacheSet, address); 337 assert(loc != -1); 338 return *m_cache[cacheSet][loc]; 339} 340 341AccessPermission 342CacheMemory::getPermission(const Address& address) const 343{ 344 assert(address == line_address(address)); 345 return lookup(address).m_Permission; 346} 347 348void 349CacheMemory::changePermission(const Address& address, 350 AccessPermission new_perm) 351{ 352 assert(address == line_address(address)); 353 lookup(address).m_Permission = new_perm; 354 Index cacheSet = addressToCacheSet(address); 355 int loc = findTagInSet(cacheSet, address); 356 if (new_perm != AccessPermission_Read_Write) { 357 DPRINTF(RubyCache, "Permission clearing lock for addr: %x\n", address); 358 m_locked[cacheSet][loc] = -1; 359 } 360 assert(getPermission(address) == new_perm); 361} 362 363// Sets the most recently used bit for a cache block 364void 365CacheMemory::setMRU(const Address& address) 366{ 367 Index cacheSet; 368 369 cacheSet = addressToCacheSet(address); 370 m_replacementPolicy_ptr-> 371 touch(cacheSet, findTagInSet(cacheSet, address), 372 g_eventQueue_ptr->getTime()); 373} 374 375void 376CacheMemory::profileMiss(const CacheMsg& msg) 377{ 378 m_profiler_ptr->addStatSample(msg.getType(), msg.getAccessMode(), 379 msg.getSize(), msg.getPrefetch()); 380} 381 382void 383CacheMemory::recordCacheContents(CacheRecorder& tr) const 384{ 385 for (int i = 0; i < m_cache_num_sets; i++) { 386 for (int j = 0; j < m_cache_assoc; j++) { 387 AccessPermission perm = m_cache[i][j]->m_Permission; 388 CacheRequestType request_type = CacheRequestType_NULL; 389 if (perm == AccessPermission_Read_Only) { 390 if (m_is_instruction_only_cache) { 391 request_type = CacheRequestType_IFETCH; 392 } else { 393 request_type = CacheRequestType_LD; 394 } 395 } else if (perm == AccessPermission_Read_Write) { 396 request_type = CacheRequestType_ST; 397 } 398 399 if (request_type != CacheRequestType_NULL) { 400#if 0 401 tr.addRecord(m_chip_ptr->getID(), m_cache[i][j].m_Address, 402 Address(0), request_type, 403 m_replacementPolicy_ptr->getLastAccess(i, j)); 404#endif 405 } 406 } 407 } 408} 409 410void 411CacheMemory::print(ostream& out) const 412{ 413 out << "Cache dump: " << m_cache_name << endl; 414 for (int i = 0; i < m_cache_num_sets; i++) { 415 for (int j = 0; j < m_cache_assoc; j++) { 416 if (m_cache[i][j] != NULL) { 417 out << " Index: " << i 418 << " way: " << j 419 << " entry: " << *m_cache[i][j] << endl; 420 } else { 421 out << " Index: " << i 422 << " way: " << j 423 << " entry: NULL" << endl; 424 } 425 } 426 } 427} 428 429void 430CacheMemory::printData(ostream& out) const 431{ 432 out << "printData() not supported" << endl; 433} 434 435void 436CacheMemory::clearStats() const 437{ 438 m_profiler_ptr->clearStats(); 439} 440 441void 442CacheMemory::printStats(ostream& out) const 443{ 444 m_profiler_ptr->printStats(out); 445} 446 447void 448CacheMemory::getMemoryValue(const Address& addr, char* value, 449 unsigned size_in_bytes) 450{ 451 AbstractCacheEntry& entry = lookup(line_address(addr)); 452 unsigned startByte = addr.getAddress() - line_address(addr).getAddress(); 453 for (unsigned i = 0; i < size_in_bytes; ++i) { 454 value[i] = entry.getDataBlk().getByte(i + startByte); 455 } 456} 457 458void 459CacheMemory::setMemoryValue(const Address& addr, char* value, 460 unsigned size_in_bytes) 461{ 462 AbstractCacheEntry& entry = lookup(line_address(addr)); 463 unsigned startByte = addr.getAddress() - line_address(addr).getAddress(); 464 assert(size_in_bytes > 0); 465 for (unsigned i = 0; i < size_in_bytes; ++i) { 466 entry.getDataBlk().setByte(i + startByte, value[i]); 467 } 468 469 // entry = lookup(line_address(addr)); 470} 471 472void 473CacheMemory::setLocked(const Address& address, int context) 474{ 475 DPRINTF(RubyCache, "Setting Lock for addr: %x to %d\n", address, context); 476 assert(address == line_address(address)); 477 Index cacheSet = addressToCacheSet(address); 478 int loc = findTagInSet(cacheSet, address); 479 assert(loc != -1); 480 m_locked[cacheSet][loc] = context; 481} 482 483void 484CacheMemory::clearLocked(const Address& address) 485{ 486 DPRINTF(RubyCache, "Clear Lock for addr: %x\n", address); 487 assert(address == line_address(address)); 488 Index cacheSet = addressToCacheSet(address); 489 int loc = findTagInSet(cacheSet, address); 490 assert(loc != -1); 491 m_locked[cacheSet][loc] = -1; 492} 493 494bool 495CacheMemory::isLocked(const Address& address, int context) 496{ 497 assert(address == line_address(address)); 498 Index cacheSet = addressToCacheSet(address); 499 int loc = findTagInSet(cacheSet, address); 500 assert(loc != -1); 501 DPRINTF(RubyCache, "Testing Lock for addr: %llx cur %d con %d\n", 502 address, m_locked[cacheSet][loc], context); 503 return m_locked[cacheSet][loc] == context; 504} 505 506