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