changeset b5bef3c8e070 in /z/repo/gem5
details: http://repo.gem5.org/gem5?cmd=changeset;node=b5bef3c8e070
description:
        mem: write streaming support via WriteInvalidate promotion

        Support full-block writes directly rather than requiring RMW:
         * a cache line is allocated in the cache upon receipt of a
           WriteInvalidateReq, not the WriteInvalidateResp.
         * only top-level caches allocate the line; the others just pass
           the request along and invalidate as necessary.
         * to close a timing window between the *Req and the *Resp, a new
           metadata bit tracks whether another cache has read a copy of
           the new line before the writeback to memory.

diffstat:

 src/mem/cache/base.cc       |    6 +-
 src/mem/cache/base.hh       |    3 +
 src/mem/cache/blk.hh        |    5 +-
 src/mem/cache/cache.hh      |    5 +
 src/mem/cache/cache_impl.hh |  238 +++++++++++++++++++++++++++++++------------
 src/mem/packet.cc           |    4 +-
 src/mem/packet.hh           |    8 -
 7 files changed, 190 insertions(+), 79 deletions(-)

diffs (truncated from 499 to 300 lines):

diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/base.cc
--- a/src/mem/cache/base.cc     Wed Sep 03 07:42:50 2014 -0400
+++ b/src/mem/cache/base.cc     Fri Jun 27 12:29:00 2014 -0500
@@ -93,9 +93,9 @@
     // if we already scheduled a retry in this cycle, but it has not yet
     // happened, cancel it
     if (sendRetryEvent.scheduled()) {
-       owner.deschedule(sendRetryEvent);
-       DPRINTF(CachePort, "Cache port %s deschedule retry\n", name());
-       mustSendRetry = true;
+        owner.deschedule(sendRetryEvent);
+        DPRINTF(CachePort, "Cache port %s deschedule retry\n", name());
+        mustSendRetry = true;
     }
 }
 
diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/base.hh
--- a/src/mem/cache/base.hh     Wed Sep 03 07:42:50 2014 -0400
+++ b/src/mem/cache/base.hh     Fri Jun 27 12:29:00 2014 -0500
@@ -94,6 +94,7 @@
         Blocked_NoMSHRs = MSHRQueue_MSHRs,
         Blocked_NoWBBuffers = MSHRQueue_WriteBuffer,
         Blocked_NoTargets,
+        Blocked_PendingWriteInvalidate,
         NUM_BLOCKED_CAUSES
     };
 
@@ -168,6 +169,8 @@
         /** Return to normal operation and accept new requests. */
         void clearBlocked();
 
+        bool isBlocked() const { return blocked; }
+
       protected:
 
         CacheSlavePort(const std::string &_name, BaseCache *_cache,
diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/blk.hh
--- a/src/mem/cache/blk.hh      Wed Sep 03 07:42:50 2014 -0400
+++ b/src/mem/cache/blk.hh      Fri Jun 27 12:29:00 2014 -0500
@@ -72,7 +72,10 @@
     /** block was a hardware prefetch yet unaccessed*/
     BlkHWPrefetched =   0x20,
     /** block holds data from the secure memory space */
-    BlkSecure =         0x40
+    BlkSecure =         0x40,
+    /** can the block transition to E? (hasn't been shared with another cache)
+      * used to close a timing gap when handling WriteInvalidate packets */
+    BlkCanGoExclusive = 0x80
 };
 
 /**
diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/cache.hh
--- a/src/mem/cache/cache.hh    Wed Sep 03 07:42:50 2014 -0400
+++ b/src/mem/cache/cache.hh    Fri Jun 27 12:29:00 2014 -0500
@@ -181,6 +181,11 @@
     const bool doFastWrites;
 
     /**
+     * Turn line-sized writes into WriteInvalidate transactions.
+     */
+    void promoteWholeLineWrites(PacketPtr pkt);
+
+    /**
      * Notify the prefetcher on every access, not just misses.
      */
     const bool prefetchOnAccess;
diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/cache_impl.hh
--- a/src/mem/cache/cache_impl.hh       Wed Sep 03 07:42:50 2014 -0400
+++ b/src/mem/cache/cache_impl.hh       Fri Jun 27 12:29:00 2014 -0500
@@ -312,30 +312,20 @@
             pkt->getAddr(), pkt->isSecure() ? "s" : "ns",
             blk ? "hit" : "miss", blk ? blk->print() : "");
 
