mem-cache: implement a probe-based interface
authorJavier Bueno <javier.bueno@metempsy.com>
Fri, 9 Nov 2018 15:02:04 +0000 (16:02 +0100)
committerJavier Bueno Hedo <javier.bueno@metempsy.com>
Wed, 14 Nov 2018 14:19:05 +0000 (14:19 +0000)
The HW Prefetcher of a cache can now listen events
from their associated CPUs and from its own cache.

Change-Id: I28aecd8faf8ed44be94464d84485bd1cea2efae3
Reviewed-on: https://gem5-review.googlesource.com/c/14155
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>

src/mem/cache/base.cc
src/mem/cache/base.hh
src/mem/cache/prefetch/Prefetcher.py
src/mem/cache/prefetch/base.cc
src/mem/cache/prefetch/base.hh
src/mem/cache/prefetch/queued.cc
src/mem/cache/prefetch/queued.hh

index ec0383dea6ef1051f0a0005d43cc3eb0ff19b506..4ca8152e978bd1a2a0e45639e40b0ec308dad61c 100644 (file)
@@ -83,7 +83,6 @@ BaseCache::BaseCache(const BaseCacheParams *p, unsigned blk_size)
       writeBuffer("write buffer", p->write_buffers, p->mshrs), // see below
       tags(p->tags),
       prefetcher(p->prefetcher),
-      prefetchOnAccess(p->prefetch_on_access),
       writeAllocator(p->write_allocator),
       writebackClean(p->writeback_clean),
       tempBlockWriteback(nullptr),
@@ -368,50 +367,29 @@ BaseCache::recvTimingReq(PacketPtr pkt)
     Tick request_time = clockEdge(lat) + pkt->headerDelay;
     // Here we reset the timing of the packet.
     pkt->headerDelay = pkt->payloadDelay = 0;
-    // track time of availability of next prefetch, if any
-    Tick next_pf_time = MaxTick;
 
     if (satisfied) {
-        // if need to notify the prefetcher we have to do it before
-        // anything else as later handleTimingReqHit might turn the
-        // packet in a response
-        if (prefetcher &&
-            (prefetchOnAccess || (blk && blk->wasPrefetched()))) {
-            if (blk)
-                blk->status &= ~BlkHWPrefetched;
-
-            // Don't notify on SWPrefetch
-            if (!pkt->cmd.isSWPrefetch()) {
-                assert(!pkt->req->isCacheMaintenance());
-                next_pf_time = prefetcher->notify(pkt);
-            }
+        // notify before anything else as later handleTimingReqHit might turn
+        // the packet in a response
+        ppHit->notify(pkt);
+
+        if (prefetcher && blk && blk->wasPrefetched()) {
+            blk->status &= ~BlkHWPrefetched;
         }
 
         handleTimingReqHit(pkt, blk, request_time);
     } else {
         handleTimingReqMiss(pkt, blk, forward_time, request_time);
 
-        // We should call the prefetcher reguardless if the request is
-        // satisfied or not, reguardless if the request is in the MSHR
-        // or not. The request could be a ReadReq hit, but still not
-        // satisfied (potentially because of a prior write to the same
-        // cache line. So, even when not satisfied, there is an MSHR
-        // already allocated for this, we need to let the prefetcher
-        // know about the request
-
-        // Don't notify prefetcher on SWPrefetch, cache maintenance
-        // operations or for writes that we are coaslescing.
-        if (prefetcher && pkt &&
-            !pkt->cmd.isSWPrefetch() &&
-            !pkt->req->isCacheMaintenance() &&
-            !(writeAllocator && writeAllocator->coalesce() &&
-              pkt->isWrite())) {
-            next_pf_time = prefetcher->notify(pkt);
-        }
+        ppMiss->notify(pkt);
     }
 
-    if (next_pf_time != MaxTick) {
-        schedMemSideSendEvent(next_pf_time);
+    if (prefetcher) {
+        // track time of availability of next prefetch, if any
+        Tick next_pf_time = prefetcher->nextPrefetchReadyTime();
+        if (next_pf_time != MaxTick) {
+            schedMemSideSendEvent(next_pf_time);
+        }
     }
 }
 
@@ -1407,6 +1385,12 @@ BaseCache::isDirty() const
     return tags->anyBlk([](CacheBlk &blk) { return blk.isDirty(); });
 }
 
