CacheMemory.cc revision 8232
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 "debug/RubyCache.hh" 31#include "mem/ruby/system/CacheMemory.hh" 32 33using namespace std; 34 35ostream& 36operator<<(ostream& out, const CacheMemory& obj) 37{ 38 obj.print(out); 39 out << flush; 40 return out; 41} 42 43CacheMemory * 44RubyCacheParams::create() 45{ 46 return new CacheMemory(this); 47} 48 49CacheMemory::CacheMemory(const Params *p) 50 : SimObject(p) 51{ 52 m_cache_size = p->size; 53 m_latency = p->latency; 54 m_cache_assoc = p->assoc; 55 m_policy = p->replacement_policy; 56 m_profiler_ptr = new CacheProfiler(name()); 57 m_start_index_bit = p->start_index_bit; 58} 59 60void 61CacheMemory::init() 62{ 63 m_cache_num_sets = (m_cache_size / m_cache_assoc) / 64 RubySystem::getBlockSizeBytes(); 65 assert(m_cache_num_sets > 1); 66 m_cache_num_set_bits = floorLog2(m_cache_num_sets); 67 assert(m_cache_num_set_bits > 0); 68 69 if (m_policy == "PSEUDO_LRU") 70 m_replacementPolicy_ptr = 71 new PseudoLRUPolicy(m_cache_num_sets, m_cache_assoc); 72 else if (m_policy == "LRU") 73 m_replacementPolicy_ptr = 74 new LRUPolicy(m_cache_num_sets, m_cache_assoc); 75 else 76 assert(false); 77 78 m_cache.resize(m_cache_num_sets); 79 for (int i = 0; i < m_cache_num_sets; i++) { 80 m_cache[i].resize(m_cache_assoc); 81 for (int j = 0; j < m_cache_assoc; j++) { 82 m_cache[i][j] = NULL; 83 } 84 } 85} 86 87CacheMemory::~CacheMemory() 88{ 89 if (m_replacementPolicy_ptr != NULL) 90 delete m_replacementPolicy_ptr; 91 delete m_profiler_ptr; 92 for (int i = 0; i < m_cache_num_sets; i++) { 93 for (int j = 0; j < m_cache_assoc; j++) { 94 delete m_cache[i][j]; 95 } 96 } 97} 98 99void 100CacheMemory::printConfig(ostream& out) 101{ 102 int block_size = RubySystem::getBlockSizeBytes(); 103 104 out << "Cache config: " << m_cache_name << endl; 105 out << " cache_associativity: " << m_cache_assoc << endl; 106 out << " num_cache_sets_bits: " << m_cache_num_set_bits << endl; 107 const int cache_num_sets = 1 << m_cache_num_set_bits; 108 out << " num_cache_sets: " << cache_num_sets << endl; 109 out << " cache_set_size_bytes: " << cache_num_sets * block_size << endl; 110 out << " cache_set_size_Kbytes: " 111 << double(cache_num_sets * block_size) / (1<<10) << endl; 112 out << " cache_set_size_Mbytes: " 113 << double(cache_num_sets * block_size) / (1<<20) << endl; 114 out << " cache_size_bytes: " 115 << cache_num_sets * block_size * m_cache_assoc << endl; 116 out << " cache_size_Kbytes: " 117 << double(cache_num_sets * block_size * m_cache_assoc) / (1<<10) 118 << endl; 119 out << " cache_size_Mbytes: " 120 << double(cache_num_sets * block_size * m_cache_assoc) / (1<<20) 121 << endl; 122} 123 124// convert a Address to its location in the cache 125Index 126CacheMemory::addressToCacheSet(const Address& address) const 127{ 128 assert(address == line_address(address)); 129 return address.bitSelect(m_start_index_bit, 130 m_start_index_bit + m_cache_num_set_bits - 1); 131} 132 133// Given a cache index: returns the index of the tag in a set. 134// returns -1 if the tag is not found. 135int 136CacheMemory::findTagInSet(Index cacheSet, const Address& tag) const 137{ 138 assert(tag == line_address(tag)); 139 // search the set for the tags 140 m5::hash_map<Address, int>::const_iterator it = m_tag_index.find(tag); 141 if (it != m_tag_index.end()) 142 if (m_cache[cacheSet][it->second]->m_Permission != 143 AccessPermission_NotPresent) 144 return it->second; 145 return -1; // Not found 146} 147 148// Given a cache index: returns the index of the tag in a set. 149// returns -1 if the tag is not found. 150int 151CacheMemory::findTagInSetIgnorePermissions(Index cacheSet, 152 const Address& tag) const 153{ 154 assert(tag == line_address(tag)); 155 // search the set for the tags 156 m5::hash_map<Address, int>::const_iterator it = m_tag_index.find(tag); 157 if (it != m_tag_index.end()) 158 return it->second; 159 return -1; // Not found 160} 161 162bool 163CacheMemory::tryCacheAccess(const Address& address, RubyRequestType type, 164 DataBlock*& data_ptr) 165{ 166 assert(address == line_address(address)); 167 DPRINTF(RubyCache, "address: %s\n", address); 168 Index cacheSet = addressToCacheSet(address); 169 int loc = findTagInSet(cacheSet, address); 170 if (loc != -1) { 171 // Do we even have a tag match? 172 AbstractCacheEntry* entry = m_cache[cacheSet][loc]; 173 m_replacementPolicy_ptr-> 174 touch(cacheSet, loc, g_eventQueue_ptr->getTime()); 175 data_ptr = &(entry->getDataBlk()); 176 177 if (entry->m_Permission == AccessPermission_Read_Write) { 178 return true; 179 } 180 if ((entry->m_Permission == AccessPermission_Read_Only) && 181 (type == RubyRequestType_LD || type == RubyRequestType_IFETCH)) { 182 return true; 183 } 184 // The line must not be accessible 185 } 186 data_ptr = NULL; 187 return false; 188} 189 190bool 191CacheMemory::testCacheAccess(const Address& address, RubyRequestType type, 192 DataBlock*& data_ptr) 193{ 194 assert(address == line_address(address)); 195 DPRINTF(RubyCache, "address: %s\n", address); 196 Index cacheSet = addressToCacheSet(address); 197 int loc = findTagInSet(cacheSet, address); 198 199 if (loc != -1) { 200 // Do we even have a tag match? 201 AbstractCacheEntry* entry = m_cache[cacheSet][loc]; 202 m_replacementPolicy_ptr-> 203 touch(cacheSet, loc, g_eventQueue_ptr->getTime()); 204 data_ptr = &(entry->getDataBlk()); 205 206 return m_cache[cacheSet][loc]->m_Permission != 207 AccessPermission_NotPresent; 208 } 209 210 data_ptr = NULL; 211 return false; 212} 213 214// tests to see if an address is present in the cache 215bool 216CacheMemory::isTagPresent(const Address& address) const 217{ 218 assert(address == line_address(address)); 219 Index cacheSet = addressToCacheSet(address); 220 int loc = findTagInSet(cacheSet, address); 221 222 if (loc == -1) { 223 // We didn't find the tag 224 DPRINTF(RubyCache, "No tag match for address: %s\n", address); 225 return false; 226 } 227 DPRINTF(RubyCache, "address: %s found\n", address); 228 return true; 229} 230 231// Returns true if there is: 232// a) a tag match on this address or there is 233// b) an unused line in the same cache "way" 234bool 235CacheMemory::cacheAvail(const Address& address) const 236{ 237 assert(address == line_address(address)); 238 239 Index cacheSet = addressToCacheSet(address); 240 241 for (int i = 0; i < m_cache_assoc; i++) { 242 AbstractCacheEntry* entry = m_cache[cacheSet][i]; 243 if (entry != NULL) { 244 if (entry->m_Address == address || 245 entry->m_Permission == AccessPermission_NotPresent) { 246 // Already in the cache or we found an empty entry 247 return true; 248 } 249 } else { 250 return true; 251 } 252 } 253 return false; 254} 255 256AbstractCacheEntry* 257CacheMemory::allocate(const Address& address, AbstractCacheEntry* entry) 258{ 259 assert(address == line_address(address)); 260 assert(!isTagPresent(address)); 261 assert(cacheAvail(address)); 262 DPRINTF(RubyCache, "address: %s\n", address); 263 264 // Find the first open slot 265 Index cacheSet = addressToCacheSet(address); 266 std::vector<AbstractCacheEntry*> &set = m_cache[cacheSet]; 267 for (int i = 0; i < m_cache_assoc; i++) { 268 if (!set[i] || set[i]->m_Permission == AccessPermission_NotPresent) { 269 set[i] = entry; // Init entry 270 set[i]->m_Address = address; 271 set[i]->m_Permission = AccessPermission_Invalid; 272 DPRINTF(RubyCache, "Allocate clearing lock for addr: %x\n", 273 address); 274 set[i]->m_locked = -1; 275 m_tag_index[address] = i; 276 277 m_replacementPolicy_ptr-> 278 touch(cacheSet, i, g_eventQueue_ptr->getTime()); 279 280 return entry; 281 } 282 } 283 panic("Allocate didn't find an available entry"); 284} 285 286void 287CacheMemory::deallocate(const Address& address) 288{ 289 assert(address == line_address(address)); 290 assert(isTagPresent(address)); 291 DPRINTF(RubyCache, "address: %s\n", address); 292 Index cacheSet = addressToCacheSet(address); 293 int loc = findTagInSet(cacheSet, address); 294 if (loc != -1) { 295 delete m_cache[cacheSet][loc]; 296 m_cache[cacheSet][loc] = NULL; 297 m_tag_index.erase(address); 298 } 299} 300 301// Returns with the physical address of the conflicting cache line 302Address 303CacheMemory::cacheProbe(const Address& address) const 304{ 305 assert(address == line_address(address)); 306 assert(!cacheAvail(address)); 307 308 Index cacheSet = addressToCacheSet(address); 309 return m_cache[cacheSet][m_replacementPolicy_ptr->getVictim(cacheSet)]-> 310 m_Address; 311} 312 313// looks an address up in the cache 314AbstractCacheEntry* 315CacheMemory::lookup(const Address& address) 316{ 317 assert(address == line_address(address)); 318 Index cacheSet = addressToCacheSet(address); 319 int loc = findTagInSet(cacheSet, address); 320 if(loc == -1) return NULL; 321 return m_cache[cacheSet][loc]; 322} 323 324// looks an address up in the cache 325const AbstractCacheEntry* 326CacheMemory::lookup(const Address& address) const 327{ 328 assert(address == line_address(address)); 329 Index cacheSet = addressToCacheSet(address); 330 int loc = findTagInSet(cacheSet, address); 331 if(loc == -1) return NULL; 332 return m_cache[cacheSet][loc]; 333} 334 335// Sets the most recently used bit for a cache block 336void 337CacheMemory::setMRU(const Address& address) 338{ 339 Index cacheSet; 340 341 cacheSet = addressToCacheSet(address); 342 m_replacementPolicy_ptr-> 343 touch(cacheSet, findTagInSet(cacheSet, address), 344 g_eventQueue_ptr->getTime()); 345} 346 347void 348CacheMemory::profileMiss(const RubyRequest& msg) 349{ 350 m_profiler_ptr->addCacheStatSample(msg.getType(), 351 msg.getAccessMode(), 352 msg.getPrefetch()); 353} 354 355void 356CacheMemory::profileGenericRequest(GenericRequestType requestType, 357 RubyAccessMode accessType, 358 PrefetchBit pfBit) 359{ 360 m_profiler_ptr->addGenericStatSample(requestType, 361 accessType, 362 pfBit); 363} 364 365void 366CacheMemory::recordCacheContents(CacheRecorder& tr) const 367{ 368 for (int i = 0; i < m_cache_num_sets; i++) { 369 for (int j = 0; j < m_cache_assoc; j++) { 370 AccessPermission perm = m_cache[i][j]->m_Permission; 371 RubyRequestType request_type = RubyRequestType_NULL; 372 if (perm == AccessPermission_Read_Only) { 373 if (m_is_instruction_only_cache) { 374 request_type = RubyRequestType_IFETCH; 375 } else { 376 request_type = RubyRequestType_LD; 377 } 378 } else if (perm == AccessPermission_Read_Write) { 379 request_type = RubyRequestType_ST; 380 } 381 382 if (request_type != RubyRequestType_NULL) { 383#if 0 384 tr.addRecord(m_chip_ptr->getID(), m_cache[i][j].m_Address, 385 Address(0), request_type, 386 m_replacementPolicy_ptr->getLastAccess(i, j)); 387#endif 388 } 389 } 390 } 391} 392 393void 394CacheMemory::print(ostream& out) const 395{ 396 out << "Cache dump: " << m_cache_name << endl; 397 for (int i = 0; i < m_cache_num_sets; i++) { 398 for (int j = 0; j < m_cache_assoc; j++) { 399 if (m_cache[i][j] != NULL) { 400 out << " Index: " << i 401 << " way: " << j 402 << " entry: " << *m_cache[i][j] << endl; 403 } else { 404 out << " Index: " << i 405 << " way: " << j 406 << " entry: NULL" << endl; 407 } 408 } 409 } 410} 411 412void 413CacheMemory::printData(ostream& out) const 414{ 415 out << "printData() not supported" << endl; 416} 417 418void 419CacheMemory::clearStats() const 420{ 421 m_profiler_ptr->clearStats(); 422} 423 424void 425CacheMemory::printStats(ostream& out) const 426{ 427 m_profiler_ptr->printStats(out); 428} 429 430void 431CacheMemory::getMemoryValue(const Address& addr, char* value, 432 unsigned size_in_bytes) 433{ 434 AbstractCacheEntry* entry = lookup(line_address(addr)); 435 unsigned startByte = addr.getAddress() - line_address(addr).getAddress(); 436 for (unsigned i = 0; i < size_in_bytes; ++i) { 437 value[i] = entry->getDataBlk().getByte(i + startByte); 438 } 439} 440 441void 442CacheMemory::setMemoryValue(const Address& addr, char* value, 443 unsigned size_in_bytes) 444{ 445 AbstractCacheEntry* entry = lookup(line_address(addr)); 446 unsigned startByte = addr.getAddress() - line_address(addr).getAddress(); 447 assert(size_in_bytes > 0); 448 for (unsigned i = 0; i < size_in_bytes; ++i) { 449 entry->getDataBlk().setByte(i + startByte, value[i]); 450 } 451 452 // entry = lookup(line_address(addr)); 453} 454 455void 456CacheMemory::setLocked(const Address& address, int context) 457{ 458 DPRINTF(RubyCache, "Setting Lock for addr: %x to %d\n", address, context); 459 assert(address == line_address(address)); 460 Index cacheSet = addressToCacheSet(address); 461 int loc = findTagInSet(cacheSet, address); 462 assert(loc != -1); 463 m_cache[cacheSet][loc]->m_locked = context; 464} 465 466void 467CacheMemory::clearLocked(const Address& address) 468{ 469 DPRINTF(RubyCache, "Clear Lock for addr: %x\n", address); 470 assert(address == line_address(address)); 471 Index cacheSet = addressToCacheSet(address); 472 int loc = findTagInSet(cacheSet, address); 473 assert(loc != -1); 474 m_cache[cacheSet][loc]->m_locked = -1; 475} 476 477bool 478CacheMemory::isLocked(const Address& address, int context) 479{ 480 assert(address == line_address(address)); 481 Index cacheSet = addressToCacheSet(address); 482 int loc = findTagInSet(cacheSet, address); 483 assert(loc != -1); 484 DPRINTF(RubyCache, "Testing Lock for addr: %llx cur %d con %d\n", 485 address, m_cache[cacheSet][loc]->m_locked, context); 486 return m_cache[cacheSet][loc]->m_locked == context; 487} 488 489