Currently the sequencer calls the function setMRU that updates the replacement policy structures with the first level caches. While functionally this is correct, the problem is that this requires calling findTagInSet() which is an expensive function. This patch removes the calls to setMRU from the sequencer. All controllers should now update the replacement policy on their own. The set and the way index for a given cache entry can be found within the AbstractCacheEntry structure. Use these indicies to update the replacement policy structures.
616 lines
19 KiB
C++
616 lines
19 KiB
C++
/*
|
|
* Copyright (c) 1999-2012 Mark D. Hill and David A. Wood
|
|
* Copyright (c) 2013 Advanced Micro Devices, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "base/intmath.hh"
|
|
#include "debug/RubyCache.hh"
|
|
#include "debug/RubyCacheTrace.hh"
|
|
#include "debug/RubyResourceStalls.hh"
|
|
#include "debug/RubyStats.hh"
|
|
#include "mem/protocol/AccessPermission.hh"
|
|
#include "mem/ruby/structures/CacheMemory.hh"
|
|
#include "mem/ruby/system/System.hh"
|
|
|
|
using namespace std;
|
|
|
|
ostream&
|
|
operator<<(ostream& out, const CacheMemory& obj)
|
|
{
|
|
obj.print(out);
|
|
out << flush;
|
|
return out;
|
|
}
|
|
|
|
CacheMemory *
|
|
RubyCacheParams::create()
|
|
{
|
|
return new CacheMemory(this);
|
|
}
|
|
|
|
CacheMemory::CacheMemory(const Params *p)
|
|
: SimObject(p),
|
|
dataArray(p->dataArrayBanks, p->dataAccessLatency,
|
|
p->start_index_bit, p->ruby_system),
|
|
tagArray(p->tagArrayBanks, p->tagAccessLatency,
|
|
p->start_index_bit, p->ruby_system)
|
|
{
|
|
m_cache_size = p->size;
|
|
m_cache_assoc = p->assoc;
|
|
m_replacementPolicy_ptr = p->replacement_policy;
|
|
m_replacementPolicy_ptr->setCache(this);
|
|
m_start_index_bit = p->start_index_bit;
|
|
m_is_instruction_only_cache = p->is_icache;
|
|
m_resource_stalls = p->resourceStalls;
|
|
}
|
|
|
|
void
|
|
CacheMemory::init()
|
|
{
|
|
m_cache_num_sets = (m_cache_size / m_cache_assoc) /
|
|
RubySystem::getBlockSizeBytes();
|
|
assert(m_cache_num_sets > 1);
|
|
m_cache_num_set_bits = floorLog2(m_cache_num_sets);
|
|
assert(m_cache_num_set_bits > 0);
|
|
|
|
m_cache.resize(m_cache_num_sets);
|
|
for (int i = 0; i < m_cache_num_sets; i++) {
|
|
m_cache[i].resize(m_cache_assoc);
|
|
for (int j = 0; j < m_cache_assoc; j++) {
|
|
m_cache[i][j] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
CacheMemory::~CacheMemory()
|
|
{
|
|
if (m_replacementPolicy_ptr != NULL)
|
|
delete m_replacementPolicy_ptr;
|
|
for (int i = 0; i < m_cache_num_sets; i++) {
|
|
for (int j = 0; j < m_cache_assoc; j++) {
|
|
delete m_cache[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
// convert a Address to its location in the cache
|
|
int64_t
|
|
CacheMemory::addressToCacheSet(Addr address) const
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
return bitSelect(address, m_start_index_bit,
|
|
m_start_index_bit + m_cache_num_set_bits - 1);
|
|
}
|
|
|
|
// Given a cache index: returns the index of the tag in a set.
|
|
// returns -1 if the tag is not found.
|
|
int
|
|
CacheMemory::findTagInSet(int64_t cacheSet, Addr tag) const
|
|
{
|
|
assert(tag == makeLineAddress(tag));
|
|
// search the set for the tags
|
|
m5::hash_map<Addr, int>::const_iterator it = m_tag_index.find(tag);
|
|
if (it != m_tag_index.end())
|
|
if (m_cache[cacheSet][it->second]->m_Permission !=
|
|
AccessPermission_NotPresent)
|
|
return it->second;
|
|
return -1; // Not found
|
|
}
|
|
|
|
// Given a cache index: returns the index of the tag in a set.
|
|
// returns -1 if the tag is not found.
|
|
int
|
|
CacheMemory::findTagInSetIgnorePermissions(int64_t cacheSet,
|
|
Addr tag) const
|
|
{
|
|
assert(tag == makeLineAddress(tag));
|
|
// search the set for the tags
|
|
m5::hash_map<Addr, int>::const_iterator it = m_tag_index.find(tag);
|
|
if (it != m_tag_index.end())
|
|
return it->second;
|
|
return -1; // Not found
|
|
}
|
|
|
|
// Given an unique cache block identifier (idx): return the valid address
|
|
// stored by the cache block. If the block is invalid/notpresent, the
|
|
// function returns the 0 address
|
|
Addr
|
|
CacheMemory::getAddressAtIdx(int idx) const
|
|
{
|
|
Addr tmp(0);
|
|
|
|
int set = idx / m_cache_assoc;
|
|
assert(set < m_cache_num_sets);
|
|
|
|
int way = idx - set * m_cache_assoc;
|
|
assert (way < m_cache_assoc);
|
|
|
|
AbstractCacheEntry* entry = m_cache[set][way];
|
|
if (entry == NULL ||
|
|
entry->m_Permission == AccessPermission_Invalid ||
|
|
entry->m_Permission == AccessPermission_NotPresent) {
|
|
return tmp;
|
|
}
|
|
return entry->m_Address;
|
|
}
|
|
|
|
bool
|
|
CacheMemory::tryCacheAccess(Addr address, RubyRequestType type,
|
|
DataBlock*& data_ptr)
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
DPRINTF(RubyCache, "address: %s\n", address);
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
if (loc != -1) {
|
|
// Do we even have a tag match?
|
|
AbstractCacheEntry* entry = m_cache[cacheSet][loc];
|
|
m_replacementPolicy_ptr->touch(cacheSet, loc, curTick());
|
|
data_ptr = &(entry->getDataBlk());
|
|
|
|
if (entry->m_Permission == AccessPermission_Read_Write) {
|
|
return true;
|
|
}
|
|
if ((entry->m_Permission == AccessPermission_Read_Only) &&
|
|
(type == RubyRequestType_LD || type == RubyRequestType_IFETCH)) {
|
|
return true;
|
|
}
|
|
// The line must not be accessible
|
|
}
|
|
data_ptr = NULL;
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CacheMemory::testCacheAccess(Addr address, RubyRequestType type,
|
|
DataBlock*& data_ptr)
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
DPRINTF(RubyCache, "address: %s\n", address);
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
|
|
if (loc != -1) {
|
|
// Do we even have a tag match?
|
|
AbstractCacheEntry* entry = m_cache[cacheSet][loc];
|
|
m_replacementPolicy_ptr->touch(cacheSet, loc, curTick());
|
|
data_ptr = &(entry->getDataBlk());
|
|
|
|
return m_cache[cacheSet][loc]->m_Permission !=
|
|
AccessPermission_NotPresent;
|
|
}
|
|
|
|
data_ptr = NULL;
|
|
return false;
|
|
}
|
|
|
|
// tests to see if an address is present in the cache
|
|
bool
|
|
CacheMemory::isTagPresent(Addr address) const
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
|
|
if (loc == -1) {
|
|
// We didn't find the tag
|
|
DPRINTF(RubyCache, "No tag match for address: %s\n", address);
|
|
return false;
|
|
}
|
|
DPRINTF(RubyCache, "address: %s found\n", address);
|
|
return true;
|
|
}
|
|
|
|
// Returns true if there is:
|
|
// a) a tag match on this address or there is
|
|
// b) an unused line in the same cache "way"
|
|
bool
|
|
CacheMemory::cacheAvail(Addr address) const
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
|
|
for (int i = 0; i < m_cache_assoc; i++) {
|
|
AbstractCacheEntry* entry = m_cache[cacheSet][i];
|
|
if (entry != NULL) {
|
|
if (entry->m_Address == address ||
|
|
entry->m_Permission == AccessPermission_NotPresent) {
|
|
// Already in the cache or we found an empty entry
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AbstractCacheEntry*
|
|
CacheMemory::allocate(Addr address, AbstractCacheEntry *entry, bool touch)
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
assert(!isTagPresent(address));
|
|
assert(cacheAvail(address));
|
|
DPRINTF(RubyCache, "address: %s\n", address);
|
|
|
|
// Find the first open slot
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
std::vector<AbstractCacheEntry*> &set = m_cache[cacheSet];
|
|
for (int i = 0; i < m_cache_assoc; i++) {
|
|
if (!set[i] || set[i]->m_Permission == AccessPermission_NotPresent) {
|
|
set[i] = entry; // Init entry
|
|
set[i]->m_Address = address;
|
|
set[i]->m_Permission = AccessPermission_Invalid;
|
|
DPRINTF(RubyCache, "Allocate clearing lock for addr: %x\n",
|
|
address);
|
|
set[i]->m_locked = -1;
|
|
m_tag_index[address] = i;
|
|
entry->setSetIndex(cacheSet);
|
|
entry->setWayIndex(i);
|
|
|
|
if (touch) {
|
|
m_replacementPolicy_ptr->touch(cacheSet, i, curTick());
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
}
|
|
panic("Allocate didn't find an available entry");
|
|
}
|
|
|
|
void
|
|
CacheMemory::deallocate(Addr address)
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
assert(isTagPresent(address));
|
|
DPRINTF(RubyCache, "address: %s\n", address);
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
if (loc != -1) {
|
|
delete m_cache[cacheSet][loc];
|
|
m_cache[cacheSet][loc] = NULL;
|
|
m_tag_index.erase(address);
|
|
}
|
|
}
|
|
|
|
// Returns with the physical address of the conflicting cache line
|
|
Addr
|
|
CacheMemory::cacheProbe(Addr address) const
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
assert(!cacheAvail(address));
|
|
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
return m_cache[cacheSet][m_replacementPolicy_ptr->getVictim(cacheSet)]->
|
|
m_Address;
|
|
}
|
|
|
|
// looks an address up in the cache
|
|
AbstractCacheEntry*
|
|
CacheMemory::lookup(Addr address)
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
if(loc == -1) return NULL;
|
|
return m_cache[cacheSet][loc];
|
|
}
|
|
|
|
// looks an address up in the cache
|
|
const AbstractCacheEntry*
|
|
CacheMemory::lookup(Addr address) const
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
if(loc == -1) return NULL;
|
|
return m_cache[cacheSet][loc];
|
|
}
|
|
|
|
// Sets the most recently used bit for a cache block
|
|
void
|
|
CacheMemory::setMRU(Addr address)
|
|
{
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
|
|
if(loc != -1)
|
|
m_replacementPolicy_ptr->touch(cacheSet, loc, curTick());
|
|
}
|
|
|
|
void
|
|
CacheMemory::setMRU(const AbstractCacheEntry *e)
|
|
{
|
|
uint32_t cacheSet = e->getSetIndex();
|
|
uint32_t loc = e->getWayIndex();
|
|
m_replacementPolicy_ptr->touch(cacheSet, loc, curTick());
|
|
}
|
|
|
|
void
|
|
CacheMemory::recordCacheContents(int cntrl, CacheRecorder* tr) const
|
|
{
|
|
uint64_t warmedUpBlocks = 0;
|
|
uint64_t totalBlocks M5_VAR_USED = (uint64_t)m_cache_num_sets *
|
|
(uint64_t)m_cache_assoc;
|
|
|
|
for (int i = 0; i < m_cache_num_sets; i++) {
|
|
for (int j = 0; j < m_cache_assoc; j++) {
|
|
if (m_cache[i][j] != NULL) {
|
|
AccessPermission perm = m_cache[i][j]->m_Permission;
|
|
RubyRequestType request_type = RubyRequestType_NULL;
|
|
if (perm == AccessPermission_Read_Only) {
|
|
if (m_is_instruction_only_cache) {
|
|
request_type = RubyRequestType_IFETCH;
|
|
} else {
|
|
request_type = RubyRequestType_LD;
|
|
}
|
|
} else if (perm == AccessPermission_Read_Write) {
|
|
request_type = RubyRequestType_ST;
|
|
}
|
|
|
|
if (request_type != RubyRequestType_NULL) {
|
|
tr->addRecord(cntrl, m_cache[i][j]->m_Address,
|
|
0, request_type,
|
|
m_replacementPolicy_ptr->getLastAccess(i, j),
|
|
m_cache[i][j]->getDataBlk());
|
|
warmedUpBlocks++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DPRINTF(RubyCacheTrace, "%s: %lli blocks of %lli total blocks"
|
|
"recorded %.2f%% \n", name().c_str(), warmedUpBlocks,
|
|
totalBlocks, (float(warmedUpBlocks) / float(totalBlocks)) * 100.0);
|
|
}
|
|
|
|
void
|
|
CacheMemory::print(ostream& out) const
|
|
{
|
|
out << "Cache dump: " << name() << endl;
|
|
for (int i = 0; i < m_cache_num_sets; i++) {
|
|
for (int j = 0; j < m_cache_assoc; j++) {
|
|
if (m_cache[i][j] != NULL) {
|
|
out << " Index: " << i
|
|
<< " way: " << j
|
|
<< " entry: " << *m_cache[i][j] << endl;
|
|
} else {
|
|
out << " Index: " << i
|
|
<< " way: " << j
|
|
<< " entry: NULL" << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CacheMemory::printData(ostream& out) const
|
|
{
|
|
out << "printData() not supported" << endl;
|
|
}
|
|
|
|
void
|
|
CacheMemory::setLocked(Addr address, int context)
|
|
{
|
|
DPRINTF(RubyCache, "Setting Lock for addr: %x to %d\n", address, context);
|
|
assert(address == makeLineAddress(address));
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
assert(loc != -1);
|
|
m_cache[cacheSet][loc]->setLocked(context);
|
|
}
|
|
|
|
void
|
|
CacheMemory::clearLocked(Addr address)
|
|
{
|
|
DPRINTF(RubyCache, "Clear Lock for addr: %x\n", address);
|
|
assert(address == makeLineAddress(address));
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
assert(loc != -1);
|
|
m_cache[cacheSet][loc]->clearLocked();
|
|
}
|
|
|
|
bool
|
|
CacheMemory::isLocked(Addr address, int context)
|
|
{
|
|
assert(address == makeLineAddress(address));
|
|
int64_t cacheSet = addressToCacheSet(address);
|
|
int loc = findTagInSet(cacheSet, address);
|
|
assert(loc != -1);
|
|
DPRINTF(RubyCache, "Testing Lock for addr: %llx cur %d con %d\n",
|
|
address, m_cache[cacheSet][loc]->m_locked, context);
|
|
return m_cache[cacheSet][loc]->isLocked(context);
|
|
}
|
|
|
|
void
|
|
CacheMemory::regStats()
|
|
{
|
|
m_demand_hits
|
|
.name(name() + ".demand_hits")
|
|
.desc("Number of cache demand hits")
|
|
;
|
|
|
|
m_demand_misses
|
|
.name(name() + ".demand_misses")
|
|
.desc("Number of cache demand misses")
|
|
;
|
|
|
|
m_demand_accesses
|
|
.name(name() + ".demand_accesses")
|
|
.desc("Number of cache demand accesses")
|
|
;
|
|
|
|
m_demand_accesses = m_demand_hits + m_demand_misses;
|
|
|
|
m_sw_prefetches
|
|
.name(name() + ".total_sw_prefetches")
|
|
.desc("Number of software prefetches")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
m_hw_prefetches
|
|
.name(name() + ".total_hw_prefetches")
|
|
.desc("Number of hardware prefetches")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
m_prefetches
|
|
.name(name() + ".total_prefetches")
|
|
.desc("Number of prefetches")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
m_prefetches = m_sw_prefetches + m_hw_prefetches;
|
|
|
|
m_accessModeType
|
|
.init(RubyRequestType_NUM)
|
|
.name(name() + ".access_mode")
|
|
.flags(Stats::pdf | Stats::total)
|
|
;
|
|
for (int i = 0; i < RubyAccessMode_NUM; i++) {
|
|
m_accessModeType
|
|
.subname(i, RubyAccessMode_to_string(RubyAccessMode(i)))
|
|
.flags(Stats::nozero)
|
|
;
|
|
}
|
|
|
|
numDataArrayReads
|
|
.name(name() + ".num_data_array_reads")
|
|
.desc("number of data array reads")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
numDataArrayWrites
|
|
.name(name() + ".num_data_array_writes")
|
|
.desc("number of data array writes")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
numTagArrayReads
|
|
.name(name() + ".num_tag_array_reads")
|
|
.desc("number of tag array reads")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
numTagArrayWrites
|
|
.name(name() + ".num_tag_array_writes")
|
|
.desc("number of tag array writes")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
numTagArrayStalls
|
|
.name(name() + ".num_tag_array_stalls")
|
|
.desc("number of stalls caused by tag array")
|
|
.flags(Stats::nozero)
|
|
;
|
|
|
|
numDataArrayStalls
|
|
.name(name() + ".num_data_array_stalls")
|
|
.desc("number of stalls caused by data array")
|
|
.flags(Stats::nozero)
|
|
;
|
|
}
|
|
|
|
// assumption: SLICC generated files will only call this function
|
|
// once **all** resources are granted
|
|
void
|
|
CacheMemory::recordRequestType(CacheRequestType requestType, Addr addr)
|
|
{
|
|
DPRINTF(RubyStats, "Recorded statistic: %s\n",
|
|
CacheRequestType_to_string(requestType));
|
|
switch(requestType) {
|
|
case CacheRequestType_DataArrayRead:
|
|
if (m_resource_stalls)
|
|
dataArray.reserve(addressToCacheSet(addr));
|
|
numDataArrayReads++;
|
|
return;
|
|
case CacheRequestType_DataArrayWrite:
|
|
if (m_resource_stalls)
|
|
dataArray.reserve(addressToCacheSet(addr));
|
|
numDataArrayWrites++;
|
|
return;
|
|
case CacheRequestType_TagArrayRead:
|
|
if (m_resource_stalls)
|
|
tagArray.reserve(addressToCacheSet(addr));
|
|
numTagArrayReads++;
|
|
return;
|
|
case CacheRequestType_TagArrayWrite:
|
|
if (m_resource_stalls)
|
|
tagArray.reserve(addressToCacheSet(addr));
|
|
numTagArrayWrites++;
|
|
return;
|
|
default:
|
|
warn("CacheMemory access_type not found: %s",
|
|
CacheRequestType_to_string(requestType));
|
|
}
|
|
}
|
|
|
|
bool
|
|
CacheMemory::checkResourceAvailable(CacheResourceType res, Addr addr)
|
|
{
|
|
if (!m_resource_stalls) {
|
|
return true;
|
|
}
|
|
|
|
if (res == CacheResourceType_TagArray) {
|
|
if (tagArray.tryAccess(addressToCacheSet(addr))) return true;
|
|
else {
|
|
DPRINTF(RubyResourceStalls,
|
|
"Tag array stall on addr %s in set %d\n",
|
|
addr, addressToCacheSet(addr));
|
|
numTagArrayStalls++;
|
|
return false;
|
|
}
|
|
} else if (res == CacheResourceType_DataArray) {
|
|
if (dataArray.tryAccess(addressToCacheSet(addr))) return true;
|
|
else {
|
|
DPRINTF(RubyResourceStalls,
|
|
"Data array stall on addr %s in set %d\n",
|
|
addr, addressToCacheSet(addr));
|
|
numDataArrayStalls++;
|
|
return false;
|
|
}
|
|
} else {
|
|
assert(false);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
CacheMemory::isBlockInvalid(int64_t cache_set, int64_t loc)
|
|
{
|
|
return (m_cache[cache_set][loc]->m_Permission == AccessPermission_Invalid);
|
|
}
|
|
|
|
bool
|
|
CacheMemory::isBlockNotBusy(int64_t cache_set, int64_t loc)
|
|
{
|
|
return (m_cache[cache_set][loc]->m_Permission != AccessPermission_Busy);
|
|
}
|