CacheMemory.cc revision 7055
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 "mem/ruby/system/CacheMemory.hh"
30
31using namespace std;
32
33ostream&
34operator<<(ostream& out, const CacheMemory& obj)
35{
36    obj.print(out);
37    out << flush;
38    return out;
39}
40
41CacheMemory *
42RubyCacheParams::create()
43{
44    return new CacheMemory(this);
45}
46
47CacheMemory::CacheMemory(const Params *p)
48    : SimObject(p)
49{
50    m_cache_size = p->size;
51    m_latency = p->latency;
52    m_cache_assoc = p->assoc;
53    m_policy = p->replacement_policy;
54    m_profiler_ptr = new CacheProfiler(name());
55}
56
57void
58CacheMemory::init()
59{
60    m_cache_num_sets = (m_cache_size / m_cache_assoc) /
61        RubySystem::getBlockSizeBytes();
62    assert(m_cache_num_sets > 1);
63    m_cache_num_set_bits = log_int(m_cache_num_sets);
64    assert(m_cache_num_set_bits > 0);
65
66    if (m_policy == "PSEUDO_LRU")
67        m_replacementPolicy_ptr =
68            new PseudoLRUPolicy(m_cache_num_sets, m_cache_assoc);
69    else if (m_policy == "LRU")
70        m_replacementPolicy_ptr =
71            new LRUPolicy(m_cache_num_sets, m_cache_assoc);
72    else
73        assert(false);
74
75    m_cache.setSize(m_cache_num_sets);
76    m_locked.setSize(m_cache_num_sets);
77    for (int i = 0; i < m_cache_num_sets; i++) {
78        m_cache[i].setSize(m_cache_assoc);
79        m_locked[i].setSize(m_cache_assoc);
80        for (int j = 0; j < m_cache_assoc; j++) {
81            m_cache[i][j] = NULL;
82            m_locked[i][j] = -1;
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(RubySystem::getBlockSizeBits(),
130        RubySystem::getBlockSizeBits() + 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, CacheRequestType type,
164                            DataBlock*& data_ptr)
165{
166    assert(address == line_address(address));
167    DEBUG_EXPR(CACHE_COMP, HighPrio, 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 == CacheRequestType_LD || type == CacheRequestType_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, CacheRequestType type,
192                             DataBlock*& data_ptr)
193{
194    assert(address == line_address(address));
195    DEBUG_EXPR(CACHE_COMP, HighPrio, 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        DEBUG_EXPR(CACHE_COMP, LowPrio, address);
225        DEBUG_MSG(CACHE_COMP, LowPrio, "No tag match");
226        return false;
227    }
228    DEBUG_EXPR(CACHE_COMP, LowPrio, address);
229    DEBUG_MSG(CACHE_COMP, LowPrio, "found");
230    return true;
231}
232
233// Returns true if there is:
234//   a) a tag match on this address or there is
235//   b) an unused line in the same cache "way"
236bool
237CacheMemory::cacheAvail(const Address& address) const
238{
239    assert(address == line_address(address));
240
241    Index cacheSet = addressToCacheSet(address);
242
243    for (int i = 0; i < m_cache_assoc; i++) {
244        AbstractCacheEntry* entry = m_cache[cacheSet][i];
245        if (entry != NULL) {
246            if (entry->m_Address == address ||
247                entry->m_Permission == AccessPermission_NotPresent) {
248                // Already in the cache or we found an empty entry
249                return true;
250            }
251        } else {
252            return true;
253        }
254    }
255    return false;
256}
257
258void
259CacheMemory::allocate(const Address& address, AbstractCacheEntry* entry)
260{
261    assert(address == line_address(address));
262    assert(!isTagPresent(address));
263    assert(cacheAvail(address));
264    DEBUG_EXPR(CACHE_COMP, HighPrio, address);
265
266    // Find the first open slot
267    Index cacheSet = addressToCacheSet(address);
268    Vector<AbstractCacheEntry*> &set = m_cache[cacheSet];
269    for (int i = 0; i < m_cache_assoc; i++) {
270        if (!set[i] || set[i]->m_Permission == AccessPermission_NotPresent) {
271            set[i] = entry;  // Init entry
272            set[i]->m_Address = address;
273            set[i]->m_Permission = AccessPermission_Invalid;
274            DPRINTF(RubyCache, "Allocate clearing lock for addr: %x\n",
275                    address);
276            m_locked[cacheSet][i] = -1;
277            m_tag_index[address] = i;
278
279            m_replacementPolicy_ptr->
280                touch(cacheSet, i, g_eventQueue_ptr->getTime());
281
282            return;
283        }
284    }
285    ERROR_MSG("Allocate didn't find an available entry");
286}
287
288void
289CacheMemory::deallocate(const Address& address)
290{
291    assert(address == line_address(address));
292    assert(isTagPresent(address));
293    DEBUG_EXPR(CACHE_COMP, HighPrio, address);
294    Index cacheSet = addressToCacheSet(address);
295    int loc = findTagInSet(cacheSet, address);
296    if (loc != -1) {
297        delete m_cache[cacheSet][loc];
298        m_cache[cacheSet][loc] = NULL;
299        DPRINTF(RubyCache, "Deallocate clearing lock for addr: %x\n",
300                address);
301        m_locked[cacheSet][loc] = -1;
302        m_tag_index.erase(address);
303    }
304}
305
306// Returns with the physical address of the conflicting cache line
307Address
308CacheMemory::cacheProbe(const Address& address) const
309{
310    assert(address == line_address(address));
311    assert(!cacheAvail(address));
312
313    Index cacheSet = addressToCacheSet(address);
314    return m_cache[cacheSet][m_replacementPolicy_ptr->getVictim(cacheSet)]->
315        m_Address;
316}
317
318// looks an address up in the cache
319AbstractCacheEntry&
320CacheMemory::lookup(const Address& address)
321{
322    assert(address == line_address(address));
323    Index cacheSet = addressToCacheSet(address);
324    int loc = findTagInSet(cacheSet, address);
325    assert(loc != -1);
326    return *m_cache[cacheSet][loc];
327}
328
329// looks an address up in the cache
330const AbstractCacheEntry&
331CacheMemory::lookup(const Address& address) const
332{
333    assert(address == line_address(address));
334    Index cacheSet = addressToCacheSet(address);
335    int loc = findTagInSet(cacheSet, address);
336    assert(loc != -1);
337    return *m_cache[cacheSet][loc];
338}
339
340AccessPermission
341CacheMemory::getPermission(const Address& address) const
342{
343    assert(address == line_address(address));
344    return lookup(address).m_Permission;
345}
346
347void
348CacheMemory::changePermission(const Address& address,
349                              AccessPermission new_perm)
350{
351    assert(address == line_address(address));
352    lookup(address).m_Permission = new_perm;
353    Index cacheSet = addressToCacheSet(address);
354    int loc = findTagInSet(cacheSet, address);
355    if (new_perm != AccessPermission_Read_Write) {
356        DPRINTF(RubyCache, "Permission clearing lock for addr: %x\n", address);
357        m_locked[cacheSet][loc] = -1;
358    }
359    assert(getPermission(address) == new_perm);
360}
361
362// Sets the most recently used bit for a cache block
363void
364CacheMemory::setMRU(const Address& address)
365{
366    Index cacheSet;
367
368    cacheSet = addressToCacheSet(address);
369    m_replacementPolicy_ptr->
370        touch(cacheSet, findTagInSet(cacheSet, address),
371              g_eventQueue_ptr->getTime());
372}
373
374void
375CacheMemory::profileMiss(const CacheMsg& msg)
376{
377    m_profiler_ptr->addStatSample(msg.getType(), msg.getAccessMode(),
378                                  msg.getSize(), msg.getPrefetch());
379}
380
381void
382CacheMemory::recordCacheContents(CacheRecorder& tr) const
383{
384    for (int i = 0; i < m_cache_num_sets; i++) {
385        for (int j = 0; j < m_cache_assoc; j++) {
386            AccessPermission perm = m_cache[i][j]->m_Permission;
387            CacheRequestType request_type = CacheRequestType_NULL;
388            if (perm == AccessPermission_Read_Only) {
389                if (m_is_instruction_only_cache) {
390                    request_type = CacheRequestType_IFETCH;
391                } else {
392                    request_type = CacheRequestType_LD;
393                }
394            } else if (perm == AccessPermission_Read_Write) {
395                request_type = CacheRequestType_ST;
396            }
397
398            if (request_type != CacheRequestType_NULL) {
399#if 0
400                tr.addRecord(m_chip_ptr->getID(), m_cache[i][j].m_Address,
401                             Address(0), request_type,
402                             m_replacementPolicy_ptr->getLastAccess(i, j));
403#endif
404            }
405        }
406    }
407}
408
409void
410CacheMemory::print(ostream& out) const
411{
412    out << "Cache dump: " << m_cache_name << endl;
413    for (int i = 0; i < m_cache_num_sets; i++) {
414        for (int j = 0; j < m_cache_assoc; j++) {
415            if (m_cache[i][j] != NULL) {
416                out << "  Index: " << i
417                    << " way: " << j
418                    << " entry: " << *m_cache[i][j] << endl;
419            } else {
420                out << "  Index: " << i
421                    << " way: " << j
422                    << " entry: NULL" << endl;
423            }
424        }
425    }
426}
427
428void
429CacheMemory::printData(ostream& out) const
430{
431    out << "printData() not supported" << endl;
432}
433
434void
435CacheMemory::clearStats() const
436{
437    m_profiler_ptr->clearStats();
438}
439
440void
441CacheMemory::printStats(ostream& out) const
442{
443    m_profiler_ptr->printStats(out);
444}
445
446void
447CacheMemory::getMemoryValue(const Address& addr, char* value,
448                            unsigned size_in_bytes)
449{
450    AbstractCacheEntry& entry = lookup(line_address(addr));
451    unsigned startByte = addr.getAddress() - line_address(addr).getAddress();
452    for (unsigned i = 0; i < size_in_bytes; ++i) {
453        value[i] = entry.getDataBlk().getByte(i + startByte);
454    }
455}
456
457void
458CacheMemory::setMemoryValue(const Address& addr, char* value,
459                            unsigned size_in_bytes)
460{
461    AbstractCacheEntry& entry = lookup(line_address(addr));
462    unsigned startByte = addr.getAddress() - line_address(addr).getAddress();
463    assert(size_in_bytes > 0);
464    for (unsigned i = 0; i < size_in_bytes; ++i) {
465        entry.getDataBlk().setByte(i + startByte, value[i]);
466    }
467
468    // entry = lookup(line_address(addr));
469}
470
471void
472CacheMemory::setLocked(const Address& address, int context)
473{
474    DPRINTF(RubyCache, "Setting Lock for addr: %x to %d\n", address, context);
475    assert(address == line_address(address));
476    Index cacheSet = addressToCacheSet(address);
477    int loc = findTagInSet(cacheSet, address);
478    assert(loc != -1);
479    m_locked[cacheSet][loc] = context;
480}
481
482void
483CacheMemory::clearLocked(const Address& address)
484{
485    DPRINTF(RubyCache, "Clear Lock for addr: %x\n", address);
486    assert(address == line_address(address));
487    Index cacheSet = addressToCacheSet(address);
488    int loc = findTagInSet(cacheSet, address);
489    assert(loc != -1);
490    m_locked[cacheSet][loc] = -1;
491}
492
493bool
494CacheMemory::isLocked(const Address& address, int context)
495{
496    assert(address == line_address(address));
497    Index cacheSet = addressToCacheSet(address);
498    int loc = findTagInSet(cacheSet, address);
499    assert(loc != -1);
500    DPRINTF(RubyCache, "Testing Lock for addr: %llx cur %d con %d\n",
501            address, m_locked[cacheSet][loc], context);
502    return m_locked[cacheSet][loc] == context;
503}
504
505