O3: Fix issue with interrupts/faults occuring in the middle of a macro-op
This patch fixes two problems with the O3 cpu model. The first is an issue with an instruction fetch causing a fault on the next address while the current macro-op is being issued. This happens when the micro-ops exceed the fetch bandwdith and then on the next cycle the fetch stage attempts to issue a request to the next line while it still has micro-ops to issue if the next line faults a fault is attached to a micro-op in the currently executing macro-op rather than a "nop" from the next instruction block. This leads to an instruction incorrectly faulting when on fetch when it had no reason to fault. A similar problem occurs with interrupts. When an interrupt occurs the fetch stage nominally stops issuing instructions immediately. This is incorrect in the case of a macro-op as the current location might not be interruptable.
This commit is contained in:
parent
fc1d2d9679
commit
6dd996aabb
3 changed files with 26 additions and 7 deletions
|
@ -219,6 +219,8 @@ AbortFault<T>::invoke(ThreadContext *tc, StaticInstPtr inst)
|
||||||
fsr.ext = 0;
|
fsr.ext = 0;
|
||||||
tc->setMiscReg(T::FsrIndex, fsr);
|
tc->setMiscReg(T::FsrIndex, fsr);
|
||||||
tc->setMiscReg(T::FarIndex, faultAddr);
|
tc->setMiscReg(T::FarIndex, faultAddr);
|
||||||
|
|
||||||
|
DPRINTF(Faults, "Abort Fault fsr=%#x faultAddr=%#x\n", fsr, faultAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -403,6 +403,9 @@ class DefaultFetch
|
||||||
|
|
||||||
StaticInstPtr macroop[Impl::MaxThreads];
|
StaticInstPtr macroop[Impl::MaxThreads];
|
||||||
|
|
||||||
|
/** Can the fetch stage redirect from an interrupt on this instruction? */
|
||||||
|
bool delayedCommit[Impl::MaxThreads];
|
||||||
|
|
||||||
/** Memory request used to access cache. */
|
/** Memory request used to access cache. */
|
||||||
RequestPtr memReq[Impl::MaxThreads];
|
RequestPtr memReq[Impl::MaxThreads];
|
||||||
|
|
||||||
|
|
|
@ -346,6 +346,7 @@ DefaultFetch<Impl>::initStage()
|
||||||
pc[tid] = cpu->pcState(tid);
|
pc[tid] = cpu->pcState(tid);
|
||||||
fetchOffset[tid] = 0;
|
fetchOffset[tid] = 0;
|
||||||
macroop[tid] = NULL;
|
macroop[tid] = NULL;
|
||||||
|
delayedCommit[tid] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ThreadID tid = 0; tid < numThreads; tid++) {
|
for (ThreadID tid = 0; tid < numThreads; tid++) {
|
||||||
|
@ -1070,6 +1071,9 @@ DefaultFetch<Impl>::buildInst(ThreadID tid, StaticInstPtr staticInst,
|
||||||
assert(numInst < fetchWidth);
|
assert(numInst < fetchWidth);
|
||||||
toDecode->insts[toDecode->size++] = instruction;
|
toDecode->insts[toDecode->size++] = instruction;
|
||||||
|
|
||||||
|
// Keep track of if we can take an interrupt at this boundary
|
||||||
|
delayedCommit[tid] = instruction->isDelayedCommit();
|
||||||
|
|
||||||
return instruction;
|
return instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1112,8 +1116,11 @@ DefaultFetch<Impl>::fetch(bool &status_change)
|
||||||
// Align the fetch PC so its at the start of a cache block.
|
// Align the fetch PC so its at the start of a cache block.
|
||||||
Addr block_PC = icacheBlockAlignPC(fetchAddr);
|
Addr block_PC = icacheBlockAlignPC(fetchAddr);
|
||||||
|
|
||||||
// Unless buffer already got the block, fetch it from icache.
|
// If buffer is no longer valid or fetchAddr has moved to point
|
||||||
if (!(cacheDataValid[tid] && block_PC == cacheDataPC[tid]) && !inRom) {
|
// to the next cache block, AND we have no remaining ucode
|
||||||
|
// from a macro-op, then start fetch from icache.
|
||||||
|
if (!(cacheDataValid[tid] && block_PC == cacheDataPC[tid])
|
||||||
|
&& !inRom && !macroop[tid]) {
|
||||||
DPRINTF(Fetch, "[tid:%i]: Attempting to translate and read "
|
DPRINTF(Fetch, "[tid:%i]: Attempting to translate and read "
|
||||||
"instruction, starting at PC %s.\n", tid, thisPC);
|
"instruction, starting at PC %s.\n", tid, thisPC);
|
||||||
|
|
||||||
|
@ -1126,7 +1133,11 @@ DefaultFetch<Impl>::fetch(bool &status_change)
|
||||||
else
|
else
|
||||||
++fetchMiscStallCycles;
|
++fetchMiscStallCycles;
|
||||||
return;
|
return;
|
||||||
} else if (checkInterrupt(thisPC.instAddr()) || isSwitchedOut()) {
|
} else if ((checkInterrupt(thisPC.instAddr()) && !delayedCommit[tid])
|
||||||
|
|| isSwitchedOut()) {
|
||||||
|
// Stall CPU if an interrupt is posted and we're not issuing
|
||||||
|
// an delayed commit micro-op currently (delayed commit instructions
|
||||||
|
// are not interruptable by interrupts, only faults)
|
||||||
++fetchMiscStallCycles;
|
++fetchMiscStallCycles;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1184,9 +1195,11 @@ DefaultFetch<Impl>::fetch(bool &status_change)
|
||||||
unsigned blkOffset = (fetchAddr - cacheDataPC[tid]) / instSize;
|
unsigned blkOffset = (fetchAddr - cacheDataPC[tid]) / instSize;
|
||||||
|
|
||||||
// Loop through instruction memory from the cache.
|
// Loop through instruction memory from the cache.
|
||||||
while (blkOffset < numInsts &&
|
// Keep issuing while we have not reached the end of the block or a
|
||||||
numInst < fetchWidth &&
|
// macroop is active and fetchWidth is available and branch is not
|
||||||
!predictedBranch) {
|
// predicted taken
|
||||||
|
while ((blkOffset < numInsts || curMacroop) &&
|
||||||
|
numInst < fetchWidth && !predictedBranch) {
|
||||||
|
|
||||||
// If we need to process more memory, do it now.
|
// If we need to process more memory, do it now.
|
||||||
if (!(curMacroop || inRom) && !predecoder.extMachInstReady()) {
|
if (!(curMacroop || inRom) && !predecoder.extMachInstReady()) {
|
||||||
|
@ -1232,7 +1245,8 @@ DefaultFetch<Impl>::fetch(bool &status_change)
|
||||||
pcOffset = 0;
|
pcOffset = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We need more bytes for this instruction.
|
// We need more bytes for this instruction so blkOffset and
|
||||||
|
// pcOffset will be updated
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue