CacheMemory.cc revision 7805
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    m_start_index_bit = p->start_index_bit;
57}
58
59void
60CacheMemory::init()
61{
62    m_cache_num_sets = (m_cache_size / m_cache_assoc) /
63        RubySystem::getBlockSizeBytes();
64    assert(m_cache_num_sets > 1);
65    m_cache_num_set_bits = floorLog2(m_cache_num_sets);
66    assert(m_cache_num_set_bits > 0);
67
68    if (m_policy == "PSEUDO_LRU")
69        m_replacementPolicy_ptr =
70            new PseudoLRUPolicy(m_cache_num_sets, m_cache_assoc);
71    else if (m_policy == "LRU")
72        m_replacementPolicy_ptr =
73            new LRUPolicy(m_cache_num_sets, m_cache_assoc);
74    else
75        assert(false);
76
77    m_cache.resize(m_cache_num_sets);
78    m_locked.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        m_locked[i].resize(m_cache_assoc);
82        for (int j = 0; j < m_cache_assoc; j++) {
83            m_cache[i][j] = NULL;
84            m_locked[i][j] = -1;
85        }
86    }
87}
88
89CacheMemory::~CacheMemory()
90{
91    if (m_replacementPolicy_ptr != NULL)
92        delete m_replacementPolicy_ptr;
93    delete m_profiler_ptr;
94    for (int i = 0; i < m_cache_num_sets; i++) {
95        for (int j = 0; j < m_cache_assoc; j++) {
96            delete m_cache[i][j];
97        }
98    }
99}
100
101void
102CacheMemory::printConfig(ostream& out)
103{
104    int block_size = RubySystem::getBlockSizeBytes();
105
106    out << "Cache config: " << m_cache_name << endl;
107    out << "  cache_associativity: " << m_cache_assoc << endl;
108    out << "  num_cache_sets_bits: " << m_cache_num_set_bits << endl;
109    const int cache_num_sets = 1 << m_cache_num_set_bits;
110    out << "  num_cache_sets: " << cache_num_sets << endl;
111    out << "  cache_set_size_bytes: " << cache_num_sets * block_size << endl;
112    out << "  cache_set_size_Kbytes: "
113        << double(cache_num_sets * block_size) / (1<<10) << endl;
114    out << "  cache_set_size_Mbytes: "
115        << double(cache_num_sets * block_size) / (1<<20) << endl;
116    out << "  cache_size_bytes: "
117        << cache_num_sets * block_size * m_cache_assoc << endl;
118    out << "  cache_size_Kbytes: "
119        << double(cache_num_sets * block_size * m_cache_assoc) / (1<<10)
120        << endl;
121    out << "  cache_size_Mbytes: "
122        << double(cache_num_sets * block_size * m_cache_assoc) / (1<<20)
123        << endl;
124}
125
126// convert a Address to its location in the cache
127Index
128CacheMemory::addressToCacheSet(const Address& address) const
129{
130    assert(address == line_address(address));
131    return address.bitSelect(m_start_index_bit,
132                             m_start_index_bit + m_cache_num_set_bits - 1);
133}
134
135// Given a cache index: returns the index of the tag in a set.
136// returns -1 if the tag is not found.
137int
138CacheMemory::findTagInSet(Index cacheSet, const Address& tag) const
139{
140    assert(tag == line_address(tag));
141    // search the set for the tags
142    m5::hash_map<Address, int>::const_iterator it = m_tag_index.find(tag);
143    if (it != m_tag_index.end())
144        if (m_cache[cacheSet][it->second]->m_Permission !=
145            AccessPermission_NotPresent)
146            return it->second;
147    return -1; // Not found
148}
149
150// Given a cache index: returns the index of the tag in a set.
151// returns -1 if the tag is not found.
152int
153CacheMemory::findTagInSetIgnorePermissions(Index cacheSet,
154                                           const Address& tag) const
155{
156    assert(tag == line_address(tag));
157    // search the set for the tags
158    m5::hash_map<Address, int>::const_iterator it = m_tag_index.find(tag);
159    if (it != m_tag_index.end())
160        return it->second;
161    return -1; // Not found
162}
163
164bool
165CacheMemory::tryCacheAccess(const Address& address, CacheRequestType type,
166                            DataBlock*& data_ptr)
167{
168    assert(address == line_address(address));
169    DPRINTF(RubyCache, "address: %s\n", address);
170    Index cacheSet = addressToCacheSet(address);
171    int loc = findTagInSet(cacheSet, address);
172    if (loc != -1) {
173        // Do we even have a tag match?
174        AbstractCacheEntry* entry = m_cache[cacheSet][loc];
175        m_replacementPolicy_ptr->
176            touch(cacheSet, loc, g_eventQueue_ptr->getTime());
177        data_ptr = &(entry->getDataBlk());
178
179        if (entry->m_Permission == AccessPermission_Read_Write) {
180            return true;
181        }
182        if ((entry->m_Permission == AccessPermission_Read_Only) &&
183            (type == CacheRequestType_LD || type == CacheRequestType_IFETCH)) {
184            return true;
185        }
186        // The line must not be accessible
187    }
188    data_ptr = NULL;
189    return false;
190}
191
192bool
193CacheMemory::testCacheAccess(const Address& address, CacheRequestType type,
194                             DataBlock*& data_ptr)
195{
196    assert(address == line_address(address));
197    DPRINTF(RubyCache, "address: %s\n", address);
198    Index cacheSet = addressToCacheSet(address);
199    int loc = findTagInSet(cacheSet, address);
200
201    if (loc != -1) {
202        // Do we even have a tag match?
203        AbstractCacheEntry* entry = m_cache[cacheSet][loc];
204        m_replacementPolicy_ptr->
205            touch(cacheSet, loc, g_eventQueue_ptr->getTime());
206        data_ptr = &(entry->getDataBlk());
207
208        return m_cache[cacheSet][loc]->m_Permission !=
209            AccessPermission_NotPresent;
210    }
211
212    data_ptr = NULL;
213    return false;
214}
215
216// tests to see if an address is present in the cache
217bool
218CacheMemory::isTagPresent(const Address& address) const
219{
220    assert(address == line_address(address));
221    Index cacheSet = addressToCacheSet(address);
222    int loc = findTagInSet(cacheSet, address);
223
224    if (loc == -1) {
225        // We didn't find the tag
226        DPRINTF(RubyCache, "No tag match for address: %s\n", address);
227        return false;
228    }
229    DPRINTF(RubyCache, "address: %s found\n", address);
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    DPRINTF(RubyCache, "address: %s\n", address);
265
266    // Find the first open slot
267    Index cacheSet = addressToCacheSet(address);
268    std::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    panic("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    DPRINTF(RubyCache, "address: %s\n", 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_Invalid) ||
356        (new_perm == AccessPermission_NotPresent) ||
357        (new_perm == AccessPermission_Stale)) {
358        DPRINTF(RubyCache, "Permission clearing lock for addr: %x\n", address);
359        m_locked[cacheSet][loc] = -1;
360    }
361    assert(getPermission(address) == new_perm);
362}
363
364// Sets the most recently used bit for a cache block
365void
366CacheMemory::setMRU(const Address& address)
367{
368    Index cacheSet;
369
370    cacheSet = addressToCacheSet(address);
371    m_replacementPolicy_ptr->
372        touch(cacheSet, findTagInSet(cacheSet, address),
373              g_eventQueue_ptr->getTime());
374}
375
376void
377CacheMemory::profileMiss(const CacheMsg& msg)
378{
379    m_profiler_ptr->addCacheStatSample(msg.getType(),
380                                       msg.getAccessMode(),
381                                       msg.getPrefetch());
382}
383
384void
385CacheMemory::profileGenericRequest(GenericRequestType requestType,
386                                   AccessModeType accessType,
387                                   PrefetchBit pfBit)
388{
389    m_profiler_ptr->addGenericStatSample(requestType,
390                                         accessType,
391                                         pfBit);
392}
393
394void
395CacheMemory::recordCacheContents(CacheRecorder& tr) const
396{
397    for (int i = 0; i < m_cache_num_sets; i++) {
398        for (int j = 0; j < m_cache_assoc; j++) {
399            AccessPermission perm = m_cache[i][j]->m_Permission;
400            CacheRequestType request_type = CacheRequestType_NULL;
401            if (perm == AccessPermission_Read_Only) {
402                if (m_is_instruction_only_cache) {
403                    request_type = CacheRequestType_IFETCH;
404                } else {
405                    request_type = CacheRequestType_LD;
406                }
407            } else if (perm == AccessPermission_Read_Write) {
408                request_type = CacheRequestType_ST;
409            }
410
411            if (request_type != CacheRequestType_NULL) {
412#if 0
413                tr.addRecord(m_chip_ptr->getID(), m_cache[i][j].m_Address,
414                             Address(0), request_type,
415                             m_replacementPolicy_ptr->getLastAccess(i, j));
416#endif
417            }
418        }
419    }
420}
421
422void
423CacheMemory::print(ostream& out) const
424{
425    out << "Cache dump: " << m_cache_name << endl;
426    for (int i = 0; i < m_cache_num_sets; i++) {
427        for (int j = 0; j < m_cache_assoc; j++) {
428            if (m_cache[i][j] != NULL) {
429                out << "  Index: " << i
430                    << " way: " << j
431                    << " entry: " << *m_cache[i][j] << endl;
432            } else {
433                out << "  Index: " << i
434                    << " way: " << j
435                    << " entry: NULL" << endl;
436            }
437        }
438    }
439}
440
441void
442CacheMemory::printData(ostream& out) const
443{
444    out << "printData() not supported" << endl;
445}
446
447void
448CacheMemory::clearStats() const
449{
450    m_profiler_ptr->clearStats();
451}
452
453void
454CacheMemory::printStats(ostream& out) const
455{
456    m_profiler_ptr->printStats(out);
457}
458
459void
460CacheMemory::getMemoryValue(const Address& addr, char* value,
461                            unsigned size_in_bytes)
462{
463    AbstractCacheEntry& entry = lookup(line_address(addr));
464    unsigned startByte = addr.getAddress() - line_address(addr).getAddress();
465    for (unsigned i = 0; i < size_in_bytes; ++i) {
466        value[i] = entry.getDataBlk().getByte(i + startByte);
467    }
468}
469
470void
471CacheMemory::setMemoryValue(const Address& addr, char* value,
472                            unsigned size_in_bytes)
473{
474    AbstractCacheEntry& entry = lookup(line_address(addr));
475    unsigned startByte = addr.getAddress() - line_address(addr).getAddress();
476    assert(size_in_bytes > 0);
477    for (unsigned i = 0; i < size_in_bytes; ++i) {
478        entry.getDataBlk().setByte(i + startByte, value[i]);
479    }
480
481    // entry = lookup(line_address(addr));
482}
483
484void
485CacheMemory::setLocked(const Address& address, int context)
486{
487    DPRINTF(RubyCache, "Setting Lock for addr: %x to %d\n", address, context);
488    assert(address == line_address(address));
489    Index cacheSet = addressToCacheSet(address);
490    int loc = findTagInSet(cacheSet, address);
491    assert(loc != -1);
492    m_locked[cacheSet][loc] = context;
493}
494
495void
496CacheMemory::clearLocked(const Address& address)
497{
498    DPRINTF(RubyCache, "Clear Lock for addr: %x\n", address);
499    assert(address == line_address(address));
500    Index cacheSet = addressToCacheSet(address);
501    int loc = findTagInSet(cacheSet, address);
502    assert(loc != -1);
503    m_locked[cacheSet][loc] = -1;
504}
505
506bool
507CacheMemory::isLocked(const Address& address, int context)
508{
509    assert(address == line_address(address));
510    Index cacheSet = addressToCacheSet(address);
511    int loc = findTagInSet(cacheSet, address);
512    assert(loc != -1);
513    DPRINTF(RubyCache, "Testing Lock for addr: %llx cur %d con %d\n",
514            address, m_locked[cacheSet][loc], context);
515    return m_locked[cacheSet][loc] == context;
516}
517
518