Faults generated at fetch are passed to the backend by creating a dummy nop instruction and giving it the fault. This unifies front end faults and normal instruction faults.

cpu/checker/cpu.cc:
    Fixups for fetch fault being sent with the instruction.
cpu/o3/fetch_impl.hh:
cpu/ozone/front_end_impl.hh:
    Send any faults generated at fetch along with a fake nop instruction to the back end.  This avoids having to use direct communication to check if the entire front end has drained; it is naturally handled through the nop's fault being handled when it reaches the head of commit.
cpu/ozone/front_end.hh:
    Add extra status TrapPending.
cpu/ozone/lw_back_end_impl.hh:
    Fetch fault handled through a dummy nop carrying the fetch fault.

    Avoid putting Nops on the exeList.

--HG--
extra : convert_revision : 8d9899748b34c204763a49c48a9b5113864f5789
This commit is contained in:
Kevin Lim 2006-05-17 14:25:10 -04:00
parent 343bff3b7d
commit 36581a5342
5 changed files with 99 additions and 100 deletions

View file

@ -607,41 +607,46 @@ Checker<DynInstPtr>::tick(DynInstPtr &completed_inst)
bool succeeded = translateInstReq(memReq);
if (!succeeded) {
warn("Instruction PC %#x was not found in the ITB!",
cpuXC->readPC());
handleError();
if (inst->getFault() == NoFault) {
warn("Instruction PC %#x was not found in the ITB!",
cpuXC->readPC());
handleError();
// go to the next instruction
cpuXC->setPC(cpuXC->readNextPC());
cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst));
// go to the next instruction
cpuXC->setPC(cpuXC->readNextPC());
cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst));
return;
return;
} else {
fault = inst->getFault();
}
}
// if (fault == NoFault)
if (fault == NoFault) {
// fault = cpuXC->mem->read(memReq, machInst);
cpuXC->mem->read(memReq, machInst);
cpuXC->mem->read(memReq, machInst);
// If we've got a valid instruction (i.e., no fault on instruction
// fetch), then execute it.
// If we've got a valid instruction (i.e., no fault on instruction
// fetch), then execute it.
// keep an instruction count
numInst++;
numInst++;
// numInsts++;
// 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);
// 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);
curStaticInst = StaticInst::decode(makeExtMI(machInst, cpuXC->readPC()));
curStaticInst = StaticInst::decode(makeExtMI(machInst, cpuXC->readPC()));
#if FULL_SYSTEM
cpuXC->setInst(machInst);
cpuXC->setInst(machInst);
#endif // FULL_SYSTEM
fault = inst->getFault();
fault = inst->getFault();
}
// Either the instruction was a fault and we should process the fault,
// or we should just go ahead execute the instruction. This assumes

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004-2005 The Regents of The University of Michigan
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,22 +27,21 @@
*/
#include "arch/isa_traits.hh"
#include "sim/byteswap.hh"
#include "cpu/exetrace.hh"
#include "cpu/o3/fetch.hh"
#include "mem/base_mem.hh"
#include "mem/mem_interface.hh"
#include "mem/mem_req.hh"
#include "sim/byteswap.hh"
#include "sim/root.hh"
#if FULL_SYSTEM
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#include "mem/functional/memory_control.hh"
#include "mem/functional/physical.hh"
#include "sim/system.hh"
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#else // !FULL_SYSTEM
#include "mem/functional/functional.hh"
#endif // FULL_SYSTEM
@ -136,14 +135,7 @@ DefaultFetch<Impl>::DefaultFetch(Params *params)
// Create a new memory request.
memReq[tid] = NULL;
// memReq[tid] = new MemReq();
/*
// Need a way of setting this correctly for parallel programs
// @todo: Figure out how to properly set asid vs thread_num.
memReq[tid]->asid = tid;
memReq[tid]->thread_num = tid;
memReq[tid]->data = new uint8_t[64];
*/
// Create space to store a cache line.
cacheData[tid] = new uint8_t[cacheBlkSize];
@ -261,10 +253,6 @@ DefaultFetch<Impl>::setCPU(FullCPU *cpu_ptr)
DPRINTF(Fetch, "Setting the CPU pointer.\n");
cpu = cpu_ptr;
// Set ExecContexts for Memory Requests
// for (int tid=0; tid < numThreads; tid++)
// memReq[tid]->xc = cpu->xcBase(tid);
// Fetch needs to start fetching instructions at the very beginning,
// so it must start up in active state.
switchToActive();
@ -362,9 +350,8 @@ DefaultFetch<Impl>::processCacheCompletion(MemReqPtr &req)
// memcpy(cacheData[tid], memReq[tid]->data, memReq[tid]->size);
// Reset the completion event to NULL.
// Reset the mem req to NULL.
memReq[tid] = NULL;
// memReq[tid]->completionEvent = NULL;
}
template <class Impl>
@ -468,10 +455,6 @@ template <class Impl>
bool
DefaultFetch<Impl>::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid)
{
// Check if the instruction exists within the cache.
// If it does, then proceed on to read the instruction and the rest
// of the instructions in the cache line until either the end of the
// cache line or a predicted taken branch is encountered.
Fault fault = NoFault;
#if FULL_SYSTEM
@ -509,7 +492,7 @@ DefaultFetch<Impl>::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid
//#endif
// In the case of faults, the fetch stage may need to stall and wait
// on what caused the fetch (ITB or Icache miss).
// for the ITB miss to be handled.
// If translation was successful, attempt to read the first
// instruction.
@ -518,7 +501,7 @@ DefaultFetch<Impl>::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid
if (cpu->system->memctrl->badaddr(memReq[tid]->paddr) ||
memReq[tid]->flags & UNCACHEABLE) {
DPRINTF(Fetch, "Fetch: Bad address %#x (hopefully on a "
"misspeculating path!",
"misspeculating path)!",
memReq[tid]->paddr);
ret_fault = TheISA::genMachineCheckFault();
return false;
@ -587,44 +570,9 @@ DefaultFetch<Impl>::doSquash(const Addr &new_PC, unsigned tid)
if (fetchStatus[tid] == IcacheMissStall && icacheInterface) {
DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n",
tid);
// icacheInterface->squash(tid);
/*
if (memReq[tid]->completionEvent) {
if (memReq[tid]->completionEvent->scheduled()) {
memReq[tid]->completionEvent->squash();
} else {
delete memReq[tid]->completionEvent;
memReq[tid]->completionEvent = NULL;
}
}
*/
memReq[tid] = NULL;
}
if (fetchStatus[tid] == TrapPending) {
// @todo: Hardcoded number here
// This is only effective if communication to and from commit
// is identical. If it's faster to commit than it is from
// commit to here, then it causes problems.
bool found_fault = false;
for (int i = 0; i > -5; --i) {
if (fetchQueue->access(i)->fetchFault) {
DPRINTF(Fetch, "[tid:%i]: Fetch used to be in a trap, "
"clearing it.\n",
tid);
fetchQueue->access(i)->fetchFault = NoFault;
found_fault = true;
}
}
if (!found_fault) {
warn("%lli Fault from fetch not found in time buffer!",
curTick);
}
toDecode->clearFetchFault = true;
}
fetchStatus[tid] = Squashing;
++fetchSquashCycles;
@ -643,7 +591,6 @@ DefaultFetch<Impl>::squashFromDecode(const Addr &new_PC,
// Tell the CPU to remove any instructions that are in flight between
// fetch and decode.
cpu->removeInstsUntil(seq_num, tid);
youngestSN = seq_num;
}
template<class Impl>
@ -829,7 +776,6 @@ DefaultFetch<Impl>::checkSignalsAndUpdate(unsigned tid)
// In any case, squash.
squash(fromCommit->commitInfo[tid].nextPC,tid);
youngestSN = fromCommit->commitInfo[tid].doneSeqNum;
// Also check if there's a mispredict that happened.
if (fromCommit->commitInfo[tid].branchMispredict) {
@ -1009,8 +955,6 @@ DefaultFetch<Impl>::fetch(bool &status_change)
// Get a sequence number.
inst_seq = cpu->getAndIncrementInstSeq();
youngestSN = inst_seq;
// Make sure this is a valid index.
assert(offset <= cacheBlkSize - instSize);
@ -1095,14 +1039,37 @@ DefaultFetch<Impl>::fetch(bool &status_change)
// This stage will not be able to continue until all the ROB
// slots are empty, at which point the fault can be handled.
// The only other way it can wake up is if a squash comes along
// and changes the PC. Not sure how to handle that case...perhaps
// have it handled by the upper level CPU class which peeks into the
// time buffer and sees if a squash comes along, in which case it
// changes the status.
// and changes the PC.
#if FULL_SYSTEM
assert(numInst != fetchWidth);
// Get a sequence number.
inst_seq = cpu->getAndIncrementInstSeq();
// We will use a nop in order to carry the fault.
ext_inst = TheISA::NoopMachInst;
// Create a new DynInst from the dummy nop.
DynInstPtr instruction = new DynInst(ext_inst, fetch_PC,
next_PC,
inst_seq, cpu);
instruction->setPredTarg(next_PC + instSize);
instruction->setThread(tid);
instruction->setASID(tid);
instruction->setState(cpu->thread[tid]);
instruction->traceData = NULL;
instruction->setInstListIt(cpu->addInst(instruction));
instruction->fault = fault;
toDecode->insts[numInst] = instruction;
toDecode->size++;
// Tell the commit stage the fault we had.
toDecode->fetchFault = fault;
toDecode->fetchFaultSN = cpu->globalSeqNum;
// toDecode->fetchFault = fault;
// toDecode->fetchFaultSN = cpu->globalSeqNum;
DPRINTF(Fetch, "[tid:%i]: Blocked, need to handle the trap.\n",tid);

View file

@ -120,6 +120,7 @@ class FrontEnd
SerializeComplete,
RenameBlocked,
QuiescePending,
TrapPending,
BEBlocked
};

View file

@ -268,11 +268,9 @@ FrontEnd<Impl>::tick()
}
if (status == RenameBlocked || status == SerializeBlocked ||
status == BEBlocked) {
// This might cause the front end to run even though it
// shouldn't, but this should only be a problem for one cycle.
// Also will cause a one cycle bubble between changing state
// and restarting.
status == TrapPending || status == BEBlocked) {
// Will cause a one cycle bubble between changing state and
// restarting.
DPRINTF(FE, "In blocked status.\n");
fetchBlockedCycles++;
@ -537,9 +535,32 @@ void
FrontEnd<Impl>::handleFault(Fault &fault)
{
DPRINTF(FE, "Fault at fetch, telling commit\n");
backEnd->fetchFault(fault);
// backEnd->fetchFault(fault);
// We're blocked on the back end until it handles this fault.
status = BEBlocked;
status = TrapPending;
// Get a sequence number.
InstSeqNum inst_seq = getAndIncrementInstSeq();
// We will use a nop in order to carry the fault.
ExtMachInst ext_inst = TheISA::NoopMachInst;
// Create a new DynInst from the dummy nop.
DynInstPtr instruction = new DynInst(ext_inst, PC,
PC+sizeof(MachInst),
inst_seq, cpu);
instruction->setPredTarg(instruction->readNextPC());
// instruction->setThread(tid);
// instruction->setASID(tid);
instruction->setState(thread);
instruction->traceData = NULL;
instruction->fault = fault;
instruction->setCanIssue();
instBuffer.push_back(instruction);
++instBufferSize;
}
template <class Impl>
@ -881,7 +902,6 @@ FrontEnd<Impl>::dumpInsts()
(*buff_it)->isSquashed());
buff_it++;
}
}
template <class Impl>

