mem: Allow read-only caches and check compliance
authorAndreas Hansson <andreas.hansson@arm.com>
Fri, 3 Jul 2015 14:14:39 +0000 (10:14 -0400)
committerAndreas Hansson <andreas.hansson@arm.com>
Fri, 3 Jul 2015 14:14:39 +0000 (10:14 -0400)
This patch adds a parameter to the BaseCache to enable a read-only
cache, for example for the instruction cache, or table-walker cache
(not for x86). A number of checks are put in place in the code to
ensure a read-only cache does not end up with dirty data.

A follow-on patch adds suitable read requests to allow a read-only
cache to explicitly ask for clean data.

configs/common/CacheConfig.py
configs/common/Caches.py
configs/common/O3_ARM_v7a.py
src/mem/cache/BaseCache.py
src/mem/cache/base.cc
src/mem/cache/base.hh
src/mem/cache/cache_impl.hh
tests/configs/base_config.py
tests/configs/x86_generic.py

index 899090af57e62c8be65d072664ef9a1a14776700..d54df74900c07f2cb7457661109ab02e922c54b1 100644 (file)
@@ -64,7 +64,7 @@ def config_cache(options, system):
             O3_ARM_v7a_DCache, O3_ARM_v7a_ICache, O3_ARM_v7aL2
     else:
         dcache_class, icache_class, l2_cache_class = \
-            L1Cache, L1Cache, L2Cache
+            L1_DCache, L1_ICache, L2Cache
 
     # Set the cache line size of the system
     system.cache_line_size = options.cacheline_size
index 6687a967c8188aa30588db5bcf0732a850289afa..2bdffc6c773aaa668fdf7c1d0b25f08566bc192b 100644 (file)
@@ -54,6 +54,12 @@ class L1Cache(BaseCache):
     tgts_per_mshr = 20
     is_top_level = True
 
+class L1_ICache(L1Cache):
+    is_read_only = True
+
+class L1_DCache(L1Cache):
+    pass
+
 class L2Cache(BaseCache):
     assoc = 8
     hit_latency = 20
@@ -81,3 +87,8 @@ class PageTableWalkerCache(BaseCache):
     tgts_per_mshr = 12
     forward_snoops = False
     is_top_level = True
+    # the x86 table walker actually writes to the table-walker cache
+    if buildEnv['TARGET_ISA'] == 'x86':
+        is_read_only = False
+    else:
+        is_read_only = True
index c291525eab6eb3446cccab943171c1b76453e3e8..b4b66df9c84ec4df32e9dfb00741a785edb9b796 100644 (file)
@@ -151,6 +151,7 @@ class O3_ARM_v7a_ICache(BaseCache):
     assoc = 2
     is_top_level = True
     forward_snoops = False
+    is_read_only = True
 
 # Data Cache
 class O3_ARM_v7a_DCache(BaseCache):
@@ -175,6 +176,7 @@ class O3_ARM_v7aWalkCache(BaseCache):
     write_buffers = 16
     is_top_level = True
     forward_snoops = False
+    is_read_only = True
 
 # L2 Cache
 class O3_ARM_v7aL2(BaseCache):
index fdb41bf75e20e2b7076a02524108053ca4bd50c6..4d67664561e6c3247c48cb51d93d42e5dda95511 100644 (file)
@@ -65,6 +65,7 @@ class BaseCache(MemObject):
     forward_snoops = Param.Bool(True,
         "Forward snoops from mem side to cpu side")
     is_top_level = Param.Bool(False, "Is this cache at the top level (e.g. L1)")
