From 009970f59b86eac6c9a35eeb175dd9e3a3079d13 Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Mon, 7 Jan 2013 13:05:52 -0500 Subject: 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. --- src/cpu/base.cc | 33 ++++++++++++++++++++++++--------- src/cpu/base.hh | 32 +++++++++++++++++++++++++++++++- src/cpu/o3/cpu.cc | 28 +++++----------------------- src/cpu/o3/cpu.hh | 7 +++---- src/cpu/simple/atomic.cc | 28 ++++++++++++---------------- src/cpu/simple/base.cc | 21 ++++++++++----------- src/cpu/simple/base.hh | 8 ++++---- src/cpu/simple/timing.cc | 47 +++++++++++++++++++++-------------------------- 8 files changed, 110 insertions(+), 94 deletions(-) (limited to 'src/cpu') diff --git a/src/cpu/base.cc b/src/cpu/base.cc index 14b5586c8..3e7a6d4b6 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -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); - - interrupts->serialize(os); + 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); + + // 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 diff --git a/src/cpu/base.hh b/src/cpu/base.hh index cd30d29bc..34b23cea0 100644 --- a/src/cpu/base.hh +++ b/src/cpu/base.hh @@ -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. diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index cb17581e5..18c536090 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -1094,35 +1094,17 @@ FullO3CPU::syscall(int64_t callnum, ThreadID tid) template void -FullO3CPU::serialize(std::ostream &os) +FullO3CPU::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 void -FullO3CPU::unserialize(Checkpoint *cp, const std::string §ion) +FullO3CPU::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 diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh index 24c4b46a8..5dd0e222d 100644 --- a/src/cpu/o3/cpu.hh +++ b/src/cpu/o3/cpu.hh @@ -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. diff --git a/src/cpu/simple/atomic.cc b/src/cpu/simple/atomic.cc index 036abdcdb..7255469a0 100644 --- a/src/cpu/simple/atomic.cc +++ b/src/cpu/simple/atomic.cc @@ -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 diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc index da965450a..13e08a6cb 100644 --- a/src/cpu/simple/base.cc +++ b/src/cpu/simple/base.cc @@ -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 diff --git a/src/cpu/simple/base.hh b/src/cpu/simple/base.hh index 9bf144326..18b97c42f 100644 --- a/src/cpu/simple/base.hh +++ b/src/cpu/simple/base.hh @@ -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. diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc index 78603be4f..f6dc1fbf6 100644 --- a/src/cpu/simple/timing.cc +++ b/src/cpu/simple/timing.cc @@ -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(); } -- cgit v1.2.3