cpu: Unify the serialization code for all of the CPU models
authorAndreas Sandberg <Andreas.Sandberg@ARM.com>
Mon, 7 Jan 2013 18:05:52 +0000 (13:05 -0500)
committerAndreas Sandberg <Andreas.Sandberg@ARM.com>
Mon, 7 Jan 2013 18:05:52 +0000 (13:05 -0500)
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
src/cpu/base.hh
src/cpu/o3/cpu.cc
src/cpu/o3/cpu.hh
src/cpu/simple/atomic.cc
src/cpu/simple/base.cc
src/cpu/simple/base.hh
src/cpu/simple/timing.cc

index 14b5586c88300b6ed160990e2a66c109d92dd880..3e7a6d4b66469fb23b25bea812b0c79e7b9b5e26 100644 (file)
@@ -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 &section)
 {
     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
index cd30d29bcaf31ebccf98143d9cdcd6a9439b88e5..34b23cea07a7ed8eaa6ff7305ba842f8399fdd61 100644 (file)
@@ -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 &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 Branch predictor pointer.
index cb17581e593c8052a55284005b615a69f52cc23f..18c536090aaa0b9f66864683e90eb0c61f86830b 100644 (file)
@@ -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 &section)
+FullO3CPU<Impl>::unserializeThread(Checkpoint *cp, const std::string &section,
+                                   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>
index 24c4b46a8f9893cd65c16b3b089ccedabe9f3a08..5dd0e222d1b1de190929614806764ab8ca689999 100644 (file)
@@ -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 &section);
+    void unserializeThread(Checkpoint *cp, const std::string &section,
+                           ThreadID tid);
 
   public:
     /** Executes a syscall.
index 036abdcdb16d8117aefadc283b58f36b8096e421..7255469a0479efc1170699717a5395ae8aab89a8 100644 (file)
@@ -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
index da965450a6a0abb435f29a774e02772adf97f3f5..13e08a6cb3e62134d4fcb0f12a94a06daaba49ee 100644 (file)
@@ -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 &section)
+BaseSimpleCPU::unserializeThread(Checkpoint *cp, const string &section,
+                                 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
index 9bf144326d994bd2ef18e2c16c53107efa78bb2b..18b97c42fce9096fcf33766cc33316e05d14c54d 100644 (file)
@@ -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 &section);
+    void serializeThread(std::ostream &os, ThreadID tid);
+    void unserializeThread(Checkpoint *cp, const std::string &section,
+                           ThreadID tid);
 
     // These functions are only used in CPU models that split
     // effective address computation from the actual memory access.
index 78603be4ffbc9c6c1d6a999070c29dbb40dfba55..f6dc1fbf63889c708a45bd43b3879ae552801fbc 100644 (file)
@@ -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();
 }