2006-05-16 19:59:29 +02:00
|
|
|
/*
|
2012-01-31 16:46:03 +01:00
|
|
|
* Copyright (c) 2011 ARM Limited
|
2013-10-15 20:22:42 +02:00
|
|
|
* Copyright (c) 2013 Advanced Micro Devices, Inc.
|
2012-01-31 16:46:03 +01:00
|
|
|
* 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.
|
|
|
|
*
|
2006-05-23 22:59:13 +02:00
|
|
|
* Copyright (c) 2006 The Regents of The University of Michigan
|
2006-05-16 19:59:29 +02:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are
|
|
|
|
* met: redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer;
|
|
|
|
* redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution;
|
|
|
|
* neither the name of the copyright holders nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived from
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2006-06-07 22:02:55 +02:00
|
|
|
*
|
|
|
|
* Authors: Kevin Lim
|
2012-01-31 16:46:03 +01:00
|
|
|
* Geoffrey Blake
|
2006-05-16 19:59:29 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <list>
|
|
|
|
#include <string>
|
|
|
|
|
2012-03-09 15:59:28 +01:00
|
|
|
#include "arch/isa_traits.hh"
|
2011-11-18 10:33:28 +01:00
|
|
|
#include "arch/vtophys.hh"
|
2006-05-16 19:59:29 +02:00
|
|
|
#include "base/refcnt.hh"
|
2009-09-23 17:34:21 +02:00
|
|
|
#include "config/the_isa.hh"
|
2011-04-15 19:44:06 +02:00
|
|
|
#include "cpu/base_dyn_inst.hh"
|
2012-01-31 16:46:03 +01:00
|
|
|
#include "cpu/exetrace.hh"
|
2013-10-15 20:22:42 +02:00
|
|
|
#include "cpu/reg_class.hh"
|
2006-06-07 21:29:53 +02:00
|
|
|
#include "cpu/simple_thread.hh"
|
2006-05-16 19:59:29 +02:00
|
|
|
#include "cpu/static_inst.hh"
|
2011-04-15 19:44:06 +02:00
|
|
|
#include "cpu/thread_context.hh"
|
2012-01-31 16:46:03 +01:00
|
|
|
#include "cpu/checker/cpu.hh"
|
|
|
|
#include "debug/Checker.hh"
|
2011-11-18 10:33:28 +01:00
|
|
|
#include "sim/full_system.hh"
|
2006-05-16 19:59:29 +02:00
|
|
|
#include "sim/sim_object.hh"
|
|
|
|
#include "sim/stats.hh"
|
|
|
|
|
|
|
|
using namespace std;
|
2012-01-31 16:46:03 +01:00
|
|
|
using namespace TheISA;
|
|
|
|
|
|
|
|
template <class Impl>
|
|
|
|
void
|
|
|
|
Checker<Impl>::advancePC(Fault fault)
|
|
|
|
{
|
|
|
|
if (fault != NoFault) {
|
|
|
|
curMacroStaticInst = StaticInst::nullStaticInstPtr;
|
|
|
|
fault->invoke(tc, curStaticInst);
|
2012-05-26 22:44:46 +02:00
|
|
|
thread->decoder.reset();
|
2012-01-31 16:46:03 +01:00
|
|
|
} 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();
|
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
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;
|
2012-05-26 22:44:46 +02:00
|
|
|
thread->decoder.reset();
|
2012-01-31 16:46:03 +01:00
|
|
|
curMacroStaticInst = StaticInst::nullStaticInstPtr;
|
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-05-16 19:59:29 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::verify(DynInstPtr &completed_inst)
|
2006-05-16 19:59:29 +02:00
|
|
|
{
|
|
|
|
DynInstPtr inst;
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
|
2006-05-23 22:59:13 +02:00
|
|
|
// Either check this instruction, or add it to a list of
|
|
|
|
// instructions waiting to be checked. Instructions must be
|
|
|
|
// checked in program order, so if a store has committed yet not
|
|
|
|
// completed, there may be some instructions that are waiting
|
|
|
|
// behind it that have completed and must be checked.
|
2006-05-16 19:59:29 +02:00
|
|
|
if (!instList.empty()) {
|
|
|
|
if (youngestSN < completed_inst->seqNum) {
|
2012-01-31 16:46:03 +01:00
|
|
|
DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n",
|
|
|
|
completed_inst->seqNum, completed_inst->pcState());
|
2006-05-16 19:59:29 +02:00
|
|
|
instList.push_back(completed_inst);
|
|
|
|
youngestSN = completed_inst->seqNum;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!instList.front()->isCompleted()) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
inst = instList.front();
|
|
|
|
instList.pop_front();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!completed_inst->isCompleted()) {
|
|
|
|
if (youngestSN < completed_inst->seqNum) {
|
2012-01-31 16:46:03 +01:00
|
|
|
DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n",
|
|
|
|
completed_inst->seqNum, completed_inst->pcState());
|
2006-05-16 19:59:29 +02:00
|
|
|
instList.push_back(completed_inst);
|
|
|
|
youngestSN = completed_inst->seqNum;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
if (youngestSN < completed_inst->seqNum) {
|
|
|
|
inst = completed_inst;
|
|
|
|
youngestSN = completed_inst->seqNum;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
// 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());
|
|
|
|
}
|
2006-08-02 18:06:59 +02:00
|
|
|
unverifiedInst = inst;
|
2012-01-31 16:46:03 +01:00
|
|
|
inst = NULL;
|
2006-08-02 18:06:59 +02:00
|
|
|
|
2006-05-23 22:59:13 +02:00
|
|
|
// 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.
|
2006-05-16 19:59:29 +02:00
|
|
|
while (1) {
|
2012-01-31 16:46:03 +01:00
|
|
|
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();
|
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
numCycles++;
|
|
|
|
|
|
|
|
Fault fault = NoFault;
|
|
|
|
|
|
|
|
// maintain $r0 semantics
|
2006-06-07 21:29:53 +02:00
|
|
|
thread->setIntReg(ZeroReg, 0);
|
2012-03-09 15:59:28 +01:00
|
|
|
#if THE_ISA == ALPHA_ISA
|
|
|
|
thread->setFloatReg(ZeroReg, 0.0);
|
|
|
|
#endif
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2006-05-23 22:59:13 +02:00
|
|
|
// Check if any recent PC changes match up with anything we
|
|
|
|
// expect to happen. This is mostly to check if traps or
|
|
|
|
// PC-based events have occurred in both the checker and CPU.
|
2006-05-16 19:59:29 +02:00
|
|
|
if (changedPC) {
|
2012-01-31 16:46:03 +01:00
|
|
|
DPRINTF(Checker, "Changed PC recently to %s\n",
|
|
|
|
thread->pcState());
|
2006-05-16 19:59:29 +02:00
|
|
|
if (willChangePC) {
|
2012-01-31 16:46:03 +01:00
|
|
|
if (newPCState == thread->pcState()) {
|
2006-05-16 19:59:29 +02:00
|
|
|
DPRINTF(Checker, "Changed PC matches expected PC\n");
|
|
|
|
} else {
|
2006-05-23 22:59:13 +02:00
|
|
|
warn("%lli: Changed PC does not match expected PC, "
|
2012-01-31 16:46:03 +01:00
|
|
|
"changed: %s, expected: %s",
|
|
|
|
curTick(), thread->pcState(), newPCState);
|
2006-06-16 19:10:47 +02:00
|
|
|
CheckerCPU::handleError();
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
willChangePC = false;
|
|
|
|
}
|
|
|
|
changedPC = false;
|
|
|
|
}
|
|
|
|
if (changedNextPC) {
|
|
|
|
DPRINTF(Checker, "Changed NextPC recently to %#x\n",
|
2012-01-31 16:46:03 +01:00
|
|
|
thread->nextInstAddr());
|
2006-05-16 19:59:29 +02:00
|
|
|
changedNextPC = false;
|
|
|
|
}
|
|
|
|
|
2006-05-23 22:59:13 +02:00
|
|
|
// Try to fetch the instruction
|
2012-01-31 16:46:03 +01:00
|
|
|
uint64_t fetchOffset = 0;
|
|
|
|
bool fetchDone = false;
|
|
|
|
|
|
|
|
while (!fetchDone) {
|
|
|
|
Addr fetch_PC = thread->instAddr();
|
|
|
|
fetch_PC = (fetch_PC & PCMask) + fetchOffset;
|
|
|
|
|
2012-05-26 22:44:46 +02:00
|
|
|
MachInst machInst;
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
// 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,
|
2012-03-09 15:59:27 +01:00
|
|
|
masterId,
|
2012-01-31 16:46:03 +01:00
|
|
|
fetch_PC, thread->contextId(),
|
|
|
|
unverifiedInst->threadNumber);
|
|
|
|
memReq->setVirt(0, fetch_PC, sizeof(MachInst),
|
2012-02-12 23:07:38 +01:00
|
|
|
Request::INST_FETCH, masterId, thread->instAddr());
|
2012-01-31 16:46:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
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 {
|
MEM: Remove the Broadcast destination from the packet
This patch simplifies the packet by removing the broadcast flag and
instead more firmly relying on (and enforcing) the semantics of
transactions in the classic memory system, i.e. request packets are
routed from a master to a slave based on the address, and when they
are created they have neither a valid source, nor destination. On
their way to the slave, the request packet is updated with a source
field for all modules that multiplex packets from multiple master
(e.g. a bus). When a request packet is turned into a response packet
(at the final slave), it moves the potentially populated source field
to the destination field, and the response packet is routed through
any multiplexing components back to the master based on the
destination field.
Modules that connect multiplexing components, such as caches and
bridges store any existing source and destination field in the sender
state as a stack (just as before).
The packet constructor is simplified in that there is no longer a need
to pass the Packet::Broadcast as the destination (this was always the
case for the classic memory system). In the case of Ruby, rather than
using the parameter to the constructor we now rely on setDest, as
there is already another three-argument constructor in the packet
class.
In many places where the packet information was printed as part of
DPRINTFs, request packets would be printed with a numeric "dest" that
would always be -1 (Broadcast) and that field is now removed from the
printing.
2012-04-14 11:45:55 +02:00
|
|
|
PacketPtr pkt = new Packet(memReq, MemCmd::ReadReq);
|
2006-05-23 22:59:13 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
pkt->dataStatic(&machInst);
|
|
|
|
icachePort->sendFunctional(pkt);
|
|
|
|
machInst = gtoh(machInst);
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
delete memReq;
|
|
|
|
delete pkt;
|
|
|
|
}
|
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
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
|
|
|
|
//If more fetch data is needed, pass it in.
|
|
|
|
Addr fetchPC = (pcState.instAddr() & PCMask) + fetchOffset;
|
2012-05-26 22:44:46 +02:00
|
|
|
thread->decoder.moreBytes(pcState, fetchPC, machInst);
|
2012-01-31 16:46:03 +01:00
|
|
|
|
|
|
|
//If an instruction is ready, decode it.
|
|
|
|
//Otherwise, we'll have to fetch beyond the
|
|
|
|
//MachInst at the current pc.
|
2012-05-26 22:44:46 +02:00
|
|
|
if (thread->decoder.instReady()) {
|
2012-01-31 16:46:03 +01:00
|
|
|
fetchDone = true;
|
2012-05-26 22:44:46 +02:00
|
|
|
instPtr = thread->decoder.decode(pcState);
|
2012-01-31 16:46:03 +01:00
|
|
|
thread->pcState(pcState);
|
|
|
|
} else {
|
|
|
|
fetchDone = false;
|
|
|
|
fetchOffset += sizeof(TheISA::MachInst);
|
|
|
|
}
|
|
|
|
|
|
|
|
//If we decoded an instruction and it's microcoded,
|
|
|
|
//start pulling out micro ops
|
|
|
|
if (instPtr && instPtr->isMacroop()) {
|
|
|
|
curMacroStaticInst = instPtr;
|
|
|
|
curStaticInst =
|
|
|
|
instPtr->fetchMicroop(pcState.microPC());
|
|
|
|
} else {
|
|
|
|
curStaticInst = instPtr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Read the next micro op from the macro-op
|
|
|
|
curStaticInst =
|
|
|
|
curMacroStaticInst->fetchMicroop(pcState.microPC());
|
|
|
|
fetchDone = true;
|
|
|
|
}
|
2006-05-17 20:25:10 +02:00
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
2012-05-26 22:44:46 +02:00
|
|
|
// reset decoder on Checker
|
|
|
|
thread->decoder.reset();
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
// Check Checker and CPU get same instruction, and record
|
|
|
|
// any faults the CPU may have had.
|
|
|
|
Fault unverifiedFault;
|
2006-05-17 20:25:10 +02:00
|
|
|
if (fault == NoFault) {
|
2012-01-31 16:46:03 +01:00
|
|
|
unverifiedFault = unverifiedInst->getFault();
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2006-05-17 20:25:10 +02:00
|
|
|
// Checks that the instruction matches what we expected it to be.
|
|
|
|
// Checks both the machine instruction and the PC.
|
2012-01-31 16:46:03 +01:00
|
|
|
validateInst(unverifiedInst);
|
2006-05-17 20:25:10 +02:00
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
// keep an instruction count
|
|
|
|
numInst++;
|
|
|
|
|
2006-06-06 20:06:30 +02:00
|
|
|
|
2006-05-16 19:59:29 +02:00
|
|
|
// 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) {
|
2012-01-31 16:46:03 +01:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
if (fault == NoFault && unverifiedFault == NoFault) {
|
|
|
|
thread->funcExeInst++;
|
|
|
|
// Checks to make sure instrution results are correct.
|
|
|
|
validateExecution(unverifiedInst);
|
2006-05-16 19:59:29 +02:00
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
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());
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
// Take any faults here
|
2006-05-16 19:59:29 +02:00
|
|
|
if (fault != NoFault) {
|
2012-03-09 15:59:27 +01:00
|
|
|
if (FullSystem) {
|
|
|
|
fault->invoke(tc, curStaticInst);
|
|
|
|
willChangePC = true;
|
|
|
|
newPCState = thread->pcState();
|
|
|
|
DPRINTF(Checker, "Fault, PC is now %s\n", newPCState);
|
|
|
|
curMacroStaticInst = StaticInst::nullStaticInstPtr;
|
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
} else {
|
2012-01-31 16:46:03 +01:00
|
|
|
advancePC(fault);
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
|
2011-11-18 10:33:28 +01:00
|
|
|
if (FullSystem) {
|
|
|
|
// @todo: Determine if these should happen only if the
|
|
|
|
// instruction hasn't faulted. In the SimpleCPU case this may
|
|
|
|
// not be true, but in the O3 or Ozone case this may be true.
|
|
|
|
Addr oldpc;
|
|
|
|
int count = 0;
|
|
|
|
do {
|
2012-02-01 07:40:08 +01:00
|
|
|
oldpc = thread->instAddr();
|
2011-11-18 10:33:28 +01:00
|
|
|
system->pcEventQueue.service(tc);
|
|
|
|
count++;
|
2012-02-01 07:40:08 +01:00
|
|
|
} while (oldpc != thread->instAddr());
|
2011-11-18 10:33:28 +01:00
|
|
|
if (count > 1) {
|
|
|
|
willChangePC = true;
|
2012-02-01 07:40:08 +01:00
|
|
|
newPCState = thread->pcState();
|
|
|
|
DPRINTF(Checker, "PC Event, PC is now %s\n", newPCState);
|
2011-11-18 10:33:28 +01:00
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
|
2006-05-23 22:59:13 +02:00
|
|
|
// @todo: Optionally can check all registers. (Or just those
|
2006-05-16 19:59:29 +02:00
|
|
|
// that have been modified).
|
|
|
|
validateState();
|
|
|
|
|
2006-05-23 22:59:13 +02:00
|
|
|
// Continue verifying instructions if there's another completed
|
|
|
|
// instruction waiting to be verified.
|
2006-05-16 19:59:29 +02:00
|
|
|
if (instList.empty()) {
|
|
|
|
break;
|
|
|
|
} else if (instList.front()->isCompleted()) {
|
2012-01-31 16:46:03 +01:00
|
|
|
unverifiedInst = NULL;
|
|
|
|
unverifiedInst = instList.front();
|
2006-05-16 19:59:29 +02:00
|
|
|
instList.pop_front();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-08-02 18:06:59 +02:00
|
|
|
unverifiedInst = NULL;
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-05-16 19:59:29 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::switchOut()
|
2006-05-16 19:59:29 +02:00
|
|
|
{
|
|
|
|
instList.clear();
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-05-16 19:59:29 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::takeOverFrom(BaseCPU *oldCPU)
|
2006-05-16 19:59:29 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-05-16 19:59:29 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::validateInst(DynInstPtr &inst)
|
2006-05-16 19:59:29 +02:00
|
|
|
{
|
2012-01-31 16:46:03 +01:00
|
|
|
if (inst->instAddr() != thread->instAddr()) {
|
|
|
|
warn("%lli: PCs do not match! Inst: %s, checker: %s",
|
|
|
|
curTick(), inst->pcState(), thread->pcState());
|
2006-05-16 19:59:29 +02:00
|
|
|
if (changedPC) {
|
2006-05-23 22:59:13 +02:00
|
|
|
warn("%lli: Changed PCs recently, may not be an error",
|
2011-01-08 06:50:29 +01:00
|
|
|
curTick());
|
2006-05-16 19:59:29 +02:00
|
|
|
} else {
|
2006-06-16 19:10:47 +02:00
|
|
|
handleError(inst);
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 22:44:46 +02:00
|
|
|
if (curStaticInst != inst->staticInst) {
|
|
|
|
warn("%lli: StaticInstPtrs don't match. (%s, %s).\n", curTick(),
|
|
|
|
curStaticInst->getName(), inst->staticInst->getName());
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-05-16 19:59:29 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::validateExecution(DynInstPtr &inst)
|
2006-05-16 19:59:29 +02:00
|
|
|
{
|
2012-01-31 16:46:03 +01:00
|
|
|
uint64_t checker_val;
|
|
|
|
uint64_t inst_val;
|
|
|
|
int idx = -1;
|
2006-06-16 19:10:47 +02:00
|
|
|
bool result_mismatch = false;
|
2012-01-31 16:46:03 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2006-06-16 19:10:47 +02:00
|
|
|
}
|
2012-01-31 16:46:03 +01:00
|
|
|
} // 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.
|
2006-06-16 19:10:47 +02:00
|
|
|
|
|
|
|
if (result_mismatch) {
|
|
|
|
warn("%lli: Instruction results do not match! (Values may not "
|
|
|
|
"actually be integers) Inst: %#x, checker: %#x",
|
2012-01-31 16:46:03 +01:00
|
|
|
curTick(), inst_val, checker_val);
|
2006-06-16 19:10:47 +02:00
|
|
|
|
|
|
|
// 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.
|
2012-01-31 16:46:03 +01:00
|
|
|
// The load/store queue in Detailed CPU can also cause problems
|
|
|
|
// if load/store forwarding is allowed.
|
2006-06-16 19:10:47 +02:00
|
|
|
if (inst->isLoad() && warnOnlyOnLoadError) {
|
2012-01-31 16:46:03 +01:00
|
|
|
copyResult(inst, inst_val, idx);
|
2006-06-16 19:10:47 +02:00
|
|
|
} else {
|
|
|
|
handleError(inst);
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
if (inst->nextInstAddr() != thread->nextInstAddr()) {
|
2006-05-23 22:59:13 +02:00
|
|
|
warn("%lli: Instruction next PCs do not match! Inst: %#x, "
|
|
|
|
"checker: %#x",
|
2012-01-31 16:46:03 +01:00
|
|
|
curTick(), inst->nextInstAddr(), thread->nextInstAddr());
|
2006-06-16 19:10:47 +02:00
|
|
|
handleError(inst);
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Checking side effect registers can be difficult if they are not
|
|
|
|
// checked simultaneously with the execution of the instruction.
|
|
|
|
// This is because other valid instructions may have modified
|
|
|
|
// these registers in the meantime, and their values are not
|
|
|
|
// stored within the DynInst.
|
|
|
|
while (!miscRegIdxs.empty()) {
|
|
|
|
int misc_reg_idx = miscRegIdxs.front();
|
|
|
|
miscRegIdxs.pop();
|
|
|
|
|
2007-03-07 21:04:31 +01:00
|
|
|
if (inst->tcBase()->readMiscRegNoEffect(misc_reg_idx) !=
|
|
|
|
thread->readMiscRegNoEffect(misc_reg_idx)) {
|
2006-05-23 22:59:13 +02:00
|
|
|
warn("%lli: Misc reg idx %i (side effect) does not match! "
|
|
|
|
"Inst: %#x, checker: %#x",
|
2011-01-08 06:50:29 +01:00
|
|
|
curTick(), misc_reg_idx,
|
2007-03-07 21:04:31 +01:00
|
|
|
inst->tcBase()->readMiscRegNoEffect(misc_reg_idx),
|
|
|
|
thread->readMiscRegNoEffect(misc_reg_idx));
|
2006-06-16 19:10:47 +02:00
|
|
|
handleError(inst);
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
|
|
|
|
// 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>
|
2006-05-16 19:59:29 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::validateState()
|
2006-05-16 19:59:29 +02:00
|
|
|
{
|
2006-08-02 18:06:59 +02:00
|
|
|
if (updateThisCycle) {
|
2012-01-31 16:46:03 +01:00
|
|
|
// 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
|
2013-01-07 19:05:33 +01:00
|
|
|
bool no_squash_from_TC = unverifiedInst->thread->noSquashFromTC;
|
|
|
|
unverifiedInst->thread->noSquashFromTC = true;
|
2012-01-31 16:46:03 +01:00
|
|
|
|
2006-08-02 18:06:59 +02:00
|
|
|
// Heavy-weight copying of all registers
|
2006-10-02 17:58:09 +02:00
|
|
|
thread->copyArchRegs(unverifiedInst->tcBase());
|
2013-01-07 19:05:33 +01:00
|
|
|
unverifiedInst->thread->noSquashFromTC = no_squash_from_TC;
|
2012-01-31 16:46:03 +01:00
|
|
|
|
|
|
|
// Set curStaticInst to unverifiedInst->staticInst
|
|
|
|
curStaticInst = unverifiedInst->staticInst;
|
2006-08-11 23:42:59 +02:00
|
|
|
// Also advance the PC. Hopefully no PC-based events happened.
|
2012-01-31 16:46:03 +01:00
|
|
|
advancePC(NoFault);
|
2006-08-02 18:06:59 +02:00
|
|
|
updateThisCycle = false;
|
2006-10-02 17:58:09 +02:00
|
|
|
}
|
2006-05-16 19:59:29 +02:00
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-06-16 19:10:47 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::copyResult(DynInstPtr &inst, uint64_t mismatch_val,
|
|
|
|
int start_idx)
|
2006-06-16 19:10:47 +02:00
|
|
|
{
|
2012-01-31 16:46:03 +01:00
|
|
|
// 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);
|
2013-10-15 20:22:42 +02:00
|
|
|
switch (regIdxToClass(idx)) {
|
|
|
|
case IntRegClass:
|
2012-01-31 16:46:03 +01:00
|
|
|
thread->setIntReg(idx, mismatch_val);
|
2013-10-15 20:22:42 +02:00
|
|
|
break;
|
|
|
|
case FloatRegClass:
|
2012-01-31 16:46:03 +01:00
|
|
|
thread->setFloatRegBits(idx, mismatch_val);
|
2013-10-15 20:22:42 +02:00
|
|
|
break;
|
|
|
|
case MiscRegClass:
|
2013-10-15 20:22:43 +02:00
|
|
|
thread->setMiscReg(idx - TheISA::Misc_Reg_Base,
|
2012-01-31 16:46:03 +01:00
|
|
|
mismatch_val);
|
2013-10-15 20:22:42 +02:00
|
|
|
break;
|
2012-01-31 16:46:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
2013-10-15 20:22:42 +02:00
|
|
|
switch (regIdxToClass(idx)) {
|
|
|
|
case IntRegClass:
|
2012-01-31 16:46:03 +01:00
|
|
|
thread->setIntReg(idx, res);
|
2013-10-15 20:22:42 +02:00
|
|
|
break;
|
|
|
|
case FloatRegClass:
|
2012-01-31 16:46:03 +01:00
|
|
|
thread->setFloatRegBits(idx, res);
|
2013-10-15 20:22:42 +02:00
|
|
|
break;
|
|
|
|
case MiscRegClass:
|
2012-01-31 16:46:03 +01:00
|
|
|
// Try to get the proper misc register index for ARM here...
|
2013-10-15 20:22:43 +02:00
|
|
|
thread->setMiscReg(idx - TheISA::Misc_Reg_Base, res);
|
2013-10-15 20:22:42 +02:00
|
|
|
break;
|
|
|
|
// else Register is out of range...
|
|
|
|
}
|
2006-06-16 19:10:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-06-16 19:10:47 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::dumpAndExit(DynInstPtr &inst)
|
2006-06-16 19:10:47 +02:00
|
|
|
{
|
|
|
|
cprintf("Error detected, instruction information:\n");
|
2012-01-31 16:46:03 +01:00
|
|
|
cprintf("PC:%s, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n"
|
2006-06-16 19:10:47 +02:00
|
|
|
"Completed:%i\n",
|
2012-01-31 16:46:03 +01:00
|
|
|
inst->pcState(),
|
|
|
|
inst->nextInstAddr(),
|
2006-06-16 19:10:47 +02:00
|
|
|
inst->seqNum,
|
|
|
|
inst->threadNumber,
|
|
|
|
inst->isCompleted());
|
|
|
|
inst->dump();
|
|
|
|
CheckerCPU::dumpAndExit();
|
|
|
|
}
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
template <class Impl>
|
2006-05-16 19:59:29 +02:00
|
|
|
void
|
2012-01-31 16:46:03 +01:00
|
|
|
Checker<Impl>::dumpInsts()
|
2006-05-16 19:59:29 +02:00
|
|
|
{
|
|
|
|
int num = 0;
|
|
|
|
|
|
|
|
InstListIt inst_list_it = --(instList.end());
|
|
|
|
|
|
|
|
cprintf("Inst list size: %i\n", instList.size());
|
|
|
|
|
|
|
|
while (inst_list_it != instList.end())
|
|
|
|
{
|
|
|
|
cprintf("Instruction:%i\n",
|
|
|
|
num);
|
|
|
|
|
2012-01-31 16:46:03 +01:00
|
|
|
cprintf("PC:%s\n[sn:%lli]\n[tid:%i]\n"
|
2006-05-16 19:59:29 +02:00
|
|
|
"Completed:%i\n",
|
2012-01-31 16:46:03 +01:00
|
|
|
(*inst_list_it)->pcState(),
|
2006-05-16 19:59:29 +02:00
|
|
|
(*inst_list_it)->seqNum,
|
|
|
|
(*inst_list_it)->threadNumber,
|
|
|
|
(*inst_list_it)->isCompleted());
|
|
|
|
|
|
|
|
cprintf("\n");
|
|
|
|
|
|
|
|
inst_list_it--;
|
|
|
|
++num;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|