diff options
author | Gabe Black <gblack@eecs.umich.edu> | 2012-01-31 22:40:08 -0800 |
---|---|---|
committer | Gabe Black <gblack@eecs.umich.edu> | 2012-01-31 22:40:08 -0800 |
commit | ea8b347dc5d375572d8d19770024ec8be5fd5017 (patch) | |
tree | 56bb75b1f071a749b7e90218d0d6b0e9265657bb /src/cpu/checker | |
parent | e88165a431a90cf7e33e205794caed898ca6fcb1 (diff) | |
parent | 7d4f18770073d968c70cd3ffcdd117f50a6056a2 (diff) | |
download | gem5-ea8b347dc5d375572d8d19770024ec8be5fd5017.tar.xz |
Merge with head, hopefully the last time for this batch.
Diffstat (limited to 'src/cpu/checker')
-rw-r--r-- | src/cpu/checker/cpu.cc | 363 | ||||
-rw-r--r-- | src/cpu/checker/cpu.hh | 128 | ||||
-rw-r--r-- | src/cpu/checker/cpu_impl.hh | 539 | ||||
-rw-r--r-- | src/cpu/checker/thread_context.hh | 106 |
4 files changed, 741 insertions, 395 deletions
diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc index ec4496eb4..c905c62eb 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> @@ -38,10 +51,11 @@ #include "cpu/simple_thread.hh" #include "cpu/static_inst.hh" #include "cpu/thread_context.hh" +#include "params/CheckerCPU.hh" +#include "sim/tlb.hh" using namespace std; -//The CheckerCPU does alpha only -using namespace AlphaISA; +using namespace TheISA; void CheckerCPU::init() @@ -52,6 +66,8 @@ CheckerCPU::CheckerCPU(Params *p) : BaseCPU(p), thread(NULL), tc(NULL) { memReq = NULL; + curStaticInst = NULL; + curMacroStaticInst = NULL; numInst = 0; startNumInst = 0; @@ -66,13 +82,14 @@ CheckerCPU::CheckerCPU(Params *p) itb = p->itb; dtb = p->dtb; systemPtr = NULL; - 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); - result.integer = 0; + updateOnError = true; } CheckerCPU::~CheckerCPU() @@ -107,216 +124,224 @@ 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 §ion) { -/* - 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; } - 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 *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 a3e95137d..6f5125625 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 "cpu/base.hh" @@ -42,6 +55,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 @@ -52,12 +67,6 @@ namespace TheISA template <class> class BaseDynInst; -class CheckerCPUParams; -class Checkpoint; -class MemInterface; -class PhysicalMemory; -class Process; -class Processor; class ThreadContext; class Request; @@ -90,11 +99,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); @@ -127,19 +136,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; @@ -147,6 +162,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; @@ -159,12 +177,6 @@ class CheckerCPU : public BaseCPU virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); - 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"); } @@ -198,17 +210,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, @@ -216,12 +236,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) { @@ -235,7 +269,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); } @@ -246,8 +279,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) { @@ -265,8 +315,17 @@ 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) + {} + ///////////////////////////////////////////////////// + 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) { } @@ -277,7 +336,8 @@ class CheckerCPU : public BaseCPU dumpAndExit(); } - bool checkFlags(Request *req); + bool checkFlags(Request *unverified_req, Addr vAddr, + Addr pAddr, int flags); void dumpAndExit(); @@ -290,7 +350,7 @@ class CheckerCPU : public BaseCPU bool changedPC; bool willChangePC; - uint64_t newPC; + TheISA::PCState newPCState; bool changedNextPC; bool exitOnError; bool updateOnError; @@ -305,24 +365,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) @@ -339,6 +406,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 7d8cc8a19..7a99feb06 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> @@ -34,25 +47,93 @@ #include "arch/vtophys.hh" #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/full_system.hh" #include "sim/sim_object.hh" #include "sim/stats.hh" 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(); + } + + 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 DynInstPtr> +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 @@ -60,8 +141,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; } @@ -75,8 +156,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; } @@ -91,17 +172,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; @@ -116,15 +209,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; @@ -133,118 +226,186 @@ 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); -#define IFETCH_FLAGS(pc) (FullSystem ? 0 : ((pc) & 1) ? PHYSICAL : 0) - - 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); - - icachePort->sendFunctional(pkt); - - delete pkt; - - // keep an instruction count - numInst++; + unverifiedFault = unverifiedInst->getFault(); - // 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) { 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; } 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 (FullSystem) { @@ -254,14 +415,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); } } @@ -269,17 +430,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; @@ -288,26 +445,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()); @@ -319,51 +476,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); } @@ -388,53 +564,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()); @@ -442,9 +643,9 @@ Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst) CheckerCPU::dumpAndExit(); } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::dumpInsts() +Checker<Impl>::dumpInsts() { int num = 0; @@ -457,9 +658,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 178ded80e..d21de9f53 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,18 +90,32 @@ 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(); } + BaseCPU *getCheckerCpuPtr() { return checkerTC->getCpuPtr(); } + + Decoder *getDecoderPtr() { return actualTC->getDecoderPtr(); } + System *getSystemPtr() { return actualTC->getSystemPtr(); } PhysicalMemory *getPhysMemPtr() { return actualTC->getPhysMemPtr(); } @@ -103,8 +130,21 @@ 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); + } + 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) @@ -118,10 +158,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); } void dumpFuncProfile() { actualTC->dumpFuncProfile(); } @@ -131,7 +171,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 §ion) @@ -145,8 +189,6 @@ class CheckerThreadContext : public ThreadContext void profileClear() { return actualTC->profileClear(); } void profileSample() { return actualTC->profileSample(); } - int threadId() { return actualTC->threadId(); } - // @todo: Do I need this? void copyArchRegs(ThreadContext *tc) { @@ -190,32 +232,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); } @@ -225,22 +271,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); } |