cpu: Stop treating TraceCPU as a BaseCPU

This is fixing a recently reported issue [1] where it is
not possible to use the TraceCPU to replay elastic traces

It requires some architectural data structures (like ArchMMU,
ArchDecoder...) which are no longer defined in the BaseCPU class at
compilation time.  Which Arch version should be used for a class
(TraceCPU) that is supposed to be ISA agnostic ? Does it really make
sense to define them for the TraceCPU? Those classes are not used anyway
during trace replay and their sole purpose would just be to comply to
the BaseCPU interface.

As there is no elegant way to make things work, this patch stops
treating the TraceCPU as a BaseCPU.

While it philosophically makes sense to treat the TraceCPU as a common
CPU (it sort of replays pre-executed instructions), a case can be made
for considering it more like a traffic generator.

[1]: https://github.com/gem5/gem5/issues/301

Change-Id: I7438169e8cc7fb6272731efb336ed2cf271c0844
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
This commit is contained in:
Giacomo Travaglini
2023-09-12 08:10:34 +01:00
parent a217c218e0
commit 9a5d900770
3 changed files with 52 additions and 28 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2013 - 2016 ARM Limited
# Copyright (c) 2013 - 2016, 2023 Arm Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@@ -34,10 +34,11 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from m5.params import *
from m5.objects.BaseCPU import BaseCPU
from m5.proxy import *
from m5.objects.ClockedObject import ClockedObject
class TraceCPU(BaseCPU):
class TraceCPU(ClockedObject):
"""Trace CPU model which replays traces generated in a prior simulation
using DerivO3CPU or its derived classes. It interfaces with L1 caches.
"""
@@ -54,13 +55,14 @@ class TraceCPU(BaseCPU):
def require_caches(cls):
return True
def addPMU(self, pmu=None):
pass
@classmethod
def support_take_over(cls):
return True
system = Param.System(Parent.any, "system object")
icache_port = RequestPort("Instruction Port")
dcache_port = RequestPort("Data Port")
instTraceFile = Param.String("", "Instruction trace file")
dataTraceFile = Param.String("", "Data dependency trace file")
sizeStoreBuffer = Param.Unsigned(

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 - 2016 ARM Limited
* Copyright (c) 2013 - 2016, 2023 Arm Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -47,7 +47,8 @@ namespace gem5
int TraceCPU::numTraceCPUs = 0;
TraceCPU::TraceCPU(const TraceCPUParams &params)
: BaseCPU(params),
: ClockedObject(params),
cacheLineSize(params.system->cacheLineSize()),
icachePort(this),
dcachePort(this),
instRequestorID(params.system->getRequestorId(this, "inst")),
@@ -109,7 +110,7 @@ TraceCPU::init()
DPRINTF(TraceCPUData, "Data memory request trace file is \"%s\".\n",
dataTraceFile);
BaseCPU::init();
ClockedObject::init();
// Get the send tick of the first instruction read request
Tick first_icache_tick = icacheGen.init();
@@ -176,7 +177,7 @@ TraceCPU::schedDcacheNext()
DPRINTF(TraceCPUData, "DcacheGen event.\n");
// Update stat for numCycles
baseStats.numCycles = clockEdge() / clockPeriod();
traceStats.numCycles = clockEdge() / clockPeriod();
dcacheGen.execute();
if (dcacheGen.isExecComplete()) {
@@ -216,7 +217,7 @@ TraceCPU::checkAndSchedExitEvent()
ADD_STAT(cpi, statistics::units::Rate<
statistics::units::Cycle, statistics::units::Count>::get(),
"Cycles per micro-op used as a proxy for CPI",
trace->baseStats.numCycles / numOps)
trace->traceStats.numCycles / numOps)
{
cpi.precision(6);
}
@@ -591,7 +592,7 @@ TraceCPU::ElasticDataGen::executeMemReq(GraphNode* node_ptr)
// stat counting this is useful to keep a check on how frequently this
// happens. If required the code could be revised to mimick splitting such
// a request into two.
unsigned blk_size = owner.cacheLineSize();
unsigned blk_size = owner.cacheLineSize;
Addr blk_offset = (node_ptr->physAddr & (Addr)(blk_size - 1));
if (!(blk_offset + node_ptr->size <= blk_size)) {
node_ptr->size = blk_size - blk_offset;
@@ -1152,6 +1153,20 @@ TraceCPU::schedDcacheNextEvent(Tick when)
}
Port &
TraceCPU::getPort(const std::string &if_name, PortID idx)
{
// Get the right port based on name. This applies to all the
// subclasses of the base CPU and relies on their implementation
// of getDataPort and getInstPort.
if (if_name == "dcache_port")
return getDataPort();
else if (if_name == "icache_port")
return getInstPort();
else
return ClockedObject::getPort(if_name, idx);
}
bool
TraceCPU::IcachePort::recvTimingResp(PacketPtr pkt)
{

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 - 2016 ARM Limited
* Copyright (c) 2013 - 2016, 2023 Arm Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -48,10 +48,13 @@
#include "cpu/base.hh"
#include "debug/TraceCPUData.hh"
#include "debug/TraceCPUInst.hh"
#include "mem/packet.hh"
#include "mem/request.hh"
#include "params/TraceCPU.hh"
#include "proto/inst_dep_record.pb.h"
#include "proto/packet.pb.h"
#include "proto/protoio.hh"
#include "sim/clocked_object.hh"
#include "sim/sim_events.hh"
namespace gem5
@@ -66,8 +69,7 @@ namespace gem5
* simulation compared to the detailed cpu model and good correlation when the
* same trace is used for playback on different memory sub-systems.
*
* The TraceCPU inherits from BaseCPU so some virtual methods need to be
* defined. It has two port subclasses inherited from RequestPort for
* The TraceCPU has two port subclasses inherited from RequestPort for
* instruction and data ports. It issues the memory requests deducing the
* timing from the trace and without performing real execution of micro-ops. As
* soon as the last dependency for an instruction is complete, its
@@ -139,7 +141,7 @@ namespace gem5
* exit.
*/
class TraceCPU : public BaseCPU
class TraceCPU : public ClockedObject
{
public:
@@ -147,15 +149,6 @@ class TraceCPU : public BaseCPU
void init();
/**
* This is a pure virtual function in BaseCPU. As we don't know how many
* insts are in the trace but only know how how many micro-ops are we
* cannot count this stat.
*
* @return 0
*/
Counter totalInsts() const { return 0; }
/**
* Return totalOps as the number of committed micro-ops plus the
* speculatively issued loads that are modelled in the TraceCPU replay.
@@ -170,9 +163,6 @@ class TraceCPU : public BaseCPU
*/
void updateNumOps(uint64_t rob_num);
/* Pure virtual function in BaseCPU. Do nothing. */
void wakeup(ThreadID tid=0) { return; }
/*
* When resuming from checkpoint in FS mode, the TraceCPU takes over from
* the old cpu. This function overrides the takeOverFrom() function in the
@@ -303,6 +293,9 @@ class TraceCPU : public BaseCPU
TraceCPU* owner;
};
/** Cache the cache line size that we get from the system */
const unsigned int cacheLineSize;
/** Port to connect to L1 instruction cache. */
IcachePort icachePort;
@@ -1112,6 +1105,8 @@ class TraceCPU : public BaseCPU
/** Stat for number of simulated micro-ops. */
statistics::Scalar numOps;
/** Number of CPU cycles simulated */
statistics::Scalar numCycles;
/** Stat for the CPI. This is really cycles per
* micro-op and not inst. */
statistics::Formula cpi;
@@ -1125,6 +1120,18 @@ class TraceCPU : public BaseCPU
/** Used to get a reference to the dcache port. */
Port &getDataPort() { return dcachePort; }
/**
* Get a port on this CPU. All CPUs have a data and
* instruction port, and this method uses getDataPort and
* getInstPort of the subclasses to resolve the two ports.
*
* @param if_name the port name
* @param idx ignored index
*
* @return a reference to the port with the given name
*/
Port &getPort(const std::string &if_name,
PortID idx=InvalidPortID) override;
};
} // namespace gem5