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:
Tiago Mück
2021-06-15 13:14:05 -05:00
parent 94d5cc17a2
commit 91cf58871e
4 changed files with 60 additions and 28 deletions

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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