summaryrefslogtreecommitdiff
path: root/src/cpu/checker
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu/checker')
-rw-r--r--src/cpu/checker/cpu.cc408
-rw-r--r--src/cpu/checker/cpu.hh416
-rw-r--r--src/cpu/checker/cpu_impl.hh479
-rw-r--r--src/cpu/checker/thread_context.hh307
4 files changed, 1610 insertions, 0 deletions
diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc
new file mode 100644
index 000000000..1540a6b94
--- /dev/null
+++ b/src/cpu/checker/cpu.cc
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#include <list>
+#include <string>
+
+#include "cpu/base.hh"
+#include "cpu/checker/cpu.hh"
+#include "cpu/simple_thread.hh"
+#include "cpu/thread_context.hh"
+#include "cpu/static_inst.hh"
+#include "mem/packet_impl.hh"
+#include "sim/byteswap.hh"
+
+#if FULL_SYSTEM
+#include "arch/vtophys.hh"
+#include "kern/kernel_stats.hh"
+#endif // FULL_SYSTEM
+
+using namespace std;
+//The CheckerCPU does alpha only
+using namespace AlphaISA;
+
+void
+CheckerCPU::init()
+{
+}
+
+CheckerCPU::CheckerCPU(Params *p)
+ : BaseCPU(p), thread(NULL), tc(NULL)
+{
+ memReq = NULL;
+
+ numInst = 0;
+ startNumInst = 0;
+ numLoad = 0;
+ startNumLoad = 0;
+ youngestSN = 0;
+
+ changedPC = willChangePC = changedNextPC = false;
+
+ exitOnError = p->exitOnError;
+ warnOnlyOnLoadError = p->warnOnlyOnLoadError;
+#if FULL_SYSTEM
+ itb = p->itb;
+ dtb = p->dtb;
+ systemPtr = NULL;
+#else
+ process = p->process;
+#endif
+
+ result.integer = 0;
+}
+
+CheckerCPU::~CheckerCPU()
+{
+}
+
+void
+CheckerCPU::setMemory(MemObject *mem)
+{
+#if !FULL_SYSTEM
+ memPtr = mem;
+ thread = new SimpleThread(this, /* thread_num */ 0, process,
+ /* asid */ 0, mem);
+
+ thread->setStatus(ThreadContext::Suspended);
+ tc = thread->getTC();
+ threadContexts.push_back(tc);
+#endif
+}
+
+void
+CheckerCPU::setSystem(System *system)
+{
+#if FULL_SYSTEM
+ systemPtr = system;
+
+ thread = new SimpleThread(this, 0, systemPtr, itb, dtb, false);
+
+ thread->setStatus(ThreadContext::Suspended);
+ tc = thread->getTC();
+ threadContexts.push_back(tc);
+ delete thread->kernelStats;
+ thread->kernelStats = NULL;
+#endif
+}
+
+void
+CheckerCPU::setIcachePort(Port *icache_port)
+{
+ icachePort = icache_port;
+}
+
+void
+CheckerCPU::setDcachePort(Port *dcache_port)
+{
+ dcachePort = dcache_port;
+}
+
+void
+CheckerCPU::serialize(ostream &os)
+{
+/*
+ BaseCPU::serialize(os);
+ SERIALIZE_SCALAR(inst);
+ nameOut(os, csprintf("%s.xc", name()));
+ thread->serialize(os);
+ cacheCompletionEvent.serialize(os);
+*/
+}
+
+void
+CheckerCPU::unserialize(Checkpoint *cp, const string &section)
+{
+/*
+ BaseCPU::unserialize(cp, section);
+ UNSERIALIZE_SCALAR(inst);
+ thread->unserialize(cp, csprintf("%s.xc", section));
+*/
+}
+
+Fault
+CheckerCPU::copySrcTranslate(Addr src)
+{
+ panic("Unimplemented!");
+}
+
+Fault
+CheckerCPU::copy(Addr dest)
+{
+ panic("Unimplemented!");
+}
+
+template <class T>
+Fault
+CheckerCPU::read(Addr addr, T &data, unsigned flags)
+{
+ // need to fill in CPU & thread IDs here
+ memReq = new Request();
+
+ memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC());
+
+ // translate to physical address
+ translateDataReadReq(memReq);
+
+ Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast);
+
+ pkt->dataStatic(&data);
+
+ if (!(memReq->getFlags() & UNCACHEABLE)) {
+ // Access memory to see if we have the same data
+ dcachePort->sendFunctional(pkt);
+ } else {
+ // Assume the data is correct if it's an uncached access
+ memcpy(&data, &unverifiedResult.integer, sizeof(T));
+ }
+
+ delete pkt;
+
+ return NoFault;
+}
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+template
+Fault
+CheckerCPU::read(Addr addr, uint64_t &data, unsigned flags);
+
+template
+Fault
+CheckerCPU::read(Addr addr, uint32_t &data, unsigned flags);
+
+template
+Fault
+CheckerCPU::read(Addr addr, uint16_t &data, unsigned flags);
+
+template
+Fault
+CheckerCPU::read(Addr addr, uint8_t &data, unsigned flags);
+
+#endif //DOXYGEN_SHOULD_SKIP_THIS
+
+template<>
+Fault
+CheckerCPU::read(Addr addr, double &data, unsigned flags)
+{
+ return read(addr, *(uint64_t*)&data, flags);
+}
+
+template<>
+Fault
+CheckerCPU::read(Addr addr, float &data, unsigned flags)
+{
+ return read(addr, *(uint32_t*)&data, flags);
+}
+
+template<>
+Fault
+CheckerCPU::read(Addr addr, int32_t &data, unsigned flags)
+{
+ return read(addr, (uint32_t&)data, flags);
+}
+
+template <class T>
+Fault
+CheckerCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
+{
+ // need to fill in CPU & thread IDs here
+ memReq = new Request();
+
+ memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC());
+
+ // translate to physical address
+ thread->translateDataWriteReq(memReq);
+
+ // Can compare the write data and result only if it's cacheable,
+ // not a store conditional, or is a store conditional that
+ // succeeded.
+ // @todo: Verify that actual memory matches up with these values.
+ // Right now it only verifies that the instruction data is the
+ // same as what was in the request that got sent to memory; there
+ // is no verification that it is the same as what is in memory.
+ // This is because the LSQ would have to be snooped in the CPU to
+ // verify this data.
+ if (unverifiedReq &&
+ !(unverifiedReq->getFlags() & UNCACHEABLE) &&
+ (!(unverifiedReq->getFlags() & LOCKED) ||
+ ((unverifiedReq->getFlags() & LOCKED) &&
+ unverifiedReq->getScResult() == 1))) {
+ T inst_data;
+/*
+ // This code would work if the LSQ allowed for snooping.
+ Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast);
+ pkt.dataStatic(&inst_data);
+
+ dcachePort->sendFunctional(pkt);
+
+ delete pkt;
+*/
+ memcpy(&inst_data, unverifiedMemData, sizeof(T));
+
+ if (data != inst_data) {
+ warn("%lli: Store value does not match value in memory! "
+ "Instruction: %#x, memory: %#x",
+ curTick, inst_data, data);
+ handleError();
+ }
+ }
+
+ // Assume the result was the same as the one passed in. This checker
+ // doesn't check if the SC should succeed or fail, it just checks the
+ // value.
+ if (res && unverifiedReq->scResultValid())
+ *res = unverifiedReq->getScResult();
+
+ return NoFault;
+}
+
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+template
+Fault
+CheckerCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res);
+
+template
+Fault
+CheckerCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res);
+
+template
+Fault
+CheckerCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res);
+
+template
+Fault
+CheckerCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res);
+
+#endif //DOXYGEN_SHOULD_SKIP_THIS
+
+template<>
+Fault
+CheckerCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
+{
+ return write(*(uint64_t*)&data, addr, flags, res);
+}
+
+template<>
+Fault
+CheckerCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
+{
+ return write(*(uint32_t*)&data, addr, flags, res);
+}
+
+template<>
+Fault
+CheckerCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
+{
+ return write((uint32_t)data, addr, flags, res);
+}
+
+
+#if FULL_SYSTEM
+Addr
+CheckerCPU::dbg_vtophys(Addr addr)
+{
+ return vtophys(tc, addr);
+}
+#endif // FULL_SYSTEM
+
+bool
+CheckerCPU::translateInstReq(Request *req)
+{
+#if FULL_SYSTEM
+ return (thread->translateInstReq(req) == NoFault);
+#else
+ thread->translateInstReq(req);
+ return true;
+#endif
+}
+
+void
+CheckerCPU::translateDataReadReq(Request *req)
+{
+ thread->translateDataReadReq(req);
+
+ if (req->getVaddr() != unverifiedReq->getVaddr()) {
+ warn("%lli: Request virtual addresses do not match! Inst: %#x, "
+ "checker: %#x",
+ curTick, unverifiedReq->getVaddr(), req->getVaddr());
+ handleError();
+ }
+ req->setPaddr(unverifiedReq->getPaddr());
+
+ if (checkFlags(req)) {
+ warn("%lli: Request flags do not match! Inst: %#x, checker: %#x",
+ curTick, unverifiedReq->getFlags(), req->getFlags());
+ handleError();
+ }
+}
+
+void
+CheckerCPU::translateDataWriteReq(Request *req)
+{
+ thread->translateDataWriteReq(req);
+
+ if (req->getVaddr() != unverifiedReq->getVaddr()) {
+ warn("%lli: Request virtual addresses do not match! Inst: %#x, "
+ "checker: %#x",
+ curTick, unverifiedReq->getVaddr(), req->getVaddr());
+ handleError();
+ }
+ req->setPaddr(unverifiedReq->getPaddr());
+
+ if (checkFlags(req)) {
+ warn("%lli: Request flags do not match! Inst: %#x, checker: %#x",
+ curTick, unverifiedReq->getFlags(), req->getFlags());
+ handleError();
+ }
+}
+
+bool
+CheckerCPU::checkFlags(Request *req)
+{
+ // Remove any dynamic flags that don't have to do with the request itself.
+ unsigned flags = unverifiedReq->getFlags();
+ unsigned mask = LOCKED | PHYSICAL | VPTE | ALTMODE | UNCACHEABLE | NO_FAULT;
+ flags = flags & (mask);
+ if (flags == req->getFlags()) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void
+CheckerCPU::dumpAndExit()
+{
+ warn("%lli: Checker PC:%#x, next PC:%#x",
+ curTick, thread->readPC(), thread->readNextPC());
+ panic("Checker found an error!");
+}
diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh
new file mode 100644
index 000000000..737b4b5d4
--- /dev/null
+++ b/src/cpu/checker/cpu.hh
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#ifndef __CPU_CHECKER_CPU_HH__
+#define __CPU_CHECKER_CPU_HH__
+
+#include <list>
+#include <queue>
+#include <map>
+
+#include "arch/types.hh"
+#include "base/statistics.hh"
+#include "config/full_system.hh"
+#include "cpu/base.hh"
+#include "cpu/base_dyn_inst.hh"
+#include "cpu/simple_thread.hh"
+#include "cpu/pc_event.hh"
+#include "cpu/static_inst.hh"
+#include "sim/eventq.hh"
+
+// forward declarations
+#if FULL_SYSTEM
+class Processor;
+class AlphaITB;
+class AlphaDTB;
+class PhysicalMemory;
+
+class RemoteGDB;
+class GDBListener;
+
+#else
+
+class Process;
+
+#endif // FULL_SYSTEM
+template <class>
+class BaseDynInst;
+class ThreadContext;
+class MemInterface;
+class Checkpoint;
+class Request;
+
+/**
+ * CheckerCPU class. Dynamically verifies instructions as they are
+ * completed by making sure that the instruction and its results match
+ * the independent execution of the benchmark inside the checker. The
+ * checker verifies instructions in order, regardless of the order in
+ * which instructions complete. There are certain results that can
+ * not be verified, specifically the result of a store conditional or
+ * the values of uncached accesses. In these cases, and with
+ * instructions marked as "IsUnverifiable", the checker assumes that
+ * the value from the main CPU's execution is correct and simply
+ * copies that value. It provides a CheckerThreadContext (see
+ * checker/thread_context.hh) that provides hooks for updating the
+ * Checker's state through any ThreadContext accesses. This allows the
+ * checker to be able to correctly verify instructions, even with
+ * external accesses to the ThreadContext that change state.
+ */
+class CheckerCPU : public BaseCPU
+{
+ protected:
+ typedef TheISA::MachInst MachInst;
+ typedef TheISA::FloatReg FloatReg;
+ typedef TheISA::FloatRegBits FloatRegBits;
+ typedef TheISA::MiscReg MiscReg;
+ public:
+ virtual void init();
+
+ struct Params : public BaseCPU::Params
+ {
+#if FULL_SYSTEM
+ AlphaITB *itb;
+ AlphaDTB *dtb;
+#else
+ Process *process;
+#endif
+ bool exitOnError;
+ bool updateOnError;
+ bool warnOnlyOnLoadError;
+ };
+
+ public:
+ CheckerCPU(Params *p);
+ virtual ~CheckerCPU();
+
+ Process *process;
+
+ void setMemory(MemObject *mem);
+
+ MemObject *memPtr;
+
+ void setSystem(System *system);
+
+ System *systemPtr;
+
+ void setIcachePort(Port *icache_port);
+
+ Port *icachePort;
+
+ void setDcachePort(Port *dcache_port);
+
+ Port *dcachePort;
+
+ virtual Port *getPort(const std::string &name, int idx)
+ {
+ panic("Not supported on checker!");
+ return NULL;
+ }
+
+ public:
+ // Primary thread being run.
+ SimpleThread *thread;
+
+ ThreadContext *tc;
+
+ AlphaITB *itb;
+ AlphaDTB *dtb;
+
+#if FULL_SYSTEM
+ Addr dbg_vtophys(Addr addr);
+#endif
+
+ union Result {
+ uint64_t integer;
+// float fp;
+ double dbl;
+ };
+
+ Result result;
+
+ // current instruction
+ MachInst machInst;
+
+ // Pointer to the one memory request.
+ RequestPtr memReq;
+
+ StaticInstPtr curStaticInst;
+
+ // number of simulated instructions
+ Counter numInst;
+ Counter startNumInst;
+
+ std::queue<int> miscRegIdxs;
+
+ virtual Counter totalInstructions() const
+ {
+ return 0;
+ }
+
+ // number of simulated loads
+ Counter numLoad;
+ Counter startNumLoad;
+
+ virtual void serialize(std::ostream &os);
+ virtual void unserialize(Checkpoint *cp, const std::string &section);
+
+ template <class T>
+ Fault read(Addr addr, T &data, unsigned flags);
+
+ template <class T>
+ Fault write(T data, Addr addr, unsigned flags, uint64_t *res);
+
+ // These functions are only used in CPU models that split
+ // effective address computation from the actual memory access.
+ void setEA(Addr EA) { panic("SimpleCPU::setEA() not implemented\n"); }
+ Addr getEA() { panic("SimpleCPU::getEA() not implemented\n"); }
+
+ void prefetch(Addr addr, unsigned flags)
+ {
+ // need to do this...
+ }
+
+ void writeHint(Addr addr, int size, unsigned flags)
+ {
+ // need to do this...
+ }
+
+ Fault copySrcTranslate(Addr src);
+
+ Fault copy(Addr dest);
+
+ // The register accessor methods provide the index of the
+ // instruction's operand (e.g., 0 or 1), not the architectural
+ // register index, to simplify the implementation of register
+ // renaming. We find the architectural register index by indexing
+ // into the instruction's own operand index table. Note that a
+ // raw pointer to the StaticInst is provided instead of a
+ // ref-counted StaticInstPtr to redice overhead. This is fine as
+ // long as these methods don't copy the pointer into any long-term
+ // storage (which is pretty hard to imagine they would have reason
+ // to do).
+
+ uint64_t readIntReg(const StaticInst *si, int idx)
+ {
+ return thread->readIntReg(si->srcRegIdx(idx));
+ }
+
+ FloatReg readFloatReg(const StaticInst *si, int idx, int width)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag;
+ return thread->readFloatReg(reg_idx, width);
+ }
+
+ FloatReg readFloatReg(const StaticInst *si, int idx)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag;
+ return thread->readFloatReg(reg_idx);
+ }
+
+ FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag;
+ return thread->readFloatRegBits(reg_idx, width);
+ }
+
+ FloatRegBits readFloatRegBits(const StaticInst *si, int idx)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag;
+ return thread->readFloatRegBits(reg_idx);
+ }
+
+ void setIntReg(const StaticInst *si, int idx, uint64_t val)
+ {
+ thread->setIntReg(si->destRegIdx(idx), val);
+ result.integer = val;
+ }
+
+ void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag;
+ thread->setFloatReg(reg_idx, val, width);
+ switch(width) {
+ case 32:
+ result.fp = val;
+ break;
+ case 64:
+ result.dbl = val;
+ break;
+ };
+ }
+
+ void setFloatReg(const StaticInst *si, int idx, FloatReg val)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag;
+ thread->setFloatReg(reg_idx, val);
+ result.dbl = (double)val;
+ }
+
+ void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val,
+ int width)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag;
+ thread->setFloatRegBits(reg_idx, val, width);
+ result.integer = val;
+ }
+
+ void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag;
+ thread->setFloatRegBits(reg_idx, val);
+ result.integer = val;
+ }
+
+ uint64_t readPC() { return thread->readPC(); }
+
+ uint64_t readNextPC() { return thread->readNextPC(); }
+
+ void setNextPC(uint64_t val) {
+ thread->setNextPC(val);
+ }
+
+ MiscReg readMiscReg(int misc_reg)
+ {
+ return thread->readMiscReg(misc_reg);
+ }
+
+ MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault)
+ {
+ return thread->readMiscRegWithEffect(misc_reg, fault);
+ }
+
+ Fault setMiscReg(int misc_reg, const MiscReg &val)
+ {
+ result.integer = val;
+ miscRegIdxs.push(misc_reg);
+ return thread->setMiscReg(misc_reg, val);
+ }
+
+ Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val)
+ {
+ miscRegIdxs.push(misc_reg);
+ return thread->setMiscRegWithEffect(misc_reg, val);
+ }
+
+ void recordPCChange(uint64_t val) { changedPC = true; newPC = val; }
+ void recordNextPCChange(uint64_t val) { changedNextPC = true; }
+
+ bool translateInstReq(Request *req);
+ void translateDataWriteReq(Request *req);
+ void translateDataReadReq(Request *req);
+
+#if FULL_SYSTEM
+ Fault hwrei() { return thread->hwrei(); }
+ int readIntrFlag() { return thread->readIntrFlag(); }
+ void setIntrFlag(int val) { thread->setIntrFlag(val); }
+ bool inPalMode() { return thread->inPalMode(); }
+ void ev5_trap(Fault fault) { fault->invoke(tc); }
+ bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); }
+#else
+ // Assume that the normal CPU's call to syscall was successful.
+ // The checker's state would have already been updated by the syscall.
+ void syscall(uint64_t callnum) { }
+#endif
+
+ void handleError()
+ {
+ if (exitOnError)
+ dumpAndExit();
+ }
+
+ bool checkFlags(Request *req);
+
+ void dumpAndExit();
+
+ ThreadContext *tcBase() { return tc; }
+ SimpleThread *threadBase() { return thread; }
+
+ Result unverifiedResult;
+ Request *unverifiedReq;
+ uint8_t *unverifiedMemData;
+
+ bool changedPC;
+ bool willChangePC;
+ uint64_t newPC;
+ bool changedNextPC;
+ bool exitOnError;
+ bool updateOnError;
+ bool warnOnlyOnLoadError;
+
+ InstSeqNum youngestSN;
+};
+
+/**
+ * Templated Checker class. This Checker class is templated on the
+ * DynInstPtr of the instruction type that will be verified. Proper
+ * template instantiations of the Checker must be placed at the bottom
+ * of checker/cpu.cc.
+ */
+template <class DynInstPtr>
+class Checker : public CheckerCPU
+{
+ public:
+ Checker(Params *p)
+ : CheckerCPU(p), updateThisCycle(false), unverifiedInst(NULL)
+ { }
+
+ void switchOut();
+ void takeOverFrom(BaseCPU *oldCPU);
+
+ void verify(DynInstPtr &inst);
+
+ void validateInst(DynInstPtr &inst);
+ void validateExecution(DynInstPtr &inst);
+ void validateState();
+
+ void copyResult(DynInstPtr &inst);
+
+ private:
+ void handleError(DynInstPtr &inst)
+ {
+ if (exitOnError) {
+ dumpAndExit(inst);
+ } else if (updateOnError) {
+ updateThisCycle = true;
+ }
+ }
+
+ void dumpAndExit(DynInstPtr &inst);
+
+ bool updateThisCycle;
+
+ DynInstPtr unverifiedInst;
+
+ std::list<DynInstPtr> instList;
+ typedef typename std::list<DynInstPtr>::iterator InstListIt;
+ void dumpInsts();
+};
+
+#endif // __CPU_CHECKER_CPU_HH__
diff --git a/src/cpu/checker/cpu_impl.hh b/src/cpu/checker/cpu_impl.hh
new file mode 100644
index 000000000..3bb81c4b9
--- /dev/null
+++ b/src/cpu/checker/cpu_impl.hh
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#include <list>
+#include <string>
+
+#include "base/refcnt.hh"
+#include "cpu/base_dyn_inst.hh"
+#include "cpu/checker/cpu.hh"
+#include "cpu/simple_thread.hh"
+#include "cpu/thread_context.hh"
+#include "cpu/static_inst.hh"
+#include "mem/packet_impl.hh"
+#include "sim/byteswap.hh"
+#include "sim/sim_object.hh"
+#include "sim/stats.hh"
+
+#if FULL_SYSTEM
+#include "arch/vtophys.hh"
+#endif // FULL_SYSTEM
+
+using namespace std;
+//The CheckerCPU does alpha only
+using namespace AlphaISA;
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
+{
+ DynInstPtr inst;
+
+ // Either check this instruction, or add it to a list of
+ // instructions waiting to be checked. Instructions must be
+ // checked in program order, so if a store has committed yet not
+ // completed, there may be some instructions that are waiting
+ // behind it that have completed and must be checked.
+ if (!instList.empty()) {
+ if (youngestSN < completed_inst->seqNum) {
+ DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n",
+ completed_inst->seqNum, completed_inst->readPC());
+ instList.push_back(completed_inst);
+ youngestSN = completed_inst->seqNum;
+ }
+
+ if (!instList.front()->isCompleted()) {
+ return;
+ } else {
+ inst = instList.front();
+ instList.pop_front();
+ }
+ } else {
+ if (!completed_inst->isCompleted()) {
+ if (youngestSN < completed_inst->seqNum) {
+ DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n",
+ completed_inst->seqNum, completed_inst->readPC());
+ instList.push_back(completed_inst);
+ youngestSN = completed_inst->seqNum;
+ }
+ return;
+ } else {
+ if (youngestSN < completed_inst->seqNum) {
+ inst = completed_inst;
+ youngestSN = completed_inst->seqNum;
+ } else {
+ return;
+ }
+ }
+ }
+
+ unverifiedInst = inst;
+
+ // Try to check all instructions that are completed, ending if we
+ // run out of instructions to check or if an instruction is not
+ // yet completed.
+ while (1) {
+ DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%#x.\n",
+ inst->seqNum, inst->readPC());
+ unverifiedResult.integer = inst->readIntResult();
+ unverifiedReq = inst->req;
+ unverifiedMemData = inst->memData;
+ numCycles++;
+
+ Fault fault = NoFault;
+
+ // maintain $r0 semantics
+ thread->setIntReg(ZeroReg, 0);
+#ifdef TARGET_ALPHA
+ thread->setFloatRegDouble(ZeroReg, 0.0);
+#endif // TARGET_ALPHA
+
+ // Check if any recent PC changes match up with anything we
+ // expect to happen. This is mostly to check if traps or
+ // PC-based events have occurred in both the checker and CPU.
+ if (changedPC) {
+ DPRINTF(Checker, "Changed PC recently to %#x\n",
+ thread->readPC());
+ if (willChangePC) {
+ if (newPC == thread->readPC()) {
+ DPRINTF(Checker, "Changed PC matches expected PC\n");
+ } else {
+ warn("%lli: Changed PC does not match expected PC, "
+ "changed: %#x, expected: %#x",
+ curTick, thread->readPC(), newPC);
+ CheckerCPU::handleError();
+ }
+ willChangePC = false;
+ }
+ changedPC = false;
+ }
+ if (changedNextPC) {
+ DPRINTF(Checker, "Changed NextPC recently to %#x\n",
+ thread->readNextPC());
+ changedNextPC = false;
+ }
+
+ // Try to fetch the instruction
+
+#if FULL_SYSTEM
+#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0
+#else
+#define IFETCH_FLAGS(pc) 0
+#endif
+
+ uint64_t fetch_PC = thread->readPC() & ~3;
+
+ // set up memory request for instruction fetch
+ memReq = new Request(inst->threadNumber, fetch_PC,
+ sizeof(uint32_t),
+ IFETCH_FLAGS(thread->readPC()),
+ fetch_PC, thread->readCpuId(), inst->threadNumber);
+
+ bool succeeded = translateInstReq(memReq);
+
+ if (!succeeded) {
+ if (inst->getFault() == NoFault) {
+ // In this case the instruction was not a dummy
+ // instruction carrying an ITB fault. In the single
+ // threaded case the ITB should still be able to
+ // translate this instruction; in the SMT case it's
+ // possible that its ITB entry was kicked out.
+ warn("%lli: Instruction PC %#x was not found in the ITB!",
+ curTick, thread->readPC());
+ handleError(inst);
+
+ // go to the next instruction
+ thread->setPC(thread->readNextPC());
+ thread->setNextPC(thread->readNextPC() + sizeof(MachInst));
+
+ break;
+ } else {
+ // The instruction is carrying an ITB fault. Handle
+ // the fault and see if our results match the CPU on
+ // the next tick().
+ fault = inst->getFault();
+ }
+ }
+
+ if (fault == NoFault) {
+ Packet *pkt = new Packet(memReq, Packet::ReadReq,
+ Packet::Broadcast);
+
+ pkt->dataStatic(&machInst);
+
+ icachePort->sendFunctional(pkt);
+
+ delete pkt;
+
+ // keep an instruction count
+ numInst++;
+
+ // decode the instruction
+ machInst = gtoh(machInst);
+ // Checks that the instruction matches what we expected it to be.
+ // Checks both the machine instruction and the PC.
+ validateInst(inst);
+
+ curStaticInst = StaticInst::decode(makeExtMI(machInst,
+ thread->readPC()));
+
+#if FULL_SYSTEM
+ thread->setInst(machInst);
+#endif // FULL_SYSTEM
+
+ fault = inst->getFault();
+ }
+
+ // Discard fetch's memReq.
+ delete memReq;
+ memReq = NULL;
+
+ // Either the instruction was a fault and we should process the fault,
+ // or we should just go ahead execute the instruction. This assumes
+ // that the instruction is properly marked as a fault.
+ if (fault == NoFault) {
+
+ thread->funcExeInst++;
+
+ if (!inst->isUnverifiable())
+ fault = curStaticInst->execute(this, NULL);
+
+ // Checks to make sure instrution results are correct.
+ validateExecution(inst);
+
+ if (curStaticInst->isLoad()) {
+ ++numLoad;
+ }
+ }
+
+ if (fault != NoFault) {
+#if FULL_SYSTEM
+ fault->invoke(tc);
+ willChangePC = true;
+ newPC = thread->readPC();
+ DPRINTF(Checker, "Fault, PC is now %#x\n", newPC);
+#endif
+ } else {
+#if THE_ISA != MIPS_ISA
+ // go to the next instruction
+ thread->setPC(thread->readNextPC());
+ thread->setNextPC(thread->readNextPC() + sizeof(MachInst));
+#else
+ // go to the next instruction
+ thread->setPC(thread->readNextPC());
+ thread->setNextPC(thread->readNextNPC());
+ thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst));
+#endif
+
+ }
+
+#if FULL_SYSTEM
+ // @todo: Determine if these should happen only if the
+ // instruction hasn't faulted. In the SimpleCPU case this may
+ // not be true, but in the O3 or Ozone case this may be true.
+ Addr oldpc;
+ int count = 0;
+ do {
+ oldpc = thread->readPC();
+ system->pcEventQueue.service(tc);
+ count++;
+ } while (oldpc != thread->readPC());
+ if (count > 1) {
+ willChangePC = true;
+ newPC = thread->readPC();
+ DPRINTF(Checker, "PC Event, PC is now %#x\n", newPC);
+ }
+#endif
+
+ // @todo: Optionally can check all registers. (Or just those
+ // that have been modified).
+ validateState();
+
+ if (memReq) {
+ delete memReq;
+ memReq = NULL;
+ }
+
+ // Continue verifying instructions if there's another completed
+ // instruction waiting to be verified.
+ if (instList.empty()) {
+ break;
+ } else if (instList.front()->isCompleted()) {
+ inst = instList.front();
+ instList.pop_front();
+ } else {
+ break;
+ }
+ }
+ unverifiedInst = NULL;
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::switchOut()
+{
+ instList.clear();
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::takeOverFrom(BaseCPU *oldCPU)
+{
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::validateInst(DynInstPtr &inst)
+{
+ if (inst->readPC() != thread->readPC()) {
+ warn("%lli: PCs do not match! Inst: %#x, checker: %#x",
+ curTick, inst->readPC(), thread->readPC());
+ if (changedPC) {
+ warn("%lli: Changed PCs recently, may not be an error",
+ curTick);
+ } else {
+ handleError(inst);
+ }
+ }
+
+ MachInst mi = static_cast<MachInst>(inst->staticInst->machInst);
+
+ if (mi != machInst) {
+ warn("%lli: Binary instructions do not match! Inst: %#x, "
+ "checker: %#x",
+ curTick, mi, machInst);
+ handleError(inst);
+ }
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::validateExecution(DynInstPtr &inst)
+{
+ bool result_mismatch = false;
+ if (inst->numDestRegs()) {
+ // @todo: Support more destination registers.
+ if (inst->isUnverifiable()) {
+ // Unverifiable instructions assume they were executed
+ // properly by the CPU. Grab the result from the
+ // instruction and write it to the register.
+ copyResult(inst);
+ } else if (result.integer != inst->readIntResult()) {
+ result_mismatch = true;
+ }
+ }
+
+ if (result_mismatch) {
+ warn("%lli: Instruction results do not match! (Values may not "
+ "actually be integers) Inst: %#x, checker: %#x",
+ curTick, inst->readIntResult(), result.integer);
+
+ // It's useful to verify load values from memory, but in MP
+ // systems the value obtained at execute may be different than
+ // the value obtained at completion. Similarly DMA can
+ // present the same problem on even UP systems. Thus there is
+ // the option to only warn on loads having a result error.
+ if (inst->isLoad() && warnOnlyOnLoadError) {
+ copyResult(inst);
+ } else {
+ handleError(inst);
+ }
+ }
+
+ if (inst->readNextPC() != thread->readNextPC()) {
+ warn("%lli: Instruction next PCs do not match! Inst: %#x, "
+ "checker: %#x",
+ curTick, inst->readNextPC(), thread->readNextPC());
+ handleError(inst);
+ }
+
+ // Checking side effect registers can be difficult if they are not
+ // checked simultaneously with the execution of the instruction.
+ // This is because other valid instructions may have modified
+ // these registers in the meantime, and their values are not
+ // stored within the DynInst.
+ while (!miscRegIdxs.empty()) {
+ int misc_reg_idx = miscRegIdxs.front();
+ miscRegIdxs.pop();
+
+ if (inst->tcBase()->readMiscReg(misc_reg_idx) !=
+ thread->readMiscReg(misc_reg_idx)) {
+ warn("%lli: Misc reg idx %i (side effect) does not match! "
+ "Inst: %#x, checker: %#x",
+ curTick, misc_reg_idx,
+ inst->tcBase()->readMiscReg(misc_reg_idx),
+ thread->readMiscReg(misc_reg_idx));
+ handleError(inst);
+ }
+ }
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::validateState()
+{
+ if (updateThisCycle) {
+ warn("%lli: Instruction PC %#x results didn't match up, copying all "
+ "registers from main CPU", curTick, unverifiedInst->readPC());
+ // Heavy-weight copying of all registers
+ cpuXC->copyArchRegs(unverifiedInst->xcBase());
+ // Also advance the PC. Hopefully no PC-based events happened.
+#if THE_ISA != MIPS_ISA
+ // go to the next instruction
+ cpuXC->setPC(cpuXC->readNextPC());
+ cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst));
+#else
+ // go to the next instruction
+ cpuXC->setPC(cpuXC->readNextPC());
+ cpuXC->setNextPC(cpuXC->readNextNPC());
+ cpuXC->setNextNPC(cpuXC->readNextNPC() + sizeof(MachInst));
+#endif
+ updateThisCycle = false;
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::copyResult(DynInstPtr &inst)
+{
+ RegIndex idx = inst->destRegIdx(0);
+ if (idx < TheISA::FP_Base_DepTag) {
+ thread->setIntReg(idx, inst->readIntResult());
+ } else if (idx < TheISA::Fpcr_DepTag) {
+ thread->setFloatRegBits(idx, inst->readIntResult());
+ } else {
+ thread->setMiscReg(idx, inst->readIntResult());
+ }
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst)
+{
+ cprintf("Error detected, instruction information:\n");
+ cprintf("PC:%#x, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n"
+ "Completed:%i\n",
+ inst->readPC(),
+ inst->readNextPC(),
+ inst->seqNum,
+ inst->threadNumber,
+ inst->isCompleted());
+ inst->dump();
+ CheckerCPU::dumpAndExit();
+}
+
+template <class DynInstPtr>
+void
+Checker<DynInstPtr>::dumpInsts()
+{
+ int num = 0;
+
+ InstListIt inst_list_it = --(instList.end());
+
+ cprintf("Inst list size: %i\n", instList.size());
+
+ while (inst_list_it != instList.end())
+ {
+ cprintf("Instruction:%i\n",
+ num);
+
+ cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
+ "Completed:%i\n",
+ (*inst_list_it)->readPC(),
+ (*inst_list_it)->seqNum,
+ (*inst_list_it)->threadNumber,
+ (*inst_list_it)->isCompleted());
+
+ cprintf("\n");
+
+ inst_list_it--;
+ ++num;
+ }
+
+}
diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh
new file mode 100644
index 000000000..8c0186dae
--- /dev/null
+++ b/src/cpu/checker/thread_context.hh
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#ifndef __CPU_CHECKER_THREAD_CONTEXT_HH__
+#define __CPU_CHECKER_THREAD_CONTEXT_HH__
+
+#include "arch/types.hh"
+#include "cpu/checker/cpu.hh"
+#include "cpu/simple_thread.hh"
+#include "cpu/thread_context.hh"
+
+class EndQuiesceEvent;
+namespace Kernel {
+ class Statistics;
+};
+
+/**
+ * Derived ThreadContext class for use with the Checker. The template
+ * parameter is the ThreadContext class used by the specific CPU being
+ * verified. This CheckerThreadContext is then used by the main CPU
+ * in place of its usual ThreadContext class. It handles updating the
+ * checker's state any time state is updated externally through the
+ * ThreadContext.
+ */
+template <class TC>
+class CheckerThreadContext : public ThreadContext
+{
+ public:
+ CheckerThreadContext(TC *actual_tc,
+ CheckerCPU *checker_cpu)
+ : actualTC(actual_tc), checkerTC(checker_cpu->thread),
+ checkerCPU(checker_cpu)
+ { }
+
+ private:
+ /** The main CPU's ThreadContext, or class that implements the
+ * ThreadContext interface. */
+ TC *actualTC;
+ /** The checker's own SimpleThread. Will be updated any time
+ * anything uses this ThreadContext to externally update a
+ * thread's state. */
+ SimpleThread *checkerTC;
+ /** Pointer to the checker CPU. */
+ CheckerCPU *checkerCPU;
+
+ public:
+
+ BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); }
+
+ void setCpuId(int id)
+ {
+ actualTC->setCpuId(id);
+ checkerTC->setCpuId(id);
+ }
+
+ int readCpuId() { return actualTC->readCpuId(); }
+
+#if FULL_SYSTEM
+ System *getSystemPtr() { return actualTC->getSystemPtr(); }
+
+ PhysicalMemory *getPhysMemPtr() { return actualTC->getPhysMemPtr(); }
+
+ AlphaITB *getITBPtr() { return actualTC->getITBPtr(); }
+
+ AlphaDTB *getDTBPtr() { return actualTC->getDTBPtr(); }
+
+ Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); }
+
+ FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); }
+
+ VirtualPort *getVirtPort(ThreadContext *tc = NULL)
+ { return actualTC->getVirtPort(); }
+
+ void delVirtPort(VirtualPort *vp) { actualTC->delVirtPort(vp); }
+#else
+ TranslatingPort *getMemPort() { return actualTC->getMemPort(); }
+
+ Process *getProcessPtr() { return actualTC->getProcessPtr(); }
+#endif
+
+ Status status() const { return actualTC->status(); }
+
+ void setStatus(Status new_status)
+ {
+ actualTC->setStatus(new_status);
+ checkerTC->setStatus(new_status);
+ }
+
+ /// Set the status to Active. Optional delay indicates number of
+ /// cycles to wait before beginning execution.
+ void activate(int delay = 1) { actualTC->activate(delay); }
+
+ /// Set the status to Suspended.
+ void suspend() { actualTC->suspend(); }
+
+ /// Set the status to Unallocated.
+ void deallocate(int delay = 0) { actualTC->deallocate(delay); }
+
+ /// Set the status to Halted.
+ void halt() { actualTC->halt(); }
+
+#if FULL_SYSTEM
+ void dumpFuncProfile() { actualTC->dumpFuncProfile(); }
+#endif
+
+ void takeOverFrom(ThreadContext *oldContext)
+ {
+ actualTC->takeOverFrom(oldContext);
+ checkerTC->takeOverFrom(oldContext);
+ }
+
+ void regStats(const std::string &name) { actualTC->regStats(name); }
+
+ void serialize(std::ostream &os) { actualTC->serialize(os); }
+ void unserialize(Checkpoint *cp, const std::string &section)
+ { actualTC->unserialize(cp, section); }
+
+#if FULL_SYSTEM
+ EndQuiesceEvent *getQuiesceEvent() { return actualTC->getQuiesceEvent(); }
+
+ Tick readLastActivate() { return actualTC->readLastActivate(); }
+ Tick readLastSuspend() { return actualTC->readLastSuspend(); }
+
+ void profileClear() { return actualTC->profileClear(); }
+ void profileSample() { return actualTC->profileSample(); }
+#endif
+
+ int getThreadNum() { return actualTC->getThreadNum(); }
+
+ // @todo: Do I need this?
+ MachInst getInst() { return actualTC->getInst(); }
+
+ // @todo: Do I need this?
+ void copyArchRegs(ThreadContext *tc)
+ {
+ actualTC->copyArchRegs(tc);
+ checkerTC->copyArchRegs(tc);
+ }
+
+ void clearArchRegs()
+ {
+ actualTC->clearArchRegs();
+ checkerTC->clearArchRegs();
+ }
+
+ //
+ // New accessors for new decoder.
+ //
+ uint64_t readIntReg(int reg_idx)
+ { return actualTC->readIntReg(reg_idx); }
+
+ FloatReg readFloatReg(int reg_idx, int width)
+ { return actualTC->readFloatReg(reg_idx, width); }
+
+ FloatReg readFloatReg(int reg_idx)
+ { return actualTC->readFloatReg(reg_idx); }
+
+ FloatRegBits readFloatRegBits(int reg_idx, int width)
+ { return actualTC->readFloatRegBits(reg_idx, width); }
+
+ FloatRegBits readFloatRegBits(int reg_idx)
+ { return actualTC->readFloatRegBits(reg_idx); }
+
+ void setIntReg(int reg_idx, uint64_t val)
+ {
+ actualTC->setIntReg(reg_idx, val);
+ checkerTC->setIntReg(reg_idx, val);
+ }
+
+ void setFloatReg(int reg_idx, FloatReg val, int width)
+ {
+ actualTC->setFloatReg(reg_idx, val, width);
+ checkerTC->setFloatReg(reg_idx, val, width);
+ }
+
+ void setFloatReg(int reg_idx, FloatReg val)
+ {
+ actualTC->setFloatReg(reg_idx, val);
+ checkerTC->setFloatReg(reg_idx, val);
+ }
+
+ void setFloatRegBits(int reg_idx, FloatRegBits val, int width)
+ {
+ actualTC->setFloatRegBits(reg_idx, val, width);
+ checkerTC->setFloatRegBits(reg_idx, val, width);
+ }
+
+ void setFloatRegBits(int reg_idx, FloatRegBits val)
+ {
+ actualTC->setFloatRegBits(reg_idx, val);
+ checkerTC->setFloatRegBits(reg_idx, val);
+ }
+
+ uint64_t readPC() { return actualTC->readPC(); }
+
+ void setPC(uint64_t val)
+ {
+ actualTC->setPC(val);
+ checkerTC->setPC(val);
+ checkerCPU->recordPCChange(val);
+ }
+
+ uint64_t readNextPC() { return actualTC->readNextPC(); }
+
+ void setNextPC(uint64_t val)
+ {
+ actualTC->setNextPC(val);
+ checkerTC->setNextPC(val);
+ checkerCPU->recordNextPCChange(val);
+ }
+
+ uint64_t readNextNPC() { return actualTC->readNextNPC(); }
+
+ void setNextNPC(uint64_t val)
+ {
+ actualTC->setNextNPC(val);
+ checkerTC->setNextNPC(val);
+ checkerCPU->recordNextPCChange(val);
+ }
+
+ MiscReg readMiscReg(int misc_reg)
+ { return actualTC->readMiscReg(misc_reg); }
+
+ MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault)
+ { return actualTC->readMiscRegWithEffect(misc_reg, fault); }
+
+ Fault setMiscReg(int misc_reg, const MiscReg &val)
+ {
+ checkerTC->setMiscReg(misc_reg, val);
+ return actualTC->setMiscReg(misc_reg, val);
+ }
+
+ Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val)
+ {
+ checkerTC->setMiscRegWithEffect(misc_reg, val);
+ return actualTC->setMiscRegWithEffect(misc_reg, val);
+ }
+
+ unsigned readStCondFailures()
+ { return actualTC->readStCondFailures(); }
+
+ void setStCondFailures(unsigned sc_failures)
+ {
+ checkerTC->setStCondFailures(sc_failures);
+ actualTC->setStCondFailures(sc_failures);
+ }
+#if FULL_SYSTEM
+ bool inPalMode() { return actualTC->inPalMode(); }
+#endif
+
+ // @todo: Fix this!
+ bool misspeculating() { return actualTC->misspeculating(); }
+
+#if !FULL_SYSTEM
+ IntReg getSyscallArg(int i) { return actualTC->getSyscallArg(i); }
+
+ // used to shift args for indirect syscall
+ void setSyscallArg(int i, IntReg val)
+ {
+ checkerTC->setSyscallArg(i, val);
+ actualTC->setSyscallArg(i, val);
+ }
+
+ void setSyscallReturn(SyscallReturn return_value)
+ {
+ checkerTC->setSyscallReturn(return_value);
+ actualTC->setSyscallReturn(return_value);
+ }
+
+ Counter readFuncExeInst() { return actualTC->readFuncExeInst(); }
+#endif
+ void changeRegFileContext(TheISA::RegContextParam param,
+ TheISA::RegContextVal val)
+ {
+ actualTC->changeRegFileContext(param, val);
+ checkerTC->changeRegFileContext(param, val);
+ }
+};
+
+#endif // __CPU_CHECKER_EXEC_CONTEXT_HH__