ruby: banked cache array resource model
authorBrad Beckmann <Brad.Beckmann@amd.com>
Wed, 11 Jul 2012 05:51:54 +0000 (22:51 -0700)
committerBrad Beckmann <Brad.Beckmann@amd.com>
Wed, 11 Jul 2012 05:51:54 +0000 (22:51 -0700)
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
src/mem/protocol/RubySlicc_Exports.sm
src/mem/protocol/RubySlicc_Types.sm
src/mem/ruby/system/BankedArray.cc [new file with mode: 0644]
src/mem/ruby/system/BankedArray.hh [new file with mode: 0644]
src/mem/ruby/system/Cache.py
src/mem/ruby/system/CacheMemory.cc
src/mem/ruby/system/CacheMemory.hh
src/mem/ruby/system/SConscript
src/mem/slicc/symbols/StateMachine.py

index caa7fe501f20f69936644878f0c8061e4b586440..d290da8752af8281f8d578b3d7a9aa4bab6fe758 100644 (file)
@@ -86,6 +86,7 @@ DebugFlag('RubySlicc')
 DebugFlag('RubySystem')
 DebugFlag('RubyTester')
 DebugFlag('RubyStats')
+DebugFlag('RubyResourceStalls')
 
 CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester',
     'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache',
index ef752c604f0766ec657549eefee612c12b139698..92739385b41856b57f8c641fcfc5fa3fde1b38f0 100644 (file)
@@ -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";
 }
index 436b39273c528ba5b7757c23ded699364c193e1c..a14af946c4df646ceea05134a830a09e2f70b60d 100644 (file)
@@ -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 (file)
index 0000000..3113393
--- /dev/null
@@ -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;
+}
diff --git a/src/mem/ruby/system/BankedArray.hh b/src/mem/ruby/system/BankedArray.hh
new file mode 100644 (file)
index 0000000..2db4d3d
--- /dev/null
@@ -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
index 79ab9b07066ae48c8f66f6097f657db7b4f36de2..2b4daa68bffe8dc4d4a8192f606382fb4ad86059 100644 (file)
@@ -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")
index a626dc13fc7bb0c29826ef86043858b57384d350..a8e3523d3cc81de75c5d177541118bd863d44e32 100644 (file)
@@ -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;
+    }
+}
+
index 53cd6b2861386a938094677193820d5dc2c59b6b..ee3c1a7fc5d696564a87d8468557e5e863b075b1 100644 (file)
@@ -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__
index baa877b3948ef46e1cb07fd8fa25f14c330c2ca1..6bb6d270782b417bead3427698e0e0646a1d31a6 100644 (file)
@@ -55,3 +55,4 @@ Source('RubyPortProxy.cc')
 Source('Sequencer.cc')
 Source('System.cc')
 Source('TimerTable.cc')
+Source('BankedArray.cc')
index 230eb1b222b666447468aae24380e7ceda5f9644..39f3a4b436017111229f1e36cc980dffe47bbea9 100644 (file)
@@ -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