From: Geoffrey Blake Date: Tue, 31 Jan 2012 15:46:03 +0000 (-0800) Subject: CheckerCPU: Re-factor CheckerCPU to be compatible with current gem5 X-Git-Tag: stable_2012_06_28~264^2~4 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=af6aaf258171027af8d3cf0ef86dddff501a3ccb;p=gem5.git CheckerCPU: Re-factor CheckerCPU to be compatible with current gem5 Brings the CheckerCPU back to life to allow FS and SE checking of the O3CPU. These changes have only been tested with the ARM ISA. Other ISAs potentially require modification. --- diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc index b504550a1..5c2478946 100644 --- a/src/arch/arm/isa.cc +++ b/src/arch/arm/isa.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 @@ -39,12 +39,17 @@ */ #include "arch/arm/isa.hh" +#include "config/use_checker.hh" #include "debug/Arm.hh" #include "debug/MiscRegs.hh" #include "sim/faults.hh" #include "sim/stat_control.hh" #include "sim/system.hh" +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + namespace ArmISA { @@ -279,7 +284,11 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc) PCState pc = tc->pcState(); pc.nextThumb(cpsr.t); pc.nextJazelle(cpsr.j); +#if USE_CHECKER + tc->pcStateNoRecord(pc); +#else tc->pcState(pc); +#endif //USE_CHECKER } else if (misc_reg >= MISCREG_CP15_UNIMP_START && misc_reg < MISCREG_CP15_END) { panic("Unimplemented CP15 register %s wrote with %#x.\n", @@ -382,6 +391,14 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc) oc = sys->getThreadContext(x); oc->getDTBPtr()->allCpusCaching(); oc->getITBPtr()->allCpusCaching(); +#if USE_CHECKER + CheckerCPU *checker = + dynamic_cast(oc->getCheckerCpuPtr()); + if (checker) { + checker->getDTBPtr()->allCpusCaching(); + checker->getITBPtr()->allCpusCaching(); + } +#endif } return; } @@ -399,6 +416,14 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc) assert(oc->getITBPtr() && oc->getDTBPtr()); oc->getITBPtr()->flushAll(); oc->getDTBPtr()->flushAll(); +#if USE_CHECKER + CheckerCPU *checker = + dynamic_cast(oc->getCheckerCpuPtr()); + if (checker) { + checker->getITBPtr()->flushAll(); + checker->getDTBPtr()->flushAll(); + } +#endif } return; case MISCREG_ITLBIALL: @@ -417,6 +442,16 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc) bits(newVal, 7,0)); oc->getDTBPtr()->flushMvaAsid(mbits(newVal, 31, 12), bits(newVal, 7,0)); +#if USE_CHECKER + CheckerCPU *checker = + dynamic_cast(oc->getCheckerCpuPtr()); + if (checker) { + checker->getITBPtr()->flushMvaAsid(mbits(newVal, 31, 12), + bits(newVal, 7,0)); + checker->getDTBPtr()->flushMvaAsid(mbits(newVal, 31, 12), + bits(newVal, 7,0)); + } +#endif } return; case MISCREG_TLBIASIDIS: @@ -427,6 +462,14 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc) assert(oc->getITBPtr() && oc->getDTBPtr()); oc->getITBPtr()->flushAsid(bits(newVal, 7,0)); oc->getDTBPtr()->flushAsid(bits(newVal, 7,0)); +#if USE_CHECKER + CheckerCPU *checker = + dynamic_cast(oc->getCheckerCpuPtr()); + if (checker) { + checker->getITBPtr()->flushAsid(bits(newVal, 7,0)); + checker->getDTBPtr()->flushAsid(bits(newVal, 7,0)); + } +#endif } return; case MISCREG_TLBIMVAAIS: @@ -437,6 +480,14 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc) assert(oc->getITBPtr() && oc->getDTBPtr()); oc->getITBPtr()->flushMva(mbits(newVal, 31,12)); oc->getDTBPtr()->flushMva(mbits(newVal, 31,12)); +#if USE_CHECKER + CheckerCPU *checker = + dynamic_cast(oc->getCheckerCpuPtr()); + if (checker) { + checker->getITBPtr()->flushMva(mbits(newVal, 31,12)); + checker->getDTBPtr()->flushMva(mbits(newVal, 31,12)); + } +#endif } return; case MISCREG_ITLBIMVA: diff --git a/src/arch/arm/isa/insts/m5ops.isa b/src/arch/arm/isa/insts/m5ops.isa index f20908d4f..222ecc647 100644 --- a/src/arch/arm/isa/insts/m5ops.isa +++ b/src/arch/arm/isa/insts/m5ops.isa @@ -247,7 +247,7 @@ let {{ m5checkpointIop = InstObjParams("m5checkpoint", "M5checkpoint", "PredOp", { "code": m5checkpoint_code, "predicate_test": predicateTest }, - ["IsNonSpeculative"]) + ["IsNonSpeculative", "IsUnverifiable"]) header_output += BasicDeclare.subst(m5checkpointIop) decoder_output += BasicConstructor.subst(m5checkpointIop) exec_output += PredOpExecute.subst(m5checkpointIop) @@ -260,7 +260,7 @@ let {{ m5readfileIop = InstObjParams("m5readfile", "M5readfile", "PredOp", { "code": m5readfileCode, "predicate_test": predicateTest }, - ["IsNonSpeculative"]) + ["IsNonSpeculative", "IsUnverifiable"]) header_output += BasicDeclare.subst(m5readfileIop) decoder_output += BasicConstructor.subst(m5readfileIop) exec_output += PredOpExecute.subst(m5readfileIop) diff --git a/src/arch/arm/isa/insts/misc.isa b/src/arch/arm/isa/insts/misc.isa index 870f037d0..45d5d8d32 100644 --- a/src/arch/arm/isa/insts/misc.isa +++ b/src/arch/arm/isa/insts/misc.isa @@ -525,7 +525,8 @@ let {{ { "code" : wfeCode, "pred_fixup" : wfePredFixUpCode, "predicate_test" : predicateTest }, - ["IsNonSpeculative", "IsQuiesce", "IsSerializeAfter"]) + ["IsNonSpeculative", "IsQuiesce", + "IsSerializeAfter", "IsUnverifiable"]) header_output += BasicDeclare.subst(wfeIop) decoder_output += BasicConstructor.subst(wfeIop) exec_output += QuiescePredOpExecuteWithFixup.subst(wfeIop) @@ -542,7 +543,8 @@ let {{ ''' wfiIop = InstObjParams("wfi", "WfiInst", "PredOp", \ { "code" : wfiCode, "predicate_test" : predicateTest }, - ["IsNonSpeculative", "IsQuiesce", "IsSerializeAfter"]) + ["IsNonSpeculative", "IsQuiesce", + "IsSerializeAfter", "IsUnverifiable"]) header_output += BasicDeclare.subst(wfiIop) decoder_output += BasicConstructor.subst(wfiIop) exec_output += QuiescePredOpExecute.subst(wfiIop) @@ -565,7 +567,7 @@ let {{ ''' sevIop = InstObjParams("sev", "SevInst", "PredOp", \ { "code" : sevCode, "predicate_test" : predicateTest }, - ["IsNonSpeculative", "IsSquashAfter"]) + ["IsNonSpeculative", "IsSquashAfter", "IsUnverifiable"]) header_output += BasicDeclare.subst(sevIop) decoder_output += BasicConstructor.subst(sevIop) exec_output += PredOpExecute.subst(sevIop) diff --git a/src/arch/arm/table_walker.cc b/src/arch/arm/table_walker.cc index b2ab010c0..9c92ebdf6 100644 --- a/src/arch/arm/table_walker.cc +++ b/src/arch/arm/table_walker.cc @@ -107,8 +107,9 @@ TableWalker::getPort(const std::string &if_name, int idx) Fault TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint8_t _cid, TLB::Mode _mode, - TLB::Translation *_trans, bool _timing) + TLB::Translation *_trans, bool _timing, bool _functional) { + assert(!(_functional && _timing)); if (!currState) { // For atomic mode, a new WalkerState instance should be only created // once per TLB. For timing mode, a new instance is generated for every @@ -136,6 +137,7 @@ TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint8_t _cid, TLB::Mode _ currState->fault = NoFault; currState->contextId = _cid; currState->timing = _timing; + currState->functional = _functional; currState->mode = _mode; /** @todo These should be cached or grabbed from cached copies in @@ -230,12 +232,21 @@ TableWalker::processWalk() stateQueueL1.size()); stateQueueL1.push_back(currState); currState = NULL; - } else { + } else if (!currState->functional) { port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t), NULL, (uint8_t*)&currState->l1Desc.data, currState->tc->getCpuPtr()->ticks(1), flag); doL1Descriptor(); f = currState->fault; + } else { + RequestPtr req = new Request(l1desc_addr, sizeof(uint32_t), flag); + PacketPtr pkt = new Packet(req, MemCmd::ReadReq, Packet::Broadcast); + pkt->dataStatic((uint8_t*)&currState->l1Desc.data); + port->sendFunctional(pkt); + doL1Descriptor(); + delete req; + delete pkt; + f = currState->fault; } return f; @@ -566,11 +577,19 @@ TableWalker::doL1Descriptor() port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t), &doL2DescEvent, (uint8_t*)&currState->l2Desc.data, currState->tc->getCpuPtr()->ticks(1)); - } else { + } else if (!currState->functional) { port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t), NULL, (uint8_t*)&currState->l2Desc.data, currState->tc->getCpuPtr()->ticks(1)); doL2Descriptor(); + } else { + RequestPtr req = new Request(l2desc_addr, sizeof(uint32_t), 0); + PacketPtr pkt = new Packet(req, MemCmd::ReadReq, Packet::Broadcast); + pkt->dataStatic((uint8_t*)&currState->l2Desc.data); + port->sendFunctional(pkt); + doL2Descriptor(); + delete req; + delete pkt; } return; default: diff --git a/src/arch/arm/table_walker.hh b/src/arch/arm/table_walker.hh index 85db1fa77..d4a2e87b5 100644 --- a/src/arch/arm/table_walker.hh +++ b/src/arch/arm/table_walker.hh @@ -294,6 +294,9 @@ class TableWalker : public MemObject /** If the mode is timing or atomic */ bool timing; + /** If the atomic mode should be functional */ + bool functional; + /** Save mode for use in delayed response */ BaseTLB::Mode mode; @@ -354,7 +357,7 @@ class TableWalker : public MemObject virtual Port *getPort(const std::string &if_name, int idx = -1); Fault walk(RequestPtr req, ThreadContext *tc, uint8_t cid, TLB::Mode mode, - TLB::Translation *_trans, bool timing); + TLB::Translation *_trans, bool timing, bool functional = false); void setTlb(TLB *_tlb) { tlb = _tlb; } void memAttrs(ThreadContext *tc, TlbEntry &te, SCTLR sctlr, diff --git a/src/arch/arm/tlb.cc b/src/arch/arm/tlb.cc index a03e445cf..f4dc47655 100644 --- a/src/arch/arm/tlb.cc +++ b/src/arch/arm/tlb.cc @@ -453,8 +453,11 @@ TLB::walkTrickBoxCheck(Addr pa, Addr va, Addr sz, bool is_exec, Fault TLB::translateFs(RequestPtr req, ThreadContext *tc, Mode mode, - Translation *translation, bool &delay, bool timing) + Translation *translation, bool &delay, bool timing, bool functional) { + // No such thing as a functional timing access + assert(!(timing && functional)); + if (!miscRegValid) { updateMiscReg(tc); DPRINTF(TLBVerbose, "TLB variables changed!\n"); @@ -541,7 +544,7 @@ TLB::translateFs(RequestPtr req, ThreadContext *tc, Mode mode, DPRINTF(TLB, "TLB Miss: Starting hardware table walker for %#x(%d)\n", vaddr, contextId); fault = tableWalker->walk(req, tc, contextId, mode, translation, - timing); + timing, functional); if (timing && fault == NoFault) { delay = true; // for timing mode, return and wait for table walk @@ -700,6 +703,20 @@ TLB::translateAtomic(RequestPtr req, ThreadContext *tc, Mode mode) return fault; } +Fault +TLB::translateFunctional(RequestPtr req, ThreadContext *tc, Mode mode) +{ + bool delay = false; + Fault fault; +#if FULL_SYSTEM + fault = translateFs(req, tc, mode, NULL, delay, false, true); +#else + fault = translateSe(req, tc, mode, NULL, delay, false); +#endif + assert(!delay); + return fault; +} + Fault TLB::translateTiming(RequestPtr req, ThreadContext *tc, Translation *translation, Mode mode) diff --git a/src/arch/arm/tlb.hh b/src/arch/arm/tlb.hh index 3464e42b3..bdfa2fc9f 100644 --- a/src/arch/arm/tlb.hh +++ b/src/arch/arm/tlb.hh @@ -182,6 +182,12 @@ class TLB : public BaseTLB */ bool translateFunctional(ThreadContext *tc, Addr vaddr, Addr &paddr); + /** + * Do a functional lookup on the TLB (for checker cpu) that + * behaves like a normal lookup without modifying any page table state. + */ + Fault translateFunctional(RequestPtr req, ThreadContext *tc, Mode mode); + /** Accessor functions for memory attributes for last accessed TLB entry */ void @@ -197,7 +203,8 @@ class TLB : public BaseTLB #if FULL_SYSTEM Fault translateFs(RequestPtr req, ThreadContext *tc, Mode mode, - Translation *translation, bool &delay, bool timing); + Translation *translation, bool &delay, + bool timing, bool functional = false); #else Fault translateSe(RequestPtr req, ThreadContext *tc, Mode mode, Translation *translation, bool &delay, bool timing); diff --git a/src/arch/arm/utility.cc b/src/arch/arm/utility.cc index 98195ab04..b31dc4324 100644 --- a/src/arch/arm/utility.cc +++ b/src/arch/arm/utility.cc @@ -41,6 +41,7 @@ #include "arch/arm/faults.hh" #include "arch/arm/isa_traits.hh" #include "arch/arm/utility.hh" +#include "config/use_checker.hh" #include "cpu/thread_context.hh" #if FULL_SYSTEM @@ -116,7 +117,11 @@ skipFunction(ThreadContext *tc) { TheISA::PCState newPC = tc->pcState(); newPC.set(tc->readIntReg(ReturnAddressReg) & ~ULL(1)); +#if USE_CHECKER + tc->pcStateNoRecord(newPC); +#else tc->pcState(newPC); +#endif } void 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(oldTC->getCheckerCpuPtr()); + CheckerCPU *newChecker = + dynamic_cast(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 #include #include +#include #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 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 + 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 + 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 + 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(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(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(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(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(val); } /** Records that one of the source registers is ready. */ @@ -872,6 +894,12 @@ BaseDynInst::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::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::initVars() reqMade = false; readyRegs = 0; - instResult.integer = 0; recordResult = true; status.reset(); @@ -157,6 +157,10 @@ BaseDynInst::initVars() #ifdef DEBUG cpu->snList.insert(seqNum); #endif + +#if USE_CHECKER + reqToVerify = NULL; +#endif } template @@ -182,6 +186,11 @@ BaseDynInst::~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 @@ -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 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 -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 #include +#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 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(_params); } + { return reinterpret_cast(_params); } CheckerCPU(Params *p); virtual ~CheckerCPU(); - Process *process; + std::vector 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; // 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 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 - Fault read(Addr addr, T &data, unsigned flags); - - template - 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 + 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(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(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(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 +template 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 instList; typedef typename std::list::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 @@ -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 +void +Checker::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 +void +Checker::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::iterator itr; + + for (itr = instList.begin(); itr != instList.end(); itr++) { + (*itr)->setCompleted(); + } -template + 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 void -Checker::verify(DynInstPtr &completed_inst) +Checker::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::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::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::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::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::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::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::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::verify(DynInstPtr &completed_inst) unverifiedInst = NULL; } -template +template void -Checker::switchOut() +Checker::switchOut() { instList.clear(); } -template +template void -Checker::takeOverFrom(BaseCPU *oldCPU) +Checker::takeOverFrom(BaseCPU *oldCPU) { } -template +template void -Checker::validateInst(DynInstPtr &inst) +Checker::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::validateInst(DynInstPtr &inst) MachInst mi = static_cast(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 +template void -Checker::validateExecution(DynInstPtr &inst) +Checker::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(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::validateExecution(DynInstPtr &inst) } } -template + +// 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 void -Checker::validateState() +Checker::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 +template void -Checker::copyResult(DynInstPtr &inst) +Checker::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(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 +template void -Checker::dumpAndExit(DynInstPtr &inst) +Checker::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::dumpAndExit(DynInstPtr &inst) CheckerCPU::dumpAndExit(); } -template +template void -Checker::dumpInsts() +Checker::dumpInsts() { int num = 0; @@ -465,9 +662,9 @@ Checker::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 + +#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 #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 > >; +class Checker; /** * Specific non-templated derived class used for SimObject configuration. */ -class O3Checker : public Checker > > +class O3Checker : public Checker { public: O3Checker(Params *p) - : Checker > >(p) + : Checker(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::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::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::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::FullO3CPU(DerivO3CPUParams *params) #if USE_CHECKER if (params->checker) { BaseCPU *temp_checker = params->checker; - checker = dynamic_cast *>(temp_checker); + checker = dynamic_cast *>(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 *checker; + Checker *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 @@ -136,6 +137,11 @@ BaseO3DynInst::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 #include +#include +#include +#include #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 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 @@ -294,6 +299,13 @@ DefaultIEW::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::setDcachePort(Port *dcache_port) { dcachePort = dcache_port; - -#if USE_CHECKER - if (cpu->checker) { - cpu->checker->setDcachePort(dcachePort); - } -#endif } template @@ -878,6 +871,11 @@ LSQUnit::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::pcState(const TheISA::PCState &val) } } +#if USE_CHECKER +template +void +O3ThreadContext::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 int O3ThreadContext::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(temp_checker); +#if FULL_SYSTEM + checker->setSystem(p->system); +#endif + // Manipulate thread context + ThreadContext *cpu_tc = tc; + tc = new CheckerThreadContext(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(); } diff --git a/src/mem/bus.cc b/src/mem/bus.cc index a20f90108..dfe4be3cc 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -436,7 +436,8 @@ Bus::recvFunctional(PacketPtr pkt) pkt->setSrc(src_id); } - // If the snooping hasn't found what we were looking for, keep going. + // If the snooping hasn't found what we were looking for and it is not + // a forwarded snoop from below, keep going. if (!pkt->isResponse() && port_id != pkt->getSrc()) { port->sendFunctional(pkt); }