mem-cache: Add a data-update probe to cache
authorDaniel R. Carvalho <odanrc@yahoo.com.br>
Sun, 1 Nov 2020 12:17:55 +0000 (13:17 +0100)
committerDaniel Carvalho <odanrc@yahoo.com.br>
Wed, 16 Dec 2020 12:13:05 +0000 (12:13 +0000)
This probe is responsible for notifying any changes to the
data contents of a block. This includes fills, overwrites,
and invalidations/evictions.

Jira: https://gem5.atlassian.net/browse/GEM5-814

Change-Id: I1ff3c09c63d5402765c2125c4d76d95b614877d6
Signed-off-by: Daniel R. Carvalho <odanrc@yahoo.com.br>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/37096
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
src/mem/cache/base.cc
src/mem/cache/base.hh

index 98467ab98cc34d38801511af53d2a91562506b36..22c28421d18290d60f5de59a0ac7a09cd81ec1fb 100644 (file)
@@ -676,6 +676,31 @@ BaseCache::functionalAccess(PacketPtr pkt, bool from_cpu_side)
     }
 }
 
+void
+BaseCache::updateBlockData(CacheBlk *blk, const PacketPtr cpkt,
+    bool has_old_data)
+{
+    DataUpdate data_update(regenerateBlkAddr(blk), blk->isSecure());
+    if (ppDataUpdate->hasListeners()) {
+        if (has_old_data) {
+            data_update.oldData = std::vector<uint64_t>(blk->data,
+                blk->data + (blkSize / sizeof(uint64_t)));
+        }
+    }
+
+    // Actually perform the data update
+    if (cpkt) {
+        cpkt->writeDataToBlock(blk->data, blkSize);
+    }
+
+    if (ppDataUpdate->hasListeners()) {
+        if (cpkt) {
+            data_update.newData = std::vector<uint64_t>(blk->data,
+                blk->data + (blkSize / sizeof(uint64_t)));
+        }
+        ppDataUpdate->notify(data_update);
+    }
+}
 
 void
 BaseCache::cmpAndSwap(CacheBlk *blk, PacketPtr pkt)
@@ -692,6 +717,13 @@ BaseCache::cmpAndSwap(CacheBlk *blk, PacketPtr pkt)
 
     assert(sizeof(uint64_t) >= pkt->getSize());
 
+    // Get a copy of the old block's contents for the probe before the update
+    DataUpdate data_update(regenerateBlkAddr(blk), blk->isSecure());
+    if (ppDataUpdate->hasListeners()) {
+        data_update.oldData = std::vector<uint64_t>(blk->data,
+            blk->data + (blkSize / sizeof(uint64_t)));
+    }
+
     overwrite_mem = true;
     // keep a copy of our possible write value, and copy what is at the
     // memory address into the packet
@@ -714,6 +746,12 @@ BaseCache::cmpAndSwap(CacheBlk *blk, PacketPtr pkt)
     if (overwrite_mem) {
         std::memcpy(blk_data, &overwrite_val, pkt->getSize());
         blk->setCoherenceBits(CacheBlk::DirtyBit);
+
+        if (ppDataUpdate->hasListeners()) {
+            data_update.newData = std::vector<uint64_t>(blk->data,
+                blk->data + (blkSize / sizeof(uint64_t)));
+            ppDataUpdate->notify(data_update);
+        }
     }
 }
 
@@ -961,6 +999,14 @@ BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool)
     // isWrite() will be true for them
     if (pkt->cmd == MemCmd::SwapReq) {
         if (pkt->isAtomicOp()) {
+            // Get a copy of the old block's contents for the probe before
+            // the update
+            DataUpdate data_update(regenerateBlkAddr(blk), blk->isSecure());
+            if (ppDataUpdate->hasListeners()) {
+                data_update.oldData = std::vector<uint64_t>(blk->data,
+                    blk->data + (blkSize / sizeof(uint64_t)));
+            }
+
             // extract data from cache and save it into the data field in
             // the packet as a return value from this atomic op
             int offset = tags->extractBlkOffset(pkt->getAddr());
@@ -970,6 +1016,13 @@ BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool)
             // execute AMO operation
             (*(pkt->getAtomicOp()))(blk_data);
 
+            // Inform of this block's data contents update
+            if (ppDataUpdate->hasListeners()) {
+                data_update.newData = std::vector<uint64_t>(blk->data,
+                    blk->data + (blkSize / sizeof(uint64_t)));
+                ppDataUpdate->notify(data_update);
+            }
+
             // set block status to dirty
             blk->setCoherenceBits(CacheBlk::DirtyBit);
         } else {
@@ -983,7 +1036,7 @@ BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool)
         assert(blk->isSet(CacheBlk::WritableBit));
         // Write or WriteLine at the first cache with block in writable state
         if (blk->checkWrite(pkt)) {
-            pkt->writeDataToBlock(blk->data, blkSize);
+            updateBlockData(blk, pkt, true);
         }
         // Always mark the line as dirty (and thus transition to the
         // Modified state) even if we are a failed StoreCond so we
@@ -1170,6 +1223,7 @@ BaseCache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
             return true;
         }
 
