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. # All rights reserved.
# #
# The license below extends only to copyright in the software and shall # 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) super().__init__(ruby_system)
self.sequencer = sequencer self.sequencer = sequencer
self.cache = cache self.cache = cache
self.use_prefetcher = False self.prefetcher = prefetcher
self.use_prefetcher = prefetcher != NULL
self.send_evictions = True self.send_evictions = True
self.is_HN = False self.is_HN = False
self.enable_DMT = False self.enable_DMT = False
@@ -268,7 +269,8 @@ class CHI_L2Controller(CHI_Cache_Controller):
super().__init__(ruby_system) super().__init__(ruby_system)
self.sequencer = NULL self.sequencer = NULL
self.cache = cache self.cache = cache
self.use_prefetcher = False self.prefetcher = prefetcher
self.use_prefetcher = prefetcher != NULL
self.allow_SD = True self.allow_SD = True
self.is_HN = False self.is_HN = False
self.enable_DMT = False self.enable_DMT = False
@@ -304,7 +306,8 @@ class CHI_HNFController(CHI_Cache_Controller):
super().__init__(ruby_system) super().__init__(ruby_system)
self.sequencer = NULL self.sequencer = NULL
self.cache = cache self.cache = cache
self.use_prefetcher = False self.prefetcher = prefetcher
self.use_prefetcher = prefetcher != NULL
self.addr_ranges = addr_ranges self.addr_ranges = addr_ranges
self.allow_SD = True self.allow_SD = True
self.is_HN = True self.is_HN = True
@@ -380,6 +383,7 @@ class CHI_DMAController(CHI_Cache_Controller):
size = "128" size = "128"
assoc = 1 assoc = 1
self.prefetcher = NULL
self.use_prefetcher = False self.use_prefetcher = False
self.cache = DummyCache() self.cache = DummyCache()
self.sequencer.dcache = NULL self.sequencer.dcache = NULL
@@ -499,11 +503,16 @@ class CHI_RNF(CHI_Node):
start_index_bit=self._block_size_bits, is_icache=False start_index_bit=self._block_size_bits, is_icache=False
) )
# Placeholders for future prefetcher support # prefetcher wrappers
if l1Iprefetcher_type != None or l1Dprefetcher_type != None: if l1Iprefetcher_type != None:
m5.fatal("Prefetching not supported yet") l1i_pf = l1Iprefetcher_type()
l1i_pf = NULL else:
l1d_pf = NULL l1i_pf = NULL
if l1Dprefetcher_type != None:
l1d_pf = l1Dprefetcher_type()
else:
l1d_pf = NULL
# cache controllers # cache controllers
cpu.l1i = CHI_L1Controller( cpu.l1i = CHI_L1Controller(
@@ -548,9 +557,11 @@ class CHI_RNF(CHI_Node):
l2_cache = cache_type( l2_cache = cache_type(
start_index_bit=self._block_size_bits, is_icache=False start_index_bit=self._block_size_bits, is_icache=False
) )
if pf_type != None: if pf_type != None:
m5.fatal("Prefetching not supported yet") l2_pf = pf_type()
l2_pf = NULL else:
l2_pf = NULL
cpu.l2 = CHI_L2Controller(self._ruby_system, l2_cache, l2_pf) cpu.l2 = CHI_L2Controller(self._ruby_system, l2_cache, l2_pf)

View File

@@ -3440,7 +3440,7 @@ action(Callback_ExpressPrefetchHit, desc="") {
cache.profilePrefetchHit(); cache.profilePrefetchHit();
peek(reqRdyPort, CHIRequestMsg) { peek(reqRdyPort, CHIRequestMsg) {
assert(in_msg.is_local_pf); 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)); assert(is_valid(tbe));
if (tbe.dataValid && tbe.is_local_pf) { if (tbe.dataValid && tbe.is_local_pf) {
assert(use_prefetcher); assert(use_prefetcher);
notifyPfComplete(tbe.addr); pfProxy.completePrefetch(tbe.addr);
} else if (tbe.dataValid && (tbe.reqType == CHIRequestType:Load)) { } else if (tbe.dataValid && (tbe.reqType == CHIRequestType:Load)) {
DPRINTF(RubySlicc, "Read data %s\n", tbe.dataBlk); 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 // FIXME: this dataBlk is likely to have stale data. This should be fixed
// if our prefetcher uses cached data to make prefetch decisions. // 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 { } else {
assert(isWriteReqType(tbe.reqType)); assert(isWriteReqType(tbe.reqType));
} }
notifyPfHit(tbe.seqReq, is_read, tbe.dataBlk); pfProxy.notifyPfHit(tbe.seqReq, is_read, tbe.dataBlk);
cache_entry.HWPrefetched := false; cache_entry.HWPrefetched := false;
} }
@@ -3600,10 +3600,11 @@ action(Profile_Fill, desc="") {
cache_entry.HWPrefetched := tbe.is_local_pf || cache_entry.HWPrefetched := tbe.is_local_pf ||
(tbe.is_remote_pf && (tbe.is_remote_pf &&
(upstream_prefetch_trains_prefetcher == false)); (upstream_prefetch_trains_prefetcher == false));
cache_entry.requestor := getRequestorID(tbe.seqReq);
// Prefetchers that use this info require notifications from both // Prefetchers that use this info require notifications from both
// demand and pf fills (unlike notifyPfHit/notifyPfMiss) // 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); sequencer.evictionCallback(address);
} }
if (use_prefetcher && is_valid(cache_entry)) { 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(); Event curTransitionEvent();
State curTransitionNextState(); 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 // Interface functions required by SLICC
@@ -307,7 +301,11 @@ bool fromSequencer(CHIRequestType reqType) {
reqType == CHIRequestType:AtomicStore; reqType == CHIRequestType:AtomicStore;
} }
bool inCache(Addr addr) { void regProbePoints() {
pfProxy.regProbePoints();
}
bool inCache(Addr addr, bool is_secure) {
CacheEntry entry := getCacheEntry(makeLineAddress(addr)); CacheEntry entry := getCacheEntry(makeLineAddress(addr));
// NOTE: we consider data for the addr to be in cache if it exists in local, // NOTE: we consider data for the addr to be in cache if it exists in local,
// upstream, or both caches. // 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)); CacheEntry entry := getCacheEntry(makeLineAddress(addr));
if (is_valid(entry)) { if (is_valid(entry)) {
return entry.HWPrefetched; 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); Addr line_addr := makeLineAddress(addr);
TBE tbe := getCurrentActiveTBE(line_addr); TBE tbe := getCurrentActiveTBE(line_addr);
return is_valid(tbe); return is_valid(tbe);
} }
bool coalesce() {
return false;
}
void notifyCoalesced(Addr addr, RubyRequestType type, RequestPtr req, void notifyCoalesced(Addr addr, RubyRequestType type, RequestPtr req,
DataBlock data_blk, bool was_miss) { DataBlock data_blk, bool was_miss) {
DPRINTF(RubySlicc, "notifyCoalesced(addr=%#x, type=%s, was_miss=%d)\n", 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:Load_Linked) ||
(type == RubyRequestType:IFETCH); (type == RubyRequestType:IFETCH);
if (was_miss) { if (was_miss) {
notifyPfMiss(req, is_read, data_blk); pfProxy.notifyPfMiss(req, is_read, data_blk);
} else { } 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. // happen in parallel. The cache tag latency is used for both cases.
CacheMemory * cache; 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 // Additional pipeline latency modeling for the different request types
// When defined, these are applied after the initial tag array read and // When defined, these are applied after the initial tag array read and
// sending necessary snoops. // sending necessary snoops.
@@ -568,6 +572,7 @@ machine(MachineType:Cache, "Cache coherency protocol") :
State state, desc="SLICC line state"; State state, desc="SLICC line state";
DataBlock DataBlk, desc="data for the block"; DataBlock DataBlk, desc="data for the block";
bool HWPrefetched, default="false", desc="Set if this cache entry was prefetched"; bool HWPrefetched, default="false", desc="Set if this cache entry was prefetched";
RequestorID requestor, desc="First requestor to fill this block";
} }
// Directory entry // Directory entry
@@ -812,6 +817,10 @@ machine(MachineType:Cache, "Cache coherency protocol") :
TBETable dvmSnpTBEs, template="<Cache_TBE>", constructor="m_number_of_DVM_snoop_TBEs"; TBETable dvmSnpTBEs, template="<Cache_TBE>", constructor="m_number_of_DVM_snoop_TBEs";
TBEStorage storDvmSnpTBEs, constructor="this, 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 // DVM data
// Queue of non-sync operations that haven't been Comp-d yet. // Queue of non-sync operations that haven't been Comp-d yet.
// Before a Sync operation can start, this queue must be emptied // Before a Sync operation can start, this queue must be emptied