+    is_read_only = Param.Bool(False, "Is this cache read only (e.g. inst)")
 
     prefetcher = Param.BasePrefetcher(NULL,"Prefetcher attached to cache")
     prefetch_on_access = Param.Bool(False,
index c2068496c74614d9c4e21818f5c1eb0af1afe609..af504d9bcfc79acf61b9e28482b2f3c2cfb6c15b 100644 (file)
@@ -79,6 +79,7 @@ BaseCache::BaseCache(const Params *p)
       numTarget(p->tgts_per_mshr),
       forwardSnoops(p->forward_snoops),
       isTopLevel(p->is_top_level),
+      isReadOnly(p->is_read_only),
       blocked(0),
       order(0),
       noTargetMSHR(NULL),
index aaf0ea69144bbda7a743c3cca1bbe4b2491b42ba..d2cb11f339326a63e968e5bde39f4c29d45e4b74 100644 (file)
@@ -309,6 +309,14 @@ class BaseCache : public MemObject
      * side */
     const bool isTopLevel;
 
+    /**
+     * Is this cache read only, for example the instruction cache, or
+     * table-walker cache. A cache that is read only should never see
+     * any writes, and should never get any dirty data (and hence
+     * never have to do any writebacks).
+     */
+    const bool isReadOnly;
+
     /**
      * Bit vector of the blocking reasons for the access path.
      * @sa #BlockedCause
@@ -516,6 +524,8 @@ class BaseCache : public MemObject
 
     MSHR *allocateWriteBuffer(PacketPtr pkt, Tick time, bool requestBus)
     {
+        // should only see clean evictions in a read-only cache
+        assert(!isReadOnly || pkt->cmd == MemCmd::CleanEvict);
         assert(pkt->isWrite() && !pkt->isRead());
         return allocateBufferInternal(&writeBuffer,
                                       blockAlign(pkt->getAddr()), blkSize,
index 117596d9b4f898972cea77f3ceca26ce426fccec..5a920589424b118323d3f3730e133d78daef29d0 100644 (file)
@@ -299,8 +299,13 @@ Cache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
     // sanity check
     assert(pkt->isRequest());
 
+    chatty_assert(!(isReadOnly && pkt->isWrite()),
+                  "Should never see a write in a read-only cache %s\n",
+                  name());
+
     DPRINTF(Cache, "%s for %s addr %#llx size %d\n", __func__,
             pkt->cmdString(), pkt->getAddr(), pkt->getSize());
+
     if (pkt->req->isUncacheable()) {
         DPRINTF(Cache, "%s%s addr %#llx uncacheable\n", pkt->cmdString(),
                 pkt->req->isInstFetch() ? " (ifetch)" : "",
@@ -1419,6 +1424,7 @@ Cache::recvTimingResp(PacketPtr pkt)
 PacketPtr
 Cache::writebackBlk(CacheBlk *blk)
 {
+    chatty_assert(!isReadOnly, "Writeback from read-only cache");
     assert(blk && blk->isValid() && blk->isDirty());
 
     writebacks[Request::wbMasterId]++;
@@ -1627,7 +1633,12 @@ Cache::handleFill(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks)
     blk->status |= BlkValid | BlkReadable;
 
     if (!pkt->sharedAsserted()) {
+        // we could get non-shared responses from memory (rather than
+        // a cache) even in a read-only cache, note that we set this
+        // bit even for a read-only cache as we use it to represent
+        // the exclusive state
         blk->status |= BlkWritable;
+
         // If we got this via cache-to-cache transfer (i.e., from a
         // cache that was an owner) and took away that owner's copy,
         // then we need to write it back.  Normally this happens
@@ -1635,8 +1646,12 @@ Cache::handleFill(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks)
         // there are cases (such as failed store conditionals or
         // compare-and-swaps) where we'll demand an exclusive copy but
         // end up not writing it.
-        if (pkt->memInhibitAsserted())
+        if (pkt->memInhibitAsserted()) {
             blk->status |= BlkDirty;
+
+            chatty_assert(!isReadOnly, "Should never see dirty snoop response "
+                          "in read-only cache %s\n", name());
+        }
     }
 
     DPRINTF(Cache, "Block addr %#llx (%s) moving from state %x to %s\n",
@@ -1785,6 +1800,10 @@ Cache::handleSnoop(PacketPtr pkt, CacheBlk *blk, bool is_timing,
                pkt->getAddr(), pkt->getSize(), blk->print());
     }
 
+    chatty_assert(!(isReadOnly && blk->isDirty()),
+                  "Should never have a dirty block in a read-only cache %s\n",
+                  name());
+
     // we may end up modifying both the block state and the packet (if
     // we respond in atomic mode), so just figure out what to do now
     // and then do it later.  If we find dirty data while snooping for a
index c440d48d96456003643c7377951c006b3437f24c..a3e1e02718a350ebf10db683a315f92939e91dc7 100644 (file)
@@ -92,8 +92,8 @@ class BaseSystem(object):
         Arguments:
           cpu -- CPU instance to work on.
         """
-        cpu.addPrivateSplitL1Caches(L1Cache(size='32kB', assoc=1),
-                                    L1Cache(size='32kB', assoc=4))
+        cpu.addPrivateSplitL1Caches(L1_ICache(size='32kB', assoc=1),
+                                    L1_DCache(size='32kB', assoc=4))
 
     def create_caches_shared(self, system):
         """Add shared caches to a system.
@@ -212,8 +212,8 @@ class BaseSESystemUniprocessor(BaseSESystem):
         # The atomic SE configurations do not use caches
         if self.mem_mode == "timing":
             # @todo We might want to revisit these rather enthusiastic L1 sizes
-            cpu.addTwoLevelCacheHierarchy(L1Cache(size='128kB'),
-                                          L1Cache(size='256kB'),
+            cpu.addTwoLevelCacheHierarchy(L1_ICache(size='128kB'),
+                                          L1_DCache(size='256kB'),
                                           L2Cache(size='2MB'))
 
     def create_caches_shared(self, system):
@@ -256,8 +256,8 @@ class BaseFSSystemUniprocessor(BaseFSSystem):
         BaseFSSystem.__init__(self, **kwargs)
 
     def create_caches_private(self, cpu):
-        cpu.addTwoLevelCacheHierarchy(L1Cache(size='32kB', assoc=1),
-                                      L1Cache(size='32kB', assoc=4),
+        cpu.addTwoLevelCacheHierarchy(L1_ICache(size='32kB', assoc=1),
+                                      L1_DCache(size='32kB', assoc=4),
                                       L2Cache(size='4MB', assoc=8))
 
     def create_caches_shared(self, system):
index 5dc8702ba364eebf330357223faa78e2b2ab5171..ad3ea31bfef3d65ecc313bc63c5e6bd855ad527e 100644 (file)
@@ -81,8 +81,8 @@ class LinuxX86FSSystem(LinuxX86SystemBuilder,
         LinuxX86SystemBuilder.__init__(self)
 
     def create_caches_private(self, cpu):
-        cpu.addPrivateSplitL1Caches(L1Cache(size='32kB', assoc=1),
-                                    L1Cache(size='32kB', assoc=4),
+        cpu.addPrivateSplitL1Caches(L1_ICache(size='32kB', assoc=1),
+                                    L1_DCache(size='32kB', assoc=4),
                                     PageTableWalkerCache(),
                                     PageTableWalkerCache())
 
@@ -100,8 +100,8 @@ class LinuxX86FSSystemUniprocessor(LinuxX86SystemBuilder,
         LinuxX86SystemBuilder.__init__(self)
 
     def create_caches_private(self, cpu):
-        cpu.addTwoLevelCacheHierarchy(L1Cache(size='32kB', assoc=1),
-                                      L1Cache(size='32kB', assoc=4),
+        cpu.addTwoLevelCacheHierarchy(L1_ICache(size='32kB', assoc=1),
+                                      L1_DCache(size='32kB', assoc=4),
                                       L2Cache(size='4MB', assoc=8),
                                       PageTableWalkerCache(),
                                       PageTableWalkerCache())