ruby: banked cache array resource model
This patch models a cache as separate tag and data arrays. The patch exposes the banked array as another resource that is checked by SLICC before a transition is allowed to execute. This is similar to how TBE entries and slots in output ports are modeled.
This commit is contained in:
parent
467093ebf2
commit
86d6b788f6
|
@ -86,6 +86,7 @@ DebugFlag('RubySlicc')
|
||||||
DebugFlag('RubySystem')
|
DebugFlag('RubySystem')
|
||||||
DebugFlag('RubyTester')
|
DebugFlag('RubyTester')
|
||||||
DebugFlag('RubyStats')
|
DebugFlag('RubyStats')
|
||||||
|
DebugFlag('RubyResourceStalls')
|
||||||
|
|
||||||
CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester',
|
CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester',
|
||||||
'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache',
|
'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache',
|
||||||
|
|
|
@ -184,6 +184,11 @@ enumeration(CacheRequestType, desc="...", default="CacheRequestType_NULL") {
|
||||||
TagArrayWrite, desc="Write access to the cache's tag array";
|
TagArrayWrite, desc="Write access to the cache's tag array";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enumeration(CacheResourceType, desc="...", default="CacheResourceType_NULL") {
|
||||||
|
DataArray, desc="Access to the cache's data array";
|
||||||
|
TagArray, desc="Access to the cache's tag array";
|
||||||
|
}
|
||||||
|
|
||||||
enumeration(DirectoryRequestType, desc="...", default="DirectoryRequestType_NULL") {
|
enumeration(DirectoryRequestType, desc="...", default="DirectoryRequestType_NULL") {
|
||||||
Default, desc="Replace this with access_types passed to the Directory Ruby object";
|
Default, desc="Replace this with access_types passed to the Directory Ruby object";
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,7 @@ structure (Sequencer, external = "yes") {
|
||||||
void profileNack(Address, int, int, uint64);
|
void profileNack(Address, int, int, uint64);
|
||||||
void evictionCallback(Address);
|
void evictionCallback(Address);
|
||||||
void recordRequestType(SequencerRequestType);
|
void recordRequestType(SequencerRequestType);
|
||||||
|
bool checkResourceAvailable(CacheResourceType, Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
structure(RubyRequest, desc="...", interface="Message", external="yes") {
|
structure(RubyRequest, desc="...", interface="Message", external="yes") {
|
||||||
|
@ -154,6 +155,7 @@ structure (CacheMemory, external = "yes") {
|
||||||
|
|
||||||
void setMRU(Address);
|
void setMRU(Address);
|
||||||
void recordRequestType(CacheRequestType);
|
void recordRequestType(CacheRequestType);
|
||||||
|
bool checkResourceAvailable(CacheResourceType, Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
structure (WireBuffer, inport="yes", outport="yes", external = "yes") {
|
structure (WireBuffer, inport="yes", outport="yes", external = "yes") {
|
||||||
|
|
57
src/mem/ruby/system/BankedArray.cc
Normal file
57
src/mem/ruby/system/BankedArray.cc
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/intmath.hh"
|
||||||
|
#include "mem/ruby/common/TypeDefines.hh"
|
||||||
|
#include "mem/ruby/system/BankedArray.hh"
|
||||||
|
#include "sim/eventq.hh"
|
||||||
|
|
||||||
|
BankedArray::BankedArray(unsigned int banks, unsigned int accessLatency, unsigned int startIndexBit) :
|
||||||
|
EventManager(&mainEventQueue)
|
||||||
|
{
|
||||||
|
this->banks = banks;
|
||||||
|
this->accessLatency = accessLatency;
|
||||||
|
this->startIndexBit = startIndexBit;
|
||||||
|
|
||||||
|
if (banks != 0) {
|
||||||
|
bankBits = floorLog2(banks);
|
||||||
|
}
|
||||||
|
|
||||||
|
busyBanks.resize(banks);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BankedArray::tryAccess(Index idx)
|
||||||
|
{
|
||||||
|
if (accessLatency == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
unsigned int bank = mapIndexToBank(idx);
|
||||||
|
assert(bank < banks);
|
||||||
|
|
||||||
|
if (busyBanks[bank].scheduled()) {
|
||||||
|
if (!(busyBanks[bank].startAccess == curTick() && busyBanks[bank].idx == idx)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true; // We tried to allocate resources twice in the same cycle for the same addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
busyBanks[bank].idx = idx;
|
||||||
|
busyBanks[bank].startAccess = curTick();
|
||||||
|
|
||||||
|
// substract 1 so that next cycle the resource available
|
||||||
|
schedule(busyBanks[bank], curTick()+accessLatency-1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
BankedArray::mapIndexToBank(Index idx)
|
||||||
|
{
|
||||||
|
if (banks == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return idx % banks;
|
||||||
|
}
|
47
src/mem/ruby/system/BankedArray.hh
Normal file
47
src/mem/ruby/system/BankedArray.hh
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
#ifndef __MEM_RUBY_SYSTEM_BANKEDARRAY_HH__
|
||||||
|
#define __MEM_RUBY_SYSTEM_BANKEDARRAY_HH__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "mem/ruby/common/TypeDefines.hh"
|
||||||
|
#include "sim/eventq.hh"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BankedArray : public EventManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
unsigned int banks;
|
||||||
|
unsigned int accessLatency;
|
||||||
|
unsigned int bankBits;
|
||||||
|
unsigned int startIndexBit;
|
||||||
|
|
||||||
|
//std::vector<bool> busyBanks;
|
||||||
|
|
||||||
|
class TickEvent : public Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TickEvent() : Event() {}
|
||||||
|
void process() {}
|
||||||
|
Index idx;
|
||||||
|
Tick startAccess;
|
||||||
|
};
|
||||||
|
friend class TickEvent;
|
||||||
|
|
||||||
|
// If the tick event is scheduled then the bank is busy
|
||||||
|
// otherwise, schedule the event and wait for it to complete
|
||||||
|
std::vector<TickEvent> busyBanks;
|
||||||
|
|
||||||
|
unsigned int mapIndexToBank(Index idx);
|
||||||
|
|
||||||
|
public:
|
||||||
|
BankedArray(unsigned int banks, unsigned int accessLatency, unsigned int startIndexBit);
|
||||||
|
|
||||||
|
// Note: We try the access based on the cache index, not the address
|
||||||
|
// This is so we don't get aliasing on blocks being replaced
|
||||||
|
bool tryAccess(Index idx);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -40,3 +40,9 @@ class RubyCache(SimObject):
|
||||||
replacement_policy = Param.String("PSEUDO_LRU", "");
|
replacement_policy = Param.String("PSEUDO_LRU", "");
|
||||||
start_index_bit = Param.Int(6, "index start, default 6 for 64-byte line");
|
start_index_bit = Param.Int(6, "index start, default 6 for 64-byte line");
|
||||||
is_icache = Param.Bool(False, "is instruction only cache");
|
is_icache = Param.Bool(False, "is instruction only cache");
|
||||||
|
|
||||||
|
dataArrayBanks = Param.Int(1, "Number of banks for the data array")
|
||||||
|
tagArrayBanks = Param.Int(1, "Number of banks for the tag array")
|
||||||
|
dataAccessLatency = Param.Int(1, "Gem5 cycles for the data array")
|
||||||
|
tagAccessLatency = Param.Int(1, "Gem5 cycles for the tag array")
|
||||||
|
resourceStalls = Param.Bool(False, "stall if there is a resource failure")
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "base/intmath.hh"
|
#include "base/intmath.hh"
|
||||||
#include "debug/RubyCache.hh"
|
#include "debug/RubyCache.hh"
|
||||||
#include "debug/RubyCacheTrace.hh"
|
#include "debug/RubyCacheTrace.hh"
|
||||||
|
#include "debug/RubyResourceStalls.hh"
|
||||||
#include "debug/RubyStats.hh"
|
#include "debug/RubyStats.hh"
|
||||||
#include "mem/protocol/AccessPermission.hh"
|
#include "mem/protocol/AccessPermission.hh"
|
||||||
#include "mem/ruby/system/CacheMemory.hh"
|
#include "mem/ruby/system/CacheMemory.hh"
|
||||||
|
@ -51,7 +52,9 @@ RubyCacheParams::create()
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheMemory::CacheMemory(const Params *p)
|
CacheMemory::CacheMemory(const Params *p)
|
||||||
: SimObject(p)
|
: SimObject(p),
|
||||||
|
dataArray(p->dataArrayBanks, p->dataAccessLatency, p->start_index_bit),
|
||||||
|
tagArray(p->tagArrayBanks, p->tagAccessLatency, p->start_index_bit)
|
||||||
{
|
{
|
||||||
m_cache_size = p->size;
|
m_cache_size = p->size;
|
||||||
m_latency = p->latency;
|
m_latency = p->latency;
|
||||||
|
@ -60,6 +63,7 @@ CacheMemory::CacheMemory(const Params *p)
|
||||||
m_profiler_ptr = new CacheProfiler(name());
|
m_profiler_ptr = new CacheProfiler(name());
|
||||||
m_start_index_bit = p->start_index_bit;
|
m_start_index_bit = p->start_index_bit;
|
||||||
m_is_instruction_only_cache = p->is_icache;
|
m_is_instruction_only_cache = p->is_icache;
|
||||||
|
m_resource_stalls = p->resourceStalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -523,4 +527,42 @@ CacheMemory::regStats() {
|
||||||
.name(name() + ".num_tag_array_writes")
|
.name(name() + ".num_tag_array_writes")
|
||||||
.desc("number of tag array writes")
|
.desc("number of tag array writes")
|
||||||
;
|
;
|
||||||
|
|
||||||
|
numTagArrayStalls
|
||||||
|
.name(name() + ".num_tag_array_stalls")
|
||||||
|
.desc("number of stalls caused by tag array")
|
||||||
|
;
|
||||||
|
|
||||||
|
numDataArrayStalls
|
||||||
|
.name(name() + ".num_data_array_stalls")
|
||||||
|
.desc("number of stalls caused by data array")
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CacheMemory::checkResourceAvailable(CacheResourceType res, Address 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "base/hashmap.hh"
|
#include "base/hashmap.hh"
|
||||||
#include "base/statistics.hh"
|
#include "base/statistics.hh"
|
||||||
|
#include "mem/protocol/CacheResourceType.hh"
|
||||||
#include "mem/protocol/CacheRequestType.hh"
|
#include "mem/protocol/CacheRequestType.hh"
|
||||||
#include "mem/protocol/GenericRequestType.hh"
|
#include "mem/protocol/GenericRequestType.hh"
|
||||||
#include "mem/protocol/RubyRequest.hh"
|
#include "mem/protocol/RubyRequest.hh"
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
#include "mem/ruby/recorder/CacheRecorder.hh"
|
#include "mem/ruby/recorder/CacheRecorder.hh"
|
||||||
#include "mem/ruby/slicc_interface/AbstractCacheEntry.hh"
|
#include "mem/ruby/slicc_interface/AbstractCacheEntry.hh"
|
||||||
#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
|
#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
|
||||||
|
#include "mem/ruby/system/BankedArray.hh"
|
||||||
#include "mem/ruby/system/LRUPolicy.hh"
|
#include "mem/ruby/system/LRUPolicy.hh"
|
||||||
#include "mem/ruby/system/PseudoLRUPolicy.hh"
|
#include "mem/ruby/system/PseudoLRUPolicy.hh"
|
||||||
#include "params/RubyCache.hh"
|
#include "params/RubyCache.hh"
|
||||||
|
@ -125,6 +127,10 @@ class CacheMemory : public SimObject
|
||||||
Stats::Scalar numTagArrayReads;
|
Stats::Scalar numTagArrayReads;
|
||||||
Stats::Scalar numTagArrayWrites;
|
Stats::Scalar numTagArrayWrites;
|
||||||
|
|
||||||
|
bool checkResourceAvailable(CacheResourceType res, Address addr);
|
||||||
|
|
||||||
|
Stats::Scalar numTagArrayStalls;
|
||||||
|
Stats::Scalar numDataArrayStalls;
|
||||||
private:
|
private:
|
||||||
// convert a Address to its location in the cache
|
// convert a Address to its location in the cache
|
||||||
Index addressToCacheSet(const Address& address) const;
|
Index addressToCacheSet(const Address& address) const;
|
||||||
|
@ -155,12 +161,16 @@ class CacheMemory : public SimObject
|
||||||
|
|
||||||
CacheProfiler* m_profiler_ptr;
|
CacheProfiler* m_profiler_ptr;
|
||||||
|
|
||||||
|
BankedArray dataArray;
|
||||||
|
BankedArray tagArray;
|
||||||
|
|
||||||
int m_cache_size;
|
int m_cache_size;
|
||||||
std::string m_policy;
|
std::string m_policy;
|
||||||
int m_cache_num_sets;
|
int m_cache_num_sets;
|
||||||
int m_cache_num_set_bits;
|
int m_cache_num_set_bits;
|
||||||
int m_cache_assoc;
|
int m_cache_assoc;
|
||||||
int m_start_index_bit;
|
int m_start_index_bit;
|
||||||
|
bool m_resource_stalls;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __MEM_RUBY_SYSTEM_CACHEMEMORY_HH__
|
#endif // __MEM_RUBY_SYSTEM_CACHEMEMORY_HH__
|
||||||
|
|
|
@ -55,3 +55,4 @@ Source('RubyPortProxy.cc')
|
||||||
Source('Sequencer.cc')
|
Source('Sequencer.cc')
|
||||||
Source('System.cc')
|
Source('System.cc')
|
||||||
Source('TimerTable.cc')
|
Source('TimerTable.cc')
|
||||||
|
Source('BankedArray.cc')
|
||||||
|
|
|
@ -1238,6 +1238,14 @@ if (!%s.areNSlotsAvailable(%s))
|
||||||
''' % (key.code, val)
|
''' % (key.code, val)
|
||||||
case_sorter.append(val)
|
case_sorter.append(val)
|
||||||
|
|
||||||
|
# Check all of the request_types for resource constraints
|
||||||
|
for request_type in request_types:
|
||||||
|
val = '''
|
||||||
|
if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
|
||||||
|
return TransitionResult_ResourceStall;
|
||||||
|
}
|
||||||
|
''' % (self.ident, request_type.ident)
|
||||||
|
case_sorter.append(val)
|
||||||
|
|
||||||
# Emit the code sequences in a sorted order. This makes the
|
# Emit the code sequences in a sorted order. This makes the
|
||||||
# output deterministic (without this the output order can vary
|
# output deterministic (without this the output order can vary
|
||||||
|
|
Loading…
Reference in a new issue