mem-ruby,mem-cache: ruby supports classic pfs

This patch adds RubyPrefetcherProxy, which provides means to inject
requests generated by the "classic" prefetchers into a SLICC prefetch
queue. It defines defines notifyPf* functions to be used by protocols
to notify a prefetcher. It also includes the probes required to
interface with the classic implementation.
AbstractController defines the accessor needed to snoop the caches.

A followup patch will add support for RubyPrefetcherProxy in the
CHI protocol.

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: Ie908150b510f951cdd6fd0fd9c95d9760ff70fb0
Signed-off-by: Tiago Mück <tiago.muck@arm.com>
This commit is contained in:
Tiago Mück
2022-09-08 14:39:46 -05:00
parent 3a7192d682
commit 94d5cc17a2
10 changed files with 463 additions and 6 deletions

View File

@@ -1,6 +1,6 @@
# -*- mode:python -*-
# Copyright (c) 2021 Arm Limited
# Copyright (c) 2021,2023 Arm Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@@ -111,6 +111,7 @@ MakeInclude('structures/DirectoryMemory.hh')
MakeInclude('structures/PerfectCacheMemory.hh')
MakeInclude('structures/PersistentTable.hh')
MakeInclude('structures/RubyPrefetcher.hh')
MakeInclude('structures/RubyPrefetcherProxy.hh')
MakeInclude('structures/TBEStorage.hh')
if env['PROTOCOL'] == 'CHI':
MakeInclude('structures/MN_TBEStorage.hh')

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021 ARM Limited
* Copyright (c) 2020-2021,2023 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
@@ -51,6 +51,8 @@ external_type(Addr, primitive="yes");
external_type(Cycles, primitive="yes", default="Cycles(0)");
external_type(Tick, primitive="yes", default="0");
external_type(RequestPtr, primitive="yes", default="nullptr");
external_type(RequestorID, primitive="yes");
external_type(prefetch::Base, primitive="yes");
structure(WriteMask, external="yes", desc="...") {
void clear();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021 ARM Limited
* Copyright (c) 2020-2021,2023 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -259,3 +259,14 @@ structure (RubyPrefetcher, external = "yes") {
void observePfHit(Addr);
void observePfMiss(Addr);
}
structure(RubyPrefetcherProxy, external = "yes") {
void notifyPfHit(RequestPtr, bool, DataBlock);
void notifyPfMiss(RequestPtr, bool, DataBlock);
void notifyPfFill(RequestPtr, DataBlock, bool);
void notifyPfEvict(Addr, bool, RequestorID);
void completePrefetch(Addr);
// SLICC controller must define its own regProbePoints and call
// this for every RubyPrefetcherProxy object present
void regProbePoints();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 ARM Limited
* Copyright (c) 2021,2023 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -60,3 +60,4 @@ Addr makeNextStrideAddress(Addr addr, int stride);
structure(BoolVec, external="yes") {
}
int countBoolVec(BoolVec bVec);
RequestorID getRequestorID(RequestPtr req);

View File

@@ -367,6 +367,28 @@ class AbstractController : public ClockedObject, public Consumer
void wakeUpAllBuffers();
bool serviceMemoryQueue();
/**
* Functions needed by CacheAccessor. These are implemented in SLICC,
* thus the const& for all args to match the generated code.
*/
virtual bool inCache(const Addr &addr, const bool &is_secure)
{ fatal("inCache: prefetching not supported"); return false; }
virtual bool hasBeenPrefetched(const Addr &addr, const bool &is_secure)
{ fatal("hasBeenPrefetched: prefetching not supported"); return false; }
virtual bool hasBeenPrefetched(const Addr &addr, const bool &is_secure,
const RequestorID &requestor)
{ fatal("hasBeenPrefetched: prefetching not supported"); return false; }
virtual bool inMissQueue(const Addr &addr, const bool &is_secure)
{ fatal("inMissQueue: prefetching not supported"); return false; }
virtual bool coalesce()
{ fatal("coalesce: prefetching not supported"); return false; }
friend class RubyPrefetcherProxy;
protected:
const NodeID m_version;
MachineID m_machineID;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021 ARM Limited
* Copyright (c) 2020-2021,2023 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -316,6 +316,12 @@ countBoolVec(BoolVec bVec)
return count;
}
inline RequestorID
getRequestorID(RequestPtr req)
{
return req->requestorId();
}
} // namespace ruby
} // namespace gem5

