mem-cache: implement a probe-based interface
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>
This commit is contained in:
committed by
Javier Bueno Hedo
parent
e8e92a12af
commit
8590243fef
59
src/mem/cache/base.cc
vendored
59
src/mem/cache/base.cc
vendored
@@ -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;
|
||||
// notify before anything else as later handleTimingReqHit might turn
|
||||
// the packet in a response
|
||||
ppHit->notify(pkt);
|
||||
|
||||
// Don't notify on SWPrefetch
|
||||
if (!pkt->cmd.isSWPrefetch()) {
|
||||
assert(!pkt->req->isCacheMaintenance());
|
||||
next_pf_time = prefetcher->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
|
||||
|
||||
22
src/mem/cache/base.hh
vendored
22
src/mem/cache/base.hh
vendored
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
36
src/mem/cache/prefetch/Prefetcher.py
vendored
36
src/mem/cache/prefetch/Prefetcher.py
vendored
@@ -40,13 +40,29 @@
|
||||
# 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"
|
||||
|
||||
47
src/mem/cache/prefetch/base.cc
vendored
47
src/mem/cache/prefetch/base.cc
vendored
@@ -51,16 +51,24 @@
|
||||
#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));
|
||||
}
|
||||
|
||||
38
src/mem/cache/prefetch/base.hh
vendored
38
src/mem/cache/prefetch/base.hh
vendored
@@ -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__
|
||||
|
||||
4
src/mem/cache/prefetch/queued.cc
vendored
4
src/mem/cache/prefetch/queued.cc
vendored
@@ -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
|
||||
|
||||
2
src/mem/cache/prefetch/queued.hh
vendored
2
src/mem/cache/prefetch/queued.hh
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user