mem: Rework the structuring of the prefetchers

Re-organizes the prefetcher class structure. Previously the
BasePrefetcher forced multiple assumptions on the prefetchers that
inherited from it. This patch makes the BasePrefetcher class truly
representative of base functionality. For example, the base class no
longer enforces FIFO order. Instead, prefetchers with FIFO requests
(like the existing stride and tagged prefetchers) now inherit from a
new QueuedPrefetcher base class.

Finally, the stride-based prefetcher now assumes a custimizable lookup table
(sets/ways) rather than the previous fully associative structure.
This commit is contained in:
Mitch Hayenga
2014-12-23 09:31:18 -05:00
parent 6cb58b2bd2
commit df82a2d003
11 changed files with 598 additions and 486 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 ARM Limited
* Copyright (c) 2013-2014 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
@@ -38,6 +38,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Ron Dreslinski
* Mitch Hayenga
*/
/**
@@ -47,20 +48,14 @@
#include <list>
#include "base/trace.hh"
#include "debug/HWPrefetch.hh"
#include "mem/cache/prefetch/base.hh"
#include "mem/cache/base.hh"
#include "mem/request.hh"
#include "sim/system.hh"
BasePrefetcher::BasePrefetcher(const Params *p)
: ClockedObject(p), size(p->size), cache(nullptr), blkSize(0),
latency(p->latency), degree(p->degree),
useMasterId(p->use_master_id), pageStop(!p->cross_pages),
serialSquash(p->serial_squash), onlyData(p->data_accesses_only),
onMissOnly(p->on_miss_only), onReadOnly(p->on_read_only),
onPrefetch(p->on_prefetch), system(p->sys),
BasePrefetcher::BasePrefetcher(const BasePrefetcherParams *p)
: ClockedObject(p), cache(nullptr), blkSize(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(name())),
pageBytes(system->getPageBytes())
{
@@ -77,239 +72,52 @@ BasePrefetcher::setCache(BaseCache *_cache)
void
BasePrefetcher::regStats()
{
pfIdentified
.name(name() + ".prefetcher.num_hwpf_identified")
.desc("number of hwpf identified")
;
pfMSHRHit
.name(name() + ".prefetcher.num_hwpf_already_in_mshr")
.desc("number of hwpf that were already in mshr")
;
pfCacheHit
.name(name() + ".prefetcher.num_hwpf_already_in_cache")
.desc("number of hwpf that were already in the cache")
;
pfBufferHit
.name(name() + ".prefetcher.num_hwpf_already_in_prefetcher")
.desc("number of hwpf that were already in the prefetch queue")
;
pfRemovedFull
.name(name() + ".prefetcher.num_hwpf_evicted")
.desc("number of hwpf removed due to no buffer left")
;
pfRemovedMSHR
.name(name() + ".prefetcher.num_hwpf_removed_MSHR_hit")
.desc("number of hwpf removed because MSHR allocated")
;
pfIssued
.name(name() + ".prefetcher.num_hwpf_issued")
.name(name() + ".num_hwpf_issued")
.desc("number of hwpf issued")
;
pfSpanPage
.name(name() + ".prefetcher.num_hwpf_span_page")
.desc("number of hwpf spanning a virtual page")
;
pfSquashed
.name(name() + ".prefetcher.num_hwpf_squashed_from_miss")
.desc("number of hwpf that got squashed due to a miss "
"aborting calculation time")
;
}
inline bool
BasePrefetcher::inCache(Addr addr, bool is_secure)
bool
BasePrefetcher::observeAccess(const PacketPtr &pkt) const
{
Addr addr = pkt->getAddr();
bool fetch = pkt->req->isInstFetch();
bool read= pkt->isRead();
bool is_secure = pkt->isSecure();
if (pkt->req->isUncacheable()) return false;
if (fetch && !onInst) return false;
if (!fetch && !onData) return false;
if (!fetch && read && !onRead) return false;
if (!fetch && !read && !onWrite) return false;
if (onMiss) {
return !inCache(addr, is_secure) &&
!inMissQueue(addr, is_secure);
}
return true;
}
bool
BasePrefetcher::inCache(Addr addr, bool is_secure) const
{
if (cache->inCache(addr, is_secure)) {
pfCacheHit++;
return true;
}
return false;
}
inline bool
BasePrefetcher::inMissQueue(Addr addr, bool is_secure)
bool
BasePrefetcher::inMissQueue(Addr addr, bool is_secure) const
{
if (cache->inMissQueue(addr, is_secure)) {
pfMSHRHit++;
return true;
}
return false;
}
PacketPtr
BasePrefetcher::getPacket()
{
DPRINTF(HWPrefetch, "Requesting a hw_pf to issue\n");
if (pf.empty()) {
DPRINTF(HWPrefetch, "No HW_PF found\n");
return NULL;
}
PacketPtr pkt = pf.begin()->pkt;
while (!pf.empty()) {
pkt = pf.begin()->pkt;
pf.pop_front();
Addr blk_addr = pkt->getAddr() & ~(Addr)(blkSize-1);
bool is_secure = pkt->isSecure();
if (!inCache(blk_addr, is_secure) && !inMissQueue(blk_addr, is_secure))
// we found a prefetch, return it
break;
DPRINTF(HWPrefetch, "addr 0x%x (%s) in cache, skipping\n",
pkt->getAddr(), is_secure ? "s" : "ns");
delete pkt->req;
delete pkt;
if (pf.empty()) {
cache->deassertMemSideBusRequest(BaseCache::Request_PF);
return NULL; // None left, all were in cache
}
}
pfIssued++;
assert(pkt != NULL);
DPRINTF(HWPrefetch, "returning 0x%x (%s)\n", pkt->getAddr(),
pkt->isSecure() ? "s" : "ns");
return pkt;
}
Tick
BasePrefetcher::notify(PacketPtr &pkt, Tick tick)
{
// Don't consult the prefetcher if any of the following conditons are true
// 1) The request is uncacheable
// 2) The request is a fetch, but we are only prefeching data
// 3) The request is a cache hit, but we are only training on misses
// 4) THe request is a write, but we are only training on reads
if (!pkt->req->isUncacheable() && !(pkt->req->isInstFetch() && onlyData) &&
!(onMissOnly && inCache(pkt->getAddr(), true)) &&
!(onReadOnly && !pkt->isRead())) {
// Calculate the blk address
Addr blk_addr = pkt->getAddr() & ~(Addr)(blkSize-1);
bool is_secure = pkt->isSecure();
// Check if miss is in pfq, if so remove it
std::list<DeferredPacket>::iterator iter = inPrefetch(blk_addr,
is_secure);
if (iter != pf.end()) {
DPRINTF(HWPrefetch, "Saw a miss to a queued prefetch addr: "
"0x%x (%s), removing it\n", blk_addr,
is_secure ? "s" : "ns");
pfRemovedMSHR++;
delete iter->pkt->req;
delete iter->pkt;
iter = pf.erase(iter);
if (pf.empty())
cache->deassertMemSideBusRequest(BaseCache::Request_PF);
}
// Remove anything in queue with delay older than time
// since everything is inserted in time order, start from end
// and work until pf.empty() or time is earlier
// This is done to emulate Aborting the previous work on a new miss
// Needed for serial calculators like GHB
if (serialSquash) {
iter = pf.end();
if (iter != pf.begin())
iter--;
while (!pf.empty() && iter->tick >= tick) {
pfSquashed++;
DPRINTF(HWPrefetch, "Squashing old prefetch addr: 0x%x\n",
iter->pkt->getAddr());
delete iter->pkt->req;
delete iter->pkt;
iter = pf.erase(iter);
if (iter != pf.begin())
iter--;
}
if (pf.empty())
cache->deassertMemSideBusRequest(BaseCache::Request_PF);
}
std::list<Addr> addresses;
std::list<Cycles> delays;
calculatePrefetch(pkt, addresses, delays);
std::list<Addr>::iterator addrIter = addresses.begin();
std::list<Cycles>::iterator delayIter = delays.begin();
for (; addrIter != addresses.end(); ++addrIter, ++delayIter) {
Addr addr = *addrIter;
pfIdentified++;
DPRINTF(HWPrefetch, "Found a pf candidate addr: 0x%x, "
"inserting into prefetch queue with delay %d time %d\n",
addr, *delayIter, time);
// Check if it is already in the pf buffer
if (inPrefetch(addr, is_secure) != pf.end()) {
pfBufferHit++;
DPRINTF(HWPrefetch, "Prefetch addr already in pf buffer\n");
continue;
}
// create a prefetch memreq
Request *prefetchReq = new Request(*addrIter, blkSize, 0, masterId);
if (is_secure)
prefetchReq->setFlags(Request::SECURE);
prefetchReq->taskId(ContextSwitchTaskId::Prefetcher);
PacketPtr prefetch =
new Packet(prefetchReq, MemCmd::HardPFReq);
prefetch->allocate();
prefetch->req->setThreadContext(pkt->req->contextId(),
pkt->req->threadId());
// Tag orefetch reqeuests with corresponding PC to train lower
// cache-level prefetchers
if (onPrefetch && pkt->req->hasPC())
prefetch->req->setPC(pkt->req->getPC());
// We just remove the head if we are full
if (pf.size() == size) {
pfRemovedFull++;
PacketPtr old_pkt = pf.begin()->pkt;
DPRINTF(HWPrefetch, "Prefetch queue full, "
"removing oldest 0x%x\n", old_pkt->getAddr());
delete old_pkt->req;
delete old_pkt;
pf.pop_front();
}
pf.push_back(DeferredPacket(tick + clockPeriod() * *delayIter,
prefetch));
}
}
return pf.empty() ? 0 : pf.front().tick;
}
std::list<BasePrefetcher::DeferredPacket>::iterator
BasePrefetcher::inPrefetch(Addr address, bool is_secure)
{
// Guaranteed to only be one match, we always check before inserting
std::list<DeferredPacket>::iterator iter;
for (iter = pf.begin(); iter != pf.end(); iter++) {
if (((*iter).pkt->getAddr() & ~(Addr)(blkSize-1)) == address &&
(*iter).pkt->isSecure() == is_secure) {
return iter;
}
}
return pf.end();
}
bool
BasePrefetcher::samePage(Addr a, Addr b) const
{