summaryrefslogtreecommitdiff
path: root/src/cpu/checker
diff options
context:
space:
mode:
authorGeoffrey Blake <geoffrey.blake@arm.com>2012-01-31 07:46:03 -0800
committerGeoffrey Blake <geoffrey.blake@arm.com>2012-01-31 07:46:03 -0800
commitaf6aaf258171027af8d3cf0ef86dddff501a3ccb (patch)
tree3473845b7217b48dcf43460f0a90ca655a7018ed /src/cpu/checker
parentade53def9252a36a39b2c4bd61196355906f0505 (diff)
downloadgem5-af6aaf258171027af8d3cf0ef86dddff501a3ccb.tar.xz
CheckerCPU: Re-factor CheckerCPU to be compatible with current gem5
Brings the CheckerCPU back to life to allow FS and SE checking of the O3CPU. These changes have only been tested with the ARM ISA. Other ISAs potentially require modification.
Diffstat (limited to 'src/cpu/checker')
-rw-r--r--src/cpu/checker/cpu.cc365
-rw-r--r--src/cpu/checker/cpu.hh123
-rw-r--r--src/cpu/checker/cpu_impl.hh543
-rw-r--r--src/cpu/checker/thread_context.hh108
4 files changed, 743 insertions, 396 deletions
diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc
index 0c7fe66bf..7c9e4f781 100644
--- a/src/cpu/checker/cpu.cc
+++ b/src/cpu/checker/cpu.cc
@@ -1,4 +1,16 @@
/*
+ * Copyright (c) 2011 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
@@ -26,6 +38,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Kevin Lim
+ * Geoffrey Blake
*/
#include <list>
@@ -36,6 +49,8 @@
#include "cpu/simple_thread.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
+#include "params/CheckerCPU.hh"
+#include "sim/tlb.hh"
#if FULL_SYSTEM
#include "arch/kernel_stats.hh"
@@ -43,8 +58,7 @@
#endif // FULL_SYSTEM
using namespace std;
-//The CheckerCPU does alpha only
-using namespace AlphaISA;
+using namespace TheISA;
void
CheckerCPU::init()
@@ -55,6 +69,8 @@ CheckerCPU::CheckerCPU(Params *p)
: BaseCPU(p), thread(NULL), tc(NULL)
{
memReq = NULL;
+ curStaticInst = NULL;
+ curMacroStaticInst = NULL;
numInst = 0;
startNumInst = 0;
@@ -66,19 +82,20 @@ CheckerCPU::CheckerCPU(Params *p)
exitOnError = p->exitOnError;
warnOnlyOnLoadError = p->warnOnlyOnLoadError;
-#if FULL_SYSTEM
itb = p->itb;
dtb = p->dtb;
+#if FULL_SYSTEM
systemPtr = NULL;
#else
- process = p->process;
- thread = new SimpleThread(this, /* thread_num */ 0, process);
+ workload = p->workload;
+ // XXX: This is a hack to get this to work some
+ thread = new SimpleThread(this, /* thread_num */ 0, workload[0], itb, dtb);
tc = thread->getTC();
threadContexts.push_back(tc);
#endif
- result.integer = 0;
+ updateOnError = true;
}
CheckerCPU::~CheckerCPU()
@@ -115,192 +132,194 @@ CheckerCPU::setDcachePort(Port *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));
-*/
}
-template <class T>
Fault
-CheckerCPU::read(Addr addr, T &data, unsigned flags)
+CheckerCPU::readMem(Addr addr, uint8_t *data, unsigned size, unsigned flags)
{
- // need to fill in CPU & thread IDs here
- memReq = new Request();
+ Fault fault = NoFault;
+ unsigned blockSize = dcachePort->peerBlockSize();
+ int fullSize = size;
+ Addr secondAddr = roundDown(addr + size - 1, blockSize);
+ bool checked_flags = false;
+ bool flags_match = true;
+ Addr pAddr = 0x0;
+
+
+ if (secondAddr > addr)
+ size = secondAddr - addr;
+
+ // Need to account for multiple accesses like the Atomic and TimingSimple
+ while (1) {
+ memReq = new Request();
+ memReq->setVirt(0, addr, size, flags, thread->pcState().instAddr());
+
+ // translate to physical address
+ fault = dtb->translateFunctional(memReq, tc, BaseTLB::Read);
+
+ if (!checked_flags && fault == NoFault && unverifiedReq) {
+ flags_match = checkFlags(unverifiedReq, memReq->getVaddr(),
+ memReq->getPaddr(), memReq->getFlags());
+ pAddr = memReq->getPaddr();
+ checked_flags = true;
+ }
- memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC());
+ // Now do the access
+ if (fault == NoFault &&
+ !memReq->getFlags().isSet(Request::NO_ACCESS)) {
+ PacketPtr pkt = new Packet(memReq,
+ memReq->isLLSC() ?
+ MemCmd::LoadLockedReq : MemCmd::ReadReq,
+ Packet::Broadcast);
+
+ pkt->dataStatic(data);
+
+ if (!(memReq->isUncacheable() || memReq->isMmappedIpr())) {
+ // 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, unverifiedMemData, size);
+ }
+
+ delete memReq;
+ memReq = NULL;
+ delete pkt;
+ }
- // translate to physical address
- dtb->translateAtomic(memReq, tc, false);
+ if (fault != NoFault) {
+ if (memReq->isPrefetch()) {
+ fault = NoFault;
+ }
+ delete memReq;
+ memReq = NULL;
+ break;
+ }
- PacketPtr pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast);
+ if (memReq != NULL) {
+ delete memReq;
+ }
- pkt->dataStatic(&data);
+ //If we don't need to access a second cache line, stop now.
+ if (secondAddr <= addr)
+ {
+ break;
+ }
- if (!(memReq->isUncacheable())) {
- // 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));
+ // Setup for accessing next cache line
+ data += size;
+ unverifiedMemData += size;
+ size = addr + fullSize - secondAddr;
+ addr = secondAddr;
}
- delete pkt;
+ if (!flags_match) {
+ warn("%lli: Flags do not match CPU:%#x %#x %#x Checker:%#x %#x %#x\n",
+ curTick(), unverifiedReq->getVaddr(), unverifiedReq->getPaddr(),
+ unverifiedReq->getFlags(), addr, pAddr, flags);
+ handleError();
+ }
- return NoFault;
+ return fault;
}
-#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)
+CheckerCPU::writeMem(uint8_t *data, unsigned size,
+ Addr addr, unsigned flags, uint64_t *res)
{
- return read(addr, *(uint64_t*)&data, flags);
-}
+ Fault fault = NoFault;
+ bool checked_flags = false;
+ bool flags_match = true;
+ Addr pAddr = 0x0;
-template<>
-Fault
-CheckerCPU::read(Addr addr, float &data, unsigned flags)
-{
- return read(addr, *(uint32_t*)&data, flags);
-}
+ unsigned blockSize = dcachePort->peerBlockSize();
+ int fullSize = size;
-template<>
-Fault
-CheckerCPU::read(Addr addr, int32_t &data, unsigned flags)
-{
- return read(addr, (uint32_t&)data, flags);
-}
+ Addr secondAddr = roundDown(addr + size - 1, blockSize);
-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
- dtb->translateAtomic(memReq, tc, true);
-
- // 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->isUncacheable()) &&
- (!(unverifiedReq->isLLSC()) ||
- ((unverifiedReq->isLLSC()) &&
- unverifiedReq->getExtraData() == 1))) {
- T inst_data;
-/*
- // This code would work if the LSQ allowed for snooping.
- PacketPtr pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast);
- pkt.dataStatic(&inst_data);
+ if (secondAddr > addr)
+ size = secondAddr - addr;
- dcachePort->sendFunctional(pkt);
+ // Need to account for a multiple access like Atomic and Timing CPUs
+ while (1) {
+ memReq = new Request();
+ memReq->setVirt(0, addr, size, flags, thread->pcState().instAddr());
- delete pkt;
-*/
- memcpy(&inst_data, unverifiedMemData, sizeof(T));
+ // translate to physical address
+ fault = dtb->translateFunctional(memReq, tc, BaseTLB::Write);
- if (data != inst_data) {
- warn("%lli: Store value does not match value in memory! "
- "Instruction: %#x, memory: %#x",
- curTick(), inst_data, data);
- handleError();
+ if (!checked_flags && fault == NoFault && unverifiedReq) {
+ flags_match = checkFlags(unverifiedReq, memReq->getVaddr(),
+ memReq->getPaddr(), memReq->getFlags());
+ pAddr = memReq->getPaddr();
+ checked_flags = true;
}
- }
-
- // 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->getExtraData();
-
- 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);
-}
+ /*
+ * We don't actually check memory for the store because there
+ * is no guarantee it has left the lsq yet, and therefore we
+ * can't verify the memory on stores without lsq snooping
+ * enabled. This is left as future work for the Checker: LSQ snooping
+ * and memory validation after stores have committed.
+ */
+
+ delete memReq;
+
+ //If we don't need to access a second cache line, stop now.
+ if (fault != NoFault || secondAddr <= addr)
+ {
+ if (fault != NoFault && memReq->isPrefetch()) {
+ fault = NoFault;
+ }
+ break;
+ }
-template<>
-Fault
-CheckerCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
-{
- return write((uint32_t)data, addr, flags, res);
+ //Update size and access address
+ size = addr + fullSize - secondAddr;
+ //And access the right address.
+ addr = secondAddr;
+ }
+
+ if (!flags_match) {
+ warn("%lli: Flags do not match CPU:%#x %#x Checker:%#x %#x %#x\n",
+ curTick(), unverifiedReq->getVaddr(), unverifiedReq->getPaddr(),
+ unverifiedReq->getFlags(), addr, pAddr, flags);
+ 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 (unverifiedReq && res && unverifiedReq->extraDataValid())
+ *res = unverifiedReq->getExtraData();
+
+ // Entire purpose here is to make sure we are getting the
+ // same data to send to the mem system as the CPU did.
+ // Cannot check this is actually what went to memory because
+ // there stores can be in ld/st queue or coherent operations
+ // overwriting values.
+ bool extraData;
+ if (unverifiedReq) {
+ extraData = unverifiedReq->extraDataValid() ?
+ unverifiedReq->getExtraData() : 1;
+ }
+
+ if (unverifiedReq && unverifiedMemData &&
+ memcmp(data, unverifiedMemData, fullSize) && extraData) {
+ warn("%lli: Store value does not match value sent to memory!\
+ data: %#x inst_data: %#x", curTick(), data,
+ unverifiedMemData);
+ handleError();
+ }
+
+ return fault;
}
-
#if FULL_SYSTEM
Addr
CheckerCPU::dbg_vtophys(Addr addr)
@@ -309,24 +328,30 @@ CheckerCPU::dbg_vtophys(Addr addr)
}
#endif // FULL_SYSTEM
+/**
+ * Checks if the flags set by the Checker and Checkee match.
+ */
bool
-CheckerCPU::checkFlags(Request *req)
+CheckerCPU::checkFlags(Request *unverified_req, Addr vAddr,
+ Addr pAddr, int flags)
{
- // 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 | PREFETCH;
- flags = flags & (mask);
- if (flags == req->getFlags()) {
+ Addr unverifiedVAddr = unverified_req->getVaddr();
+ Addr unverifiedPAddr = unverified_req->getPaddr();
+ int unverifiedFlags = unverified_req->getFlags();
+
+ if (unverifiedVAddr != vAddr ||
+ unverifiedPAddr != pAddr ||
+ unverifiedFlags != flags) {
return false;
- } else {
- return true;
}
+
+ return true;
}
void
CheckerCPU::dumpAndExit()
{
- warn("%lli: Checker PC:%#x, next PC:%#x",
- curTick(), thread->readPC(), thread->readNextPC());
+ warn("%lli: Checker PC:%s",
+ curTick(), thread->pcState());
panic("Checker found an error!");
}
diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh
index 1e3a17a34..1419051a6 100644
--- a/src/cpu/checker/cpu.hh
+++ b/src/cpu/checker/cpu.hh
@@ -1,4 +1,16 @@
/*
+ * Copyright (c) 2011 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
@@ -35,6 +47,7 @@
#include <map>
#include <queue>
+#include "arch/predecoder.hh"
#include "arch/types.hh"
#include "base/statistics.hh"
#include "config/full_system.hh"
@@ -43,6 +56,8 @@
#include "cpu/pc_event.hh"
#include "cpu/simple_thread.hh"
#include "cpu/static_inst.hh"
+#include "debug/Checker.hh"
+#include "params/CheckerCPU.hh"
#include "sim/eventq.hh"
// forward declarations
@@ -61,7 +76,6 @@ class Process;
#endif // FULL_SYSTEM
template <class>
class BaseDynInst;
-class CheckerCPUParams;
class ThreadContext;
class MemInterface;
class Checkpoint;
@@ -96,11 +110,11 @@ class CheckerCPU : public BaseCPU
public:
typedef CheckerCPUParams Params;
const Params *params() const
- { return reinterpret_cast<const Params *>(_params); }
+ { return reinterpret_cast<const Params *>(_params); }
CheckerCPU(Params *p);
virtual ~CheckerCPU();
- Process *process;
+ std::vector<Process*> workload;
void setSystem(System *system);
@@ -135,19 +149,25 @@ class CheckerCPU : public BaseCPU
union Result {
uint64_t integer;
-// float fp;
double dbl;
+ void set(uint64_t i) { integer = i; }
+ void set(double d) { dbl = d; }
+ void get(uint64_t& i) { i = integer; }
+ void get(double& d) { d = dbl; }
};
- Result result;
+ // ISAs like ARM can have multiple destination registers to check,
+ // keep them all in a std::queue
+ std::queue<Result> result;
// current instruction
- MachInst machInst;
+ TheISA::MachInst machInst;
// Pointer to the one memory request.
RequestPtr memReq;
StaticInstPtr curStaticInst;
+ StaticInstPtr curMacroStaticInst;
// number of simulated instructions
Counter numInst;
@@ -155,6 +175,9 @@ class CheckerCPU : public BaseCPU
std::queue<int> miscRegIdxs;
+ TheISA::TLB* getITBPtr() { return itb; }
+ TheISA::TLB* getDTBPtr() { return dtb; }
+
virtual Counter totalInstructions() const
{
return 0;
@@ -167,12 +190,6 @@ class CheckerCPU : public BaseCPU
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"); }
@@ -206,17 +223,25 @@ class CheckerCPU : public BaseCPU
return thread->readFloatRegBits(reg_idx);
}
+ template <class T>
+ void setResult(T t)
+ {
+ Result instRes;
+ instRes.set(t);
+ result.push(instRes);
+ }
+
void setIntRegOperand(const StaticInst *si, int idx, uint64_t val)
{
thread->setIntReg(si->destRegIdx(idx), val);
- result.integer = val;
+ setResult<uint64_t>(val);
}
void setFloatRegOperand(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;
+ setResult<double>(val);
}
void setFloatRegOperandBits(const StaticInst *si, int idx,
@@ -224,12 +249,26 @@ class CheckerCPU : public BaseCPU
{
int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag;
thread->setFloatRegBits(reg_idx, val);
- result.integer = val;
+ setResult<uint64_t>(val);
}
- uint64_t instAddr() { return thread->instAddr(); }
+ bool readPredicate() { return thread->readPredicate(); }
+ void setPredicate(bool val)
+ {
+ thread->setPredicate(val);
+ }
- uint64_t nextInstAddr() { return thread->nextInstAddr(); }
+ TheISA::PCState pcState() { return thread->pcState(); }
+ void pcState(const TheISA::PCState &val)
+ {
+ DPRINTF(Checker, "Changing PC to %s, old PC %s.\n",
+ val, thread->pcState());
+ thread->pcState(val);
+ }
+ Addr instAddr() { return thread->instAddr(); }
+ Addr nextInstAddr() { return thread->nextInstAddr(); }
+ MicroPC microPC() { return thread->microPC(); }
+ //////////////////////////////////////////
MiscReg readMiscRegNoEffect(int misc_reg)
{
@@ -243,7 +282,6 @@ class CheckerCPU : public BaseCPU
void setMiscRegNoEffect(int misc_reg, const MiscReg &val)
{
- result.integer = val;
miscRegIdxs.push(misc_reg);
return thread->setMiscRegNoEffect(misc_reg, val);
}
@@ -254,8 +292,25 @@ class CheckerCPU : public BaseCPU
return thread->setMiscReg(misc_reg, val);
}
- void recordPCChange(uint64_t val) { changedPC = true; newPC = val; }
- void recordNextPCChange(uint64_t val) { changedNextPC = true; }
+ MiscReg readMiscRegOperand(const StaticInst *si, int idx)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::Ctrl_Base_DepTag;
+ return thread->readMiscReg(reg_idx);
+ }
+
+ void setMiscRegOperand(
+ const StaticInst *si, int idx, const MiscReg &val)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::Ctrl_Base_DepTag;
+ return thread->setMiscReg(reg_idx, val);
+ }
+ /////////////////////////////////////////
+
+ void recordPCChange(const TheISA::PCState &val)
+ {
+ changedPC = true;
+ newPCState = val;
+ }
void demapPage(Addr vaddr, uint64_t asn)
{
@@ -273,9 +328,18 @@ class CheckerCPU : public BaseCPU
this->dtb->demapPage(vaddr, asn);
}
+ Fault readMem(Addr addr, uint8_t *data, unsigned size, unsigned flags);
+ Fault writeMem(uint8_t *data, unsigned size,
+ Addr addr, unsigned flags, uint64_t *res);
+
+ void setStCondFailures(unsigned sc_failures)
+ {}
+ /////////////////////////////////////////////////////
+
#if FULL_SYSTEM
Fault hwrei() { return thread->hwrei(); }
bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); }
+ void wakeup() { }
#else
// Assume that the normal CPU's call to syscall was successful.
// The checker's state would have already been updated by the syscall.
@@ -288,7 +352,8 @@ class CheckerCPU : public BaseCPU
dumpAndExit();
}
- bool checkFlags(Request *req);
+ bool checkFlags(Request *unverified_req, Addr vAddr,
+ Addr pAddr, int flags);
void dumpAndExit();
@@ -301,7 +366,7 @@ class CheckerCPU : public BaseCPU
bool changedPC;
bool willChangePC;
- uint64_t newPC;
+ TheISA::PCState newPCState;
bool changedNextPC;
bool exitOnError;
bool updateOnError;
@@ -316,24 +381,31 @@ class CheckerCPU : public BaseCPU
* template instantiations of the Checker must be placed at the bottom
* of checker/cpu.cc.
*/
-template <class DynInstPtr>
+template <class Impl>
class Checker : public CheckerCPU
{
+ private:
+ typedef typename Impl::DynInstPtr DynInstPtr;
+
public:
Checker(Params *p)
- : CheckerCPU(p), updateThisCycle(false), unverifiedInst(NULL)
+ : CheckerCPU(p), updateThisCycle(false), unverifiedInst(NULL),
+ predecoder(NULL)
{ }
void switchOut();
void takeOverFrom(BaseCPU *oldCPU);
+ void advancePC(Fault fault);
+
void verify(DynInstPtr &inst);
void validateInst(DynInstPtr &inst);
void validateExecution(DynInstPtr &inst);
void validateState();
- void copyResult(DynInstPtr &inst);
+ void copyResult(DynInstPtr &inst, uint64_t mismatch_val, int start_idx);
+ void handlePendingInt();
private:
void handleError(DynInstPtr &inst)
@@ -350,6 +422,7 @@ class Checker : public CheckerCPU
bool updateThisCycle;
DynInstPtr unverifiedInst;
+ TheISA::Predecoder predecoder;
std::list<DynInstPtr> instList;
typedef typename std::list<DynInstPtr>::iterator InstListIt;
diff --git a/src/cpu/checker/cpu_impl.hh b/src/cpu/checker/cpu_impl.hh
index 8694dae21..8c8789cb6 100644
--- a/src/cpu/checker/cpu_impl.hh
+++ b/src/cpu/checker/cpu_impl.hh
@@ -1,4 +1,16 @@
/*
+ * Copyright (c) 2011 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
@@ -26,6 +38,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Kevin Lim
+ * Geoffrey Blake
*/
#include <list>
@@ -33,11 +46,13 @@
#include "base/refcnt.hh"
#include "config/the_isa.hh"
-#include "cpu/checker/cpu.hh"
#include "cpu/base_dyn_inst.hh"
+#include "cpu/exetrace.hh"
#include "cpu/simple_thread.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
+#include "cpu/checker/cpu.hh"
+#include "debug/Checker.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
@@ -46,15 +61,81 @@
#endif // FULL_SYSTEM
using namespace std;
-//The CheckerCPU does alpha only
-using namespace AlphaISA;
+using namespace TheISA;
+
+template <class Impl>
+void
+Checker<Impl>::advancePC(Fault fault)
+{
+ if (fault != NoFault) {
+ curMacroStaticInst = StaticInst::nullStaticInstPtr;
+ fault->invoke(tc, curStaticInst);
+ predecoder.reset();
+ } else {
+ if (curStaticInst) {
+ if (curStaticInst->isLastMicroop())
+ curMacroStaticInst = StaticInst::nullStaticInstPtr;
+ TheISA::PCState pcState = thread->pcState();
+ TheISA::advancePC(pcState, curStaticInst);
+ thread->pcState(pcState);
+ DPRINTF(Checker, "Advancing PC to %s.\n", thread->pcState());
+ }
+ }
+}
+//////////////////////////////////////////////////
+
+template <class Impl>
+void
+Checker<Impl>::handlePendingInt()
+{
+ DPRINTF(Checker, "IRQ detected at PC: %s with %d insts in buffer\n",
+ thread->pcState(), instList.size());
+ DynInstPtr boundaryInst = NULL;
+ if (!instList.empty()) {
+ // Set the instructions as completed and verify as much as possible.
+ DynInstPtr inst;
+ typename std::list<DynInstPtr>::iterator itr;
+
+ for (itr = instList.begin(); itr != instList.end(); itr++) {
+ (*itr)->setCompleted();
+ }
-template <class DynInstPtr>
+ inst = instList.front();
+ boundaryInst = instList.back();
+ verify(inst); // verify the instructions
+ inst = NULL;
+ }
+ if ((!boundaryInst && curMacroStaticInst &&
+ curStaticInst->isDelayedCommit() &&
+ !curStaticInst->isLastMicroop()) ||
+ (boundaryInst && boundaryInst->isDelayedCommit() &&
+ !boundaryInst->isLastMicroop())) {
+ panic("%lli: Trying to take an interrupt in middle of "
+ "a non-interuptable instruction!", curTick());
+ }
+ boundaryInst = NULL;
+ predecoder.reset();
+ curMacroStaticInst = StaticInst::nullStaticInstPtr;
+}
+
+template <class Impl>
void
-Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
+Checker<Impl>::verify(DynInstPtr &completed_inst)
{
DynInstPtr inst;
+ // Make sure serializing instructions are actually
+ // seen as serializing to commit. instList should be
+ // empty in these cases.
+ if ((completed_inst->isSerializing() ||
+ completed_inst->isSerializeBefore()) &&
+ (!instList.empty() ?
+ (instList.front()->seqNum != completed_inst->seqNum) : 0)) {
+ panic("%lli: Instruction sn:%lli at PC %s is serializing before but is"
+ " entering instList with other instructions\n", curTick(),
+ completed_inst->seqNum, completed_inst->pcState());
+ }
+
// 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
@@ -62,8 +143,8 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
// 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());
+ DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n",
+ completed_inst->seqNum, completed_inst->pcState());
instList.push_back(completed_inst);
youngestSN = completed_inst->seqNum;
}
@@ -77,8 +158,8 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
} 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());
+ DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n",
+ completed_inst->seqNum, completed_inst->pcState());
instList.push_back(completed_inst);
youngestSN = completed_inst->seqNum;
}
@@ -93,17 +174,29 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
}
}
+ // Make sure a serializing instruction is actually seen as
+ // serializing. instList should be empty here
+ if (inst->isSerializeAfter() && !instList.empty()) {
+ panic("%lli: Instruction sn:%lli at PC %s is serializing after but is"
+ " exiting instList with other instructions\n", curTick(),
+ completed_inst->seqNum, completed_inst->pcState());
+ }
unverifiedInst = inst;
+ inst = NULL;
// 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;
+ DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%s.\n",
+ unverifiedInst->seqNum, unverifiedInst->pcState());
+ unverifiedReq = NULL;
+ unverifiedReq = unverifiedInst->reqToVerify;
+ unverifiedMemData = unverifiedInst->memData;
+ // Make sure results queue is empty
+ while (!result.empty()) {
+ result.pop();
+ }
numCycles++;
Fault fault = NoFault;
@@ -118,15 +211,15 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
// 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());
+ DPRINTF(Checker, "Changed PC recently to %s\n",
+ thread->pcState());
if (willChangePC) {
- if (newPC == thread->readPC()) {
+ if (newPCState == thread->pcState()) {
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);
+ "changed: %s, expected: %s",
+ curTick(), thread->pcState(), newPCState);
CheckerCPU::handleError();
}
willChangePC = false;
@@ -135,124 +228,188 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
}
if (changedNextPC) {
DPRINTF(Checker, "Changed NextPC recently to %#x\n",
- thread->readNextPC());
+ thread->nextInstAddr());
changedNextPC = false;
}
// Try to fetch the instruction
+ uint64_t fetchOffset = 0;
+ bool fetchDone = false;
+
+ while (!fetchDone) {
+ Addr fetch_PC = thread->instAddr();
+ fetch_PC = (fetch_PC & PCMask) + fetchOffset;
+
+ // If not in the middle of a macro instruction
+ if (!curMacroStaticInst) {
+ // set up memory request for instruction fetch
+ memReq = new Request(unverifiedInst->threadNumber, fetch_PC,
+ sizeof(MachInst),
+ 0,
+ fetch_PC, thread->contextId(),
+ unverifiedInst->threadNumber);
+ memReq->setVirt(0, fetch_PC, sizeof(MachInst),
+ Request::INST_FETCH, thread->instAddr());
+
+
+ fault = itb->translateFunctional(memReq, tc, BaseTLB::Execute);
+
+ if (fault != NoFault) {
+ if (unverifiedInst->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 %s was not found in the "
+ "ITB!", curTick(), thread->pcState());
+ handleError(unverifiedInst);
+
+ // go to the next instruction
+ advancePC(NoFault);
+
+ // Give up on an ITB fault..
+ delete memReq;
+ unverifiedInst = NULL;
+ return;
+ } else {
+ // The instruction is carrying an ITB fault. Handle
+ // the fault and see if our results match the CPU on
+ // the next tick().
+ fault = unverifiedInst->getFault();
+ delete memReq;
+ break;
+ }
+ } else {
+ PacketPtr pkt = new Packet(memReq,
+ MemCmd::ReadReq,
+ Packet::Broadcast);
-#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->contextId(),
- inst->threadNumber);
-
- bool succeeded = itb->translateAtomic(memReq, thread);
-
- 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);
+ pkt->dataStatic(&machInst);
+ icachePort->sendFunctional(pkt);
+ machInst = gtoh(machInst);
- // go to the next instruction
- thread->setPC(thread->readNextPC());
- thread->setNextPC(thread->readNextPC() + sizeof(MachInst));
+ delete memReq;
+ delete pkt;
+ }
+ }
- 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) {
+ TheISA::PCState pcState = thread->pcState();
+
+ if (isRomMicroPC(pcState.microPC())) {
+ fetchDone = true;
+ curStaticInst =
+ microcodeRom.fetchMicroop(pcState.microPC(), NULL);
+ } else if (!curMacroStaticInst) {
+ //We're not in the middle of a macro instruction
+ StaticInstPtr instPtr = NULL;
+
+ //Predecode, ie bundle up an ExtMachInst
+ predecoder.setTC(thread->getTC());
+ //If more fetch data is needed, pass it in.
+ Addr fetchPC = (pcState.instAddr() & PCMask) + fetchOffset;
+ predecoder.moreBytes(pcState, fetchPC, machInst);
+
+ //If an instruction is ready, decode it.
+ //Otherwise, we'll have to fetch beyond the
+ //MachInst at the current pc.
+ if (predecoder.extMachInstReady()) {
+ fetchDone = true;
+ ExtMachInst newMachInst =
+ predecoder.getExtMachInst(pcState);
+ thread->pcState(pcState);
+ instPtr = thread->decoder.decode(newMachInst,
+ pcState.instAddr());
+ machInst = newMachInst;
+ } else {
+ fetchDone = false;
+ fetchOffset += sizeof(TheISA::MachInst);
+ }
+
+ //If we decoded an instruction and it's microcoded,
+ //start pulling out micro ops
+ if (instPtr && instPtr->isMacroop()) {
+ curMacroStaticInst = instPtr;
+ curStaticInst =
+ instPtr->fetchMicroop(pcState.microPC());
+ } else {
+ curStaticInst = instPtr;
+ }
+ } else {
+ // Read the next micro op from the macro-op
+ curStaticInst =
+ curMacroStaticInst->fetchMicroop(pcState.microPC());
+ fetchDone = true;
+ }
}
}
+ // reset predecoder on Checker
+ predecoder.reset();
+ // Check Checker and CPU get same instruction, and record
+ // any faults the CPU may have had.
+ Fault unverifiedFault;
if (fault == NoFault) {
- PacketPtr pkt = new Packet(memReq, Packet::ReadReq,
- Packet::Broadcast);
-
- pkt->dataStatic(&machInst);
+ unverifiedFault = unverifiedInst->getFault();
- 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);
-
-#if THE_ISA == ALPHA_ISA
- curStaticInst = StaticInst::decode(makeExtMI(machInst,
- thread->readPC()));
-#elif THE_ISA == SPARC_ISA
- curStaticInst = StaticInst::decode(makeExtMI(machInst,
- thread->getTC()));
-#endif
-
- fault = inst->getFault();
+ validateInst(unverifiedInst);
}
- // Discard fetch's memReq.
- delete memReq;
- memReq = NULL;
+ // keep an instruction count
+ numInst++;
+
// 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) {
+ // Execute Checker instruction and trace
+ if (!unverifiedInst->isUnverifiable()) {
+ Trace::InstRecord *traceData = tracer->getInstRecord(curTick(),
+ tc,
+ curStaticInst,
+ pcState(),
+ curMacroStaticInst);
+ fault = curStaticInst->execute(this, traceData);
+ if (traceData) {
+ traceData->dump();
+ delete traceData;
+ }
+ }
- thread->funcExeInst++;
-
- if (!inst->isUnverifiable())
- fault = curStaticInst->execute(this, NULL);
-
- // Checks to make sure instrution results are correct.
- validateExecution(inst);
+ if (fault == NoFault && unverifiedFault == NoFault) {
+ thread->funcExeInst++;
+ // Checks to make sure instrution results are correct.
+ validateExecution(unverifiedInst);
- if (curStaticInst->isLoad()) {
- ++numLoad;
+ if (curStaticInst->isLoad()) {
+ ++numLoad;
+ }
+ } else if (fault != NoFault && unverifiedFault == NoFault) {
+ panic("%lli: sn: %lli at PC: %s took a fault in checker "
+ "but not in driver CPU\n", curTick(),
+ unverifiedInst->seqNum, unverifiedInst->pcState());
+ } else if (fault == NoFault && unverifiedFault != NoFault) {
+ panic("%lli: sn: %lli at PC: %s took a fault in driver "
+ "CPU but not in checker\n", curTick(),
+ unverifiedInst->seqNum, unverifiedInst->pcState());
}
}
+ // Take any faults here
if (fault != NoFault) {
#if FULL_SYSTEM
fault->invoke(tc, curStaticInst);
willChangePC = true;
- newPC = thread->readPC();
- DPRINTF(Checker, "Fault, PC is now %#x\n", newPC);
+ newPCState = thread->pcState();
+ DPRINTF(Checker, "Fault, PC is now %s\n", newPCState);
+ curMacroStaticInst = StaticInst::nullStaticInstPtr;
#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
-
+ advancePC(fault);
}
#if FULL_SYSTEM
@@ -262,14 +419,14 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
Addr oldpc;
int count = 0;
do {
- oldpc = thread->readPC();
+ oldpc = thread->instAddr();
system->pcEventQueue.service(tc);
count++;
- } while (oldpc != thread->readPC());
+ } while (oldpc != thread->instAddr());
if (count > 1) {
willChangePC = true;
- newPC = thread->readPC();
- DPRINTF(Checker, "PC Event, PC is now %#x\n", newPC);
+ newPCState = thread->pcState();
+ DPRINTF(Checker, "PC Event, PC is now %s\n", newPCState);
}
#endif
@@ -277,17 +434,13 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
// 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();
+ unverifiedInst = NULL;
+ unverifiedInst = instList.front();
instList.pop_front();
} else {
break;
@@ -296,26 +449,26 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst)
unverifiedInst = NULL;
}
-template <class DynInstPtr>
+template <class Impl>
void
-Checker<DynInstPtr>::switchOut()
+Checker<Impl>::switchOut()
{
instList.clear();
}
-template <class DynInstPtr>
+template <class Impl>
void
-Checker<DynInstPtr>::takeOverFrom(BaseCPU *oldCPU)
+Checker<Impl>::takeOverFrom(BaseCPU *oldCPU)
{
}
-template <class DynInstPtr>
+template <class Impl>
void
-Checker<DynInstPtr>::validateInst(DynInstPtr &inst)
+Checker<Impl>::validateInst(DynInstPtr &inst)
{
- if (inst->readPC() != thread->readPC()) {
- warn("%lli: PCs do not match! Inst: %#x, checker: %#x",
- curTick(), inst->readPC(), thread->readPC());
+ if (inst->instAddr() != thread->instAddr()) {
+ warn("%lli: PCs do not match! Inst: %s, checker: %s",
+ curTick(), inst->pcState(), thread->pcState());
if (changedPC) {
warn("%lli: Changed PCs recently, may not be an error",
curTick());
@@ -327,51 +480,70 @@ Checker<DynInstPtr>::validateInst(DynInstPtr &inst)
MachInst mi = static_cast<MachInst>(inst->staticInst->machInst);
if (mi != machInst) {
- warn("%lli: Binary instructions do not match! Inst: %#x, "
+ panic("%lli: Binary instructions do not match! Inst: %#x, "
"checker: %#x",
curTick(), mi, machInst);
handleError(inst);
}
}
-template <class DynInstPtr>
+template <class Impl>
void
-Checker<DynInstPtr>::validateExecution(DynInstPtr &inst)
+Checker<Impl>::validateExecution(DynInstPtr &inst)
{
+ uint64_t checker_val;
+ uint64_t inst_val;
+ int idx = -1;
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 (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, 0, idx);
+ } else if (inst->numDestRegs() > 0 && !result.empty()) {
+ DPRINTF(Checker, "Dest regs %d, number of checker dest regs %d\n",
+ inst->numDestRegs(), result.size());
+ for (int i = 0; i < inst->numDestRegs() && !result.empty(); i++) {
+ result.front().get(checker_val);
+ result.pop();
+ inst_val = 0;
+ inst->template popResult<uint64_t>(inst_val);
+ if (checker_val != inst_val) {
+ result_mismatch = true;
+ idx = i;
+ break;
+ }
}
- }
+ } // Checker CPU checks all the saved results in the dyninst passed by
+ // the cpu model being checked against the saved results present in
+ // the static inst executed in the Checker. Sometimes the number
+ // of saved results differs between the dyninst and static inst, but
+ // this is ok and not a bug. May be worthwhile to try and correct this.
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);
+ curTick(), inst_val, checker_val);
// 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.
+ // The load/store queue in Detailed CPU can also cause problems
+ // if load/store forwarding is allowed.
if (inst->isLoad() && warnOnlyOnLoadError) {
- copyResult(inst);
+ copyResult(inst, inst_val, idx);
} else {
handleError(inst);
}
}
- if (inst->readNextPC() != thread->readNextPC()) {
+ if (inst->nextInstAddr() != thread->nextInstAddr()) {
warn("%lli: Instruction next PCs do not match! Inst: %#x, "
"checker: %#x",
- curTick(), inst->readNextPC(), thread->readNextPC());
+ curTick(), inst->nextInstAddr(), thread->nextInstAddr());
handleError(inst);
}
@@ -396,53 +568,78 @@ Checker<DynInstPtr>::validateExecution(DynInstPtr &inst)
}
}
-template <class DynInstPtr>
+
+// This function is weird, if it is called it means the Checker and
+// O3 have diverged, so panic is called for now. It may be useful
+// to resynch states and continue if the divergence is a false positive
+template <class Impl>
void
-Checker<DynInstPtr>::validateState()
+Checker<Impl>::validateState()
{
if (updateThisCycle) {
- warn("%lli: Instruction PC %#x results didn't match up, copying all "
- "registers from main CPU", curTick(), unverifiedInst->readPC());
+ // Change this back to warn if divergences end up being false positives
+ panic("%lli: Instruction PC %#x results didn't match up, copying all "
+ "registers from main CPU", curTick(), unverifiedInst->instAddr());
+
+ // Terribly convoluted way to make sure O3 model does not implode
+ bool inSyscall = unverifiedInst->thread->inSyscall;
+ unverifiedInst->thread->inSyscall = true;
+
// Heavy-weight copying of all registers
thread->copyArchRegs(unverifiedInst->tcBase());
+ unverifiedInst->thread->inSyscall = inSyscall;
+
+ // Set curStaticInst to unverifiedInst->staticInst
+ curStaticInst = unverifiedInst->staticInst;
// Also advance the PC. Hopefully no PC-based events happened.
-#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
+ advancePC(NoFault);
updateThisCycle = false;
}
}
-template <class DynInstPtr>
+template <class Impl>
void
-Checker<DynInstPtr>::copyResult(DynInstPtr &inst)
+Checker<Impl>::copyResult(DynInstPtr &inst, uint64_t mismatch_val,
+ int start_idx)
{
- 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->setMiscRegNoEffect(idx, inst->readIntResult());
+ // We've already popped one dest off the queue,
+ // so do the fix-up then start with the next dest reg;
+ if (start_idx >= 0) {
+ RegIndex idx = inst->destRegIdx(start_idx);
+ if (idx < TheISA::FP_Base_DepTag) {
+ thread->setIntReg(idx, mismatch_val);
+ } else if (idx < TheISA::Ctrl_Base_DepTag) {
+ thread->setFloatRegBits(idx, mismatch_val);
+ } else if (idx < TheISA::Max_DepTag) {
+ thread->setMiscReg(idx - TheISA::Ctrl_Base_DepTag,
+ mismatch_val);
+ }
+ }
+ start_idx++;
+ uint64_t res = 0;
+ for (int i = start_idx; i < inst->numDestRegs(); i++) {
+ RegIndex idx = inst->destRegIdx(i);
+ inst->template popResult<uint64_t>(res);
+ if (idx < TheISA::FP_Base_DepTag) {
+ thread->setIntReg(idx, res);
+ } else if (idx < TheISA::Ctrl_Base_DepTag) {
+ thread->setFloatRegBits(idx, res);
+ } else if (idx < TheISA::Max_DepTag) {
+ // Try to get the proper misc register index for ARM here...
+ thread->setMiscReg(idx - TheISA::Ctrl_Base_DepTag, res);
+ } // else Register is out of range...
}
}
-template <class DynInstPtr>
+template <class Impl>
void
-Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst)
+Checker<Impl>::dumpAndExit(DynInstPtr &inst)
{
cprintf("Error detected, instruction information:\n");
- cprintf("PC:%#x, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n"
+ cprintf("PC:%s, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n"
"Completed:%i\n",
- inst->readPC(),
- inst->readNextPC(),
+ inst->pcState(),
+ inst->nextInstAddr(),
inst->seqNum,
inst->threadNumber,
inst->isCompleted());
@@ -450,9 +647,9 @@ Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst)
CheckerCPU::dumpAndExit();
}
-template <class DynInstPtr>
+template <class Impl>
void
-Checker<DynInstPtr>::dumpInsts()
+Checker<Impl>::dumpInsts()
{
int num = 0;
@@ -465,9 +662,9 @@ Checker<DynInstPtr>::dumpInsts()
cprintf("Instruction:%i\n",
num);
- cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
+ cprintf("PC:%s\n[sn:%lli]\n[tid:%i]\n"
"Completed:%i\n",
- (*inst_list_it)->readPC(),
+ (*inst_list_it)->pcState(),
(*inst_list_it)->seqNum,
(*inst_list_it)->threadNumber,
(*inst_list_it)->isCompleted());
diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh
index 4eb3eabfd..d66854b23 100644
--- a/src/cpu/checker/thread_context.hh
+++ b/src/cpu/checker/thread_context.hh
@@ -1,4 +1,16 @@
/*
+ * Copyright (c) 2011 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
@@ -36,6 +48,7 @@
#include "cpu/checker/cpu.hh"
#include "cpu/simple_thread.hh"
#include "cpu/thread_context.hh"
+#include "debug/Checker.hh"
class EndQuiesceEvent;
namespace TheISA {
@@ -77,21 +90,35 @@ class CheckerThreadContext : public ThreadContext
BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); }
- void setCpuId(int id)
+ int cpuId() { return actualTC->cpuId(); }
+
+ int contextId() { return actualTC->contextId(); }
+
+ void setContextId(int id)
{
- actualTC->setCpuId(id);
- checkerTC->setCpuId(id);
+ actualTC->setContextId(id);
+ checkerTC->setContextId(id);
}
- int cpuId() { return actualTC->cpuId(); }
+ /** Returns this thread's ID number. */
+ int threadId() { return actualTC->threadId(); }
+ void setThreadId(int id)
+ {
+ checkerTC->setThreadId(id);
+ actualTC->setThreadId(id);
+ }
TheISA::TLB *getITBPtr() { return actualTC->getITBPtr(); }
TheISA::TLB *getDTBPtr() { return actualTC->getDTBPtr(); }
-#if FULL_SYSTEM
+ BaseCPU *getCheckerCpuPtr() { return checkerTC->getCpuPtr(); }
+
+ Decoder *getDecoderPtr() { return actualTC->getDecoderPtr(); }
+
System *getSystemPtr() { return actualTC->getSystemPtr(); }
+#if FULL_SYSTEM
PhysicalMemory *getPhysMemPtr() { return actualTC->getPhysMemPtr(); }
TheISA::Kernel::Statistics *getKernelStats()
@@ -101,10 +128,23 @@ class CheckerThreadContext : public ThreadContext
FSTranslatingPortProxy* getVirtProxy()
{ return actualTC->getVirtProxy(); }
+
+ //XXX: How does this work now?
+ void initMemProxies(ThreadContext *tc)
+ { actualTC->initMemProxies(tc); }
+
+ void connectMemPorts(ThreadContext *tc)
+ {
+ actualTC->connectMemPorts(tc);
+ }
#else
SETranslatingPortProxy* getMemProxy() { return actualTC->getMemProxy(); }
Process *getProcessPtr() { return actualTC->getProcessPtr(); }
+
+ /** Executes a syscall in SE mode. */
+ void syscall(int64_t callnum)
+ { return actualTC->syscall(callnum); }
#endif
Status status() const { return actualTC->status(); }
@@ -120,10 +160,10 @@ class CheckerThreadContext : public ThreadContext
void activate(int delay = 1) { actualTC->activate(delay); }
/// Set the status to Suspended.
- void suspend() { actualTC->suspend(); }
+ void suspend(int delay) { actualTC->suspend(delay); }
/// Set the status to Halted.
- void halt() { actualTC->halt(); }
+ void halt(int delay) { actualTC->halt(delay); }
#if FULL_SYSTEM
void dumpFuncProfile() { actualTC->dumpFuncProfile(); }
@@ -135,7 +175,11 @@ class CheckerThreadContext : public ThreadContext
checkerTC->copyState(oldContext);
}
- void regStats(const std::string &name) { actualTC->regStats(name); }
+ void regStats(const std::string &name)
+ {
+ actualTC->regStats(name);
+ checkerTC->regStats(name);
+ }
void serialize(std::ostream &os) { actualTC->serialize(os); }
void unserialize(Checkpoint *cp, const std::string &section)
@@ -151,8 +195,6 @@ class CheckerThreadContext : public ThreadContext
void profileSample() { return actualTC->profileSample(); }
#endif
- int threadId() { return actualTC->threadId(); }
-
// @todo: Do I need this?
void copyArchRegs(ThreadContext *tc)
{
@@ -196,32 +238,36 @@ class CheckerThreadContext : public ThreadContext
checkerTC->setFloatRegBits(reg_idx, val);
}
- uint64_t readPC() { return actualTC->readPC(); }
+ /** Reads this thread's PC state. */
+ TheISA::PCState pcState()
+ { return actualTC->pcState(); }
- void setPC(uint64_t val)
+ /** Sets this thread's PC state. */
+ void pcState(const TheISA::PCState &val)
{
- actualTC->setPC(val);
- checkerTC->setPC(val);
+ DPRINTF(Checker, "Changing PC to %s, old PC %s\n",
+ val, checkerTC->pcState());
+ checkerTC->pcState(val);
checkerCPU->recordPCChange(val);
+ return actualTC->pcState(val);
}
- uint64_t readNextPC() { return actualTC->readNextPC(); }
-
- void setNextPC(uint64_t val)
+ void pcStateNoRecord(const TheISA::PCState &val)
{
- actualTC->setNextPC(val);
- checkerTC->setNextPC(val);
- checkerCPU->recordNextPCChange(val);
+ return actualTC->pcState(val);
}
- uint64_t readNextNPC() { return actualTC->readNextNPC(); }
+ /** Reads this thread's PC. */
+ Addr instAddr()
+ { return actualTC->instAddr(); }
- void setNextNPC(uint64_t val)
- {
- actualTC->setNextNPC(val);
- checkerTC->setNextNPC(val);
- checkerCPU->recordNextPCChange(val);
- }
+ /** Reads this thread's next PC. */
+ Addr nextInstAddr()
+ { return actualTC->nextInstAddr(); }
+
+ /** Reads this thread's next PC. */
+ MicroPC microPC()
+ { return actualTC->microPC(); }
MiscReg readMiscRegNoEffect(int misc_reg)
{ return actualTC->readMiscRegNoEffect(misc_reg); }
@@ -231,22 +277,28 @@ class CheckerThreadContext : public ThreadContext
void setMiscRegNoEffect(int misc_reg, const MiscReg &val)
{
+ DPRINTF(Checker, "Setting misc reg with no effect: %d to both Checker"
+ " and O3..\n", misc_reg);
checkerTC->setMiscRegNoEffect(misc_reg, val);
actualTC->setMiscRegNoEffect(misc_reg, val);
}
void setMiscReg(int misc_reg, const MiscReg &val)
{
+ DPRINTF(Checker, "Setting misc reg with effect: %d to both Checker"
+ " and O3..\n", misc_reg);
checkerTC->setMiscReg(misc_reg, val);
actualTC->setMiscReg(misc_reg, val);
}
+ int flattenIntIndex(int reg) { return actualTC->flattenIntIndex(reg); }
+ int flattenFloatIndex(int reg) { return actualTC->flattenFloatIndex(reg); }
+
unsigned readStCondFailures()
{ return actualTC->readStCondFailures(); }
void setStCondFailures(unsigned sc_failures)
{
- checkerTC->setStCondFailures(sc_failures);
actualTC->setStCondFailures(sc_failures);
}