changeset 929fd978ab4e in /z/repo/gem5
details: http://repo.gem5.org/gem5?cmd=changeset;node=929fd978ab4e
description:
        mem: Add an option to perform clean writebacks from caches

        This patch adds the necessary commands and cache functionality to
        allow clean writebacks. This functionality is crucial, especially when
        having exclusive (victim) caches. For example, if read-only L1
        instruction caches are not sending clean writebacks, there will never
        be any spills from the L1 to the L2. At the moment the cache model
        defaults to not sending clean writebacks, and this should possibly be
        re-evaluated.

        The implementation of clean writebacks relies on a new packet command
        WritebackClean, which acts much like a Writeback (renamed
        WritebackDirty), and also much like a CleanEvict. On eviction of a
        clean block the cache either sends a clean evict, or a clean
        writeback, and if any copies are still cached upstream the clean
        evict/writeback is dropped. Similarly, if a clean evict/writeback
        reaches a cache where there are outstanding MSHRs for the block, the
        packet is dropped. In the typical case though, the clean writeback
        allocates a block in the downstream cache, and marks it writable if
        the evicted block was writable.

        The patch changes the O3_ARM_v7a L1 cache configuration and the
        default L1 caches in config/common/Caches.py

diffstat:

 configs/common/Caches.py     |    4 +
 configs/common/O3_ARM_v7a.py |    6 +
 src/mem/abstract_mem.cc      |    2 +-
 src/mem/cache/Cache.py       |    8 ++
 src/mem/cache/base.hh        |    3 -
 src/mem/cache/cache.cc       |  161 ++++++++++++++++++++++++++++--------------
 src/mem/cache/cache.hh       |    9 ++
 src/mem/coherent_xbar.cc     |   16 ++-
 src/mem/packet.cc            |   13 ++-
 src/mem/packet.hh            |   36 ++++++---
 src/mem/snoop_filter.cc      |    6 +-
 11 files changed, 180 insertions(+), 84 deletions(-)

diffs (truncated from 619 to 300 lines):

diff -r 8149b36b8803 -r 929fd978ab4e configs/common/Caches.py
--- a/configs/common/Caches.py  Fri Nov 06 03:26:42 2015 -0500
+++ b/configs/common/Caches.py  Fri Nov 06 03:26:43 2015 -0500
@@ -55,6 +55,8 @@
 
 class L1_ICache(L1Cache):
     is_read_only = True
+    # Writeback clean lines as well
+    writeback_clean = True
 
 class L1_DCache(L1Cache):
     pass
@@ -89,3 +91,5 @@
         is_read_only = False
     else:
         is_read_only = True
+        # Writeback clean lines as well
+        writeback_clean = True
diff -r 8149b36b8803 -r 929fd978ab4e configs/common/O3_ARM_v7a.py
--- a/configs/common/O3_ARM_v7a.py      Fri Nov 06 03:26:42 2015 -0500
+++ b/configs/common/O3_ARM_v7a.py      Fri Nov 06 03:26:43 2015 -0500
@@ -151,6 +151,8 @@
     assoc = 2
     forward_snoops = False
     is_read_only = True
+    # Writeback clean lines as well
+    writeback_clean = True
 
 # Data Cache
 class O3_ARM_v7a_DCache(Cache):
@@ -161,6 +163,8 @@
     size = '32kB'
     assoc = 2
     write_buffers = 16
+    # Consider the L2 a victim cache also for clean lines
+    writeback_clean = True
 
 # TLB Cache
 # Use a cache as a L2 TLB
@@ -174,6 +178,8 @@
     write_buffers = 16
     forward_snoops = False
     is_read_only = True
+    # Writeback clean lines as well
+    writeback_clean = True
 
 # L2 Cache
 class O3_ARM_v7aL2(Cache):