-    if (blk != NULL) {
-
-        if (pkt->needsExclusive() ? blk->isWritable() : blk->isReadable()) {
-            // OK to satisfy access
-            incHitCount(pkt);
-            satisfyCpuSideRequest(pkt, blk);
-            return true;
-        }
-    }
-
-    // Can't satisfy access normally... either no block (blk == NULL)
-    // or have block but need exclusive & only have shared.
-
     // 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) {
+    // all).  Like writebacks, we write into the cache upon initial
+    // receipt of a write-invalidate packets as well.
+    if ((pkt->cmd == MemCmd::Writeback) ||
+       ((pkt->cmd == MemCmd::WriteInvalidateReq) && isTopLevel)) {
         assert(blkSize == pkt->getSize());
         if (blk == NULL) {
             // need to do a replacement
             blk = allocateBlock(pkt->getAddr(), pkt->isSecure(), writebacks);
             if (blk == NULL) {
                 // no replaceable block available, give up.
-                // writeback will be forwarded to next level.
+                // Writeback will be forwarded to next level,
+                // WriteInvalidate will be retried.
                 incMissCount(pkt);
                 return false;
             }
@@ -347,17 +337,41 @@
             }
         }
         std::memcpy(blk->data, pkt->getPtr<uint8_t>(), blkSize);
-        blk->status |= BlkDirty;
-        if (pkt->isSupplyExclusive()) {
-            blk->status |= BlkWritable;
+        if (pkt->cmd == MemCmd::Writeback) {
+            blk->status |= BlkDirty;
+            if (pkt->isSupplyExclusive()) {
+                blk->status |= BlkWritable;
+            }
+            // nothing else to do; writeback doesn't expect response
+            assert(!pkt->needsResponse());
+        } else if (pkt->cmd == MemCmd::WriteInvalidateReq) {
+            assert(blk->isReadable()); // implicitly checks for Valid bit also
+            blk->status |= (BlkDirty | BlkCanGoExclusive);
+            blk->status &= ~BlkWritable;
+            ++fastWrites;
         }
-        // nothing else to do; writeback doesn't expect response
-        assert(!pkt->needsResponse());
         DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
         incHitCount(pkt);
         return true;
+    } else if ((pkt->cmd == MemCmd::WriteInvalidateReq) && !isTopLevel) {
+        if (blk != NULL) {
+            assert(blk != tempBlock);
+            tags->invalidate(blk);
+            blk->invalidate();
+        }
+        return true;
+    } else if ((blk != NULL) &&
+               (pkt->needsExclusive() ? blk->isWritable()
+                                      : blk->isReadable())) {
+        // OK to satisfy access
+        incHitCount(pkt);
+        satisfyCpuSideRequest(pkt, blk);
+        return true;
     }
 
+    // Can't satisfy access normally... either no block (blk == NULL)
+    // or have block but need exclusive & only have shared.
+
     incMissCount(pkt);
 
     if (blk == NULL && pkt->isLLSC() && pkt->isWrite()) {
@@ -414,6 +428,19 @@
 }
 
 template<class TagStore>
+void
+Cache<TagStore>::promoteWholeLineWrites(PacketPtr pkt)
+{
+    // Cache line clearing instructions
+    if (doFastWrites && (pkt->cmd == MemCmd::WriteReq) &&
+        (pkt->getSize() == blkSize) && (pkt->getOffset(blkSize) == 0)) {
+        pkt->cmd = MemCmd::WriteInvalidateReq;
+        DPRINTF(Cache, "packet promoted from Write to WriteInvalidate\n");
+        assert(isTopLevel); // should only happen at L1 or I/O cache
+    }
+}
+
+template<class TagStore>
 bool
 Cache<TagStore>::recvTimingReq(PacketPtr pkt)
 {
@@ -439,6 +466,8 @@
         return true;
     }
 
