Adding gem5 source to svn.

git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1819 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
friemel
2012-10-24 19:18:57 +00:00
parent f7ff71bd46
commit b41eec3f65
3222 changed files with 658579 additions and 1 deletions

View File

@ -0,0 +1,35 @@
# -*- mode:python -*-
# Copyright (c) 2003-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: Steve Reinhardt
Import('*')
CpuModel('CheckerCPU', 'checker_cpu_exec.cc',
'#include "cpu/checker/cpu.hh"',
{ 'CPU_exec_context': 'CheckerCPU' })

View File

@ -0,0 +1,353 @@
/*
* 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.
*
* 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
* Geoffrey Blake
*/
#include <list>
#include <string>
#include "arch/kernel_stats.hh"
#include "arch/vtophys.hh"
#include "cpu/checker/cpu.hh"
#include "cpu/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "params/CheckerCPU.hh"
#include "sim/full_system.hh"
#include "sim/tlb.hh"
using namespace std;
using namespace TheISA;
void
CheckerCPU::init()
{
masterId = systemPtr->getMasterId(name());
}
CheckerCPU::CheckerCPU(Params *p)
: BaseCPU(p, true), thread(NULL), tc(NULL)
{
memReq = NULL;
curStaticInst = NULL;
curMacroStaticInst = NULL;
numInst = 0;
startNumInst = 0;
numLoad = 0;
startNumLoad = 0;
youngestSN = 0;
changedPC = willChangePC = changedNextPC = false;
exitOnError = p->exitOnError;
warnOnlyOnLoadError = p->warnOnlyOnLoadError;
itb = p->itb;
dtb = p->dtb;
systemPtr = NULL;
workload = p->workload;
thread = NULL;
updateOnError = true;
}
CheckerCPU::~CheckerCPU()
{
}
void
CheckerCPU::setSystem(System *system)
{
systemPtr = system;
if (FullSystem) {
thread = new SimpleThread(this, 0, systemPtr, itb, dtb, false);
} else {
thread = new SimpleThread(this, 0, systemPtr,
workload.size() ? workload[0] : NULL,
itb, dtb);
}
tc = thread->getTC();
threadContexts.push_back(tc);
thread->kernelStats = NULL;
// Thread should never be null after this
assert(thread != NULL);
}
void
CheckerCPU::setIcachePort(CpuPort *icache_port)
{
icachePort = icache_port;
}
void
CheckerCPU::setDcachePort(CpuPort *dcache_port)
{
dcachePort = dcache_port;
}
void
CheckerCPU::serialize(ostream &os)
{
}
void
CheckerCPU::unserialize(Checkpoint *cp, const string &section)
{
}
Fault
CheckerCPU::readMem(Addr addr, uint8_t *data, unsigned size, unsigned flags)
{
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, masterId, 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;
}
// Now do the access
if (fault == NoFault &&
!memReq->getFlags().isSet(Request::NO_ACCESS)) {
PacketPtr pkt = new Packet(memReq,
memReq->isLLSC() ?
MemCmd::LoadLockedReq :
MemCmd::ReadReq);
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;
}
if (fault != NoFault) {
if (memReq->isPrefetch()) {
fault = NoFault;
}
delete memReq;
memReq = NULL;
break;
}
if (memReq != NULL) {
delete memReq;
}
//If we don't need to access a second cache line, stop now.
if (secondAddr <= addr)
{
break;
}
// Setup for accessing next cache line
data += size;
unverifiedMemData += size;
size = addr + fullSize - secondAddr;
addr = secondAddr;
}
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 fault;
}
Fault
CheckerCPU::writeMem(uint8_t *data, unsigned size,
Addr addr, unsigned flags, uint64_t *res)
{
Fault fault = NoFault;
bool checked_flags = false;
bool flags_match = true;
Addr pAddr = 0x0;
unsigned blockSize = dcachePort->peerBlockSize();
int fullSize = size;
Addr secondAddr = roundDown(addr + size - 1, blockSize);
if (secondAddr > addr)
size = secondAddr - addr;
// Need to account for a multiple access like Atomic and Timing CPUs
while (1) {
memReq = new Request();
memReq->setVirt(0, addr, size, flags, masterId, thread->pcState().instAddr());
// translate to physical address
fault = dtb->translateFunctional(memReq, tc, BaseTLB::Write);
if (!checked_flags && fault == NoFault && unverifiedReq) {
flags_match = checkFlags(unverifiedReq, memReq->getVaddr(),
memReq->getPaddr(), memReq->getFlags());
pAddr = memReq->getPaddr();
checked_flags = true;
}
/*
* 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.
*/
bool was_prefetch = memReq->isPrefetch();
delete memReq;
//If we don't need to access a second cache line, stop now.
if (fault != NoFault || secondAddr <= addr)
{
if (fault != NoFault && was_prefetch) {
fault = NoFault;
}
break;
}
//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;
}
Addr
CheckerCPU::dbg_vtophys(Addr addr)
{
return vtophys(tc, addr);
}
/**
* Checks if the flags set by the Checker and Checkee match.
*/
bool
CheckerCPU::checkFlags(Request *unverified_req, Addr vAddr,
Addr pAddr, int flags)
{
Addr unverifiedVAddr = unverified_req->getVaddr();
Addr unverifiedPAddr = unverified_req->getPaddr();
int unverifiedFlags = unverified_req->getFlags();
if (unverifiedVAddr != vAddr ||
unverifiedPAddr != pAddr ||
unverifiedFlags != flags) {
return false;
}
return true;
}
void
CheckerCPU::dumpAndExit()
{
warn("%lli: Checker PC:%s",
curTick(), thread->pcState());
panic("Checker found an error!");
}

View File

@ -0,0 +1,438 @@
/*
* 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.
*
* 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 <map>
#include <queue>
#include "arch/types.hh"
#include "base/statistics.hh"
#include "cpu/base.hh"
#include "cpu/base_dyn_inst.hh"
#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
namespace TheISA
{
class TLB;
}
template <class>
class BaseDynInst;
class ThreadContext;
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;
/** id attached to all issued requests */
MasterID masterId;
public:
virtual void init();
public:
typedef CheckerCPUParams Params;
const Params *params() const
{ return reinterpret_cast<const Params *>(_params); }
CheckerCPU(Params *p);
virtual ~CheckerCPU();
std::vector<Process*> workload;
void setSystem(System *system);
System *systemPtr;
void setIcachePort(CpuPort *icache_port);
CpuPort *icachePort;
void setDcachePort(CpuPort *dcache_port);
CpuPort *dcachePort;
CpuPort &getDataPort()
{
panic("Not supported on checker!");
return *dcachePort;
}
CpuPort &getInstPort()
{
panic("Not supported on checker!");
return *icachePort;
}
public:
// Primary thread being run.
SimpleThread *thread;
ThreadContext *tc;
TheISA::TLB *itb;
TheISA::TLB *dtb;
Addr dbg_vtophys(Addr addr);
union Result {
uint64_t integer;
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; }
};
// ISAs like ARM can have multiple destination registers to check,
// keep them all in a std::queue
std::queue<Result> result;
// Pointer to the one memory request.
RequestPtr memReq;
StaticInstPtr curStaticInst;
StaticInstPtr curMacroStaticInst;
// number of simulated instructions
Counter numInst;
Counter startNumInst;
std::queue<int> miscRegIdxs;
TheISA::TLB* getITBPtr() { return itb; }
TheISA::TLB* getDTBPtr() { return dtb; }
virtual Counter totalInsts() const
{
return 0;
}
virtual Counter totalOps() 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);
// 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"); }
// 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 readIntRegOperand(const StaticInst *si, int idx)
{
return thread->readIntReg(si->srcRegIdx(idx));
}
FloatReg readFloatRegOperand(const StaticInst *si, int idx)
{
int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag;
return thread->readFloatReg(reg_idx);
}
FloatRegBits readFloatRegOperandBits(const StaticInst *si, int idx)
{
int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag;
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);
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);
setResult<double>(val);
}
void setFloatRegOperandBits(const StaticInst *si, int idx,
FloatRegBits val)
{
int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag;
thread->setFloatRegBits(reg_idx, val);
setResult<uint64_t>(val);
}
bool readPredicate() { return thread->readPredicate(); }
void setPredicate(bool val)
{
thread->setPredicate(val);
}
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)
{
return thread->readMiscRegNoEffect(misc_reg);
}
MiscReg readMiscReg(int misc_reg)
{
return thread->readMiscReg(misc_reg);
}
void setMiscRegNoEffect(int misc_reg, const MiscReg &val)
{
miscRegIdxs.push(misc_reg);
return thread->setMiscRegNoEffect(misc_reg, val);
}
void setMiscReg(int misc_reg, const MiscReg &val)
{
miscRegIdxs.push(misc_reg);
return thread->setMiscReg(misc_reg, val);
}
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);
}
#if THE_ISA == MIPS_ISA
uint64_t readRegOtherThread(int misc_reg)
{
panic("MIPS MT not defined for CheckerCPU.\n");
return 0;
}
void setRegOtherThread(int misc_reg, const TheISA::MiscReg &val)
{
panic("MIPS MT not defined for CheckerCPU.\n");
}
#endif
/////////////////////////////////////////
void recordPCChange(const TheISA::PCState &val)
{
changedPC = true;
newPCState = val;
}
void demapPage(Addr vaddr, uint64_t asn)
{
this->itb->demapPage(vaddr, asn);
this->dtb->demapPage(vaddr, asn);
}
void demapInstPage(Addr vaddr, uint64_t asn)
{
this->itb->demapPage(vaddr, asn);
}
void demapDataPage(Addr vaddr, uint64_t asn)
{
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)
{}
/////////////////////////////////////////////////////
Fault hwrei() { return thread->hwrei(); }
bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); }
void wakeup() { }
// 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) { }
void handleError()
{
if (exitOnError)
dumpAndExit();
}
bool checkFlags(Request *unverified_req, Addr vAddr,
Addr pAddr, int flags);
void dumpAndExit();
ThreadContext *tcBase() { return tc; }
SimpleThread *threadBase() { return thread; }
Result unverifiedResult;
Request *unverifiedReq;
uint8_t *unverifiedMemData;
bool changedPC;
bool willChangePC;
TheISA::PCState newPCState;
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 Impl>
class Checker : public CheckerCPU
{
private:
typedef typename Impl::DynInstPtr DynInstPtr;
public:
Checker(Params *p)
: CheckerCPU(p), updateThisCycle(false), unverifiedInst(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, uint64_t mismatch_val, int start_idx);
void handlePendingInt();
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__

View File

@ -0,0 +1,670 @@
/*
* 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.
*
* 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
* Geoffrey Blake
*/
#include <list>
#include <string>
#include "arch/isa_traits.hh"
#include "arch/vtophys.hh"
#include "base/refcnt.hh"
#include "config/the_isa.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/full_system.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
using namespace std;
using namespace TheISA;
template <class Impl>
void
Checker<Impl>::advancePC(Fault fault)
{
if (fault != NoFault) {
curMacroStaticInst = StaticInst::nullStaticInstPtr;
fault->invoke(tc, curStaticInst);
thread->decoder.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();
}
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;
thread->decoder.reset();
curMacroStaticInst = StaticInst::nullStaticInstPtr;
}
template <class Impl>
void
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
// 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:%s to list\n",
completed_inst->seqNum, completed_inst->pcState());
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:%s to list\n",
completed_inst->seqNum, completed_inst->pcState());
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;
}
}
}
// 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:%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;
// maintain $r0 semantics
thread->setIntReg(ZeroReg, 0);
#if THE_ISA == ALPHA_ISA
thread->setFloatReg(ZeroReg, 0.0);
#endif
// 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 %s\n",
thread->pcState());
if (willChangePC) {
if (newPCState == thread->pcState()) {
DPRINTF(Checker, "Changed PC matches expected PC\n");
} else {
warn("%lli: Changed PC does not match expected PC, "
"changed: %s, expected: %s",
curTick(), thread->pcState(), newPCState);
CheckerCPU::handleError();
}
willChangePC = false;
}
changedPC = false;
}
if (changedNextPC) {
DPRINTF(Checker, "Changed NextPC recently to %#x\n",
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;
MachInst machInst;
// 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,
masterId,
fetch_PC, thread->contextId(),
unverifiedInst->threadNumber);
memReq->setVirt(0, fetch_PC, sizeof(MachInst),
Request::INST_FETCH, masterId, 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);
pkt->dataStatic(&machInst);
icachePort->sendFunctional(pkt);
machInst = gtoh(machInst);
delete memReq;
delete pkt;
}
}
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
thread->decoder.setTC(thread->getTC());
//If more fetch data is needed, pass it in.
Addr fetchPC = (pcState.instAddr() & PCMask) + fetchOffset;
thread->decoder.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 (thread->decoder.instReady()) {
fetchDone = true;
instPtr = thread->decoder.decode(pcState);
thread->pcState(pcState);
} 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 decoder on Checker
thread->decoder.reset();
// Check Checker and CPU get same instruction, and record
// any faults the CPU may have had.
Fault unverifiedFault;
if (fault == NoFault) {
unverifiedFault = unverifiedInst->getFault();
// Checks that the instruction matches what we expected it to be.
// Checks both the machine instruction and the PC.
validateInst(unverifiedInst);
}
// 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;
}
}
if (fault == NoFault && unverifiedFault == NoFault) {
thread->funcExeInst++;
// Checks to make sure instrution results are correct.
validateExecution(unverifiedInst);
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 (FullSystem) {
fault->invoke(tc, curStaticInst);
willChangePC = true;
newPCState = thread->pcState();
DPRINTF(Checker, "Fault, PC is now %s\n", newPCState);
curMacroStaticInst = StaticInst::nullStaticInstPtr;
}
} else {
advancePC(fault);
}
if (FullSystem) {
// @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->instAddr();
system->pcEventQueue.service(tc);
count++;
} while (oldpc != thread->instAddr());
if (count > 1) {
willChangePC = true;
newPCState = thread->pcState();
DPRINTF(Checker, "PC Event, PC is now %s\n", newPCState);
}
}
// @todo: Optionally can check all registers. (Or just those
// that have been modified).
validateState();
// Continue verifying instructions if there's another completed
// instruction waiting to be verified.
if (instList.empty()) {
break;
} else if (instList.front()->isCompleted()) {
unverifiedInst = NULL;
unverifiedInst = instList.front();
instList.pop_front();
} else {
break;
}
}
unverifiedInst = NULL;
}
template <class Impl>
void
Checker<Impl>::switchOut()
{
instList.clear();
}
template <class Impl>
void
Checker<Impl>::takeOverFrom(BaseCPU *oldCPU)
{
}
template <class Impl>
void
Checker<Impl>::validateInst(DynInstPtr &inst)
{
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());
} else {
handleError(inst);
}
}
if (curStaticInst != inst->staticInst) {
warn("%lli: StaticInstPtrs don't match. (%s, %s).\n", curTick(),
curStaticInst->getName(), inst->staticInst->getName());
}
}
template <class Impl>
void
Checker<Impl>::validateExecution(DynInstPtr &inst)
{
uint64_t checker_val;
uint64_t inst_val;
int idx = -1;
bool result_mismatch = false;
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_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, inst_val, idx);
} else {
handleError(inst);
}
}
if (inst->nextInstAddr() != thread->nextInstAddr()) {
warn("%lli: Instruction next PCs do not match! Inst: %#x, "
"checker: %#x",
curTick(), inst->nextInstAddr(), thread->nextInstAddr());
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()->readMiscRegNoEffect(misc_reg_idx) !=
thread->readMiscRegNoEffect(misc_reg_idx)) {
warn("%lli: Misc reg idx %i (side effect) does not match! "
"Inst: %#x, checker: %#x",
curTick(), misc_reg_idx,
inst->tcBase()->readMiscRegNoEffect(misc_reg_idx),
thread->readMiscRegNoEffect(misc_reg_idx));
handleError(inst);
}
}
}
// 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<Impl>::validateState()
{
if (updateThisCycle) {
// 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.
advancePC(NoFault);
updateThisCycle = false;
}
}
template <class Impl>
void
Checker<Impl>::copyResult(DynInstPtr &inst, uint64_t mismatch_val,
int start_idx)
{
// 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 Impl>
void
Checker<Impl>::dumpAndExit(DynInstPtr &inst)
{
cprintf("Error detected, instruction information:\n");
cprintf("PC:%s, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n"
"Completed:%i\n",
inst->pcState(),
inst->nextInstAddr(),
inst->seqNum,
inst->threadNumber,
inst->isCompleted());
inst->dump();
CheckerCPU::dumpAndExit();
}
template <class Impl>
void
Checker<Impl>::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:%s\n[sn:%lli]\n[tid:%i]\n"
"Completed:%i\n",
(*inst_list_it)->pcState(),
(*inst_list_it)->seqNum,
(*inst_list_it)->threadNumber,
(*inst_list_it)->isCompleted());
cprintf("\n");
inst_list_it--;
++num;
}
}