+bool
+BaseCache::coalesce() const
+{
+    return writeAllocator && writeAllocator->coalesce();
+}
+
 void
 BaseCache::writebackVisitor(CacheBlk &blk)
 {
@@ -2210,6 +2194,13 @@ BaseCache::regStats()
         ;
 }
 
+void
+BaseCache::regProbePoints()
+{
+    ppHit = new ProbePointArg<PacketPtr>(this->getProbeManager(), "Hit");
+    ppMiss = new ProbePointArg<PacketPtr>(this->getProbeManager(), "Miss");
+}
+
 ///////////////
 //
 // CpuSidePort
index 52b0fdbcd082b8aa3db9fe5f12d00215b92cb04e..ad5ff3bc4a5154b4acc19dd40f688d14368f6d36 100644 (file)
@@ -75,6 +75,7 @@
 #include "mem/request.hh"
 #include "params/WriteAllocator.hh"
 #include "sim/eventq.hh"
+#include "sim/probe/probe.hh"
 #include "sim/serialize.hh"
 #include "sim/sim_exit.hh"
 #include "sim/system.hh"
@@ -324,10 +325,11 @@ class BaseCache : public MemObject
     /** Prefetcher */
     BasePrefetcher *prefetcher;
 
-    /**
-     * Notify the prefetcher on every access, not just misses.
-     */
-    const bool prefetchOnAccess;
+    /** To probe when a cache hit occurs */
+    ProbePointArg<PacketPtr> *ppHit;
+
+    /** To probe when a cache miss occurs */
+    ProbePointArg<PacketPtr> *ppMiss;
 
     /**
      * The writeAllocator drive optimizations for streaming writes.
@@ -989,6 +991,9 @@ class BaseCache : public MemObject
      */
     void regStats() override;
 
+    /** Registers probes. */
+    void regProbePoints() override;
+
   public:
     BaseCache(const BaseCacheParams *p, unsigned blk_size);
     ~BaseCache();
@@ -1135,6 +1140,14 @@ class BaseCache : public MemObject
 
     }
 
+    /**
+     * Checks if the cache is coalescing writes
+     *
+     * @return True if the cache is coalescing writes
+     */
+    bool coalesce() const;
+
+
     /**
      * Cache block visitor that writes back dirty cache blocks using
      * functional writes.
@@ -1175,7 +1188,6 @@ class BaseCache : public MemObject
      */
     void serialize(CheckpointOut &cp) const override;
     void unserialize(CheckpointIn &cp) override;