+    promoteWholeLineWrites(pkt);
+
     if (pkt->memInhibitAsserted()) {
         DPRINTF(Cache, "mem inhibited on 0x%x (%s): not responding\n",
                 pkt->getAddr(), pkt->isSecure() ? "s" : "ns");
@@ -496,35 +525,26 @@
 
     bool satisfied = access(pkt, blk, lat, writebacks);
 
-#if 0
-    /** @todo make the fast write alloc (wh64) work with coherence. */
-
-    // If this is a block size write/hint (WH64) allocate the block here
-    // if the coherence protocol allows it.
-    if (!blk && pkt->getSize() >= blkSize && coherence->allowFastWrites() &&
-        (pkt->cmd == MemCmd::WriteReq
-         || pkt->cmd == MemCmd::WriteInvalidateReq) ) {
-        // not outstanding misses, can do this
-        MSHR *outstanding_miss = mshrQueue.findMatch(pkt->getAddr(),
-                                                     pkt->isSecure());
-        if (pkt->cmd == MemCmd::WriteInvalidateReq || !outstanding_miss) {
-            if (outstanding_miss) {
-                warn("WriteInv doing a fastallocate"
-                     "with an outstanding miss to the same address\n");
-            }
-            blk = handleFill(NULL, pkt, BlkValid | BlkWritable,
-                                   writebacks);
-            ++fastWrites;
-        }
-    }
-#endif
-
     // track time of availability of next prefetch, if any
     Tick next_pf_time = 0;
 
     bool needsResponse = pkt->needsResponse();
 
+    if (pkt->cmd == MemCmd::WriteInvalidateReq) {
+        if (!satisfied && isTopLevel) {
+            // access() tried to allocate a block but it could not; abort.
+            setBlocked(Blocked_PendingWriteInvalidate);
+            return false;
+        }
+        satisfied = false;
+        // we need to take the miss path (allocate MSHR, etc.) for
+        // WriteInvalidates because they always need to propagate
+        // throughout the memory system
+    }
+
     if (satisfied) {
+        // hit (for all other request types)
+
         if (prefetcher && (prefetchOnAccess || (blk && blk->wasPrefetched()))) 
{
             if (blk)
                 blk->status &= ~BlkHWPrefetched;
@@ -551,6 +571,16 @@
         // @todo: Make someone pay for this
         pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
 
+        if (blk && blk->isValid() && (blk->status & BlkCanGoExclusive) &&
+            pkt->isWrite() && (pkt->cmd != MemCmd::WriteInvalidateReq)) {
+            // Packet is a Write (needs exclusive) should be delayed because
+            // a WriteInvalidate is pending.  Instead of going the MSHR route,
+            // the Packet should be replayed, since if the block transitions
+            // to Exclusive the write can complete immediately.
+            setBlocked(Blocked_PendingWriteInvalidate);
+            return false;
+        }
+
         Addr blk_addr = blockAlign(pkt->getAddr());
         MSHR *mshr = mshrQueue.findMatch(blk_addr, pkt->isSecure());
 
@@ -639,7 +669,10 @@
             if (pkt->cmd == MemCmd::Writeback) {
                 allocateWriteBuffer(pkt, time, true);
             } else {
-                if (blk && blk->isValid()) {
+                if (pkt->cmd == MemCmd::WriteInvalidateReq) {
+                    // a WriteInvalidate is not a normal write miss;
+                    // the assertions below are not applicable.
+                } else if (blk && blk->isValid()) {
                     // If we have a write miss to a valid block, we
                     // need to mark the block non-readable.  Otherwise
                     // if we allow reads while there's an outstanding
@@ -655,7 +688,8 @@
                     // internally, and have a sufficiently weak memory
                     // model, this is probably unnecessary, but at some
                     // point it must have seemed like we needed it...
-                    assert(pkt->needsExclusive() && !blk->isWritable());
+                    assert(pkt->needsExclusive());
+                    assert(!blk->isWritable());
                     blk->status &= ~BlkReadable;
                 }
 
@@ -697,6 +731,12 @@
         return NULL;
     }
 
+    // WriteInvalidates for cache line clearing instructions don't
+    // require a read; just send directly to the bus.
+    if (cpu_pkt->cmd == MemCmd::WriteInvalidateReq) {
+        return NULL;
+    }
+
     if (!blkValid &&
         (cpu_pkt->cmd == MemCmd::Writeback || cpu_pkt->isUpgrade())) {
         // Writebacks that weren't allocated in access() and upgrades
@@ -716,7 +756,8 @@
     if (blkValid && useUpgrades) {
         // only reason to be here is that blk is shared
         // (read-only) and we need exclusive
-        assert(needsExclusive && !blk->isWritable());
+        assert(needsExclusive);
+        assert(!blk->isWritable());
         cmd = cpu_pkt->isLLSC() ? MemCmd::SCUpgradeReq : MemCmd::UpgradeReq;
     } else if (cpu_pkt->cmd == MemCmd::SCUpgradeFailReq ||
                cpu_pkt->cmd == MemCmd::StoreCondFailReq) {
@@ -751,6 +792,8 @@
     if (system->bypassCaches())
         return ticksToCycles(memSidePort->sendAtomic(pkt));
 
+    promoteWholeLineWrites(pkt);
+
_______________________________________________
gem5-dev mailing list
gem5-dev@gem5.org
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to