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