diff options
Diffstat (limited to 'src/cpu')
29 files changed, 1226 insertions, 459 deletions
diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py index 6800b4c91..77ba35b19 100644 --- a/src/cpu/BaseCPU.py +++ b/src/cpu/BaseCPU.py @@ -178,7 +178,6 @@ class BaseCPU(MemObject): self.connectUncachedPorts(uncached_bus) def addPrivateSplitL1Caches(self, ic, dc, iwc = None, dwc = None): - assert(len(self._cached_ports) < 7) self.icache = ic self.dcache = dc self.icache_port = ic.cpu_side @@ -195,6 +194,11 @@ class BaseCPU(MemObject): "dtb_walker_cache.mem_side"] else: self._cached_ports += ["itb.walker.port", "dtb.walker.port"] + # Checker doesn't need its own tlb caches because it does + # functional accesses only + if buildEnv['USE_CHECKER']: + self._cached_ports += ["checker.itb.walker.port", \ + "checker.dtb.walker.port"] def addTwoLevelCacheHierarchy(self, ic, dc, l2c, iwc = None, dwc = None): self.addPrivateSplitL1Caches(ic, dc, iwc, dwc) diff --git a/src/cpu/CheckerCPU.py b/src/cpu/CheckerCPU.py index 132254413..c144c4678 100644 --- a/src/cpu/CheckerCPU.py +++ b/src/cpu/CheckerCPU.py @@ -35,7 +35,7 @@ class CheckerCPU(BaseCPU): exitOnError = Param.Bool(False, "Exit on an error") updateOnError = Param.Bool(False, "Update the checker with the main CPU's state on an error") - warnOnlyOnLoadError = Param.Bool(False, + warnOnlyOnLoadError = Param.Bool(True, "If a load result is incorrect, only print a warning and do not exit") function_trace = Param.Bool(False, "Enable function trace") function_trace_start = Param.Tick(0, "Cycle to start function trace") diff --git a/src/cpu/DummyChecker.py b/src/cpu/DummyChecker.py new file mode 100644 index 000000000..3c276e1d2 --- /dev/null +++ b/src/cpu/DummyChecker.py @@ -0,0 +1,42 @@ +# Copyright (c) 2010-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. +# +# 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: Geoffrey Blake + +from m5.params import * +from BaseCPU import BaseCPU + +class DummyChecker(BaseCPU): + type = 'DummyChecker' diff --git a/src/cpu/SConscript b/src/cpu/SConscript index a1074cb8b..5b276380e 100644 --- a/src/cpu/SConscript +++ b/src/cpu/SConscript @@ -137,7 +137,9 @@ if env['FULL_SYSTEM']: Source('legiontrace.cc') if env['USE_CHECKER']: + SimObject('DummyChecker.py') Source('checker/cpu.cc') + Source('dummy_checker_builder.cc') DebugFlag('Checker') checker_supports = False for i in CheckerSupportedCPUList: diff --git a/src/cpu/base.cc b/src/cpu/base.cc index 370be7ee1..fe840cd35 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -53,6 +53,7 @@ #include "base/misc.hh" #include "base/output.hh" #include "base/trace.hh" +#include "config/use_checker.hh" #include "cpu/base.hh" #include "cpu/cpuevent.hh" #include "cpu/profile.hh" @@ -64,6 +65,10 @@ #include "sim/sim_exit.hh" #include "sim/system.hh" +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + // Hack #include "sim/stat_control.hh" @@ -219,7 +224,10 @@ BaseCPU::BaseCPU(Params *p) } } #if FULL_SYSTEM - interrupts->setCPU(this); + // Check if CPU model has interrupts connected. The CheckerCPU + // cannot take interrupts directly for example. + if (interrupts) + interrupts->setCPU(this); profileEvent = NULL; if (params()->profile) @@ -408,6 +416,35 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU, Port *ic, Port *dc) new_dtb_port->setPeer(peer); peer->setPeer(new_dtb_port); } + +#if USE_CHECKER + Port *old_checker_itb_port, *old_checker_dtb_port; + Port *new_checker_itb_port, *new_checker_dtb_port; + + CheckerCPU *oldChecker = + dynamic_cast<CheckerCPU*>(oldTC->getCheckerCpuPtr()); + CheckerCPU *newChecker = + dynamic_cast<CheckerCPU*>(newTC->getCheckerCpuPtr()); + old_checker_itb_port = oldChecker->getITBPtr()->getPort(); + old_checker_dtb_port = oldChecker->getDTBPtr()->getPort(); + new_checker_itb_port = newChecker->getITBPtr()->getPort(); + new_checker_dtb_port = newChecker->getDTBPtr()->getPort(); + + // Move over any table walker ports if they exist for checker + if (new_checker_itb_port && !new_checker_itb_port->isConnected()) { + assert(old_checker_itb_port); + Port *peer = old_checker_itb_port->getPeer();; + new_checker_itb_port->setPeer(peer); + peer->setPeer(new_checker_itb_port); + } + if (new_checker_dtb_port && !new_checker_dtb_port->isConnected()) { + assert(old_checker_dtb_port); + Port *peer = old_checker_dtb_port->getPeer();; + new_checker_dtb_port->setPeer(peer); + peer->setPeer(new_checker_dtb_port); + } +#endif + } #if FULL_SYSTEM diff --git a/src/cpu/base_dyn_inst.hh b/src/cpu/base_dyn_inst.hh index 5719fc84d..14889a206 100644 --- a/src/cpu/base_dyn_inst.hh +++ b/src/cpu/base_dyn_inst.hh @@ -48,6 +48,7 @@ #include <bitset> #include <list> #include <string> +#include <queue> #include "arch/faults.hh" #include "arch/utility.hh" @@ -55,6 +56,7 @@ #include "base/trace.hh" #include "config/full_system.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/o3/comm.hh" #include "cpu/exetrace.hh" #include "cpu/inst_seq.hh" @@ -176,6 +178,11 @@ class BaseDynInst : public FastAlloc, public RefCounted RequestPtr savedSreqLow; RequestPtr savedSreqHigh; +#if USE_CHECKER + // Need a copy of main request pointer to verify on writes. + RequestPtr reqToVerify; +#endif //USE_CHECKER + /** @todo: Consider making this private. */ public: /** The sequence number of the instruction. */ @@ -248,14 +255,17 @@ class BaseDynInst : public FastAlloc, public RefCounted 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; } }; - /** The result of the instruction; assumes for now that there's only one - * destination register. + /** The result of the instruction; assumes an instruction can have many + * destination registers. */ - Result instResult; + std::queue<Result> instResult; /** Records changes to result? */ bool recordResult; @@ -558,56 +568,68 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns the logical register index of the i'th source register. */ RegIndex srcRegIdx(int i) const { return staticInst->srcRegIdx(i); } - /** Returns the result of an integer instruction. */ - uint64_t readIntResult() { return instResult.integer; } + /** Pops a result off the instResult queue */ + template <class T> + void popResult(T& t) + { + if (!instResult.empty()) { + instResult.front().get(t); + instResult.pop(); + } + } - /** Returns the result of a floating point instruction. */ - float readFloatResult() { return (float)instResult.dbl; } + /** Read the most recent result stored by this instruction */ + template <class T> + void readResult(T& t) + { + instResult.back().get(t); + } - /** Returns the result of a floating point (double) instruction. */ - double readDoubleResult() { return instResult.dbl; } + /** Pushes a result onto the instResult queue */ + template <class T> + void setResult(T t) + { + if (recordResult) { + Result instRes; + instRes.set(t); + instResult.push(instRes); + } + } /** Records an integer register being set to a value. */ void setIntRegOperand(const StaticInst *si, int idx, uint64_t val) { - if (recordResult) - instResult.integer = val; + setResult<uint64_t>(val); } /** Records an fp register being set to a value. */ void setFloatRegOperand(const StaticInst *si, int idx, FloatReg val, int width) { - if (recordResult) { - if (width == 32) - instResult.dbl = (double)val; - else if (width == 64) - instResult.dbl = val; - else - panic("Unsupported width!"); + if (width == 32 || width == 64) { + setResult<double>(val); + } else { + panic("Unsupported width!"); } } /** Records an fp register being set to a value. */ void setFloatRegOperand(const StaticInst *si, int idx, FloatReg val) { - if (recordResult) - instResult.dbl = (double)val; + setResult<double>(val); } /** Records an fp register being set to an integer value. */ void setFloatRegOperandBits(const StaticInst *si, int idx, uint64_t val, int width) { - if (recordResult) - instResult.integer = val; + setResult<uint64_t>(val); } /** Records an fp register being set to an integer value. */ void setFloatRegOperandBits(const StaticInst *si, int idx, uint64_t val) { - if (recordResult) - instResult.integer = val; + setResult<uint64_t>(val); } /** Records that one of the source registers is ready. */ @@ -872,6 +894,12 @@ BaseDynInst<Impl>::readMem(Addr addr, uint8_t *data, effAddr = req->getVaddr(); effSize = size; effAddrValid = true; +#if USE_CHECKER + if (reqToVerify != NULL) { + delete reqToVerify; + } + reqToVerify = new Request(*req); +#endif //USE_CHECKER fault = cpu->read(req, sreqLow, sreqHigh, data, lqIdx); } else { // Commit will have to clean up whatever happened. Set this @@ -927,6 +955,12 @@ BaseDynInst<Impl>::writeMem(uint8_t *data, unsigned size, effAddr = req->getVaddr(); effSize = size; effAddrValid = true; +#if USE_CHECKER + if (reqToVerify != NULL) { + delete reqToVerify; + } + reqToVerify = new Request(*req); +#endif // USE_CHECKER fault = cpu->write(req, sreqLow, sreqHigh, data, sqIdx); } diff --git a/src/cpu/base_dyn_inst_impl.hh b/src/cpu/base_dyn_inst_impl.hh index a37ec5e25..d2ecd01ff 100644 --- a/src/cpu/base_dyn_inst_impl.hh +++ b/src/cpu/base_dyn_inst_impl.hh @@ -48,6 +48,7 @@ #include "base/cprintf.hh" #include "base/trace.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/base_dyn_inst.hh" #include "cpu/exetrace.hh" #include "debug/DynInst.hh" @@ -117,7 +118,6 @@ BaseDynInst<Impl>::initVars() reqMade = false; readyRegs = 0; - instResult.integer = 0; recordResult = true; status.reset(); @@ -157,6 +157,10 @@ BaseDynInst<Impl>::initVars() #ifdef DEBUG cpu->snList.insert(seqNum); #endif + +#if USE_CHECKER + reqToVerify = NULL; +#endif } template <class Impl> @@ -182,6 +186,11 @@ BaseDynInst<Impl>::~BaseDynInst() #ifdef DEBUG cpu->snList.erase(seqNum); #endif + +#if USE_CHECKER + if (reqToVerify) + delete reqToVerify; +#endif // USE_CHECKER } #ifdef DEBUG diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc index 0c7fe66bf..7c9e4f781 100644 --- a/src/cpu/checker/cpu.cc +++ b/src/cpu/checker/cpu.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -26,6 +38,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Kevin Lim + * Geoffrey Blake */ #include <list> @@ -36,6 +49,8 @@ #include "cpu/simple_thread.hh" #include "cpu/static_inst.hh" #include "cpu/thread_context.hh" +#include "params/CheckerCPU.hh" +#include "sim/tlb.hh" #if FULL_SYSTEM #include "arch/kernel_stats.hh" @@ -43,8 +58,7 @@ #endif // FULL_SYSTEM using namespace std; -//The CheckerCPU does alpha only -using namespace AlphaISA; +using namespace TheISA; void CheckerCPU::init() @@ -55,6 +69,8 @@ CheckerCPU::CheckerCPU(Params *p) : BaseCPU(p), thread(NULL), tc(NULL) { memReq = NULL; + curStaticInst = NULL; + curMacroStaticInst = NULL; numInst = 0; startNumInst = 0; @@ -66,19 +82,20 @@ CheckerCPU::CheckerCPU(Params *p) exitOnError = p->exitOnError; warnOnlyOnLoadError = p->warnOnlyOnLoadError; -#if FULL_SYSTEM itb = p->itb; dtb = p->dtb; +#if FULL_SYSTEM systemPtr = NULL; #else - process = p->process; - thread = new SimpleThread(this, /* thread_num */ 0, process); + workload = p->workload; + // XXX: This is a hack to get this to work some + thread = new SimpleThread(this, /* thread_num */ 0, workload[0], itb, dtb); tc = thread->getTC(); threadContexts.push_back(tc); #endif - result.integer = 0; + updateOnError = true; } CheckerCPU::~CheckerCPU() @@ -115,192 +132,194 @@ CheckerCPU::setDcachePort(Port *dcache_port) void CheckerCPU::serialize(ostream &os) { -/* - BaseCPU::serialize(os); - SERIALIZE_SCALAR(inst); - nameOut(os, csprintf("%s.xc", name())); - thread->serialize(os); - cacheCompletionEvent.serialize(os); -*/ } void CheckerCPU::unserialize(Checkpoint *cp, const string §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; } - #if FULL_SYSTEM Addr CheckerCPU::dbg_vtophys(Addr addr) @@ -309,24 +328,30 @@ CheckerCPU::dbg_vtophys(Addr addr) } #endif // FULL_SYSTEM +/** + * Checks if the flags set by the Checker and Checkee match. + */ bool -CheckerCPU::checkFlags(Request *req) +CheckerCPU::checkFlags(Request *unverified_req, Addr vAddr, + Addr pAddr, int flags) { - // Remove any dynamic flags that don't have to do with the request itself. - unsigned flags = unverifiedReq->getFlags(); - unsigned mask = LOCKED | PHYSICAL | VPTE | ALTMODE | UNCACHEABLE | PREFETCH; - flags = flags & (mask); - if (flags == req->getFlags()) { + Addr unverifiedVAddr = unverified_req->getVaddr(); + Addr unverifiedPAddr = unverified_req->getPaddr(); + int unverifiedFlags = unverified_req->getFlags(); + + if (unverifiedVAddr != vAddr || + unverifiedPAddr != pAddr || + unverifiedFlags != flags) { return false; - } else { - return true; } + + return true; } void CheckerCPU::dumpAndExit() { - warn("%lli: Checker PC:%#x, next PC:%#x", - curTick(), thread->readPC(), thread->readNextPC()); + warn("%lli: Checker PC:%s", + curTick(), thread->pcState()); panic("Checker found an error!"); } diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh index 1e3a17a34..1419051a6 100644 --- a/src/cpu/checker/cpu.hh +++ b/src/cpu/checker/cpu.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -35,6 +47,7 @@ #include <map> #include <queue> +#include "arch/predecoder.hh" #include "arch/types.hh" #include "base/statistics.hh" #include "config/full_system.hh" @@ -43,6 +56,8 @@ #include "cpu/pc_event.hh" #include "cpu/simple_thread.hh" #include "cpu/static_inst.hh" +#include "debug/Checker.hh" +#include "params/CheckerCPU.hh" #include "sim/eventq.hh" // forward declarations @@ -61,7 +76,6 @@ class Process; #endif // FULL_SYSTEM template <class> class BaseDynInst; -class CheckerCPUParams; class ThreadContext; class MemInterface; class Checkpoint; @@ -96,11 +110,11 @@ class CheckerCPU : public BaseCPU public: typedef CheckerCPUParams Params; const Params *params() const - { return reinterpret_cast<const Params *>(_params); } + { return reinterpret_cast<const Params *>(_params); } CheckerCPU(Params *p); virtual ~CheckerCPU(); - Process *process; + std::vector<Process*> workload; void setSystem(System *system); @@ -135,19 +149,25 @@ class CheckerCPU : public BaseCPU union Result { uint64_t integer; -// float fp; double dbl; + void set(uint64_t i) { integer = i; } + void set(double d) { dbl = d; } + void get(uint64_t& i) { i = integer; } + void get(double& d) { d = dbl; } }; - Result result; + // ISAs like ARM can have multiple destination registers to check, + // keep them all in a std::queue + std::queue<Result> result; // current instruction - MachInst machInst; + TheISA::MachInst machInst; // Pointer to the one memory request. RequestPtr memReq; StaticInstPtr curStaticInst; + StaticInstPtr curMacroStaticInst; // number of simulated instructions Counter numInst; @@ -155,6 +175,9 @@ class CheckerCPU : public BaseCPU std::queue<int> miscRegIdxs; + TheISA::TLB* getITBPtr() { return itb; } + TheISA::TLB* getDTBPtr() { return dtb; } + virtual Counter totalInstructions() const { return 0; @@ -167,12 +190,6 @@ class CheckerCPU : public BaseCPU virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §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"); } @@ -206,17 +223,25 @@ class CheckerCPU : public BaseCPU return thread->readFloatRegBits(reg_idx); } + template <class T> + void setResult(T t) + { + Result instRes; + instRes.set(t); + result.push(instRes); + } + void setIntRegOperand(const StaticInst *si, int idx, uint64_t val) { thread->setIntReg(si->destRegIdx(idx), val); - result.integer = val; + setResult<uint64_t>(val); } void setFloatRegOperand(const StaticInst *si, int idx, FloatReg val) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; thread->setFloatReg(reg_idx, val); - result.dbl = (double)val; + setResult<double>(val); } void setFloatRegOperandBits(const StaticInst *si, int idx, @@ -224,12 +249,26 @@ class CheckerCPU : public BaseCPU { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; thread->setFloatRegBits(reg_idx, val); - result.integer = val; + setResult<uint64_t>(val); } - uint64_t instAddr() { return thread->instAddr(); } + bool readPredicate() { return thread->readPredicate(); } + void setPredicate(bool val) + { + thread->setPredicate(val); + } - uint64_t nextInstAddr() { return thread->nextInstAddr(); } + TheISA::PCState pcState() { return thread->pcState(); } + void pcState(const TheISA::PCState &val) + { + DPRINTF(Checker, "Changing PC to %s, old PC %s.\n", + val, thread->pcState()); + thread->pcState(val); + } + Addr instAddr() { return thread->instAddr(); } + Addr nextInstAddr() { return thread->nextInstAddr(); } + MicroPC microPC() { return thread->microPC(); } + ////////////////////////////////////////// MiscReg readMiscRegNoEffect(int misc_reg) { @@ -243,7 +282,6 @@ class CheckerCPU : public BaseCPU void setMiscRegNoEffect(int misc_reg, const MiscReg &val) { - result.integer = val; miscRegIdxs.push(misc_reg); return thread->setMiscRegNoEffect(misc_reg, val); } @@ -254,8 +292,25 @@ class CheckerCPU : public BaseCPU return thread->setMiscReg(misc_reg, val); } - void recordPCChange(uint64_t val) { changedPC = true; newPC = val; } - void recordNextPCChange(uint64_t val) { changedNextPC = true; } + MiscReg readMiscRegOperand(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::Ctrl_Base_DepTag; + return thread->readMiscReg(reg_idx); + } + + void setMiscRegOperand( + const StaticInst *si, int idx, const MiscReg &val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::Ctrl_Base_DepTag; + return thread->setMiscReg(reg_idx, val); + } + ///////////////////////////////////////// + + void recordPCChange(const TheISA::PCState &val) + { + changedPC = true; + newPCState = val; + } void demapPage(Addr vaddr, uint64_t asn) { @@ -273,9 +328,18 @@ class CheckerCPU : public BaseCPU this->dtb->demapPage(vaddr, asn); } + Fault readMem(Addr addr, uint8_t *data, unsigned size, unsigned flags); + Fault writeMem(uint8_t *data, unsigned size, + Addr addr, unsigned flags, uint64_t *res); + + void setStCondFailures(unsigned sc_failures) + {} + ///////////////////////////////////////////////////// + #if FULL_SYSTEM Fault hwrei() { return thread->hwrei(); } bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); } + void wakeup() { } #else // Assume that the normal CPU's call to syscall was successful. // The checker's state would have already been updated by the syscall. @@ -288,7 +352,8 @@ class CheckerCPU : public BaseCPU dumpAndExit(); } - bool checkFlags(Request *req); + bool checkFlags(Request *unverified_req, Addr vAddr, + Addr pAddr, int flags); void dumpAndExit(); @@ -301,7 +366,7 @@ class CheckerCPU : public BaseCPU bool changedPC; bool willChangePC; - uint64_t newPC; + TheISA::PCState newPCState; bool changedNextPC; bool exitOnError; bool updateOnError; @@ -316,24 +381,31 @@ class CheckerCPU : public BaseCPU * template instantiations of the Checker must be placed at the bottom * of checker/cpu.cc. */ -template <class DynInstPtr> +template <class Impl> class Checker : public CheckerCPU { + private: + typedef typename Impl::DynInstPtr DynInstPtr; + public: Checker(Params *p) - : CheckerCPU(p), updateThisCycle(false), unverifiedInst(NULL) + : CheckerCPU(p), updateThisCycle(false), unverifiedInst(NULL), + predecoder(NULL) { } void switchOut(); void takeOverFrom(BaseCPU *oldCPU); + void advancePC(Fault fault); + void verify(DynInstPtr &inst); void validateInst(DynInstPtr &inst); void validateExecution(DynInstPtr &inst); void validateState(); - void copyResult(DynInstPtr &inst); + void copyResult(DynInstPtr &inst, uint64_t mismatch_val, int start_idx); + void handlePendingInt(); private: void handleError(DynInstPtr &inst) @@ -350,6 +422,7 @@ class Checker : public CheckerCPU bool updateThisCycle; DynInstPtr unverifiedInst; + TheISA::Predecoder predecoder; std::list<DynInstPtr> instList; typedef typename std::list<DynInstPtr>::iterator InstListIt; diff --git a/src/cpu/checker/cpu_impl.hh b/src/cpu/checker/cpu_impl.hh index 8694dae21..8c8789cb6 100644 --- a/src/cpu/checker/cpu_impl.hh +++ b/src/cpu/checker/cpu_impl.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -26,6 +38,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Kevin Lim + * Geoffrey Blake */ #include <list> @@ -33,11 +46,13 @@ #include "base/refcnt.hh" #include "config/the_isa.hh" -#include "cpu/checker/cpu.hh" #include "cpu/base_dyn_inst.hh" +#include "cpu/exetrace.hh" #include "cpu/simple_thread.hh" #include "cpu/static_inst.hh" #include "cpu/thread_context.hh" +#include "cpu/checker/cpu.hh" +#include "debug/Checker.hh" #include "sim/sim_object.hh" #include "sim/stats.hh" @@ -46,15 +61,81 @@ #endif // FULL_SYSTEM using namespace std; -//The CheckerCPU does alpha only -using namespace AlphaISA; +using namespace TheISA; + +template <class Impl> +void +Checker<Impl>::advancePC(Fault fault) +{ + if (fault != NoFault) { + curMacroStaticInst = StaticInst::nullStaticInstPtr; + fault->invoke(tc, curStaticInst); + predecoder.reset(); + } else { + if (curStaticInst) { + if (curStaticInst->isLastMicroop()) + curMacroStaticInst = StaticInst::nullStaticInstPtr; + TheISA::PCState pcState = thread->pcState(); + TheISA::advancePC(pcState, curStaticInst); + thread->pcState(pcState); + DPRINTF(Checker, "Advancing PC to %s.\n", thread->pcState()); + } + } +} +////////////////////////////////////////////////// + +template <class Impl> +void +Checker<Impl>::handlePendingInt() +{ + DPRINTF(Checker, "IRQ detected at PC: %s with %d insts in buffer\n", + thread->pcState(), instList.size()); + DynInstPtr boundaryInst = NULL; + if (!instList.empty()) { + // Set the instructions as completed and verify as much as possible. + DynInstPtr inst; + typename std::list<DynInstPtr>::iterator itr; + + for (itr = instList.begin(); itr != instList.end(); itr++) { + (*itr)->setCompleted(); + } -template <class DynInstPtr> + inst = instList.front(); + boundaryInst = instList.back(); + verify(inst); // verify the instructions + inst = NULL; + } + if ((!boundaryInst && curMacroStaticInst && + curStaticInst->isDelayedCommit() && + !curStaticInst->isLastMicroop()) || + (boundaryInst && boundaryInst->isDelayedCommit() && + !boundaryInst->isLastMicroop())) { + panic("%lli: Trying to take an interrupt in middle of " + "a non-interuptable instruction!", curTick()); + } + boundaryInst = NULL; + predecoder.reset(); + curMacroStaticInst = StaticInst::nullStaticInstPtr; +} + +template <class Impl> void -Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) +Checker<Impl>::verify(DynInstPtr &completed_inst) { DynInstPtr inst; + // Make sure serializing instructions are actually + // seen as serializing to commit. instList should be + // empty in these cases. + if ((completed_inst->isSerializing() || + completed_inst->isSerializeBefore()) && + (!instList.empty() ? + (instList.front()->seqNum != completed_inst->seqNum) : 0)) { + panic("%lli: Instruction sn:%lli at PC %s is serializing before but is" + " entering instList with other instructions\n", curTick(), + completed_inst->seqNum, completed_inst->pcState()); + } + // Either check this instruction, or add it to a list of // instructions waiting to be checked. Instructions must be // checked in program order, so if a store has committed yet not @@ -62,8 +143,8 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) // behind it that have completed and must be checked. if (!instList.empty()) { if (youngestSN < completed_inst->seqNum) { - DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", - completed_inst->seqNum, completed_inst->readPC()); + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n", + completed_inst->seqNum, completed_inst->pcState()); instList.push_back(completed_inst); youngestSN = completed_inst->seqNum; } @@ -77,8 +158,8 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) } else { if (!completed_inst->isCompleted()) { if (youngestSN < completed_inst->seqNum) { - DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", - completed_inst->seqNum, completed_inst->readPC()); + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n", + completed_inst->seqNum, completed_inst->pcState()); instList.push_back(completed_inst); youngestSN = completed_inst->seqNum; } @@ -93,17 +174,29 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) } } + // Make sure a serializing instruction is actually seen as + // serializing. instList should be empty here + if (inst->isSerializeAfter() && !instList.empty()) { + panic("%lli: Instruction sn:%lli at PC %s is serializing after but is" + " exiting instList with other instructions\n", curTick(), + completed_inst->seqNum, completed_inst->pcState()); + } unverifiedInst = inst; + inst = NULL; // Try to check all instructions that are completed, ending if we // run out of instructions to check or if an instruction is not // yet completed. while (1) { - DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%#x.\n", - inst->seqNum, inst->readPC()); - unverifiedResult.integer = inst->readIntResult(); - unverifiedReq = inst->req; - unverifiedMemData = inst->memData; + DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%s.\n", + unverifiedInst->seqNum, unverifiedInst->pcState()); + unverifiedReq = NULL; + unverifiedReq = unverifiedInst->reqToVerify; + unverifiedMemData = unverifiedInst->memData; + // Make sure results queue is empty + while (!result.empty()) { + result.pop(); + } numCycles++; Fault fault = NoFault; @@ -118,15 +211,15 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) // expect to happen. This is mostly to check if traps or // PC-based events have occurred in both the checker and CPU. if (changedPC) { - DPRINTF(Checker, "Changed PC recently to %#x\n", - thread->readPC()); + DPRINTF(Checker, "Changed PC recently to %s\n", + thread->pcState()); if (willChangePC) { - if (newPC == thread->readPC()) { + if (newPCState == thread->pcState()) { DPRINTF(Checker, "Changed PC matches expected PC\n"); } else { warn("%lli: Changed PC does not match expected PC, " - "changed: %#x, expected: %#x", - curTick(), thread->readPC(), newPC); + "changed: %s, expected: %s", + curTick(), thread->pcState(), newPCState); CheckerCPU::handleError(); } willChangePC = false; @@ -135,124 +228,188 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) } if (changedNextPC) { DPRINTF(Checker, "Changed NextPC recently to %#x\n", - thread->readNextPC()); + thread->nextInstAddr()); changedNextPC = false; } // Try to fetch the instruction + uint64_t fetchOffset = 0; + bool fetchDone = false; + + while (!fetchDone) { + Addr fetch_PC = thread->instAddr(); + fetch_PC = (fetch_PC & PCMask) + fetchOffset; + + // If not in the middle of a macro instruction + if (!curMacroStaticInst) { + // set up memory request for instruction fetch + memReq = new Request(unverifiedInst->threadNumber, fetch_PC, + sizeof(MachInst), + 0, + fetch_PC, thread->contextId(), + unverifiedInst->threadNumber); + memReq->setVirt(0, fetch_PC, sizeof(MachInst), + Request::INST_FETCH, thread->instAddr()); + + + fault = itb->translateFunctional(memReq, tc, BaseTLB::Execute); + + if (fault != NoFault) { + if (unverifiedInst->getFault() == NoFault) { + // In this case the instruction was not a dummy + // instruction carrying an ITB fault. In the single + // threaded case the ITB should still be able to + // translate this instruction; in the SMT case it's + // possible that its ITB entry was kicked out. + warn("%lli: Instruction PC %s was not found in the " + "ITB!", curTick(), thread->pcState()); + handleError(unverifiedInst); + + // go to the next instruction + advancePC(NoFault); + + // Give up on an ITB fault.. + delete memReq; + unverifiedInst = NULL; + return; + } else { + // The instruction is carrying an ITB fault. Handle + // the fault and see if our results match the CPU on + // the next tick(). + fault = unverifiedInst->getFault(); + delete memReq; + break; + } + } else { + PacketPtr pkt = new Packet(memReq, + MemCmd::ReadReq, + Packet::Broadcast); -#if FULL_SYSTEM -#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 -#else -#define IFETCH_FLAGS(pc) 0 -#endif - - uint64_t fetch_PC = thread->readPC() & ~3; - - // set up memory request for instruction fetch - memReq = new Request(inst->threadNumber, fetch_PC, - sizeof(uint32_t), - IFETCH_FLAGS(thread->readPC()), - fetch_PC, thread->contextId(), - inst->threadNumber); - - bool succeeded = itb->translateAtomic(memReq, thread); - - if (!succeeded) { - if (inst->getFault() == NoFault) { - // In this case the instruction was not a dummy - // instruction carrying an ITB fault. In the single - // threaded case the ITB should still be able to - // translate this instruction; in the SMT case it's - // possible that its ITB entry was kicked out. - warn("%lli: Instruction PC %#x was not found in the ITB!", - curTick(), thread->readPC()); - handleError(inst); + pkt->dataStatic(&machInst); + icachePort->sendFunctional(pkt); + machInst = gtoh(machInst); - // go to the next instruction - thread->setPC(thread->readNextPC()); - thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); + delete memReq; + delete pkt; + } + } - break; - } else { - // The instruction is carrying an ITB fault. Handle - // the fault and see if our results match the CPU on - // the next tick(). - fault = inst->getFault(); + if (fault == NoFault) { + TheISA::PCState pcState = thread->pcState(); + + if (isRomMicroPC(pcState.microPC())) { + fetchDone = true; + curStaticInst = + microcodeRom.fetchMicroop(pcState.microPC(), NULL); + } else if (!curMacroStaticInst) { + //We're not in the middle of a macro instruction + StaticInstPtr instPtr = NULL; + + //Predecode, ie bundle up an ExtMachInst + predecoder.setTC(thread->getTC()); + //If more fetch data is needed, pass it in. + Addr fetchPC = (pcState.instAddr() & PCMask) + fetchOffset; + predecoder.moreBytes(pcState, fetchPC, machInst); + + //If an instruction is ready, decode it. + //Otherwise, we'll have to fetch beyond the + //MachInst at the current pc. + if (predecoder.extMachInstReady()) { + fetchDone = true; + ExtMachInst newMachInst = + predecoder.getExtMachInst(pcState); + thread->pcState(pcState); + instPtr = thread->decoder.decode(newMachInst, + pcState.instAddr()); + machInst = newMachInst; + } else { + fetchDone = false; + fetchOffset += sizeof(TheISA::MachInst); + } + + //If we decoded an instruction and it's microcoded, + //start pulling out micro ops + if (instPtr && instPtr->isMacroop()) { + curMacroStaticInst = instPtr; + curStaticInst = + instPtr->fetchMicroop(pcState.microPC()); + } else { + curStaticInst = instPtr; + } + } else { + // Read the next micro op from the macro-op + curStaticInst = + curMacroStaticInst->fetchMicroop(pcState.microPC()); + fetchDone = true; + } } } + // reset predecoder on Checker + predecoder.reset(); + // Check Checker and CPU get same instruction, and record + // any faults the CPU may have had. + Fault unverifiedFault; if (fault == NoFault) { - PacketPtr pkt = new Packet(memReq, Packet::ReadReq, - Packet::Broadcast); - - pkt->dataStatic(&machInst); + unverifiedFault = unverifiedInst->getFault(); - icachePort->sendFunctional(pkt); - - delete pkt; - - // keep an instruction count - numInst++; - - // decode the instruction - machInst = gtoh(machInst); // Checks that the instruction matches what we expected it to be. // Checks both the machine instruction and the PC. - validateInst(inst); - -#if THE_ISA == ALPHA_ISA - curStaticInst = StaticInst::decode(makeExtMI(machInst, - thread->readPC())); -#elif THE_ISA == SPARC_ISA - curStaticInst = StaticInst::decode(makeExtMI(machInst, - thread->getTC())); -#endif - - fault = inst->getFault(); + validateInst(unverifiedInst); } - // Discard fetch's memReq. - delete memReq; - memReq = NULL; + // keep an instruction count + numInst++; + // Either the instruction was a fault and we should process the fault, // or we should just go ahead execute the instruction. This assumes // that the instruction is properly marked as a fault. if (fault == NoFault) { + // Execute Checker instruction and trace + if (!unverifiedInst->isUnverifiable()) { + Trace::InstRecord *traceData = tracer->getInstRecord(curTick(), + tc, + curStaticInst, + pcState(), + curMacroStaticInst); + fault = curStaticInst->execute(this, traceData); + if (traceData) { + traceData->dump(); + delete traceData; + } + } - thread->funcExeInst++; - - if (!inst->isUnverifiable()) - fault = curStaticInst->execute(this, NULL); - - // Checks to make sure instrution results are correct. - validateExecution(inst); + if (fault == NoFault && unverifiedFault == NoFault) { + thread->funcExeInst++; + // Checks to make sure instrution results are correct. + validateExecution(unverifiedInst); - if (curStaticInst->isLoad()) { - ++numLoad; + if (curStaticInst->isLoad()) { + ++numLoad; + } + } else if (fault != NoFault && unverifiedFault == NoFault) { + panic("%lli: sn: %lli at PC: %s took a fault in checker " + "but not in driver CPU\n", curTick(), + unverifiedInst->seqNum, unverifiedInst->pcState()); + } else if (fault == NoFault && unverifiedFault != NoFault) { + panic("%lli: sn: %lli at PC: %s took a fault in driver " + "CPU but not in checker\n", curTick(), + unverifiedInst->seqNum, unverifiedInst->pcState()); } } + // Take any faults here if (fault != NoFault) { #if FULL_SYSTEM fault->invoke(tc, curStaticInst); willChangePC = true; - newPC = thread->readPC(); - DPRINTF(Checker, "Fault, PC is now %#x\n", newPC); + newPCState = thread->pcState(); + DPRINTF(Checker, "Fault, PC is now %s\n", newPCState); + curMacroStaticInst = StaticInst::nullStaticInstPtr; #endif } else { -#if THE_ISA != MIPS_ISA - // go to the next instruction - thread->setPC(thread->readNextPC()); - thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); -#else - // go to the next instruction - thread->setPC(thread->readNextPC()); - thread->setNextPC(thread->readNextNPC()); - thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); -#endif - + advancePC(fault); } #if FULL_SYSTEM @@ -262,14 +419,14 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) Addr oldpc; int count = 0; do { - oldpc = thread->readPC(); + oldpc = thread->instAddr(); system->pcEventQueue.service(tc); count++; - } while (oldpc != thread->readPC()); + } while (oldpc != thread->instAddr()); if (count > 1) { willChangePC = true; - newPC = thread->readPC(); - DPRINTF(Checker, "PC Event, PC is now %#x\n", newPC); + newPCState = thread->pcState(); + DPRINTF(Checker, "PC Event, PC is now %s\n", newPCState); } #endif @@ -277,17 +434,13 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) // that have been modified). validateState(); - if (memReq) { - delete memReq; - memReq = NULL; - } - // Continue verifying instructions if there's another completed // instruction waiting to be verified. if (instList.empty()) { break; } else if (instList.front()->isCompleted()) { - inst = instList.front(); + unverifiedInst = NULL; + unverifiedInst = instList.front(); instList.pop_front(); } else { break; @@ -296,26 +449,26 @@ Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) unverifiedInst = NULL; } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::switchOut() +Checker<Impl>::switchOut() { instList.clear(); } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::takeOverFrom(BaseCPU *oldCPU) +Checker<Impl>::takeOverFrom(BaseCPU *oldCPU) { } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::validateInst(DynInstPtr &inst) +Checker<Impl>::validateInst(DynInstPtr &inst) { - if (inst->readPC() != thread->readPC()) { - warn("%lli: PCs do not match! Inst: %#x, checker: %#x", - curTick(), inst->readPC(), thread->readPC()); + if (inst->instAddr() != thread->instAddr()) { + warn("%lli: PCs do not match! Inst: %s, checker: %s", + curTick(), inst->pcState(), thread->pcState()); if (changedPC) { warn("%lli: Changed PCs recently, may not be an error", curTick()); @@ -327,51 +480,70 @@ Checker<DynInstPtr>::validateInst(DynInstPtr &inst) MachInst mi = static_cast<MachInst>(inst->staticInst->machInst); if (mi != machInst) { - warn("%lli: Binary instructions do not match! Inst: %#x, " + panic("%lli: Binary instructions do not match! Inst: %#x, " "checker: %#x", curTick(), mi, machInst); handleError(inst); } } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::validateExecution(DynInstPtr &inst) +Checker<Impl>::validateExecution(DynInstPtr &inst) { + uint64_t checker_val; + uint64_t inst_val; + int idx = -1; bool result_mismatch = false; - if (inst->numDestRegs()) { - // @todo: Support more destination registers. - if (inst->isUnverifiable()) { - // Unverifiable instructions assume they were executed - // properly by the CPU. Grab the result from the - // instruction and write it to the register. - copyResult(inst); - } else if (result.integer != inst->readIntResult()) { - result_mismatch = true; + + if (inst->isUnverifiable()) { + // Unverifiable instructions assume they were executed + // properly by the CPU. Grab the result from the + // instruction and write it to the register. + copyResult(inst, 0, idx); + } else if (inst->numDestRegs() > 0 && !result.empty()) { + DPRINTF(Checker, "Dest regs %d, number of checker dest regs %d\n", + inst->numDestRegs(), result.size()); + for (int i = 0; i < inst->numDestRegs() && !result.empty(); i++) { + result.front().get(checker_val); + result.pop(); + inst_val = 0; + inst->template popResult<uint64_t>(inst_val); + if (checker_val != inst_val) { + result_mismatch = true; + idx = i; + break; + } } - } + } // Checker CPU checks all the saved results in the dyninst passed by + // the cpu model being checked against the saved results present in + // the static inst executed in the Checker. Sometimes the number + // of saved results differs between the dyninst and static inst, but + // this is ok and not a bug. May be worthwhile to try and correct this. if (result_mismatch) { warn("%lli: Instruction results do not match! (Values may not " "actually be integers) Inst: %#x, checker: %#x", - curTick(), inst->readIntResult(), result.integer); + curTick(), inst_val, checker_val); // It's useful to verify load values from memory, but in MP // systems the value obtained at execute may be different than // the value obtained at completion. Similarly DMA can // present the same problem on even UP systems. Thus there is // the option to only warn on loads having a result error. + // The load/store queue in Detailed CPU can also cause problems + // if load/store forwarding is allowed. if (inst->isLoad() && warnOnlyOnLoadError) { - copyResult(inst); + copyResult(inst, inst_val, idx); } else { handleError(inst); } } - if (inst->readNextPC() != thread->readNextPC()) { + if (inst->nextInstAddr() != thread->nextInstAddr()) { warn("%lli: Instruction next PCs do not match! Inst: %#x, " "checker: %#x", - curTick(), inst->readNextPC(), thread->readNextPC()); + curTick(), inst->nextInstAddr(), thread->nextInstAddr()); handleError(inst); } @@ -396,53 +568,78 @@ Checker<DynInstPtr>::validateExecution(DynInstPtr &inst) } } -template <class DynInstPtr> + +// This function is weird, if it is called it means the Checker and +// O3 have diverged, so panic is called for now. It may be useful +// to resynch states and continue if the divergence is a false positive +template <class Impl> void -Checker<DynInstPtr>::validateState() +Checker<Impl>::validateState() { if (updateThisCycle) { - warn("%lli: Instruction PC %#x results didn't match up, copying all " - "registers from main CPU", curTick(), unverifiedInst->readPC()); + // Change this back to warn if divergences end up being false positives + panic("%lli: Instruction PC %#x results didn't match up, copying all " + "registers from main CPU", curTick(), unverifiedInst->instAddr()); + + // Terribly convoluted way to make sure O3 model does not implode + bool inSyscall = unverifiedInst->thread->inSyscall; + unverifiedInst->thread->inSyscall = true; + // Heavy-weight copying of all registers thread->copyArchRegs(unverifiedInst->tcBase()); + unverifiedInst->thread->inSyscall = inSyscall; + + // Set curStaticInst to unverifiedInst->staticInst + curStaticInst = unverifiedInst->staticInst; // Also advance the PC. Hopefully no PC-based events happened. -#if THE_ISA != MIPS_ISA - // go to the next instruction - thread->setPC(thread->readNextPC()); - thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); -#else - // go to the next instruction - thread->setPC(thread->readNextPC()); - thread->setNextPC(thread->readNextNPC()); - thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); -#endif + advancePC(NoFault); updateThisCycle = false; } } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::copyResult(DynInstPtr &inst) +Checker<Impl>::copyResult(DynInstPtr &inst, uint64_t mismatch_val, + int start_idx) { - RegIndex idx = inst->destRegIdx(0); - if (idx < TheISA::FP_Base_DepTag) { - thread->setIntReg(idx, inst->readIntResult()); - } else if (idx < TheISA::Fpcr_DepTag) { - thread->setFloatRegBits(idx, inst->readIntResult()); - } else { - thread->setMiscRegNoEffect(idx, inst->readIntResult()); + // We've already popped one dest off the queue, + // so do the fix-up then start with the next dest reg; + if (start_idx >= 0) { + RegIndex idx = inst->destRegIdx(start_idx); + if (idx < TheISA::FP_Base_DepTag) { + thread->setIntReg(idx, mismatch_val); + } else if (idx < TheISA::Ctrl_Base_DepTag) { + thread->setFloatRegBits(idx, mismatch_val); + } else if (idx < TheISA::Max_DepTag) { + thread->setMiscReg(idx - TheISA::Ctrl_Base_DepTag, + mismatch_val); + } + } + start_idx++; + uint64_t res = 0; + for (int i = start_idx; i < inst->numDestRegs(); i++) { + RegIndex idx = inst->destRegIdx(i); + inst->template popResult<uint64_t>(res); + if (idx < TheISA::FP_Base_DepTag) { + thread->setIntReg(idx, res); + } else if (idx < TheISA::Ctrl_Base_DepTag) { + thread->setFloatRegBits(idx, res); + } else if (idx < TheISA::Max_DepTag) { + // Try to get the proper misc register index for ARM here... + thread->setMiscReg(idx - TheISA::Ctrl_Base_DepTag, res); + } // else Register is out of range... } } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst) +Checker<Impl>::dumpAndExit(DynInstPtr &inst) { cprintf("Error detected, instruction information:\n"); - cprintf("PC:%#x, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n" + cprintf("PC:%s, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n" "Completed:%i\n", - inst->readPC(), - inst->readNextPC(), + inst->pcState(), + inst->nextInstAddr(), inst->seqNum, inst->threadNumber, inst->isCompleted()); @@ -450,9 +647,9 @@ Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst) CheckerCPU::dumpAndExit(); } -template <class DynInstPtr> +template <class Impl> void -Checker<DynInstPtr>::dumpInsts() +Checker<Impl>::dumpInsts() { int num = 0; @@ -465,9 +662,9 @@ Checker<DynInstPtr>::dumpInsts() cprintf("Instruction:%i\n", num); - cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + cprintf("PC:%s\n[sn:%lli]\n[tid:%i]\n" "Completed:%i\n", - (*inst_list_it)->readPC(), + (*inst_list_it)->pcState(), (*inst_list_it)->seqNum, (*inst_list_it)->threadNumber, (*inst_list_it)->isCompleted()); diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh index 4eb3eabfd..d66854b23 100644 --- a/src/cpu/checker/thread_context.hh +++ b/src/cpu/checker/thread_context.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -36,6 +48,7 @@ #include "cpu/checker/cpu.hh" #include "cpu/simple_thread.hh" #include "cpu/thread_context.hh" +#include "debug/Checker.hh" class EndQuiesceEvent; namespace TheISA { @@ -77,21 +90,35 @@ class CheckerThreadContext : public ThreadContext BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); } - void setCpuId(int id) + int cpuId() { return actualTC->cpuId(); } + + int contextId() { return actualTC->contextId(); } + + void setContextId(int id) { - actualTC->setCpuId(id); - checkerTC->setCpuId(id); + actualTC->setContextId(id); + checkerTC->setContextId(id); } - int cpuId() { return actualTC->cpuId(); } + /** Returns this thread's ID number. */ + int threadId() { return actualTC->threadId(); } + void setThreadId(int id) + { + checkerTC->setThreadId(id); + actualTC->setThreadId(id); + } TheISA::TLB *getITBPtr() { return actualTC->getITBPtr(); } TheISA::TLB *getDTBPtr() { return actualTC->getDTBPtr(); } -#if FULL_SYSTEM + BaseCPU *getCheckerCpuPtr() { return checkerTC->getCpuPtr(); } + + Decoder *getDecoderPtr() { return actualTC->getDecoderPtr(); } + System *getSystemPtr() { return actualTC->getSystemPtr(); } +#if FULL_SYSTEM PhysicalMemory *getPhysMemPtr() { return actualTC->getPhysMemPtr(); } TheISA::Kernel::Statistics *getKernelStats() @@ -101,10 +128,23 @@ class CheckerThreadContext : public ThreadContext FSTranslatingPortProxy* getVirtProxy() { return actualTC->getVirtProxy(); } + + //XXX: How does this work now? + void initMemProxies(ThreadContext *tc) + { actualTC->initMemProxies(tc); } + + void connectMemPorts(ThreadContext *tc) + { + actualTC->connectMemPorts(tc); + } #else SETranslatingPortProxy* getMemProxy() { return actualTC->getMemProxy(); } Process *getProcessPtr() { return actualTC->getProcessPtr(); } + + /** Executes a syscall in SE mode. */ + void syscall(int64_t callnum) + { return actualTC->syscall(callnum); } #endif Status status() const { return actualTC->status(); } @@ -120,10 +160,10 @@ class CheckerThreadContext : public ThreadContext void activate(int delay = 1) { actualTC->activate(delay); } /// Set the status to Suspended. - void suspend() { actualTC->suspend(); } + void suspend(int delay) { actualTC->suspend(delay); } /// Set the status to Halted. - void halt() { actualTC->halt(); } + void halt(int delay) { actualTC->halt(delay); } #if FULL_SYSTEM void dumpFuncProfile() { actualTC->dumpFuncProfile(); } @@ -135,7 +175,11 @@ class CheckerThreadContext : public ThreadContext checkerTC->copyState(oldContext); } - void regStats(const std::string &name) { actualTC->regStats(name); } + void regStats(const std::string &name) + { + actualTC->regStats(name); + checkerTC->regStats(name); + } void serialize(std::ostream &os) { actualTC->serialize(os); } void unserialize(Checkpoint *cp, const std::string §ion) @@ -151,8 +195,6 @@ class CheckerThreadContext : public ThreadContext void profileSample() { return actualTC->profileSample(); } #endif - int threadId() { return actualTC->threadId(); } - // @todo: Do I need this? void copyArchRegs(ThreadContext *tc) { @@ -196,32 +238,36 @@ class CheckerThreadContext : public ThreadContext checkerTC->setFloatRegBits(reg_idx, val); } - uint64_t readPC() { return actualTC->readPC(); } + /** Reads this thread's PC state. */ + TheISA::PCState pcState() + { return actualTC->pcState(); } - void setPC(uint64_t val) + /** Sets this thread's PC state. */ + void pcState(const TheISA::PCState &val) { - actualTC->setPC(val); - checkerTC->setPC(val); + DPRINTF(Checker, "Changing PC to %s, old PC %s\n", + val, checkerTC->pcState()); + checkerTC->pcState(val); checkerCPU->recordPCChange(val); + return actualTC->pcState(val); } - uint64_t readNextPC() { return actualTC->readNextPC(); } - - void setNextPC(uint64_t val) + void pcStateNoRecord(const TheISA::PCState &val) { - actualTC->setNextPC(val); - checkerTC->setNextPC(val); - checkerCPU->recordNextPCChange(val); + return actualTC->pcState(val); } - uint64_t readNextNPC() { return actualTC->readNextNPC(); } + /** Reads this thread's PC. */ + Addr instAddr() + { return actualTC->instAddr(); } - void setNextNPC(uint64_t val) - { - actualTC->setNextNPC(val); - checkerTC->setNextNPC(val); - checkerCPU->recordNextPCChange(val); - } + /** Reads this thread's next PC. */ + Addr nextInstAddr() + { return actualTC->nextInstAddr(); } + + /** Reads this thread's next PC. */ + MicroPC microPC() + { return actualTC->microPC(); } MiscReg readMiscRegNoEffect(int misc_reg) { return actualTC->readMiscRegNoEffect(misc_reg); } @@ -231,22 +277,28 @@ class CheckerThreadContext : public ThreadContext void setMiscRegNoEffect(int misc_reg, const MiscReg &val) { + DPRINTF(Checker, "Setting misc reg with no effect: %d to both Checker" + " and O3..\n", misc_reg); checkerTC->setMiscRegNoEffect(misc_reg, val); actualTC->setMiscRegNoEffect(misc_reg, val); } void setMiscReg(int misc_reg, const MiscReg &val) { + DPRINTF(Checker, "Setting misc reg with effect: %d to both Checker" + " and O3..\n", misc_reg); checkerTC->setMiscReg(misc_reg, val); actualTC->setMiscReg(misc_reg, val); } + int flattenIntIndex(int reg) { return actualTC->flattenIntIndex(reg); } + int flattenFloatIndex(int reg) { return actualTC->flattenFloatIndex(reg); } + unsigned readStCondFailures() { return actualTC->readStCondFailures(); } void setStCondFailures(unsigned sc_failures) { - checkerTC->setStCondFailures(sc_failures); actualTC->setStCondFailures(sc_failures); } diff --git a/src/cpu/dummy_checker_builder.cc b/src/cpu/dummy_checker_builder.cc new file mode 100644 index 000000000..28f060931 --- /dev/null +++ b/src/cpu/dummy_checker_builder.cc @@ -0,0 +1,100 @@ +/* + * 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. + * + * 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: Geoffrey Blake + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "params/DummyChecker.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class MemObject; + +/** + * Specific non-templated derived class used for SimObject configuration. + */ +class DummyChecker : public CheckerCPU +{ + public: + DummyChecker(Params *p) + : CheckerCPU(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// DummyChecker Simulation Object +// +DummyChecker * +DummyCheckerParams::create() +{ + DummyChecker::Params *params = new DummyChecker::Params(); + params->name = name; + params->numThreads = numThreads; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + Tick temp2 = progress_interval; + params->progress_interval = 0; + temp2++; + + params->itb = itb; + params->dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; +#if FULL_SYSTEM + params->profile = profile; + params->interrupts = NULL; +#else + params->workload = workload; +#endif + + DummyChecker *cpu = new DummyChecker(params); + return cpu; +} diff --git a/src/cpu/o3/O3CPU.py b/src/cpu/o3/O3CPU.py index 6f721a11b..edce7d8c7 100644 --- a/src/cpu/o3/O3CPU.py +++ b/src/cpu/o3/O3CPU.py @@ -41,14 +41,20 @@ class DerivO3CPU(BaseCPU): if buildEnv['USE_CHECKER']: if not buildEnv['FULL_SYSTEM']: - checker = Param.BaseCPU(O3Checker(workload=Parent.workload, + # FIXME: Shouldn't need to derefernce Parent.workload + # Somewhere in the param parsing code + # src/python/m5/params.py is and error that + # has trouble converting the workload parameter properly. + checker = Param.BaseCPU(O3Checker(workload=Parent.workload[0], exitOnError=False, updateOnError=True, - warnOnlyOnLoadError=False), - "checker") + warnOnlyOnLoadError=True), + "checker") else: - checker = Param.BaseCPU(O3Checker(exitOnError=False, updateOnError=True, - warnOnlyOnLoadError=False), "checker") + checker = Param.BaseCPU(O3Checker(exitOnError=False, + updateOnError=True, + warnOnlyOnLoadError=True), + "checker") checker.itb = Parent.itb checker.dtb = Parent.dtb diff --git a/src/cpu/o3/O3Checker.py b/src/cpu/o3/O3Checker.py index d0c4ce537..d53e5e527 100644 --- a/src/cpu/o3/O3Checker.py +++ b/src/cpu/o3/O3Checker.py @@ -34,7 +34,7 @@ class O3Checker(BaseCPU): exitOnError = Param.Bool(False, "Exit on an error") updateOnError = Param.Bool(False, "Update the checker with the main CPU's state on an error") - warnOnlyOnLoadError = Param.Bool(False, + warnOnlyOnLoadError = Param.Bool(True, "If a load result is incorrect, only print a warning and do not exit") function_trace = Param.Bool(False, "Enable function trace") function_trace_start = Param.Tick(0, "Cycle to start function trace") diff --git a/src/cpu/o3/checker_builder.cc b/src/cpu/o3/checker_builder.cc index 5d0bd2ed2..73e8b5162 100644 --- a/src/cpu/o3/checker_builder.cc +++ b/src/cpu/o3/checker_builder.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. * @@ -31,8 +43,8 @@ #include <string> #include "cpu/checker/cpu_impl.hh" -#include "cpu/o3/alpha/dyn_inst.hh" -#include "cpu/o3/alpha/impl.hh" +#include "cpu/o3/dyn_inst.hh" +#include "cpu/o3/impl.hh" #include "cpu/inst_seq.hh" #include "params/O3Checker.hh" #include "sim/process.hh" @@ -41,16 +53,16 @@ class MemObject; template -class Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >; +class Checker<O3CPUImpl>; /** * Specific non-templated derived class used for SimObject configuration. */ -class O3Checker : public Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > > +class O3Checker : public Checker<O3CPUImpl> { public: O3Checker(Params *p) - : Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >(p) + : Checker<O3CPUImpl>(p) { } }; @@ -63,7 +75,7 @@ O3CheckerParams::create() { O3Checker::Params *params = new O3Checker::Params(); params->name = name; - params->numberOfThreads = 1; + params->numThreads = numThreads; params->max_insts_any_thread = 0; params->max_insts_all_threads = 0; params->max_loads_any_thread = 0; @@ -71,10 +83,8 @@ O3CheckerParams::create() params->exitOnError = exitOnError; params->updateOnError = updateOnError; params->warnOnlyOnLoadError = warnOnlyOnLoadError; - params->deferRegistration = defer_registration; - params->functionTrace = function_trace; - params->functionTraceStart = function_trace_start; params->clock = clock; + params->tracer = tracer; // Hack to touch all parameters. Consider not deriving Checker // from BaseCPU..it's not really a CPU in the end. Counter temp; @@ -92,8 +102,9 @@ O3CheckerParams::create() params->cpu_id = cpu_id; #if FULL_SYSTEM params->profile = profile; + params->interrupts = NULL; #else - params->process = workload; + params->workload = workload; #endif O3Checker *cpu = new O3Checker(params); diff --git a/src/cpu/o3/commit_impl.hh b/src/cpu/o3/commit_impl.hh index 9ff31a622..cb2f9a634 100644 --- a/src/cpu/o3/commit_impl.hh +++ b/src/cpu/o3/commit_impl.hh @@ -728,6 +728,12 @@ DefaultCommit<Impl>::handleInterrupt() assert(!thread[0]->inSyscall); thread[0]->inSyscall = true; +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->handlePendingInt(); + } +#endif + // CPU will handle interrupt. cpu->processInterrupts(interrupt); @@ -775,7 +781,8 @@ DefaultCommit<Impl>::commit() { #if FULL_SYSTEM - // Check for any interrupt that we've already squashed for and start processing it. + // Check for any interrupt that we've already squashed for and + // start processing it. if (interrupt != NoFault) handleInterrupt(); @@ -1133,7 +1140,8 @@ DefaultCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) head_inst->setCompleted(); #if USE_CHECKER - if (cpu->checker && head_inst->isStore()) { + if (cpu->checker) { + // Need to check the instruction before its fault is processed cpu->checker->verify(head_inst); } #endif diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index 7e0b4cee7..49843ee9b 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -68,6 +68,7 @@ #if USE_CHECKER #include "cpu/checker/cpu.hh" +#include "cpu/checker/thread_context.hh" #endif #if THE_ISA == ALPHA_ISA @@ -268,7 +269,7 @@ FullO3CPU<Impl>::FullO3CPU(DerivO3CPUParams *params) #if USE_CHECKER if (params->checker) { BaseCPU *temp_checker = params->checker; - checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); + checker = dynamic_cast<Checker<Impl> *>(temp_checker); checker->setIcachePort(&icachePort); #if FULL_SYSTEM checker->setSystem(params->system); diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh index 121253475..a874b1e9f 100644 --- a/src/cpu/o3/cpu.hh +++ b/src/cpu/o3/cpu.hh @@ -732,7 +732,7 @@ class FullO3CPU : public BaseO3CPU * instruction results at run time. This can be set to NULL if it * is not being used. */ - Checker<DynInstPtr> *checker; + Checker<Impl> *checker; #endif /** Pointer to the system. */ diff --git a/src/cpu/o3/dyn_inst_impl.hh b/src/cpu/o3/dyn_inst_impl.hh index 500d63de8..8a01b2575 100644 --- a/src/cpu/o3/dyn_inst_impl.hh +++ b/src/cpu/o3/dyn_inst_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010-2011 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -41,6 +41,7 @@ */ #include "base/cp_annotate.hh" +#include "config/use_checker.hh" #include "cpu/o3/dyn_inst.hh" template <class Impl> @@ -136,6 +137,11 @@ BaseO3DynInst<Impl>::completeAcc(PacketPtr pkt) bool in_syscall = this->thread->inSyscall; this->thread->inSyscall = true; +#if USE_CHECKER + if (this->isStoreConditional()) { + this->reqToVerify->setExtraData(pkt->req->getExtraData()); + } +#endif this->fault = this->staticInst->completeAcc(pkt, this, this->traceData); this->thread->inSyscall = in_syscall; diff --git a/src/cpu/o3/fetch_impl.hh b/src/cpu/o3/fetch_impl.hh index d145fb099..463509cf1 100644 --- a/src/cpu/o3/fetch_impl.hh +++ b/src/cpu/o3/fetch_impl.hh @@ -43,6 +43,9 @@ #include <algorithm> #include <cstring> +#include <list> +#include <map> +#include <queue> #include "arch/isa_traits.hh" #include "arch/utility.hh" @@ -50,7 +53,6 @@ #include "config/the_isa.hh" #include "config/use_checker.hh" #include "cpu/base.hh" -#include "cpu/checker/cpu.hh" #include "cpu/o3/fetch.hh" #include "cpu/exetrace.hh" #include "debug/Activity.hh" @@ -68,6 +70,10 @@ #include "sim/system.hh" #endif // FULL_SYSTEM +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif // USE_CHECKER + using namespace std; template<class Impl> diff --git a/src/cpu/o3/iew_impl.hh b/src/cpu/o3/iew_impl.hh index 92c8875e4..698dd15c4 100644 --- a/src/cpu/o3/iew_impl.hh +++ b/src/cpu/o3/iew_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010-2011 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -48,6 +48,7 @@ #include "arch/utility.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/o3/fu_pool.hh" #include "cpu/o3/iew.hh" #include "cpu/timebuf.hh" @@ -56,6 +57,10 @@ #include "debug/IEW.hh" #include "params/DerivO3CPU.hh" +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif // USE_CHECKER + using namespace std; template<class Impl> @@ -294,6 +299,13 @@ DefaultIEW<Impl>::initStage() ldstQueue.numFreeEntries(tid); } +// Initialize the checker's dcache port here +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setDcachePort(cpu->getDcachePort()); + } +#endif + cpu->activateStage(O3CPU::IEWIdx); } diff --git a/src/cpu/o3/lsq_unit_impl.hh b/src/cpu/o3/lsq_unit_impl.hh index d0db6f6fe..facd88597 100644 --- a/src/cpu/o3/lsq_unit_impl.hh +++ b/src/cpu/o3/lsq_unit_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010-2011 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -45,7 +45,6 @@ #include "arch/locked_mem.hh" #include "base/str.hh" #include "config/the_isa.hh" -#include "config/use_checker.hh" #include "cpu/o3/lsq.hh" #include "cpu/o3/lsq_unit.hh" #include "debug/Activity.hh" @@ -246,12 +245,6 @@ void LSQUnit<Impl>::setDcachePort(Port *dcache_port) { dcachePort = dcache_port; - -#if USE_CHECKER - if (cpu->checker) { - cpu->checker->setDcachePort(dcachePort); - } -#endif } template<class Impl> @@ -878,6 +871,11 @@ LSQUnit<Impl>::writebackStores() inst->seqNum); WritebackEvent *wb = new WritebackEvent(inst, data_pkt, this); cpu->schedule(wb, curTick() + 1); +#if USE_CHECKER + // Make sure to set the LLSC data for verification + inst->reqToVerify->setExtraData(0); + inst->completeAcc(data_pkt); +#endif completeStore(storeWBIdx); incrStIdx(storeWBIdx); continue; diff --git a/src/cpu/o3/thread_context.hh b/src/cpu/o3/thread_context.hh index 0205c63ef..23f3830d3 100755 --- a/src/cpu/o3/thread_context.hh +++ b/src/cpu/o3/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) 2004-2006 The Regents of The University of Michigan * All rights reserved. * @@ -32,6 +44,7 @@ #define __CPU_O3_THREAD_CONTEXT_HH__ #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/o3/isa_specific.hh" #include "cpu/thread_context.hh" @@ -71,6 +84,10 @@ class O3ThreadContext : public ThreadContext /** Returns a pointer to the DTB. */ TheISA::TLB *getDTBPtr() { return cpu->dtb; } +#if USE_CHECKER + BaseCPU *getCheckerCpuPtr() { return NULL; } +#endif + Decoder *getDecoderPtr() { return &cpu->fetch.decoder; } /** Returns a pointer to this CPU. */ @@ -181,6 +198,10 @@ class O3ThreadContext : public ThreadContext /** Sets this thread's PC state. */ virtual void pcState(const TheISA::PCState &val); +#if USE_CHECKER + virtual void pcStateNoRecord(const TheISA::PCState &val); +#endif + /** Reads this thread's PC. */ virtual Addr instAddr() { return cpu->instAddr(thread->threadId()); } diff --git a/src/cpu/o3/thread_context_impl.hh b/src/cpu/o3/thread_context_impl.hh index 4c2fee22d..5a412c161 100755 --- a/src/cpu/o3/thread_context_impl.hh +++ b/src/cpu/o3/thread_context_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010-2011 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -43,6 +43,7 @@ #include "arch/registers.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/o3/thread_context.hh" #include "cpu/quiesce_event.hh" #include "debug/O3CPU.hh" @@ -323,6 +324,20 @@ O3ThreadContext<Impl>::pcState(const TheISA::PCState &val) } } +#if USE_CHECKER +template <class Impl> +void +O3ThreadContext<Impl>::pcStateNoRecord(const TheISA::PCState &val) +{ + cpu->pcState(val, thread->threadId()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->threadId()); + } +} +#endif + template <class Impl> int O3ThreadContext<Impl>::flattenIntIndex(int reg) diff --git a/src/cpu/simple/BaseSimpleCPU.py b/src/cpu/simple/BaseSimpleCPU.py index 9f528bc20..ea2c642e6 100644 --- a/src/cpu/simple/BaseSimpleCPU.py +++ b/src/cpu/simple/BaseSimpleCPU.py @@ -26,9 +26,18 @@ # # Authors: Gabe Black +from m5.defines import buildEnv from m5.params import * from BaseCPU import BaseCPU +if buildEnv['USE_CHECKER']: + from DummyChecker import DummyChecker + class BaseSimpleCPU(BaseCPU): type = 'BaseSimpleCPU' abstract = True + + if buildEnv['USE_CHECKER']: + checker = Param.BaseCPU(DummyChecker(), "checker") + checker.itb = BaseCPU.itb + checker.dtb = BaseCPU.dtb diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc index 70e2c39e6..2ec9e661f 100644 --- a/src/cpu/simple/base.cc +++ b/src/cpu/simple/base.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010-2011 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -52,6 +52,7 @@ #include "base/trace.hh" #include "base/types.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/simple/base.hh" #include "cpu/base.hh" #include "cpu/exetrace.hh" @@ -82,6 +83,11 @@ #include "mem/mem_object.hh" #endif // FULL_SYSTEM +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#include "cpu/checker/thread_context.hh" +#endif + using namespace std; using namespace TheISA; @@ -99,6 +105,21 @@ BaseSimpleCPU::BaseSimpleCPU(BaseSimpleCPUParams *p) tc = thread->getTC(); +#if USE_CHECKER + if (p->checker) { + BaseCPU *temp_checker = p->checker; + checker = dynamic_cast<CheckerCPU *>(temp_checker); +#if FULL_SYSTEM + checker->setSystem(p->system); +#endif + // Manipulate thread context + ThreadContext *cpu_tc = tc; + tc = new CheckerThreadContext<ThreadContext>(cpu_tc, this->checker); + } else { + checker = NULL; + } +#endif + numInst = 0; startNumInst = 0; numLoad = 0; diff --git a/src/cpu/simple/base.hh b/src/cpu/simple/base.hh index ad281aa2b..56e5e5608 100644 --- a/src/cpu/simple/base.hh +++ b/src/cpu/simple/base.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) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -37,6 +49,7 @@ #include "base/statistics.hh" #include "config/full_system.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/base.hh" #include "cpu/decode.hh" #include "cpu/pc_event.hh" @@ -48,6 +61,10 @@ #include "sim/eventq.hh" #include "sim/system.hh" +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + // forward declarations #if FULL_SYSTEM class Processor; @@ -120,6 +137,10 @@ class BaseSimpleCPU : public BaseCPU * objects to modify this thread's state. */ ThreadContext *tc; + +#if USE_CHECKER + CheckerCPU *checker; +#endif protected: enum Status { diff --git a/src/cpu/simple_thread.hh b/src/cpu/simple_thread.hh index 57e83b4d1..b8dae5d01 100644 --- a/src/cpu/simple_thread.hh +++ b/src/cpu/simple_thread.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) 2001-2006 The Regents of The University of Michigan * All rights reserved. * @@ -40,6 +52,7 @@ #include "base/types.hh" #include "config/full_system.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" #include "cpu/decode.hh" #include "cpu/thread_context.hh" #include "cpu/thread_state.hh" @@ -200,6 +213,10 @@ class SimpleThread : public ThreadState TheISA::TLB *getDTBPtr() { return dtb; } +#if USE_CHECKER + BaseCPU *getCheckerCpuPtr() { return NULL; } +#endif + Decoder *getDecoderPtr() { return &decoder; } System *getSystemPtr() { return system; } @@ -295,7 +312,10 @@ class SimpleThread : public ThreadState { int flatIndex = isa.flattenFloatIndex(reg_idx); assert(flatIndex < TheISA::NumFloatRegs); - floatRegs.i[flatIndex] = val; + // XXX: Fix array out of bounds compiler error for gem5.fast + // when checkercpu enabled + if (flatIndex < TheISA::NumFloatRegs) + floatRegs.i[flatIndex] = val; DPRINTF(FloatRegs, "Setting float reg %d (%d) bits to %#x, %#f.\n", reg_idx, flatIndex, val, floatRegs.f[flatIndex]); } @@ -312,6 +332,14 @@ class SimpleThread : public ThreadState _pcState = val; } +#if USE_CHECKER + void + pcStateNoRecord(const TheISA::PCState &val) + { + _pcState = val; + } +#endif + Addr instAddr() { diff --git a/src/cpu/thread_context.hh b/src/cpu/thread_context.hh index d80d26e3d..946c35249 100644 --- a/src/cpu/thread_context.hh +++ b/src/cpu/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. * @@ -39,6 +51,7 @@ #include "base/types.hh" #include "config/full_system.hh" #include "config/the_isa.hh" +#include "config/use_checker.hh" // @todo: Figure out a more architecture independent way to obtain the ITB and // DTB pointers. @@ -121,6 +134,10 @@ class ThreadContext virtual TheISA::TLB *getDTBPtr() = 0; +#if USE_CHECKER + virtual BaseCPU *getCheckerCpuPtr() = 0; +#endif + virtual Decoder *getDecoderPtr() = 0; virtual System *getSystemPtr() = 0; @@ -205,6 +222,10 @@ class ThreadContext virtual void pcState(const TheISA::PCState &val) = 0; +#if USE_CHECKER + virtual void pcStateNoRecord(const TheISA::PCState &val) = 0; +#endif + virtual Addr instAddr() = 0; virtual Addr nextInstAddr() = 0; @@ -296,6 +317,10 @@ class ProxyThreadContext : public ThreadContext TheISA::TLB *getDTBPtr() { return actualTC->getDTBPtr(); } +#if USE_CHECKER + BaseCPU *getCheckerCpuPtr() { return actualTC->getCheckerCpuPtr(); } +#endif + Decoder *getDecoderPtr() { return actualTC->getDecoderPtr(); } System *getSystemPtr() { return actualTC->getSystemPtr(); } @@ -382,6 +407,10 @@ class ProxyThreadContext : public ThreadContext void pcState(const TheISA::PCState &val) { actualTC->pcState(val); } +#if USE_CHECKER + void pcStateNoRecord(const TheISA::PCState &val) { actualTC->pcState(val); } +#endif + Addr instAddr() { return actualTC->instAddr(); } Addr nextInstAddr() { return actualTC->nextInstAddr(); } MicroPC microPC() { return actualTC->microPC(); } |