+        const bool has_old_data = blk && blk->isValid();
         if (!blk) {
             // need to do a replacement
             blk = allocateBlock(pkt, writebacks);
@@ -1206,7 +1260,8 @@ BaseCache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
         }
         // nothing else to do; writeback doesn't expect response
         assert(!pkt->needsResponse());
-        pkt->writeDataToBlock(blk->data, blkSize);
+
+        updateBlockData(blk, pkt, has_old_data);
         DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
         incHitCount(pkt);
 
@@ -1240,6 +1295,7 @@ BaseCache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
         // of the block as well.
         assert(blkSize == pkt->getSize());
 
+        const bool has_old_data = blk && blk->isValid();
         if (!blk) {
             if (pkt->writeThrough()) {
                 // if this is a write through packet, we don't try to
@@ -1279,7 +1335,8 @@ BaseCache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
         }
         // nothing else to do; writeback doesn't expect response
         assert(!pkt->needsResponse());
-        pkt->writeDataToBlock(blk->data, blkSize);
+
+        updateBlockData(blk, pkt, has_old_data);
         DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
 
         incHitCount(pkt);
@@ -1352,6 +1409,7 @@ BaseCache::handleFill(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks,
     assert(pkt->isResponse());
     Addr addr = pkt->getAddr();
     bool is_secure = pkt->isSecure();
+    const bool has_old_data = blk && blk->isValid();
 #if TRACING_ON
     const std::string old_state = blk ? blk->print() : "";
 #endif
@@ -1433,7 +1491,7 @@ BaseCache::handleFill(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks,
         assert(pkt->hasData());
         assert(pkt->getSize() == blkSize);
 
-        pkt->writeDataToBlock(blk->data, blkSize);
+        updateBlockData(blk, pkt, has_old_data);
     }
     // The block will be ready when the payload arrives and the fill is done
     blk->setWhenReady(clockEdge(fillLatency) + pkt->headerDelay +
@@ -1507,6 +1565,9 @@ BaseCache::invalidateBlock(CacheBlk *blk)
         stats.unusedPrefetches++;
     }
 
+    // Notify that the data contents for this address are no longer present
+    updateBlockData(blk, nullptr, blk->isValid());
+
     // If handling a block present in the Tags, let it do its invalidation
     // process, which will update stats and invalidate the block itself
     if (blk != tempBlock) {
@@ -2325,6 +2386,8 @@ BaseCache::regProbePoints()
     ppHit = new ProbePointArg<PacketPtr>(this->getProbeManager(), "Hit");
     ppMiss = new ProbePointArg<PacketPtr>(this->getProbeManager(), "Miss");
     ppFill = new ProbePointArg<PacketPtr>(this->getProbeManager(), "Fill");
+    ppDataUpdate =
+        new ProbePointArg<DataUpdate>(this->getProbeManager(), "Data Update");
 }
 
 ///////////////
index 8dad1b7f74903f9442c794942092d58bf86805ad..5342eaab0043f76b3fafc954395bd0ffe320e742 100644 (file)
@@ -108,6 +108,28 @@ class BaseCache : public ClockedObject
         NUM_BLOCKED_CAUSES
     };
 
+    /**
+     * A data contents update is composed of the updated block's address,
+     * the old contents, and the new contents.
+     * @sa ppDataUpdate
+     */
+    struct DataUpdate
+    {
+        /** The updated block's address. */
+        Addr addr;
+        /** Whether the block belongs to the secure address space. */
+        bool isSecure;
+        /** The stale data contents. If zero-sized this update is a fill. */
+        std::vector<uint64_t> oldData;
+        /** The new data contents. If zero-sized this is an invalidation. */
+        std::vector<uint64_t> newData;
+
+        DataUpdate(Addr _addr, bool is_secure)
+          : addr(_addr), isSecure(is_secure), oldData(), newData()
+        {
+        }
+    };
+
   protected:
 
     /**
@@ -334,6 +356,13 @@ class BaseCache : public ClockedObject
     /** To probe when a cache fill occurs */
     ProbePointArg<PacketPtr> *ppFill;
 
+    /**
+     * To probe when the contents of a block are updated. Content updates
+     * include data fills, overwrites, and invalidations, which means that
+     * this probe partially overlaps with other probes.
+     */
+    ProbePointArg<DataUpdate> *ppDataUpdate;
+
     /**
      * The writeAllocator drive optimizations for streaming writes.
      * It first determines whether a WriteReq MSHR should be delayed,
@@ -574,6 +603,18 @@ class BaseCache : public ClockedObject
      */
     virtual void functionalAccess(PacketPtr pkt, bool from_cpu_side);
 
+    /**
+     * Update the data contents of a block. When no packet is provided no
+     * data will be written to the block, which means that this was likely
+     * triggered by an invalidation.
+     *
+     * @param blk The block being updated.
+     * @param cpkt The packet containing the new data.
+     * @param has_old_data Whether this block had data previously.
+     */
+    void updateBlockData(CacheBlk *blk, const PacketPtr cpkt,
+        bool has_old_data);
+
     /**
      * Handle doing the Compare and Swap function for SPARC.
      */