cpu: Unify the serialization code for all of the CPU models
Cleanup the serialization code for the simple CPUs and the O3 CPU. The CPU-specific code has been replaced with a (un)serializeThread that serializes the thread state / context of a specific thread. Assuming that the thread state class uses the CPU-specific thread state uses the base thread state serialization code, this allows us to restore a checkpoint with any of the CPU models.
This commit is contained in:
parent
5fb00e1df6
commit
009970f59b
8 changed files with 109 additions and 93 deletions
|
@ -524,21 +524,36 @@ BaseCPU::serialize(std::ostream &os)
|
|||
{
|
||||
SERIALIZE_SCALAR(instCnt);
|
||||
|
||||
/* Unlike _pid, _taskId is not serialized, as they are dynamically
|
||||
* assigned unique ids that are only meaningful for the duration of
|
||||
* a specific run. We will need to serialize the entire taskMap in
|
||||
* system. */
|
||||
SERIALIZE_SCALAR(_pid);
|
||||
if (!_switchedOut) {
|
||||
/* Unlike _pid, _taskId is not serialized, as they are dynamically
|
||||
* assigned unique ids that are only meaningful for the duration of
|
||||
* a specific run. We will need to serialize the entire taskMap in
|
||||
* system. */
|
||||
SERIALIZE_SCALAR(_pid);
|
||||
|
||||
interrupts->serialize(os);
|
||||
interrupts->serialize(os);
|
||||
|
||||
// Serialize the threads, this is done by the CPU implementation.
|
||||
for (ThreadID i = 0; i < numThreads; ++i) {
|
||||
nameOut(os, csprintf("%s.xc.%i", name(), i));
|
||||
serializeThread(os, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseCPU::unserialize(Checkpoint *cp, const std::string §ion)
|
||||
{
|
||||
UNSERIALIZE_SCALAR(instCnt);
|
||||
UNSERIALIZE_SCALAR(_pid);
|
||||
interrupts->unserialize(cp, section);
|
||||
|
||||
if (!_switchedOut) {
|
||||
UNSERIALIZE_SCALAR(_pid);
|
||||
interrupts->unserialize(cp, section);
|
||||
|
||||
// Unserialize the threads, this is done by the CPU implementation.
|
||||
for (ThreadID i = 0; i < numThreads; ++i)
|
||||
unserializeThread(cp, csprintf("%s.xc.%i", section, i), i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -365,17 +365,47 @@ class BaseCPU : public MemObject
|
|||
|
||||
/**
|
||||
* Serialize this object to the given output stream.
|
||||
*
|
||||
* @note CPU models should normally overload the serializeThread()
|
||||
* method instead of the serialize() method as this provides a
|
||||
* uniform data format for all CPU models and promotes better code
|
||||
* reuse.
|
||||
*
|
||||
* @param os The stream to serialize to.
|
||||
*/
|
||||
virtual void serialize(std::ostream &os);
|
||||
|
||||
/**
|
||||
* Reconstruct the state of this object from a checkpoint.
|
||||
*
|
||||
* @note CPU models should normally overload the
|
||||
* unserializeThread() method instead of the unserialize() method
|
||||
* as this provides a uniform data format for all CPU models and
|
||||
* promotes better code reuse.
|
||||
|
||||
* @param cp The checkpoint use.
|
||||
* @param section The section name of this object
|
||||
* @param section The section name of this object.
|
||||
*/
|
||||
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
||||
|
||||
/**
|
||||
* Serialize a single thread.
|
||||
*
|
||||
* @param os The stream to serialize to.
|
||||
* @param tid ID of the current thread.
|
||||
*/
|
||||
virtual void serializeThread(std::ostream &os, ThreadID tid) {};
|
||||
|
||||
/**
|
||||
* Unserialize one thread.
|
||||
*
|
||||
* @param cp The checkpoint use.
|
||||
* @param section The section name of this thread.
|
||||
* @param tid ID of the current thread.
|
||||
*/
|
||||
virtual void unserializeThread(Checkpoint *cp, const std::string §ion,
|
||||
ThreadID tid) {};
|
||||
|
||||
/**
|
||||
* Return pointer to CPU's branch predictor (NULL if none).
|
||||
* @return Branch predictor pointer.
|
||||
|
|
|
@ -1094,35 +1094,17 @@ FullO3CPU<Impl>::syscall(int64_t callnum, ThreadID tid)
|
|||
|
||||
template <class Impl>
|
||||
void
|
||||
FullO3CPU<Impl>::serialize(std::ostream &os)
|
||||
FullO3CPU<Impl>::serializeThread(std::ostream &os, ThreadID tid)
|
||||
{
|
||||
Drainable::State so_state(getDrainState());
|
||||
SERIALIZE_ENUM(so_state);
|
||||
BaseCPU::serialize(os);
|
||||
nameOut(os, csprintf("%s.tickEvent", name()));
|
||||
tickEvent.serialize(os);
|
||||
|
||||
for (ThreadID i = 0; i < thread.size(); i++) {
|
||||
nameOut(os, csprintf("%s.xc.%i", name(), i));
|
||||
thread[i]->serialize(os);
|
||||
}
|
||||
thread[tid]->serialize(os);
|
||||
}
|
||||
|
||||
template <class Impl>
|
||||
void
|
||||
FullO3CPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion)
|
||||
FullO3CPU<Impl>::unserializeThread(Checkpoint *cp, const std::string §ion,
|
||||
ThreadID tid)
|
||||
{
|
||||
Drainable::State so_state;
|
||||
UNSERIALIZE_ENUM(so_state);
|
||||
BaseCPU::unserialize(cp, section);
|
||||
tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
|
||||
|
||||
for (ThreadID i = 0; i < thread.size(); i++) {
|
||||
thread[i]->unserialize(cp,
|
||||
csprintf("%s.xc.%i", section, i));
|
||||
if (thread[i]->status() == ThreadContext::Active)
|
||||
activateThread(i);
|
||||
}
|
||||
thread[tid]->unserialize(cp, section);
|
||||
}
|
||||
|
||||
template <class Impl>
|
||||
|
|
|
@ -446,11 +446,10 @@ class FullO3CPU : public BaseO3CPU
|
|||
/** Is the CPU draining? */
|
||||
bool isDraining() const { return getDrainState() == Drainable::Draining; }
|
||||
|
||||
/** Serialize state. */
|
||||
virtual void serialize(std::ostream &os);
|
||||
void serializeThread(std::ostream &os, ThreadID tid);
|
||||
|
||||
/** Unserialize from a checkpoint. */
|
||||
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
||||
void unserializeThread(Checkpoint *cp, const std::string §ion,
|
||||
ThreadID tid);
|
||||
|
||||
public:
|
||||
/** Executes a syscall.
|
||||
|
|
|
@ -132,7 +132,7 @@ unsigned int
|
|||
AtomicSimpleCPU::drain(DrainManager *dm)
|
||||
{
|
||||
assert(!drain_manager);
|
||||
if (_status == SwitchedOut)
|
||||
if (switchedOut())
|
||||
return 0;
|
||||
|
||||
if (!isDrained()) {
|
||||
|
@ -151,8 +151,9 @@ AtomicSimpleCPU::drain(DrainManager *dm)
|
|||
void
|
||||
AtomicSimpleCPU::drainResume()
|
||||
{
|
||||
assert(!tickEvent.scheduled());
|
||||
assert(!drain_manager);
|
||||
if (_status == Idle || _status == SwitchedOut)
|
||||
if (switchedOut())
|
||||
return;
|
||||
|
||||
DPRINTF(SimpleCPU, "Resume\n");
|
||||
|
@ -161,9 +162,16 @@ AtomicSimpleCPU::drainResume()
|
|||
"'atomic' mode.\n");
|
||||
}
|
||||
|
||||
assert(!tickEvent.scheduled());
|
||||
if (thread->status() == ThreadContext::Active)
|
||||
assert(!threadContexts.empty());
|
||||
if (threadContexts.size() > 1)
|
||||
fatal("The atomic CPU only supports one thread.\n");
|
||||
|
||||
if (thread->status() == ThreadContext::Active) {
|
||||
schedule(tickEvent, nextCycle());
|
||||
_status = BaseSimpleCPU::Running;
|
||||
} else {
|
||||
_status = BaseSimpleCPU::Idle;
|
||||
}
|
||||
|
||||
system->totalNumInsts = 0;
|
||||
}
|
||||
|
@ -194,8 +202,6 @@ AtomicSimpleCPU::switchOut()
|
|||
assert(!tickEvent.scheduled());
|
||||
assert(_status == BaseSimpleCPU::Running || _status == Idle);
|
||||
assert(isDrained());
|
||||
|
||||
_status = SwitchedOut;
|
||||
}
|
||||
|
||||
|
||||
|
@ -207,16 +213,6 @@ AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
|
|||
// The tick event should have been descheduled by drain()
|
||||
assert(!tickEvent.scheduled());
|
||||
|
||||
assert(!threadContexts.empty());
|
||||
if (threadContexts.size() > 1)
|
||||
fatal("The atomic CPU only supports one thread.\n");
|
||||
|
||||
// If the ThreadContext is active, mark the CPU as running.
|
||||
if (thread->status() == ThreadContext::Active)
|
||||
_status = BaseSimpleCPU::Running;
|
||||
else
|
||||
_status = Idle;
|
||||
|
||||
ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
|
||||
data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
|
||||
data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2011 ARM Limited
|
||||
* Copyright (c) 2010-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
|
@ -283,22 +283,21 @@ BaseSimpleCPU::resetStats()
|
|||
}
|
||||
|
||||
void
|
||||
BaseSimpleCPU::serialize(ostream &os)
|
||||
BaseSimpleCPU::serializeThread(ostream &os, ThreadID tid)
|
||||
{
|
||||
SERIALIZE_ENUM(_status);
|
||||
BaseCPU::serialize(os);
|
||||
// SERIALIZE_SCALAR(inst);
|
||||
nameOut(os, csprintf("%s.xc.0", name()));
|
||||
assert(_status == Idle || _status == Running);
|
||||
assert(tid == 0);
|
||||
|
||||
thread->serialize(os);
|
||||
}
|
||||
|
||||
void
|
||||
BaseSimpleCPU::unserialize(Checkpoint *cp, const string §ion)
|
||||
BaseSimpleCPU::unserializeThread(Checkpoint *cp, const string §ion,
|
||||
ThreadID tid)
|
||||
{
|
||||
UNSERIALIZE_ENUM(_status);
|
||||
BaseCPU::unserialize(cp, section);
|
||||
// UNSERIALIZE_SCALAR(inst);
|
||||
thread->unserialize(cp, csprintf("%s.xc.0", section));
|
||||
if (tid != 0)
|
||||
fatal("Trying to load more than one thread into a SimpleCPU\n");
|
||||
thread->unserialize(cp, section);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2011 ARM Limited
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
|
@ -137,7 +137,6 @@ class BaseSimpleCPU : public BaseCPU
|
|||
DcacheRetry,
|
||||
DcacheWaitResponse,
|
||||
DcacheWaitSwitch,
|
||||
SwitchedOut
|
||||
};
|
||||
|
||||
Status _status;
|
||||
|
@ -265,8 +264,9 @@ class BaseSimpleCPU : public BaseCPU
|
|||
Stats::Scalar dcacheRetryCycles;
|
||||
Counter lastDcacheRetry;
|
||||
|
||||
virtual void serialize(std::ostream &os);
|
||||
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
||||
void serializeThread(std::ostream &os, ThreadID tid);
|
||||
void unserializeThread(Checkpoint *cp, const std::string §ion,
|
||||
ThreadID tid);
|
||||
|
||||
// These functions are only used in CPU models that split
|
||||
// effective address computation from the actual memory access.
|
||||
|
|
|
@ -109,9 +109,12 @@ TimingSimpleCPU::~TimingSimpleCPU()
|
|||
unsigned int
|
||||
TimingSimpleCPU::drain(DrainManager *drain_manager)
|
||||
{
|
||||
assert(!drainManager);
|
||||
if (switchedOut())
|
||||
return 0;
|
||||
|
||||
if (_status == Idle ||
|
||||
(_status == BaseSimpleCPU::Running && isDrained()) ||
|
||||
_status == SwitchedOut) {
|
||||
(_status == BaseSimpleCPU::Running && isDrained())) {
|
||||
assert(!fetchEvent.scheduled());
|
||||
DPRINTF(Drain, "No need to drain.\n");
|
||||
return 0;
|
||||
|
@ -122,10 +125,8 @@ TimingSimpleCPU::drain(DrainManager *drain_manager)
|
|||
// The fetch event can become descheduled if a drain didn't
|
||||
// succeed on the first attempt. We need to reschedule it if
|
||||
// the CPU is waiting for a microcode routine to complete.
|
||||
if (_status == BaseSimpleCPU::Running && !isDrained() &&
|
||||
!fetchEvent.scheduled()) {
|
||||
if (_status == BaseSimpleCPU::Running && !fetchEvent.scheduled())
|
||||
schedule(fetchEvent, nextCycle());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -135,15 +136,25 @@ void
|
|||
TimingSimpleCPU::drainResume()
|
||||
{
|
||||
assert(!fetchEvent.scheduled());
|
||||
assert(!drainManager);
|
||||
if (switchedOut())
|
||||
return;
|
||||
|
||||
DPRINTF(SimpleCPU, "Resume\n");
|
||||
if (_status != SwitchedOut && _status != Idle) {
|
||||
if (system->getMemoryMode() != Enums::timing) {
|
||||
fatal("The timing CPU requires the memory system to be in "
|
||||
"'timing' mode.\n");
|
||||
}
|
||||
if (system->getMemoryMode() != Enums::timing) {
|
||||
fatal("The timing CPU requires the memory system to be in "
|
||||
"'timing' mode.\n");
|
||||
}
|
||||
|
||||
assert(!threadContexts.empty());
|
||||
if (threadContexts.size() > 1)
|
||||
fatal("The timing CPU only supports one thread.\n");
|
||||
|
||||
if (thread->status() == ThreadContext::Active) {
|
||||
schedule(fetchEvent, nextCycle());
|
||||
_status = BaseSimpleCPU::Running;
|
||||
} else {
|
||||
_status = BaseSimpleCPU::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +185,6 @@ TimingSimpleCPU::switchOut()
|
|||
assert(!stayAtPC);
|
||||
assert(microPC() == 0);
|
||||
|
||||
_status = SwitchedOut;
|
||||
numCycles += curCycle() - previousCycle;
|
||||
}
|
||||
|
||||
|
@ -184,21 +194,6 @@ TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
|
|||
{
|
||||
BaseSimpleCPU::takeOverFrom(oldCPU);
|
||||
|
||||
// if any of this CPU's ThreadContexts are active, mark the CPU as
|
||||
// running and schedule its tick event.
|
||||
for (int i = 0; i < threadContexts.size(); ++i) {
|
||||
ThreadContext *tc = threadContexts[i];
|
||||
if (tc->status() == ThreadContext::Active &&
|
||||
_status != BaseSimpleCPU::Running) {
|
||||
_status = BaseSimpleCPU::Running;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_status != BaseSimpleCPU::Running) {
|
||||
_status = Idle;
|
||||
}
|
||||
assert(threadContexts.size() == 1);
|
||||
previousCycle = curCycle();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue