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