-
 };
 
 /**
index 320755d7531e5d09d84ff47383f9ae92995ec34c..316a6d0ba91dd0e07f2ea0f3f27e40081effaaf4 100644 (file)
 #          Mitch Hayenga
 
 from ClockedObject import ClockedObject
+from m5.SimObject import *
 from m5.params import *
 from m5.proxy import *
 
+class HWPProbeEvent(object):
+    def __init__(self, prefetcher, obj, *listOfNames):
+        self.obj = obj
+        self.prefetcher = prefetcher
+        self.names = listOfNames
+
+    def register(self):
+        if self.obj:
+            for name in self.names:
+                self.prefetcher.getCCObject().addEventProbe(
+                    self.obj.getCCObject(), name)
+
 class BasePrefetcher(ClockedObject):
     type = 'BasePrefetcher'
     abstract = True
     cxx_header = "mem/cache/prefetch/base.hh"
+    cxx_exports = [
+        PyBindMethod("addEventProbe"),
+    ]
     sys = Param.System(Parent.any, "System this prefetcher belongs to")
 
     on_miss = Param.Bool(False, "Only notify prefetcher on misses")
@@ -54,6 +70,26 @@ class BasePrefetcher(ClockedObject):
     on_write = Param.Bool(True, "Notify prefetcher on writes")
     on_data  = Param.Bool(True, "Notify prefetcher on data accesses")
     on_inst  = Param.Bool(True, "Notify prefetcher on instruction accesses")
+    prefetch_on_access = Param.Bool(Parent.prefetch_on_access,
+        "Notify the hardware prefetcher on every access (not just misses)")
+
+    _events = []
+    def addEvent(self, newObject):
+        self._events.append(newObject)
+
+    # Override the normal SimObject::regProbeListeners method and
+    # register deferred event handlers.
+    def regProbeListeners(self):
+        for event in self._events:
+           event.register()
+        self.getCCObject().regProbeListeners()
+
+    def listenFromProbe(self, simObj, *probeNames):
+        if not isinstance(simObj, SimObject):
+            raise TypeError("argument must be of SimObject type")
+        if len(probeNames) <= 0:
+            raise TypeError("probeNames must have at least one element")
+        self.addEvent(HWPProbeEvent(self, simObj, *probeNames))
 
 class QueuedPrefetcher(BasePrefetcher):
     type = "QueuedPrefetcher"
index 22a12ba5fd925260f9d1c5755587b41625312ab6..41c02ac72f72e5ef7fc9faf90ab8480515a2aea4 100644 (file)
 #include <cassert>
 
 #include "base/intmath.hh"
+#include "cpu/base.hh"
 #include "mem/cache/base.hh"
 #include "params/BasePrefetcher.hh"
 #include "sim/system.hh"
 
+void
+BasePrefetcher::PrefetchListener::notify(const PacketPtr &pkt)
+{
+    parent.probeNotify(pkt);
+}
+
 BasePrefetcher::BasePrefetcher(const BasePrefetcherParams *p)
-    : ClockedObject(p), cache(nullptr), blkSize(0), lBlkSize(0),
+    : ClockedObject(p), listeners(), cache(nullptr), blkSize(0), lBlkSize(0),
       system(p->sys), onMiss(p->on_miss), onRead(p->on_read),
       onWrite(p->on_write), onData(p->on_data), onInst(p->on_inst),
       masterId(system->getMasterId(this)),
-      pageBytes(system->getPageBytes())
+      pageBytes(system->getPageBytes()),
+      prefetchOnAccess(p->prefetch_on_access)
 {
 }
 
@@ -163,3 +171,38 @@ BasePrefetcher::pageIthBlockAddress(Addr page, uint32_t blockIndex) const
 {
     return page + (blockIndex << lBlkSize);
 }
+
+void
+BasePrefetcher::probeNotify(const PacketPtr &pkt)
+{
+    // Don't notify prefetcher on SWPrefetch, cache maintenance
+    // operations or for writes that we are coaslescing.
+    if (pkt->cmd.isSWPrefetch()) return;
+    if (pkt->req->isCacheMaintenance()) return;
+    if (pkt->isWrite() && cache != nullptr && cache->coalesce()) return;
+    notify(pkt);
+}
+
+void
+BasePrefetcher::regProbeListeners()
+{
+    /**
+     * If no probes were added by the configuration scripts, connect to the
+     * parent cache using the probe "Miss". Also connect to "Hit", if the
+     * cache is configured to prefetch on accesses.
+     */
+    if (listeners.empty() && cache != nullptr) {
+        ProbeManager *pm(cache->getProbeManager());
+        listeners.push_back(new PrefetchListener(*this, pm, "Miss"));
+        if (prefetchOnAccess) {
+            listeners.push_back(new PrefetchListener(*this, pm, "Hit"));
+        }
+    }
+}
+
+void
+BasePrefetcher::addEventProbe(SimObject *obj, const char *name)
+{
+    ProbeManager *pm(obj->getProbeManager());
+    listeners.push_back(new PrefetchListener(*this, pm, name));
+}
index cc54ab1b830c85667f81698efc8b7c101a444648..394c85c94f10f31747e917b0f6fcc172cb97ac5e 100644 (file)
@@ -56,6 +56,7 @@
 #include "mem/packet.hh"
 #include "mem/request.hh"
 #include "sim/clocked_object.hh"