View File

@@ -0,0 +1,234 @@
/*
* Copyright (c) 2023 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mem/ruby/structures/RubyPrefetcherProxy.hh"
#include "debug/HWPrefetch.hh"
#include "mem/ruby/system/RubySystem.hh"
namespace gem5
{
namespace ruby
{
RubyPrefetcherProxy::RubyPrefetcherProxy(AbstractController* _parent,
prefetch::Base* _prefetcher,
MessageBuffer *_pf_queue)
:Named(_parent->name()),
prefetcher(_prefetcher),
cacheCntrl(_parent),
pfQueue(_pf_queue),
pfEvent([this]{ issuePrefetch(); }, name()),
ppHit(nullptr), ppMiss(nullptr),
ppFill(nullptr), ppDataUpdate(nullptr)
{
fatal_if(!cacheCntrl,
"initializing a RubyPrefetcherProxy without a parent");
if (prefetcher) {
fatal_if(!pfQueue,
"%s initializing a RubyPrefetcherProxy without a prefetch queue",
name());
prefetcher->setParentInfo(
cacheCntrl->params().system,
cacheCntrl->getProbeManager(),
RubySystem::getBlockSizeBytes());
}
}
void
RubyPrefetcherProxy::scheduleNextPrefetch()
{
if (pfEvent.scheduled())
return;
Tick next_pf_time = std::max(prefetcher->nextPrefetchReadyTime(),
cacheCntrl->clockEdge(Cycles(1)));
if (next_pf_time != MaxTick) {
DPRINTF(HWPrefetch, "Next prefetch ready at %d\n", next_pf_time);
cacheCntrl->schedule(&pfEvent, next_pf_time);
}
}
void
RubyPrefetcherProxy::deschedulePrefetch()
{
if (pfEvent.scheduled())
cacheCntrl->deschedule(&pfEvent);
}
void
RubyPrefetcherProxy::completePrefetch(Addr addr)
{
assert(makeLineAddress(addr) == addr);
assert(issuedPfPkts.count(addr) == 1);
DPRINTF(HWPrefetch, "Prefetch request for addr %#x completed\n", addr);
delete issuedPfPkts[addr];
issuedPfPkts.erase(addr);
}
void
RubyPrefetcherProxy::issuePrefetch()
{
assert(prefetcher);
assert(pfQueue);
if (pfQueue->areNSlotsAvailable(1, curTick())) {
PacketPtr pkt = prefetcher->getPacket();
if (pkt) {
DPRINTF(HWPrefetch, "Next prefetch ready %s\n", pkt->print());
unsigned blk_size = RubySystem::getBlockSizeBytes();
Addr line_addr = pkt->getBlockAddr(blk_size);
if (issuedPfPkts.count(line_addr) == 0) {
DPRINTF(HWPrefetch, "Issued PF request for paddr=%#x, "
"line_addr=%#x, is_write=%d\n",
pkt->getAddr(), line_addr,
pkt->needsWritable());
RubyRequestType req_type = pkt->needsWritable() ?
RubyRequestType_ST : RubyRequestType_LD;
std::shared_ptr<RubyRequest> msg =
std::make_shared<RubyRequest>(cacheCntrl->clockEdge(),
pkt->getAddr(),
blk_size,
0, // pc
req_type,
RubyAccessMode_Supervisor,
pkt,
PrefetchBit_Yes);
// enqueue request into prefetch queue to the cache
pfQueue->enqueue(msg, cacheCntrl->clockEdge(),
cacheCntrl->cyclesToTicks(Cycles(1)));
// track all pending PF requests
issuedPfPkts[line_addr] = pkt;
} else {
DPRINTF(HWPrefetch, "Aborted PF request for address being "
"prefetched\n");
delete pkt;
}
}
} else {
DPRINTF(HWPrefetch, "No prefetch slots are available\n");
}
scheduleNextPrefetch();
}
void
RubyPrefetcherProxy::notifyPfHit(const RequestPtr& req, bool is_read,
const DataBlock& data_blk)
{
assert(ppHit);
assert(req);
Packet pkt(req, is_read ? Packet::makeReadCmd(req) :
Packet::makeWriteCmd(req));
// NOTE: for now we only communicate physical address with prefetchers
pkt.dataStaticConst<uint8_t>(data_blk.getData(getOffset(req->getPaddr()),
pkt.getSize()));
DPRINTF(HWPrefetch, "notify hit: %s\n", pkt.print());
ppHit->notify(CacheAccessProbeArg(&pkt, *this));
scheduleNextPrefetch();
}
void
RubyPrefetcherProxy::notifyPfMiss(const RequestPtr& req, bool is_read,
const DataBlock& data_blk)
{
assert(ppMiss);
assert(req);
Packet pkt(req, is_read ? Packet::makeReadCmd(req) :
Packet::makeWriteCmd(req));
// NOTE: for now we only communicate physical address with prefetchers
pkt.dataStaticConst<uint8_t>(data_blk.getData(getOffset(req->getPaddr()),
pkt.getSize()));
DPRINTF(HWPrefetch, "notify miss: %s\n", pkt.print());
ppMiss->notify(CacheAccessProbeArg(&pkt, *this));
scheduleNextPrefetch();
}
void
RubyPrefetcherProxy::notifyPfFill(const RequestPtr& req,
const DataBlock& data_blk,
bool from_pf)
{
assert(ppFill);
assert(req);
Packet pkt(req, Packet::makeReadCmd(req));
if (from_pf)
pkt.cmd = Packet::Command::HardPFReq;
// NOTE: for now we only communicate physical address with prefetchers
pkt.dataStaticConst<uint8_t>(data_blk.getData(getOffset(req->getPaddr()),
pkt.getSize()));
DPRINTF(HWPrefetch, "notify fill: %s\n", pkt.print());
ppFill->notify(CacheAccessProbeArg(&pkt, *this));
scheduleNextPrefetch();
}
void
RubyPrefetcherProxy::notifyPfEvict(Addr blkAddr, bool hwPrefetched,
RequestorID requestorID)
{
DPRINTF(HWPrefetch, "notify evict: %#x hw_pf=%d\n", blkAddr, hwPrefetched);
CacheDataUpdateProbeArg data_update(
blkAddr, false, requestorID, *this);
data_update.hwPrefetched = hwPrefetched;
ppDataUpdate->notify(data_update);
scheduleNextPrefetch();
}
void
RubyPrefetcherProxy::regProbePoints()
{
assert(cacheCntrl);
ppHit = new ProbePointArg<CacheAccessProbeArg>(
cacheCntrl->getProbeManager(), "Hit");
ppMiss = new ProbePointArg<CacheAccessProbeArg>(
cacheCntrl->getProbeManager(), "Miss");
ppFill = new ProbePointArg<CacheAccessProbeArg>(
cacheCntrl->getProbeManager(), "Fill");
ppDataUpdate =
new ProbePointArg<CacheDataUpdateProbeArg>(
cacheCntrl->getProbeManager(), "Data Update");
}
} // namespace ruby
} // namespace gem5

View File

@@ -0,0 +1,178 @@
/*
* Copyright (c) 2023 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __MEM_RUBY_STRUCTURES_RUBY_PREFETCHER_WRAPPER_HH__
#define __MEM_RUBY_STRUCTURES_RUBY_PREFETCHER_WRAPPER_HH__
#include <unordered_map>
#include "mem/cache/cache_probe_arg.hh"
#include "mem/cache/prefetch/base.hh"
#include "mem/ruby/slicc_interface/AbstractController.hh"
#include "mem/ruby/slicc_interface/RubyRequest.hh"
namespace gem5
{
namespace ruby
{
/**
* This is a proxy for prefetcher class in classic memory. This wrapper
* enables a SLICC machine to interact with classic prefetchers.
*
* The expected use case for this class is to instantiate it in the SLICC
* state machine definition and provide a pointer to the prefetcher object
* (typically defined in SLICC as one of the SM's configuration parameters)
* and the prefetch queue where prefetch requests will be inserted.
*
* The SLICC SM can them use the notifyPF* functions to notify the prefetcher.
*
* Notes:
*
* This object's regProbePoints() must be called explicitly. The SLICC SM may
* defined it's own regProbePoints() to call it.
*
* completePrefetch(Addr) must be called when a request injected into the
* prefetch queue is completed.
*
* A nullptr prefetcher can be provided, in which case the notifyPf* are
* no-ops.
*
*/
class RubyPrefetcherProxy : public CacheAccessor, public Named
{
public:
RubyPrefetcherProxy(AbstractController* parent,
prefetch::Base* prefetcher,
MessageBuffer *pf_queue);
/** Deschedled the ready prefetch event */
void deschedulePrefetch();
/** Notifies a completed prefetch request */
void completePrefetch(Addr addr);
/**
* Notify PF probes hit/miss/fill
*/
void notifyPfHit(const RequestPtr& req, bool is_read,
const DataBlock& data_blk);
void notifyPfMiss(const RequestPtr& req, bool is_read,
const DataBlock& data_blk);
void notifyPfFill(const RequestPtr& req, const DataBlock& data_blk,
bool from_pf);
void notifyPfEvict(Addr blkAddr, bool hwPrefetched,
RequestorID requestorID);
/** Registers probes. */
void regProbePoints();
private:
/** Schedule the next ready prefetch */
void scheduleNextPrefetch();
/** Issue prefetch to the contoller prefetch queue */
void issuePrefetch();
/** Prefetcher from classic memory */
prefetch::Base* prefetcher;
/** Ruby cache controller */
AbstractController* cacheCntrl;
/** Prefetch queue to the cache controller */
MessageBuffer* pfQueue;
/** List of issued prefetch request packets */
std::unordered_map<Addr, PacketPtr> issuedPfPkts;
/** Prefetch event */
EventFunctionWrapper pfEvent;
/** To probe when a cache hit occurs */
ProbePointArg<CacheAccessProbeArg> *ppHit;
/** To probe when a cache miss occurs */
ProbePointArg<CacheAccessProbeArg> *ppMiss;
/** To probe when a cache fill occurs */
ProbePointArg<CacheAccessProbeArg> *ppFill;
/**
* To probe when the contents of a block are updated. Content updates
* include data fills, overwrites, and invalidations, which means that
* this probe partially overlaps with other probes.
*/
ProbePointArg<CacheDataUpdateProbeArg> *ppDataUpdate;
public:
/** Accessor functions */
bool inCache(Addr addr, bool is_secure) const override
{
return cacheCntrl->inCache(addr, is_secure);
}
bool hasBeenPrefetched(Addr addr, bool is_secure) const override
{
return cacheCntrl->hasBeenPrefetched(addr, is_secure);
}
bool hasBeenPrefetched(Addr addr, bool is_secure,
RequestorID requestor) const override
{
return cacheCntrl->hasBeenPrefetched(addr, is_secure, requestor);
}
bool inMissQueue(Addr addr, bool is_secure) const override
{
return cacheCntrl->inMissQueue(addr, is_secure);
}
bool coalesce() const override
{ return cacheCntrl->coalesce(); }
};
} // namespace ruby
} // namespace gem5
#endif // __MEM_RUBY_STRUCTURES_RUBY_PREFETCHER_WRAPPER_HH__

View File

@@ -1,6 +1,6 @@
# -*- mode:python -*-
# Copyright (c) 2021 ARM Limited
# Copyright (c) 2021,2023 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@@ -53,6 +53,7 @@ Source('CacheMemory.cc')
Source('WireBuffer.cc')
Source('PersistentTable.cc')
Source('RubyPrefetcher.cc')
Source('RubyPrefetcherProxy.cc')
Source('TimerTable.cc')
Source('BankedArray.cc')
Source('TBEStorage.cc')

View File

@@ -63,6 +63,7 @@ python_class_map = {
"MessageBuffer": "MessageBuffer",
"DMASequencer": "DMASequencer",
"RubyPrefetcher": "RubyPrefetcher",
"prefetch::Base": "BasePrefetcher",
"Cycles": "Cycles",
"Addr": "Addr",
}