diff -r 8149b36b8803 -r 929fd978ab4e src/mem/abstract_mem.cc
--- a/src/mem/abstract_mem.cc   Fri Nov 06 03:26:42 2015 -0500
+++ b/src/mem/abstract_mem.cc   Fri Nov 06 03:26:43 2015 -0500
@@ -329,7 +329,7 @@
         return;
     }
 
-    if (pkt->cmd == MemCmd::CleanEvict) {
+    if (pkt->cmd == MemCmd::CleanEvict || pkt->cmd == MemCmd::WritebackClean) {
         DPRINTF(MemoryAccess, "CleanEvict  on 0x%x: not responding\n",
                 pkt->getAddr());
       return;
diff -r 8149b36b8803 -r 929fd978ab4e src/mem/cache/Cache.py
--- a/src/mem/cache/Cache.py    Fri Nov 06 03:26:42 2015 -0500
+++ b/src/mem/cache/Cache.py    Fri Nov 06 03:26:43 2015 -0500
@@ -103,3 +103,11 @@
     # cache a line is dropped for a mostly exclusive cache.
     clusivity = Param.Clusivity('mostly_incl',
                                 "Clusivity with upstream cache")
+
+    # Determine if this cache sends out writebacks for clean lines, or
+    # simply clean evicts. In cases where a downstream cache is mostly
+    # exclusive with respect to this cache (acting as a victim cache),
+    # the clean writebacks are essential for performance. In general
+    # this should be set to True for anything but the last-level
+    # cache.
+    writeback_clean = Param.Bool(False, "Writeback clean lines")
diff -r 8149b36b8803 -r 929fd978ab4e src/mem/cache/base.hh
--- a/src/mem/cache/base.hh     Fri Nov 06 03:26:42 2015 -0500
+++ b/src/mem/cache/base.hh     Fri Nov 06 03:26:43 2015 -0500
@@ -521,9 +521,6 @@
         // should only see writes or clean evicts here
         assert(pkt->isWrite() || pkt->cmd == MemCmd::CleanEvict);
 
-        // if this is a read-only cache we should never see any writes
-        assert(!(isReadOnly && pkt->isWrite()));
-
         return allocateBufferInternal(&writeBuffer,
                                       blockAlign(pkt->getAddr()), blkSize,
                                       pkt, time, true);
diff -r 8149b36b8803 -r 929fd978ab4e src/mem/cache/cache.cc
--- a/src/mem/cache/cache.cc    Fri Nov 06 03:26:42 2015 -0500
+++ b/src/mem/cache/cache.cc    Fri Nov 06 03:26:43 2015 -0500
@@ -70,6 +70,7 @@
       doFastWrites(true),
       prefetchOnAccess(p->prefetch_on_access),
       clusivity(p->clusivity),
+      writebackClean(p->writeback_clean),
       tempBlockWriteback(nullptr),
       writebackTempBlockAtomicEvent(this, false,
                                     EventBase::Delayed_Writeback_Pri)
@@ -317,7 +318,7 @@
         // flush and invalidate any existing block
         CacheBlk *old_blk(tags->findBlock(pkt->getAddr(), pkt->isSecure()));
         if (old_blk && old_blk->isValid()) {
-            if (old_blk->isDirty())
+            if (old_blk->isDirty() || writebackClean)
                 writebacks.push_back(writebackBlk(old_blk));
             else
                 writebacks.push_back(cleanEvictBlk(old_blk));
@@ -343,7 +344,7 @@
             blk ? "hit " + blk->print() : "miss");
 
 
-    if (pkt->evictingBlock()) {
+    if (pkt->isEviction()) {
         // We check for presence of block in above caches before issuing
         // Writeback or CleanEvict to write buffer. Therefore the only
         // possible cases can be of a CleanEvict packet coming from above
@@ -356,26 +357,49 @@
         if (writeBuffer.findMatches(pkt->getAddr(), pkt->isSecure(),
                                    outgoing)) {
             assert(outgoing.size() == 1);
-            PacketPtr wbPkt = outgoing[0]->getTarget()->pkt;
-            assert(pkt->cmd == MemCmd::CleanEvict &&
-                   wbPkt->cmd == MemCmd::Writeback);
-            // As the CleanEvict is coming from above, it would have snooped
-            // into other peer caches of the same level while traversing the
-            // crossbar. If a copy of the block had been found, the CleanEvict
-            // would have been deleted in the crossbar. Now that the
-            // CleanEvict is here we can be sure none of the other upper level
-            // caches connected to this cache have the block, so we can clear
-            // the BLOCK_CACHED flag in the Writeback if set and discard the
-            // CleanEvict by returning true.
-            wbPkt->clearBlockCached();
-            return true;
+            MSHR *wb_entry = outgoing[0];
+            assert(wb_entry->getNumTargets() == 1);
+            PacketPtr wbPkt = wb_entry->getTarget()->pkt;
+            assert(wbPkt->isWriteback());
+
+            if (pkt->isCleanEviction()) {
+                // The CleanEvict and WritebackClean snoops into other
+                // peer caches of the same level while traversing the
+                // crossbar. If a copy of the block is found, the
+                // packet is deleted in the crossbar. Hence, none of
+                // the other upper level caches connected to this
+                // cache have the block, so we can clear the
+                // BLOCK_CACHED flag in the Writeback if set and
+                // discard the CleanEvict by returning true.
+                wbPkt->clearBlockCached();
+                return true;
+            } else {
+                assert(pkt->cmd == MemCmd::WritebackDirty);
+                // Dirty writeback from above trumps our clean
+                // writeback... discard here
+                // Note: markInService will remove entry from writeback buffer.
+                markInService(wb_entry, false);
+                delete wbPkt;
+            }
         }
     }
 
     // Writeback handling is special case.  We can write the block into
     // the cache without having a writeable copy (or any copy at all).
-    if (pkt->cmd == MemCmd::Writeback) {
+    if (pkt->isWriteback()) {
         assert(blkSize == pkt->getSize());
+
+        // we could get a clean writeback while we are having
+        // outstanding accesses to a block, do the simple thing for
+        // now and drop the clean writeback so that we do not upset
+        // any ordering/decisions about ownership already taken
+        if (pkt->cmd == MemCmd::WritebackClean &&
+            mshrQueue.findMatch(pkt->getAddr(), pkt->isSecure())) {
+            DPRINTF(Cache, "Clean writeback %#llx to block with MSHR, "
+                    "dropping\n", pkt->getAddr());
+            return true;
+        }
+
         if (blk == NULL) {
             // need to do a replacement
             blk = allocateBlock(pkt->getAddr(), pkt->isSecure(), writebacks);
@@ -391,7 +415,11 @@
                 blk->status |= BlkSecure;
             }
         }
-        blk->status |= BlkDirty;
+        // only mark the block dirty if we got a writeback command,
+        // and leave it as is for a clean writeback
+        if (pkt->cmd == MemCmd::WritebackDirty) {
+            blk->status |= BlkDirty;
+        }
         // if shared is not asserted we got the writeback in modified
         // state, if it is asserted we are in the owned state
         if (!pkt->sharedAsserted()) {
@@ -463,7 +491,13 @@
                 // this is a non-snoop request packet which does not require a
                 // response.
                 delete wbPkt;
+            } else if (wbPkt->cmd == MemCmd::WritebackClean) {
+                // clean writeback, do not send since the block is
+                // still cached above
+                assert(writebackClean);
+                delete wbPkt;
             } else {
+                assert(wbPkt->cmd == MemCmd::WritebackDirty);
                 // Set BLOCK_CACHED flag in Writeback and send below, so that
                 // the Writeback does not reset the bit corresponding to this
                 // address in the snoop filter below.
@@ -490,7 +524,7 @@
         // isCachedAbove returns true we set BLOCK_CACHED flag in Writebacks
         // and discard CleanEvicts.
         if (isCachedAbove(wbPkt, false)) {
-            if (wbPkt->cmd == MemCmd::Writeback) {
+            if (wbPkt->cmd == MemCmd::WritebackDirty) {
                 // Set BLOCK_CACHED flag in Writeback and send below,
                 // so that the Writeback does not reset the bit
                 // corresponding to this address in the snoop filter
@@ -694,6 +728,10 @@
             // by access(), that calls accessBlock() function.
             cpuSidePort->schedTimingResp(pkt, request_time, true);
         } else {
+            DPRINTF(Cache, "%s satisfied %s addr %#llx, no response needed\n",
+                    __func__, pkt->cmdString(), pkt->getAddr(),
+                    pkt->getSize());
+
             // queue the packet for deletion, as the sending cache is
             // still relying on it; if the block is found in access(),
             // CleanEvict and Writeback messages will be deleted
@@ -765,9 +803,9 @@
 
             // Coalesce unless it was a software prefetch (see above).
             if (pkt) {
-                assert(pkt->cmd != MemCmd::Writeback);
-                // CleanEvicts corresponding to blocks which have outstanding
-                // requests in MSHRs can be deleted here.
+                assert(!pkt->isWriteback());
+                // CleanEvicts corresponding to blocks which have
+                // outstanding requests in MSHRs are simply sunk here
                 if (pkt->cmd == MemCmd::CleanEvict) {
                     pendingDelete.reset(pkt);
                 } else {
@@ -820,7 +858,7 @@
                 mshr_misses[pkt->cmdToIndex()][pkt->req->masterId()]++;
             }
 
-            if (pkt->evictingBlock() ||
+            if (pkt->isEviction() ||
                 (pkt->req->isUncacheable() && pkt->isWrite())) {
                 // We use forward_time here because there is an
                 // uncached memory write, forwarded to WriteBuffer.
@@ -888,7 +926,7 @@
 
     if (!blkValid &&
         (cpu_pkt->isUpgrade() ||
-         cpu_pkt->evictingBlock())) {
+         cpu_pkt->isEviction())) {
         // Writebacks that weren't allocated in access() and upgrades
         // from upper-level caches that missed completely just go
         // through.
@@ -1108,8 +1146,8 @@
             schedule(writebackTempBlockAtomicEvent, curTick());
         }
 
-        tempBlockWriteback = blk->isDirty() ? writebackBlk(blk) :
-            cleanEvictBlk(blk);
+        tempBlockWriteback = (blk->isDirty() || writebackClean) ?
+            writebackBlk(blk) : cleanEvictBlk(blk);
         blk->invalidate();
     }
 
@@ -1458,7 +1496,7 @@
         // Writebacks/CleanEvicts to write buffer. It specifies the latency to
         // allocate an internal buffer and to schedule an event to the
         // queued port.
-        if (blk->isDirty()) {
+        if (blk->isDirty() || writebackClean) {
             PacketPtr wbPkt = writebackBlk(blk);
             allocateWriteBuffer(wbPkt, forward_time);
             // Set BLOCK_CACHED flag if cached above.
@@ -1484,41 +1522,50 @@
 PacketPtr
 Cache::writebackBlk(CacheBlk *blk)
 {
-    chatty_assert(!isReadOnly, "Writeback from read-only cache");
-    assert(blk && blk->isValid() && blk->isDirty());
+    chatty_assert(!isReadOnly || writebackClean,
+                  "Writeback from read-only cache");
+    assert(blk && blk->isValid() && (blk->isDirty() || writebackClean));
 
     writebacks[Request::wbMasterId]++;
 
-    Request *writebackReq =
-        new Request(tags->regenerateBlkAddr(blk->tag, blk->set), blkSize, 0,
-                Request::wbMasterId);
+    Request *req = new Request(tags->regenerateBlkAddr(blk->tag, blk->set),
+                               blkSize, 0, Request::wbMasterId);
     if (blk->isSecure())
-        writebackReq->setFlags(Request::SECURE);
+        req->setFlags(Request::SECURE);
 
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to