CacheMemory.hh revision 6285
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/*
30 * CacheMemory.hh
31 *
32 * Description:
33 *
34 * $Id: CacheMemory.hh,v 3.7 2004/06/18 20:15:15 beckmann Exp $
35 *
36 */
37
38#ifndef CACHEMEMORY_H
39#define CACHEMEMORY_H
40
41#include "mem/ruby/common/Global.hh"
42#include "mem/protocol/AccessPermission.hh"
43#include "mem/ruby/common/Address.hh"
44#include "mem/ruby/recorder/CacheRecorder.hh"
45#include "mem/protocol/CacheRequestType.hh"
46#include "mem/gems_common/Vector.hh"
47#include "mem/ruby/common/DataBlock.hh"
48#include "mem/protocol/MachineType.hh"
49#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
50#include "mem/ruby/system/PseudoLRUPolicy.hh"
51#include "mem/ruby/system/LRUPolicy.hh"
52#include "mem/ruby/slicc_interface/AbstractCacheEntry.hh"
53#include "mem/ruby/system/System.hh"
54#include "mem/ruby/slicc_interface/AbstractController.hh"
55#include <vector>
56
57class CacheMemory {
58public:
59
60  // Constructors
61  CacheMemory(const string & name);
62  void init(const vector<string> & argv);
63
64  // Destructor
65  ~CacheMemory();
66
67  // factory
68  //  static CacheMemory* createCache(int level, int num, char split_type, AbstractCacheEntry* (*entry_factory)());
69  //  static CacheMemory* getCache(int cache_id);
70
71  // Public Methods
72  void printConfig(ostream& out);
73
74  // perform a cache access and see if we hit or not.  Return true on a hit.
75  bool tryCacheAccess(const Address& address, CacheRequestType type, DataBlock*& data_ptr);
76
77  // similar to above, but doesn't require full access check
78  bool testCacheAccess(const Address& address, CacheRequestType type, DataBlock*& data_ptr);
79
80  // tests to see if an address is present in the cache
81  bool isTagPresent(const Address& address) const;
82
83  // Returns true if there is:
84  //   a) a tag match on this address or there is
85  //   b) an unused line in the same cache "way"
86  bool cacheAvail(const Address& address) const;
87
88  // find an unused entry and sets the tag appropriate for the address
89  void allocate(const Address& address, AbstractCacheEntry* new_entry);
90
91  // Explicitly free up this address
92  void deallocate(const Address& address);
93
94  // Returns with the physical address of the conflicting cache line
95  Address cacheProbe(const Address& address) const;
96
97  // looks an address up in the cache
98  AbstractCacheEntry& lookup(const Address& address);
99  const AbstractCacheEntry& lookup(const Address& address) const;
100
101  // Get/Set permission of cache block
102  AccessPermission getPermission(const Address& address) const;
103  void changePermission(const Address& address, AccessPermission new_perm);
104
105  int getLatency() const { return m_latency; }
106
107  // Hook for checkpointing the contents of the cache
108  void recordCacheContents(CacheRecorder& tr) const;
109  void setAsInstructionCache(bool is_icache) { m_is_instruction_only_cache = is_icache; }
110
111  // Set this address to most recently used
112  void setMRU(const Address& address);
113
114  void getMemoryValue(const Address& addr, char* value,
115                      unsigned int size_in_bytes );
116  void setMemoryValue(const Address& addr, char* value,
117                      unsigned int size_in_bytes );
118
119  // Print cache contents
120  void print(ostream& out) const;
121  void printData(ostream& out) const;
122
123private:
124  // Private Methods
125
126  // convert a Address to its location in the cache
127  Index addressToCacheSet(const Address& address) const;
128
129  // Given a cache tag: returns the index of the tag in a set.
130  // returns -1 if the tag is not found.
131  int findTagInSet(Index line, const Address& tag) const;
132  int findTagInSetIgnorePermissions(Index cacheSet, const Address& tag) const;
133
134  // Private copy constructor and assignment operator
135  CacheMemory(const CacheMemory& obj);
136  CacheMemory& operator=(const CacheMemory& obj);
137
138private:
139  const string m_cache_name;
140  AbstractController* m_controller;
141  int m_latency;
142
143  // Data Members (m_prefix)
144  bool m_is_instruction_only_cache;
145  bool m_is_data_only_cache;
146
147  // The first index is the # of cache lines.
148  // The second index is the the amount associativity.
149  Vector<Vector<AbstractCacheEntry*> > m_cache;
150
151  AbstractReplacementPolicy *m_replacementPolicy_ptr;
152
153  int m_cache_num_sets;
154  int m_cache_num_set_bits;
155  int m_cache_assoc;
156
157  static Vector< CacheMemory* > m_all_caches;
158};
159/*
160inline
161CacheMemory* CacheMemory::getCache(int cache_id)
162{
163  assert(cache_id < RubyConfig::getNumberOfCaches());
164  if (m_all_caches[cache_id] == NULL) {
165    cerr << "ERROR: Tried to obtain CacheMemory that hasn't been created yet." << endl;
166    assert(0);
167  }
168  return m_all_caches[cache_id];
169}
170
171inline
172CacheMemory* CacheMemory::createCache(int level, int num, char split_type_c, AbstractCacheEntry* (*entry_factory)())
173{
174  string split_type;
175  switch(split_type_c) {
176  case 'i':
177    split_type = "instruction"; break;
178  case 'd':
179    split_type = "data"; break;
180  default:
181    split_type = "unified"; break;
182  }
183  int cache_id = RubyConfig::getCacheIDFromParams(level, num, split_type);
184  assert(cache_id < RubyConfig::getNumberOfCaches());
185  if (m_all_caches.size() == 0) {
186    m_all_caches.setSize(RubyConfig::getNumberOfCaches());
187    for (int i=0; i<m_all_caches.size(); i++)
188      m_all_caches[i] = NULL;
189  }
190
191  string type = RubyConfig::getCacheType(cache_id);
192  if ( type == "SetAssociativeCache" ) {
193    m_all_caches[cache_id] = new CacheMemory(cache_id, entry_factory);
194  }
195  return m_all_caches[cache_id];
196}
197*/
198// Output operator declaration
199//ostream& operator<<(ostream& out, const CacheMemory<ENTRY>& obj);
200
201// ******************* Definitions *******************
202
203// Output operator definition
204inline
205ostream& operator<<(ostream& out, const CacheMemory& obj)
206{
207  obj.print(out);
208  out << flush;
209  return out;
210}
211
212
213// ****************************************************************
214
215inline
216CacheMemory::CacheMemory(const string & name)
217  : m_cache_name(name)
218{
219}
220
221inline
222void CacheMemory::init(const vector<string> & argv)
223{
224  int cache_size = 0;
225  string policy;
226
227  m_controller = NULL;
228  for (uint32 i=0; i<argv.size(); i+=2) {
229    if (argv[i] == "size_kb") {
230      cache_size = atoi(argv[i+1].c_str());
231    } else if (argv[i] == "latency") {
232      m_latency = atoi(argv[i+1].c_str());
233    } else if (argv[i] == "assoc") {
234      m_cache_assoc = atoi(argv[i+1].c_str());
235    } else if (argv[i] == "replacement_policy") {
236      policy = argv[i+1];
237    } else if (argv[i] == "controller") {
238      m_controller = RubySystem::getController(argv[i+1]);
239    } else {
240      cerr << "WARNING: CacheMemory: Unknown configuration parameter: " << argv[i] << endl;
241    }
242  }
243
244  m_cache_num_sets = cache_size / m_cache_assoc;
245  m_cache_num_set_bits = log_int(m_cache_num_sets);
246
247  if(policy == "PSEUDO_LRU")
248    m_replacementPolicy_ptr = new PseudoLRUPolicy(m_cache_num_sets, m_cache_assoc);
249  else if (policy == "LRU")
250    m_replacementPolicy_ptr = new LRUPolicy(m_cache_num_sets, m_cache_assoc);
251  else
252    assert(false);
253
254  m_cache.setSize(m_cache_num_sets);
255  for (int i = 0; i < m_cache_num_sets; i++) {
256    m_cache[i].setSize(m_cache_assoc);
257    for (int j = 0; j < m_cache_assoc; j++) {
258      m_cache[i][j] = NULL;
259    }
260  }
261}
262/*
263inline
264CacheMemory::CacheMemory(int cache_id, AbstractCacheEntry* (*entry_factory)())
265{
266  string split_type;
267
268  m_cache_id = cache_id;
269  m_entry_factory = entry_factory;
270
271  m_cache_num_set_bits = RubyConfig::getNumberOfCacheSetBits(cache_id);
272  m_cache_num_sets = RubyConfig::getNumberOfCacheSets(cache_id);
273  m_cache_assoc = RubyConfig::getCacheAssoc(cache_id);
274  split_type = RubyConfig::getCacheSplitType(cache_id);
275  m_is_instruction_only_cache = m_is_data_only_cache = false;
276  if (split_type == "instruction")
277    m_is_instruction_only_cache = true;
278  else if (split_type == "data")
279    m_is_data_only_cache = true;
280  else
281    assert(split_type == "unified");
282
283  if(RubyConfig::getCacheReplacementPolicy(cache_id) == "PSEUDO_LRU")
284    m_replacementPolicy_ptr = new PseudoLRUPolicy(m_cache_num_sets, m_cache_assoc);
285  else if(RubyConfig::getCacheReplacementPolicy(cache_id) == "LRU")
286    m_replacementPolicy_ptr = new LRUPolicy(m_cache_num_sets, m_cache_assoc);
287  else
288    assert(false);
289
290  m_cache.setSize(m_cache_num_sets);
291  for (int i = 0; i < m_cache_num_sets; i++) {
292    m_cache[i].setSize(m_cache_assoc);
293    for (int j = 0; j < m_cache_assoc; j++) {
294      m_cache[i][j] = m_entry_factory();
295    }
296  }
297}
298*/
299inline
300CacheMemory::~CacheMemory()
301{
302  if(m_replacementPolicy_ptr != NULL)
303    delete m_replacementPolicy_ptr;
304}
305
306inline
307void CacheMemory::printConfig(ostream& out)
308{
309  out << "Cache config: " << m_cache_name << endl;
310  if (m_controller != NULL)
311    out << "  controller: " << m_controller->getName() << endl;
312  out << "  cache_associativity: " << m_cache_assoc << endl;
313  out << "  num_cache_sets_bits: " << m_cache_num_set_bits << endl;
314  const int cache_num_sets = 1 << m_cache_num_set_bits;
315  out << "  num_cache_sets: " << cache_num_sets << endl;
316  out << "  cache_set_size_bytes: " << cache_num_sets * RubySystem::getBlockSizeBytes() << endl;
317  out << "  cache_set_size_Kbytes: "
318      << double(cache_num_sets * RubySystem::getBlockSizeBytes()) / (1<<10) << endl;
319  out << "  cache_set_size_Mbytes: "
320      << double(cache_num_sets * RubySystem::getBlockSizeBytes()) / (1<<20) << endl;
321  out << "  cache_size_bytes: "
322      << cache_num_sets * RubySystem::getBlockSizeBytes() * m_cache_assoc << endl;
323  out << "  cache_size_Kbytes: "
324      << double(cache_num_sets * RubySystem::getBlockSizeBytes() * m_cache_assoc) / (1<<10) << endl;
325  out << "  cache_size_Mbytes: "
326      << double(cache_num_sets * RubySystem::getBlockSizeBytes() * m_cache_assoc) / (1<<20) << endl;
327}
328
329// PRIVATE METHODS
330
331// convert a Address to its location in the cache
332inline
333Index CacheMemory::addressToCacheSet(const Address& address) const
334{
335  assert(address == line_address(address));
336  Index temp = -1;
337  return address.bitSelect(RubySystem::getBlockSizeBits(), RubySystem::getBlockSizeBits() + m_cache_num_set_bits-1);
338}
339
340// Given a cache index: returns the index of the tag in a set.
341// returns -1 if the tag is not found.
342inline
343int CacheMemory::findTagInSet(Index cacheSet, const Address& tag) const
344{
345  assert(tag == line_address(tag));
346  // search the set for the tags
347  for (int i=0; i < m_cache_assoc; i++) {
348    if ((m_cache[cacheSet][i] != NULL) &&
349        (m_cache[cacheSet][i]->m_Address == tag) &&
350        (m_cache[cacheSet][i]->m_Permission != AccessPermission_NotPresent)) {
351      return i;
352    }
353  }
354  return -1; // Not found
355}
356
357// Given a cache index: returns the index of the tag in a set.
358// returns -1 if the tag is not found.
359inline
360int CacheMemory::findTagInSetIgnorePermissions(Index cacheSet, const Address& tag) const
361{
362  assert(tag == line_address(tag));
363  // search the set for the tags
364  for (int i=0; i < m_cache_assoc; i++) {
365    if (m_cache[cacheSet][i] != NULL && m_cache[cacheSet][i]->m_Address == tag)
366      return i;
367  }
368  return -1; // Not found
369}
370
371// PUBLIC METHODS
372inline
373bool CacheMemory::tryCacheAccess(const Address& address,
374                                 CacheRequestType type,
375                                 DataBlock*& data_ptr)
376{
377  assert(address == line_address(address));
378  DEBUG_EXPR(CACHE_COMP, HighPrio, address);
379  Index cacheSet = addressToCacheSet(address);
380  int loc = findTagInSet(cacheSet, address);
381  if(loc != -1){ // Do we even have a tag match?
382    AbstractCacheEntry* entry = m_cache[cacheSet][loc];
383    m_replacementPolicy_ptr->touch(cacheSet, loc, g_eventQueue_ptr->getTime());
384    data_ptr = &(entry->getDataBlk());
385
386    if(entry->m_Permission == AccessPermission_Read_Write) {
387      return true;
388    }
389    if ((entry->m_Permission == AccessPermission_Read_Only) &&
390        (type == CacheRequestType_LD || type == CacheRequestType_IFETCH)) {
391      return true;
392    }
393    // The line must not be accessible
394  }
395  data_ptr = NULL;
396  return false;
397}
398
399inline
400bool CacheMemory::testCacheAccess(const Address& address,
401                                  CacheRequestType type,
402                                  DataBlock*& data_ptr)
403{
404  assert(address == line_address(address));
405  DEBUG_EXPR(CACHE_COMP, HighPrio, address);
406  Index cacheSet = addressToCacheSet(address);
407  int loc = findTagInSet(cacheSet, address);
408  if(loc != -1){ // Do we even have a tag match?
409    AbstractCacheEntry* entry = m_cache[cacheSet][loc];
410    m_replacementPolicy_ptr->touch(cacheSet, loc, g_eventQueue_ptr->getTime());
411    data_ptr = &(entry->getDataBlk());
412
413    return (m_cache[cacheSet][loc]->m_Permission != AccessPermission_NotPresent);
414  }
415  data_ptr = NULL;
416  return false;
417}
418
419// tests to see if an address is present in the cache
420inline
421bool CacheMemory::isTagPresent(const Address& address) const
422{
423  assert(address == line_address(address));
424  Index cacheSet = addressToCacheSet(address);
425  int location = findTagInSet(cacheSet, address);
426
427  if (location == -1) {
428    // We didn't find the tag
429    DEBUG_EXPR(CACHE_COMP, LowPrio, address);
430    DEBUG_MSG(CACHE_COMP, LowPrio, "No tag match");
431    return false;
432  }
433  DEBUG_EXPR(CACHE_COMP, LowPrio, address);
434  DEBUG_MSG(CACHE_COMP, LowPrio, "found");
435  return true;
436}
437
438// Returns true if there is:
439//   a) a tag match on this address or there is
440//   b) an unused line in the same cache "way"
441inline
442bool CacheMemory::cacheAvail(const Address& address) const
443{
444  assert(address == line_address(address));
445
446  Index cacheSet = addressToCacheSet(address);
447
448  for (int i=0; i < m_cache_assoc; i++) {
449    AbstractCacheEntry* entry = m_cache[cacheSet][i];
450    if (entry != NULL) {
451      if (entry->m_Address == address ||                         // Already in the cache
452          entry->m_Permission == AccessPermission_NotPresent) {  // We found an empty entry
453        return true;
454      }
455    } else {
456      return true;
457    }
458  }
459  return false;
460}
461
462inline
463void CacheMemory::allocate(const Address& address, AbstractCacheEntry* entry)
464{
465  assert(address == line_address(address));
466  assert(!isTagPresent(address));
467  assert(cacheAvail(address));
468  DEBUG_EXPR(CACHE_COMP, HighPrio, address);
469
470  // Find the first open slot
471  Index cacheSet = addressToCacheSet(address);
472  for (int i=0; i < m_cache_assoc; i++) {
473    if (m_cache[cacheSet][i] == NULL ||
474        m_cache[cacheSet][i]->m_Permission == AccessPermission_NotPresent) {
475      m_cache[cacheSet][i] = entry;  // Init entry
476      m_cache[cacheSet][i]->m_Address = address;
477      m_cache[cacheSet][i]->m_Permission = AccessPermission_Invalid;
478
479      m_replacementPolicy_ptr->touch(cacheSet, i, g_eventQueue_ptr->getTime());
480
481      return;
482    }
483  }
484  ERROR_MSG("Allocate didn't find an available entry");
485}
486
487inline
488void CacheMemory::deallocate(const Address& address)
489{
490  assert(address == line_address(address));
491  assert(isTagPresent(address));
492  DEBUG_EXPR(CACHE_COMP, HighPrio, address);
493  Index cacheSet = addressToCacheSet(address);
494  int location = findTagInSet(cacheSet, address);
495  if (location != -1){
496    delete m_cache[cacheSet][location];
497    m_cache[cacheSet][location] = NULL;
498  }
499}
500
501// Returns with the physical address of the conflicting cache line
502inline
503Address CacheMemory::cacheProbe(const Address& address) const
504{
505  assert(address == line_address(address));
506  assert(!cacheAvail(address));
507
508  Index cacheSet = addressToCacheSet(address);
509  return m_cache[cacheSet][m_replacementPolicy_ptr->getVictim(cacheSet)]->m_Address;
510}
511
512// looks an address up in the cache
513inline
514AbstractCacheEntry& CacheMemory::lookup(const Address& address)
515{
516  assert(address == line_address(address));
517  Index cacheSet = addressToCacheSet(address);
518  int loc = findTagInSet(cacheSet, address);
519  assert(loc != -1);
520  return *m_cache[cacheSet][loc];
521}
522
523// looks an address up in the cache
524inline
525const AbstractCacheEntry& CacheMemory::lookup(const Address& address) const
526{
527  assert(address == line_address(address));
528  Index cacheSet = addressToCacheSet(address);
529  int loc = findTagInSet(cacheSet, address);
530  assert(loc != -1);
531  return *m_cache[cacheSet][loc];
532}
533
534inline
535AccessPermission CacheMemory::getPermission(const Address& address) const
536{
537  assert(address == line_address(address));
538  return lookup(address).m_Permission;
539}
540
541inline
542void CacheMemory::changePermission(const Address& address, AccessPermission new_perm)
543{
544  assert(address == line_address(address));
545  lookup(address).m_Permission = new_perm;
546  assert(getPermission(address) == new_perm);
547}
548
549// Sets the most recently used bit for a cache block
550inline
551void CacheMemory::setMRU(const Address& address)
552{
553  Index cacheSet;
554
555  cacheSet = addressToCacheSet(address);
556  m_replacementPolicy_ptr->touch(cacheSet,
557                                 findTagInSet(cacheSet, address),
558                                 g_eventQueue_ptr->getTime());
559}
560
561inline
562void CacheMemory::recordCacheContents(CacheRecorder& tr) const
563{
564  for (int i = 0; i < m_cache_num_sets; i++) {
565    for (int j = 0; j < m_cache_assoc; j++) {
566      AccessPermission perm = m_cache[i][j]->m_Permission;
567      CacheRequestType request_type = CacheRequestType_NULL;
568      if (perm == AccessPermission_Read_Only) {
569        if (m_is_instruction_only_cache) {
570          request_type = CacheRequestType_IFETCH;
571        } else {
572          request_type = CacheRequestType_LD;
573        }
574      } else if (perm == AccessPermission_Read_Write) {
575        request_type = CacheRequestType_ST;
576      }
577
578      if (request_type != CacheRequestType_NULL) {
579        //        tr.addRecord(m_chip_ptr->getID(), m_cache[i][j].m_Address,
580        //                     Address(0), request_type, m_replacementPolicy_ptr->getLastAccess(i, j));
581      }
582    }
583  }
584}
585
586inline
587void CacheMemory::print(ostream& out) const
588{
589  out << "Cache dump: " << m_cache_name << endl;
590  for (int i = 0; i < m_cache_num_sets; i++) {
591    for (int j = 0; j < m_cache_assoc; j++) {
592      if (m_cache[i][j] != NULL) {
593        out << "  Index: " << i
594            << " way: " << j
595            << " entry: " << *m_cache[i][j] << endl;
596      } else {
597        out << "  Index: " << i
598            << " way: " << j
599            << " entry: NULL" << endl;
600      }
601    }
602  }
603}
604
605inline
606void CacheMemory::printData(ostream& out) const
607{
608  out << "printData() not supported" << endl;
609}
610
611inline
612void CacheMemory::getMemoryValue(const Address& addr, char* value,
613                                 unsigned int size_in_bytes ){
614  AbstractCacheEntry& entry = lookup(line_address(addr));
615  unsigned int startByte = addr.getAddress() - line_address(addr).getAddress();
616  for(unsigned int i=0; i<size_in_bytes; ++i){
617    value[i] = entry.getDataBlk().getByte(i + startByte);
618  }
619}
620
621inline
622void CacheMemory::setMemoryValue(const Address& addr, char* value,
623                                 unsigned int size_in_bytes ){
624  AbstractCacheEntry& entry = lookup(line_address(addr));
625  unsigned int startByte = addr.getAddress() - line_address(addr).getAddress();
626  assert(size_in_bytes > 0);
627  for(unsigned int i=0; i<size_in_bytes; ++i){
628    entry.getDataBlk().setByte(i + startByte, value[i]);
629  }
630
631  //  entry = lookup(line_address(addr));
632}
633
634#endif //CACHEMEMORY_H
635
636