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:
Andreas Sandberg 2013-01-07 13:05:52 -05:00
parent 5fb00e1df6
commit 009970f59b
8 changed files with 109 additions and 93 deletions

View file

@ -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 &section) BaseCPU::unserialize(Checkpoint *cp, const std::string &section)
{ {
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

View file

@ -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 &section); virtual void unserialize(Checkpoint *cp, const std::string &section);
/**
* 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 &section,
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.

View file

@ -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 &section) FullO3CPU<Impl>::unserializeThread(Checkpoint *cp, const std::string &section,
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>

View file

@ -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 &section,
virtual void unserialize(Checkpoint *cp, const std::string &section); ThreadID tid);
public: public:
/** Executes a syscall. /** Executes a syscall.

View file

@ -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

View file

@ -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 &section) BaseSimpleCPU::unserializeThread(Checkpoint *cp, const string &section,
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

View file

@ -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 &section); void unserializeThread(Checkpoint *cp, const std::string &section,
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.

View file

@ -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();
} }