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);
|
SERIALIZE_SCALAR(instCnt);
|
||||||
|
|
||||||
/* Unlike _pid, _taskId is not serialized, as they are dynamically
|
if (!_switchedOut) {
|
||||||
* assigned unique ids that are only meaningful for the duration of
|
/* Unlike _pid, _taskId is not serialized, as they are dynamically
|
||||||
* a specific run. We will need to serialize the entire taskMap in
|
* assigned unique ids that are only meaningful for the duration of
|
||||||
* system. */
|
* a specific run. We will need to serialize the entire taskMap in
|
||||||
SERIALIZE_SCALAR(_pid);
|
* 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
|
void
|
||||||
BaseCPU::unserialize(Checkpoint *cp, const std::string §ion)
|
BaseCPU::unserialize(Checkpoint *cp, const std::string §ion)
|
||||||
{
|
{
|
||||||
UNSERIALIZE_SCALAR(instCnt);
|
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
|
void
|
||||||
|
|
|
@ -365,17 +365,47 @@ class BaseCPU : public MemObject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize this object to the given output stream.
|
* 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.
|
* @param os The stream to serialize to.
|
||||||
*/
|
*/
|
||||||
virtual void serialize(std::ostream &os);
|
virtual void serialize(std::ostream &os);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconstruct the state of this object from a checkpoint.
|
* 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 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);
|
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 pointer to CPU's branch predictor (NULL if none).
|
||||||
* @return Branch predictor pointer.
|
* @return Branch predictor pointer.
|
||||||
|
|
|
@ -1094,35 +1094,17 @@ FullO3CPU<Impl>::syscall(int64_t callnum, ThreadID tid)
|
||||||
|
|
||||||
template <class Impl>
|
template <class Impl>
|
||||||
void
|
void
|
||||||
FullO3CPU<Impl>::serialize(std::ostream &os)
|
FullO3CPU<Impl>::serializeThread(std::ostream &os, ThreadID tid)
|
||||||
{
|
{
|
||||||
Drainable::State so_state(getDrainState());
|
thread[tid]->serialize(os);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Impl>
|
template <class Impl>
|
||||||
void
|
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;
|
thread[tid]->unserialize(cp, section);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Impl>
|
template <class Impl>
|
||||||
|
|
|
@ -446,11 +446,10 @@ class FullO3CPU : public BaseO3CPU
|
||||||
/** Is the CPU draining? */
|
/** Is the CPU draining? */
|
||||||
bool isDraining() const { return getDrainState() == Drainable::Draining; }
|
bool isDraining() const { return getDrainState() == Drainable::Draining; }
|
||||||
|
|
||||||
/** Serialize state. */
|
void serializeThread(std::ostream &os, ThreadID tid);
|
||||||
virtual void serialize(std::ostream &os);
|
|
||||||
|
|
||||||
/** Unserialize from a checkpoint. */
|
void unserializeThread(Checkpoint *cp, const std::string §ion,
|
||||||
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
ThreadID tid);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Executes a syscall.
|
/** Executes a syscall.
|
||||||
|
|
|
@ -132,7 +132,7 @@ unsigned int
|
||||||
AtomicSimpleCPU::drain(DrainManager *dm)
|
AtomicSimpleCPU::drain(DrainManager *dm)
|
||||||
{
|
{
|
||||||
assert(!drain_manager);
|
assert(!drain_manager);
|
||||||
if (_status == SwitchedOut)
|
if (switchedOut())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!isDrained()) {
|
if (!isDrained()) {
|
||||||
|
@ -151,8 +151,9 @@ AtomicSimpleCPU::drain(DrainManager *dm)
|
||||||
void
|
void
|
||||||
AtomicSimpleCPU::drainResume()
|
AtomicSimpleCPU::drainResume()
|
||||||
{
|
{
|
||||||
|
assert(!tickEvent.scheduled());
|
||||||
assert(!drain_manager);
|
assert(!drain_manager);
|
||||||
if (_status == Idle || _status == SwitchedOut)
|
if (switchedOut())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DPRINTF(SimpleCPU, "Resume\n");
|
DPRINTF(SimpleCPU, "Resume\n");
|
||||||
|
@ -161,9 +162,16 @@ AtomicSimpleCPU::drainResume()
|
||||||
"'atomic' mode.\n");
|
"'atomic' mode.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!tickEvent.scheduled());
|
assert(!threadContexts.empty());
|
||||||
if (thread->status() == ThreadContext::Active)
|
if (threadContexts.size() > 1)
|
||||||
|
fatal("The atomic CPU only supports one thread.\n");
|
||||||
|
|
||||||
|
if (thread->status() == ThreadContext::Active) {
|
||||||
schedule(tickEvent, nextCycle());
|
schedule(tickEvent, nextCycle());
|
||||||
|
_status = BaseSimpleCPU::Running;
|
||||||
|
} else {
|
||||||
|
_status = BaseSimpleCPU::Idle;
|
||||||
|
}
|
||||||
|
|
||||||
system->totalNumInsts = 0;
|
system->totalNumInsts = 0;
|
||||||
}
|
}
|
||||||
|
@ -194,8 +202,6 @@ AtomicSimpleCPU::switchOut()
|
||||||
assert(!tickEvent.scheduled());
|
assert(!tickEvent.scheduled());
|
||||||
assert(_status == BaseSimpleCPU::Running || _status == Idle);
|
assert(_status == BaseSimpleCPU::Running || _status == Idle);
|
||||||
assert(isDrained());
|
assert(isDrained());
|
||||||
|
|
||||||
_status = SwitchedOut;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -207,16 +213,6 @@ AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
|
||||||
// The tick event should have been descheduled by drain()
|
// The tick event should have been descheduled by drain()
|
||||||
assert(!tickEvent.scheduled());
|
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
|
ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
|
||||||
data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
|
data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
|
||||||
data_write_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
|
* 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
|
||||||
|
@ -283,22 +283,21 @@ BaseSimpleCPU::resetStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseSimpleCPU::serialize(ostream &os)
|
BaseSimpleCPU::serializeThread(ostream &os, ThreadID tid)
|
||||||
{
|
{
|
||||||
SERIALIZE_ENUM(_status);
|
assert(_status == Idle || _status == Running);
|
||||||
BaseCPU::serialize(os);
|
assert(tid == 0);
|
||||||
// SERIALIZE_SCALAR(inst);
|
|
||||||
nameOut(os, csprintf("%s.xc.0", name()));
|
|
||||||
thread->serialize(os);
|
thread->serialize(os);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseSimpleCPU::unserialize(Checkpoint *cp, const string §ion)
|
BaseSimpleCPU::unserializeThread(Checkpoint *cp, const string §ion,
|
||||||
|
ThreadID tid)
|
||||||
{
|
{
|
||||||
UNSERIALIZE_ENUM(_status);
|
if (tid != 0)
|
||||||
BaseCPU::unserialize(cp, section);
|
fatal("Trying to load more than one thread into a SimpleCPU\n");
|
||||||
// UNSERIALIZE_SCALAR(inst);
|
thread->unserialize(cp, section);
|
||||||
thread->unserialize(cp, csprintf("%s.xc.0", section));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 ARM Limited
|
* Copyright (c) 2011-2012 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
|
||||||
|
@ -137,7 +137,6 @@ class BaseSimpleCPU : public BaseCPU
|
||||||
DcacheRetry,
|
DcacheRetry,
|
||||||
DcacheWaitResponse,
|
DcacheWaitResponse,
|
||||||
DcacheWaitSwitch,
|
DcacheWaitSwitch,
|
||||||
SwitchedOut
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Status _status;
|
Status _status;
|
||||||
|
@ -265,8 +264,9 @@ class BaseSimpleCPU : public BaseCPU
|
||||||
Stats::Scalar dcacheRetryCycles;
|
Stats::Scalar dcacheRetryCycles;
|
||||||
Counter lastDcacheRetry;
|
Counter lastDcacheRetry;
|
||||||
|
|
||||||
virtual void serialize(std::ostream &os);
|
void serializeThread(std::ostream &os, ThreadID tid);
|
||||||
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
void unserializeThread(Checkpoint *cp, const std::string §ion,
|
||||||
|
ThreadID tid);
|
||||||
|
|
||||||
// These functions are only used in CPU models that split
|
// These functions are only used in CPU models that split
|
||||||
// effective address computation from the actual memory access.
|
// effective address computation from the actual memory access.
|
||||||
|
|
|
@ -109,9 +109,12 @@ TimingSimpleCPU::~TimingSimpleCPU()
|
||||||
unsigned int
|
unsigned int
|
||||||
TimingSimpleCPU::drain(DrainManager *drain_manager)
|
TimingSimpleCPU::drain(DrainManager *drain_manager)
|
||||||
{
|
{
|
||||||
|
assert(!drainManager);
|
||||||
|
if (switchedOut())
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (_status == Idle ||
|
if (_status == Idle ||
|
||||||
(_status == BaseSimpleCPU::Running && isDrained()) ||
|
(_status == BaseSimpleCPU::Running && isDrained())) {
|
||||||
_status == SwitchedOut) {
|
|
||||||
assert(!fetchEvent.scheduled());
|
assert(!fetchEvent.scheduled());
|
||||||
DPRINTF(Drain, "No need to drain.\n");
|
DPRINTF(Drain, "No need to drain.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -122,10 +125,8 @@ TimingSimpleCPU::drain(DrainManager *drain_manager)
|
||||||
// The fetch event can become descheduled if a drain didn't
|
// The fetch event can become descheduled if a drain didn't
|
||||||
// succeed on the first attempt. We need to reschedule it if
|
// succeed on the first attempt. We need to reschedule it if
|
||||||
// the CPU is waiting for a microcode routine to complete.
|
// the CPU is waiting for a microcode routine to complete.
|
||||||
if (_status == BaseSimpleCPU::Running && !isDrained() &&
|
if (_status == BaseSimpleCPU::Running && !fetchEvent.scheduled())
|
||||||
!fetchEvent.scheduled()) {
|
|
||||||
schedule(fetchEvent, nextCycle());
|
schedule(fetchEvent, nextCycle());
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -135,15 +136,25 @@ void
|
||||||
TimingSimpleCPU::drainResume()
|
TimingSimpleCPU::drainResume()
|
||||||
{
|
{
|
||||||
assert(!fetchEvent.scheduled());
|
assert(!fetchEvent.scheduled());
|
||||||
|
assert(!drainManager);
|
||||||
|
if (switchedOut())
|
||||||
|
return;
|
||||||
|
|
||||||
DPRINTF(SimpleCPU, "Resume\n");
|
DPRINTF(SimpleCPU, "Resume\n");
|
||||||
if (_status != SwitchedOut && _status != Idle) {
|
if (system->getMemoryMode() != Enums::timing) {
|
||||||
if (system->getMemoryMode() != Enums::timing) {
|
fatal("The timing CPU requires the memory system to be in "
|
||||||
fatal("The timing CPU requires the memory system to be in "
|
"'timing' mode.\n");
|
||||||
"'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());
|
schedule(fetchEvent, nextCycle());
|
||||||
|
_status = BaseSimpleCPU::Running;
|
||||||
|
} else {
|
||||||
|
_status = BaseSimpleCPU::Idle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +185,6 @@ TimingSimpleCPU::switchOut()
|
||||||
assert(!stayAtPC);
|
assert(!stayAtPC);
|
||||||
assert(microPC() == 0);
|
assert(microPC() == 0);
|
||||||
|
|
||||||
_status = SwitchedOut;
|
|
||||||
numCycles += curCycle() - previousCycle;
|
numCycles += curCycle() - previousCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,21 +194,6 @@ TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
|
||||||
{
|
{
|
||||||
BaseSimpleCPU::takeOverFrom(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();
|
previousCycle = curCycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue