mem-ruby: support prefetcher in CHI protocol
Use RubyPrefetcherProxy to support prefetchers in the CHI cache controller L1I/L1D/L2 prefechers can now be added by specifying a non-null prefetcher type when configuring a CHI_RNF. Related JIRA: https://gem5.atlassian.net/browse/GEM5-457 https://gem5.atlassian.net/browse/GEM5-1112 Additional authors: Tuan Ta <tuan.ta2@arm.com> Change-Id: I41dc637969acaab058b22a8c9c3931fa137eeace Signed-off-by: Tiago Mück <tiago.muck@arm.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2021,2022 ARM Limited
|
||||
# Copyright (c) 2021-2023 ARM Limited
|
||||
# All rights reserved.
|
||||
#
|
||||
# The license below extends only to copyright in the software and shall
|
||||
@@ -231,7 +231,8 @@ class CHI_L1Controller(CHI_Cache_Controller):
|
||||
super().__init__(ruby_system)
|
||||
self.sequencer = sequencer
|
||||
self.cache = cache
|
||||
self.use_prefetcher = False
|
||||
self.prefetcher = prefetcher
|
||||
self.use_prefetcher = prefetcher != NULL
|
||||
self.send_evictions = True
|
||||
self.is_HN = False
|
||||
self.enable_DMT = False
|
||||
@@ -268,7 +269,8 @@ class CHI_L2Controller(CHI_Cache_Controller):
|
||||
super().__init__(ruby_system)
|
||||
self.sequencer = NULL
|
||||
self.cache = cache
|
||||
self.use_prefetcher = False
|
||||
self.prefetcher = prefetcher
|
||||
self.use_prefetcher = prefetcher != NULL
|
||||
self.allow_SD = True
|
||||
self.is_HN = False
|
||||
self.enable_DMT = False
|
||||
@@ -304,7 +306,8 @@ class CHI_HNFController(CHI_Cache_Controller):
|
||||
super().__init__(ruby_system)
|
||||
self.sequencer = NULL
|
||||
self.cache = cache
|
||||
self.use_prefetcher = False
|
||||
self.prefetcher = prefetcher
|
||||
self.use_prefetcher = prefetcher != NULL
|
||||
self.addr_ranges = addr_ranges
|
||||
self.allow_SD = True
|
||||
self.is_HN = True
|
||||
@@ -380,6 +383,7 @@ class CHI_DMAController(CHI_Cache_Controller):
|
||||
size = "128"
|
||||
assoc = 1
|
||||
|
||||
self.prefetcher = NULL
|
||||
self.use_prefetcher = False
|
||||
self.cache = DummyCache()
|
||||
self.sequencer.dcache = NULL
|
||||
@@ -499,11 +503,16 @@ class CHI_RNF(CHI_Node):
|
||||
start_index_bit=self._block_size_bits, is_icache=False
|
||||
)
|
||||
|
||||
# Placeholders for future prefetcher support
|
||||
if l1Iprefetcher_type != None or l1Dprefetcher_type != None:
|
||||
m5.fatal("Prefetching not supported yet")
|
||||
l1i_pf = NULL
|
||||
l1d_pf = NULL
|
||||
# prefetcher wrappers
|
||||
if l1Iprefetcher_type != None:
|
||||
l1i_pf = l1Iprefetcher_type()
|
||||
else:
|
||||
l1i_pf = NULL
|
||||
|
||||
if l1Dprefetcher_type != None:
|
||||
l1d_pf = l1Dprefetcher_type()
|
||||
else:
|
||||
l1d_pf = NULL
|
||||
|
||||
# cache controllers
|
||||
cpu.l1i = CHI_L1Controller(
|
||||
@@ -548,9 +557,11 @@ class CHI_RNF(CHI_Node):
|
||||
l2_cache = cache_type(
|
||||
start_index_bit=self._block_size_bits, is_icache=False
|
||||
)
|
||||
|
||||
if pf_type != None:
|
||||
m5.fatal("Prefetching not supported yet")
|
||||
l2_pf = NULL
|
||||
l2_pf = pf_type()
|
||||
else:
|
||||
l2_pf = NULL
|
||||
|
||||
cpu.l2 = CHI_L2Controller(self._ruby_system, l2_cache, l2_pf)
|
||||
|
||||
|
||||
@@ -3440,7 +3440,7 @@ action(Callback_ExpressPrefetchHit, desc="") {
|
||||
cache.profilePrefetchHit();
|
||||
peek(reqRdyPort, CHIRequestMsg) {
|
||||
assert(in_msg.is_local_pf);
|
||||
notifyPfComplete(in_msg.addr);
|
||||
pfProxy.completePrefetch(in_msg.addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3451,7 +3451,7 @@ action(Callback_Miss, desc="") {
|
||||
assert(is_valid(tbe));
|
||||
if (tbe.dataValid && tbe.is_local_pf) {
|
||||
assert(use_prefetcher);
|
||||
notifyPfComplete(tbe.addr);
|
||||
pfProxy.completePrefetch(tbe.addr);
|
||||
|
||||
} else if (tbe.dataValid && (tbe.reqType == CHIRequestType:Load)) {
|
||||
DPRINTF(RubySlicc, "Read data %s\n", tbe.dataBlk);
|
||||
@@ -3562,7 +3562,7 @@ action(Profile_Miss, desc="") {
|
||||
|
||||
// FIXME: this dataBlk is likely to have stale data. This should be fixed
|
||||
// if our prefetcher uses cached data to make prefetch decisions.
|
||||
notifyPfMiss(tbe.seqReq, is_read, tbe.dataBlk);
|
||||
pfProxy.notifyPfMiss(tbe.seqReq, is_read, tbe.dataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3586,7 +3586,7 @@ action(Profile_Hit, desc="") {
|
||||
} else {
|
||||
assert(isWriteReqType(tbe.reqType));
|
||||
}
|
||||
notifyPfHit(tbe.seqReq, is_read, tbe.dataBlk);
|
||||
pfProxy.notifyPfHit(tbe.seqReq, is_read, tbe.dataBlk);
|
||||
|
||||
cache_entry.HWPrefetched := false;
|
||||
}
|
||||
@@ -3600,10 +3600,11 @@ action(Profile_Fill, desc="") {
|
||||
cache_entry.HWPrefetched := tbe.is_local_pf ||
|
||||
(tbe.is_remote_pf &&
|
||||
(upstream_prefetch_trains_prefetcher == false));
|
||||
cache_entry.requestor := getRequestorID(tbe.seqReq);
|
||||
|
||||
// Prefetchers that use this info require notifications from both
|
||||
// demand and pf fills (unlike notifyPfHit/notifyPfMiss)
|
||||
notifyPfFill(tbe.seqReq, tbe.dataBlk, tbe.is_local_pf);
|
||||
pfProxy.notifyPfFill(tbe.seqReq, tbe.dataBlk, tbe.is_local_pf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3617,7 +3618,7 @@ action(Profile_Eviction, desc="") {
|
||||
sequencer.evictionCallback(address);
|
||||
}
|
||||
if (use_prefetcher && is_valid(cache_entry)) {
|
||||
notifyPfEvict(address, cache_entry.HWPrefetched);
|
||||
pfProxy.notifyPfEvict(address, cache_entry.HWPrefetched, cache_entry.requestor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,12 +68,6 @@ void outgoingTransactionEnd(Addr, bool, bool);
|
||||
Event curTransitionEvent();
|
||||
State curTransitionNextState();
|
||||
|
||||
// Placeholders for future prefetch support
|
||||
void notifyPfHit(RequestPtr req, bool is_read, DataBlock blk) { }
|
||||
void notifyPfMiss(RequestPtr req, bool is_read, DataBlock blk) { }
|
||||
void notifyPfFill(RequestPtr req, DataBlock blk, bool from_pf) { }
|
||||
void notifyPfEvict(Addr blkAddr, bool hwPrefetched) { }
|
||||
void notifyPfComplete(Addr addr) { }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Interface functions required by SLICC
|
||||
@@ -307,7 +301,11 @@ bool fromSequencer(CHIRequestType reqType) {
|
||||
reqType == CHIRequestType:AtomicStore;
|
||||
}
|
||||
|
||||
bool inCache(Addr addr) {
|
||||
void regProbePoints() {
|
||||
pfProxy.regProbePoints();
|
||||
}
|
||||
|
||||
bool inCache(Addr addr, bool is_secure) {
|
||||
CacheEntry entry := getCacheEntry(makeLineAddress(addr));
|
||||
// NOTE: we consider data for the addr to be in cache if it exists in local,
|
||||
// upstream, or both caches.
|
||||
@@ -318,7 +316,16 @@ bool inCache(Addr addr) {
|
||||
}
|
||||
}
|
||||
|
||||
bool hasBeenPrefetched(Addr addr) {
|
||||
bool hasBeenPrefetched(Addr addr, bool is_secure, RequestorID requestor) {
|
||||
CacheEntry entry := getCacheEntry(makeLineAddress(addr));
|
||||
if (is_valid(entry)) {
|
||||
return entry.HWPrefetched && (entry.requestor == requestor);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasBeenPrefetched(Addr addr, bool is_secure) {
|
||||
CacheEntry entry := getCacheEntry(makeLineAddress(addr));
|
||||
if (is_valid(entry)) {
|
||||
return entry.HWPrefetched;
|
||||
@@ -327,12 +334,16 @@ bool hasBeenPrefetched(Addr addr) {
|
||||
}
|
||||
}
|
||||
|
||||
bool inMissQueue(Addr addr) {
|
||||
bool inMissQueue(Addr addr, bool is_secure) {
|
||||
Addr line_addr := makeLineAddress(addr);
|
||||
TBE tbe := getCurrentActiveTBE(line_addr);
|
||||
return is_valid(tbe);
|
||||
}
|
||||
|
||||
bool coalesce() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void notifyCoalesced(Addr addr, RubyRequestType type, RequestPtr req,
|
||||
DataBlock data_blk, bool was_miss) {
|
||||
DPRINTF(RubySlicc, "notifyCoalesced(addr=%#x, type=%s, was_miss=%d)\n",
|
||||
@@ -347,9 +358,9 @@ void notifyCoalesced(Addr addr, RubyRequestType type, RequestPtr req,
|
||||
(type == RubyRequestType:Load_Linked) ||
|
||||
(type == RubyRequestType:IFETCH);
|
||||
if (was_miss) {
|
||||
notifyPfMiss(req, is_read, data_blk);
|
||||
pfProxy.notifyPfMiss(req, is_read, data_blk);
|
||||
} else {
|
||||
notifyPfHit(req, is_read, data_blk);
|
||||
pfProxy.notifyPfHit(req, is_read, data_blk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ machine(MachineType:Cache, "Cache coherency protocol") :
|
||||
// happen in parallel. The cache tag latency is used for both cases.
|
||||
CacheMemory * cache;
|
||||
|
||||
// Prefetcher to insert prefetch requests
|
||||
// null if the cache does not support prefetching
|
||||
prefetch::Base * prefetcher;
|
||||
|
||||
// Additional pipeline latency modeling for the different request types
|
||||
// When defined, these are applied after the initial tag array read and
|
||||
// sending necessary snoops.
|
||||
@@ -568,6 +572,7 @@ machine(MachineType:Cache, "Cache coherency protocol") :
|
||||
State state, desc="SLICC line state";
|
||||
DataBlock DataBlk, desc="data for the block";
|
||||
bool HWPrefetched, default="false", desc="Set if this cache entry was prefetched";
|
||||
RequestorID requestor, desc="First requestor to fill this block";
|
||||
}
|
||||
|
||||
// Directory entry
|
||||
@@ -812,6 +817,10 @@ machine(MachineType:Cache, "Cache coherency protocol") :
|
||||
TBETable dvmSnpTBEs, template="<Cache_TBE>", constructor="m_number_of_DVM_snoop_TBEs";
|
||||
TBEStorage storDvmSnpTBEs, constructor="this, m_number_of_DVM_snoop_TBEs";
|
||||
|
||||
// Interface to prefetchers
|
||||
RubyPrefetcherProxy pfProxy, constructor="this, m_prefetcher_ptr, m_prefetchQueue_ptr";
|
||||
|
||||
|
||||
// DVM data
|
||||
// Queue of non-sync operations that haven't been Comp-d yet.
|
||||
// Before a Sync operation can start, this queue must be emptied
|
||||
|
||||
Reference in New Issue
Block a user