View File

@ -0,0 +1,306 @@
/*
* 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.
*
* 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 "config/the_isa.hh"
#include "cpu/checker/cpu.hh"
#include "cpu/simple_thread.hh"
#include "cpu/thread_context.hh"
#include "debug/Checker.hh"
class EndQuiesceEvent;
namespace TheISA {
namespace Kernel {
class Statistics;
};
class Decoder;
};
/**
* 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(); }
int cpuId() { return actualTC->cpuId(); }
int contextId() { return actualTC->contextId(); }
void setContextId(int id)
{
actualTC->setContextId(id);
checkerTC->setContextId(id);
}
/** 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(); }
CheckerCPU *getCheckerCpuPtr()
{
return checkerCPU;
}
TheISA::Decoder *getDecoderPtr() { return actualTC->getDecoderPtr(); }
System *getSystemPtr() { return actualTC->getSystemPtr(); }
TheISA::Kernel::Statistics *getKernelStats()
{ return actualTC->getKernelStats(); }
Process *getProcessPtr() { return actualTC->getProcessPtr(); }
PortProxy &getPhysProxy() { return actualTC->getPhysProxy(); }
FSTranslatingPortProxy &getVirtProxy()
{ return actualTC->getVirtProxy(); }
void initMemProxies(ThreadContext *tc)
{ actualTC->initMemProxies(tc); }
void connectMemPorts(ThreadContext *tc)
{
actualTC->connectMemPorts(tc);
}
SETranslatingPortProxy &getMemProxy() { return actualTC->getMemProxy(); }
/** Executes a syscall in SE mode. */
void syscall(int64_t callnum)
{ return actualTC->syscall(callnum); }
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(int delay) { actualTC->suspend(delay); }
/// Set the status to Halted.
void halt(int delay) { actualTC->halt(delay); }
void dumpFuncProfile() { actualTC->dumpFuncProfile(); }
void takeOverFrom(ThreadContext *oldContext)
{
actualTC->takeOverFrom(oldContext);
checkerTC->copyState(oldContext);
}
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)
{ actualTC->unserialize(cp, section); }
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(); }
// @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)
{ return actualTC->readFloatReg(reg_idx); }
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)
{
actualTC->setFloatReg(reg_idx, val);
checkerTC->setFloatReg(reg_idx, val);
}
void setFloatRegBits(int reg_idx, FloatRegBits val)
{
actualTC->setFloatRegBits(reg_idx, val);
checkerTC->setFloatRegBits(reg_idx, val);
}
/** Reads this thread's PC state. */
TheISA::PCState pcState()
{ return actualTC->pcState(); }
/** Sets this thread's PC state. */
void pcState(const TheISA::PCState &val)
{
DPRINTF(Checker, "Changing PC to %s, old PC %s\n",
val, checkerTC->pcState());
checkerTC->pcState(val);
checkerCPU->recordPCChange(val);
return actualTC->pcState(val);
}
void pcStateNoRecord(const TheISA::PCState &val)
{
return actualTC->pcState(val);
}
/** Reads this thread's PC. */
Addr instAddr()
{ return actualTC->instAddr(); }
/** 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); }
MiscReg readMiscReg(int misc_reg)
{ return actualTC->readMiscReg(misc_reg); }
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)
{
actualTC->setStCondFailures(sc_failures);
}
// @todo: Fix this!
bool misspeculating() { return actualTC->misspeculating(); }
Counter readFuncExeInst() { return actualTC->readFuncExeInst(); }
};
#endif // __CPU_CHECKER_EXEC_CONTEXT_HH__