View file

@ -652,7 +652,7 @@ LWBackEnd<Impl>::tick()
squashFromTrap();
} else if (xcSquash) {
squashFromXC();
} else if (fetchHasFault && robEmpty() && frontEnd->isEmpty() && !LSQ.hasStoresToWB()) {
} /*else if (fetchHasFault && robEmpty() && frontEnd->isEmpty() && !LSQ.hasStoresToWB()) {
DPRINTF(BE, "ROB and front end empty, handling fetch fault\n");
Fault fetch_fault = frontEnd->getFault();
if (fetch_fault == NoFault) {
@ -662,7 +662,7 @@ LWBackEnd<Impl>::tick()
handleFault(fetch_fault);
fetchHasFault = false;
}
}
}*/
#endif
if (dispatchStatus != Blocked) {
@ -777,6 +777,12 @@ LWBackEnd<Impl>::dispatchInsts()
inst->seqNum);
exeList.push(inst);
}
} else if (inst->isNop()) {
DPRINTF(BE, "Nop encountered [sn:%lli], skipping exeList.\n",
inst->seqNum);
inst->setIssued();
inst->setExecuted();
inst->setCanCommit();
} else {
DPRINTF(BE, "Instruction [sn:%lli] ready, addding to exeList.\n",
inst->seqNum);