From 86d6b788f6d7b523c750ffb64d6d8920ec741c49 Mon Sep 17 00:00:00 2001 From: Brad Beckmann Date: Tue, 10 Jul 2012 22:51:54 -0700 Subject: [PATCH] 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. --- src/mem/SConscript | 1 + src/mem/protocol/RubySlicc_Exports.sm | 5 +++ src/mem/protocol/RubySlicc_Types.sm | 2 + src/mem/ruby/system/BankedArray.cc | 57 +++++++++++++++++++++++++++ src/mem/ruby/system/BankedArray.hh | 47 ++++++++++++++++++++++ src/mem/ruby/system/Cache.py | 6 +++ src/mem/ruby/system/CacheMemory.cc | 44 ++++++++++++++++++++- src/mem/ruby/system/CacheMemory.hh | 10 +++++ src/mem/ruby/system/SConscript | 1 + src/mem/slicc/symbols/StateMachine.py | 8 ++++ 10 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 src/mem/ruby/system/BankedArray.cc create mode 100644 src/mem/ruby/system/BankedArray.hh diff --git a/src/mem/SConscript b/src/mem/SConscript index caa7fe501..d290da875 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -86,6 +86,7 @@ DebugFlag('RubySlicc') DebugFlag('RubySystem') DebugFlag('RubyTester') DebugFlag('RubyStats') +DebugFlag('RubyResourceStalls') CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester', 'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache', diff --git a/src/mem/protocol/RubySlicc_Exports.sm b/src/mem/protocol/RubySlicc_Exports.sm index ef752c604..92739385b 100644 --- a/src/mem/protocol/RubySlicc_Exports.sm +++ b/src/mem/protocol/RubySlicc_Exports.sm @@ -184,6 +184,11 @@ enumeration(CacheRequestType, desc="...", default="CacheRequestType_NULL") { 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") { Default, desc="Replace this with access_types passed to the Directory Ruby object"; } diff --git a/src/mem/protocol/RubySlicc_Types.sm b/src/mem/protocol/RubySlicc_Types.sm index 436b39273..a14af946c 100644 --- a/src/mem/protocol/RubySlicc_Types.sm +++ b/src/mem/protocol/RubySlicc_Types.sm @@ -109,6 +109,7 @@ structure (Sequencer, external = "yes") { void profileNack(Address, int, int, uint64); void evictionCallback(Address); void recordRequestType(SequencerRequestType); + bool checkResourceAvailable(CacheResourceType, Address); } structure(RubyRequest, desc="...", interface="Message", external="yes") { @@ -154,6 +155,7 @@ structure (CacheMemory, external = "yes") { void setMRU(Address); void recordRequestType(CacheRequestType); + bool checkResourceAvailable(CacheResourceType, Address); } structure (WireBuffer, inport="yes", outport="yes", external = "yes") { diff --git a/src/mem/ruby/system/BankedArray.cc b/src/mem/ruby/system/BankedArray.cc new file mode 100644 index 000000000..3113393a1 --- /dev/null +++ b/src/mem/ruby/system/BankedArray.cc @@ -0,0 +1,57 @@ + + +#include + +#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; +} diff --git a/src/mem/ruby/system/BankedArray.hh b/src/mem/ruby/system/BankedArray.hh new file mode 100644 index 000000000..2db4d3d95 --- /dev/null +++ b/src/mem/ruby/system/BankedArray.hh @@ -0,0 +1,47 @@ + +#ifndef __MEM_RUBY_SYSTEM_BANKEDARRAY_HH__ +#define __MEM_RUBY_SYSTEM_BANKEDARRAY_HH__ + +#include + +#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 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 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 diff --git a/src/mem/ruby/system/Cache.py b/src/mem/ruby/system/Cache.py index 79ab9b070..2b4daa68b 100644 --- a/src/mem/ruby/system/Cache.py +++ b/src/mem/ruby/system/Cache.py @@ -40,3 +40,9 @@ class RubyCache(SimObject): replacement_policy = Param.String("PSEUDO_LRU", ""); start_index_bit = Param.Int(6, "index start, default 6 for 64-byte line"); 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") diff --git a/src/mem/ruby/system/CacheMemory.cc b/src/mem/ruby/system/CacheMemory.cc index a626dc13f..a8e3523d3 100644 --- a/src/mem/ruby/system/CacheMemory.cc +++ b/src/mem/ruby/system/CacheMemory.cc @@ -29,6 +29,7 @@ #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/system/CacheMemory.hh" @@ -51,7 +52,9 @@ RubyCacheParams::create() } 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_latency = p->latency; @@ -60,6 +63,7 @@ CacheMemory::CacheMemory(const Params *p) m_profiler_ptr = new CacheProfiler(name()); m_start_index_bit = p->start_index_bit; m_is_instruction_only_cache = p->is_icache; + m_resource_stalls = p->resourceStalls; } void @@ -523,4 +527,42 @@ CacheMemory::regStats() { .name(name() + ".num_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; + } +} + diff --git a/src/mem/ruby/system/CacheMemory.hh b/src/mem/ruby/system/CacheMemory.hh index 53cd6b286..ee3c1a7fc 100644 --- a/src/mem/ruby/system/CacheMemory.hh +++ b/src/mem/ruby/system/CacheMemory.hh @@ -35,6 +35,7 @@ #include "base/hashmap.hh" #include "base/statistics.hh" +#include "mem/protocol/CacheResourceType.hh" #include "mem/protocol/CacheRequestType.hh" #include "mem/protocol/GenericRequestType.hh" #include "mem/protocol/RubyRequest.hh" @@ -43,6 +44,7 @@ #include "mem/ruby/recorder/CacheRecorder.hh" #include "mem/ruby/slicc_interface/AbstractCacheEntry.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/PseudoLRUPolicy.hh" #include "params/RubyCache.hh" @@ -125,6 +127,10 @@ class CacheMemory : public SimObject Stats::Scalar numTagArrayReads; Stats::Scalar numTagArrayWrites; + bool checkResourceAvailable(CacheResourceType res, Address addr); + + Stats::Scalar numTagArrayStalls; + Stats::Scalar numDataArrayStalls; private: // convert a Address to its location in the cache Index addressToCacheSet(const Address& address) const; @@ -155,12 +161,16 @@ class CacheMemory : public SimObject CacheProfiler* m_profiler_ptr; + BankedArray dataArray; + BankedArray tagArray; + int m_cache_size; std::string m_policy; int m_cache_num_sets; int m_cache_num_set_bits; int m_cache_assoc; int m_start_index_bit; + bool m_resource_stalls; }; #endif // __MEM_RUBY_SYSTEM_CACHEMEMORY_HH__ diff --git a/src/mem/ruby/system/SConscript b/src/mem/ruby/system/SConscript index baa877b39..6bb6d2707 100644 --- a/src/mem/ruby/system/SConscript +++ b/src/mem/ruby/system/SConscript @@ -55,3 +55,4 @@ Source('RubyPortProxy.cc') Source('Sequencer.cc') Source('System.cc') Source('TimerTable.cc') +Source('BankedArray.cc') diff --git a/src/mem/slicc/symbols/StateMachine.py b/src/mem/slicc/symbols/StateMachine.py index 230eb1b22..39f3a4b43 100644 --- a/src/mem/slicc/symbols/StateMachine.py +++ b/src/mem/slicc/symbols/StateMachine.py @@ -1238,6 +1238,14 @@ if (!%s.areNSlotsAvailable(%s)) ''' % (key.code, 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 # output deterministic (without this the output order can vary