+#include "sim/probe/probe.hh"
 
 class BaseCache;
 struct BasePrefetcherParams;
@@ -63,6 +64,19 @@ class System;
 
 class BasePrefetcher : public ClockedObject
 {
+    class PrefetchListener : public ProbeListenerArgBase<PacketPtr>
+    {
+      public:
+        PrefetchListener(BasePrefetcher &_parent, ProbeManager *pm,
+                         const std::string &name)
+            : ProbeListenerArgBase(pm, name),
+              parent(_parent) {}
+        void notify(const PacketPtr &pkt) override;
+      protected:
+        BasePrefetcher &parent;
+    };
+
+    std::vector<PrefetchListener *> listeners;
   protected:
 
     // PARAMETERS
@@ -99,6 +113,9 @@ class BasePrefetcher : public ClockedObject
 
     const Addr pageBytes;
 
+    /** Prefetch on every access, not just misses */
+    const bool prefetchOnAccess;
+
     /** Determine if this access should be observed */
     bool observeAccess(const PacketPtr &pkt) const;
 
@@ -135,14 +152,31 @@ class BasePrefetcher : public ClockedObject
     /**
      * Notify prefetcher of cache access (may be any access or just
      * misses, depending on cache parameters.)
-     * @retval Time of next prefetch availability, or MaxTick if none.
      */
-    virtual Tick notify(const PacketPtr &pkt) = 0;
+    virtual void notify(const PacketPtr &pkt) = 0;
 
     virtual PacketPtr getPacket() = 0;
 
     virtual Tick nextPrefetchReadyTime() const = 0;
 
     virtual void regStats();
+
+    /**
+     * Register probe points for this object.
+     */
+    void regProbeListeners() override;
+
+    /**
+     * Process a notification event from the ProbeListener.
+     * @param pkt The memory request causing the event
+     */
+    void probeNotify(const PacketPtr &pkt);
+
+    /**
+     * Add a SimObject and a probe name to listen events from
+     * @param obj The SimObject pointer to listen from
+     * @param name The probe name
+     */
+    void addEventProbe(SimObject *obj, const char *name);
 };
 #endif //__MEM_CACHE_PREFETCH_BASE_HH__
index 3c5647ae30fa6ba1d96bd5c6497fc31fdd8d9c26..f9a036d454d21de162d12af9c5a7cccda5357afc 100644 (file)
@@ -63,7 +63,7 @@ QueuedPrefetcher::~QueuedPrefetcher()
     }
 }
 
-Tick
+void
 QueuedPrefetcher::notify(const PacketPtr &pkt)
 {
     // Verify this access type is observed by prefetcher
@@ -110,8 +110,6 @@ QueuedPrefetcher::notify(const PacketPtr &pkt)
             }
         }
     }
-
-    return pfq.empty() ? MaxTick : pfq.front().tick;
 }
 
 PacketPtr
index bb38377c138bc881877347e975de2e0c3806dfcf..811818fdcebfcb2f78968ff387ffd2e332c3e14e 100644 (file)
@@ -115,7 +115,7 @@ class QueuedPrefetcher : public BasePrefetcher
     QueuedPrefetcher(const QueuedPrefetcherParams *p);
     virtual ~QueuedPrefetcher();
 
-    Tick notify(const PacketPtr &pkt);
+    void notify(const PacketPtr &pkt) override;
     PacketPtr insert(AddrPriority& info, bool is_secure);
 
     // Note: This should really be pure virtual, but doesnt go well with params