cpu: Fix broken squashAfter implementation in O3 CPU

Commit can currently both commit and squash in the same cycle. This
confuses other stages since the signals coming from the commit stage
can only signal either a squash or a commit in a cycle. This changeset
changes the behavior of squashAfter so that it commits all
instructions, including the instruction that requested the squash, in
the first cycle and then starts to squash in the next cycle.
This commit is contained in:
Andreas Sandberg 2013-01-07 13:05:45 -05:00
parent a2077ccf02
commit 8db27aa230
2 changed files with 71 additions and 28 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 ARM Limited
* Copyright (c) 2010-2012 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
@ -129,7 +129,8 @@ class DefaultCommit
Idle,
ROBSquashing,
TrapPending,
FetchTrapPending
FetchTrapPending,
SquashAfterPending, //< Committing instructions before a squash.
};
/** Commit policy for SMT mode. */
@ -259,13 +260,38 @@ class DefaultCommit
/** Handles squashing due to an TC write. */
void squashFromTC(ThreadID tid);
/** Handles squashing from instruction with SquashAfter set.
/** Handles a squash from a squashAfter() request. */
void squashFromSquashAfter(ThreadID tid);
/**
* Handle squashing from instruction with SquashAfter set.
*
* This differs from the other squashes as it squashes following
* instructions instead of the current instruction and doesn't
* clean up various status bits about traps/tc writes pending.
* clean up various status bits about traps/tc writes
* pending. Since there might have been instructions committed by
* the commit stage before the squashing instruction was reached
* and we can't commit and squash in the same cycle, we have to
* squash in two steps:
*
* <ol>
* <li>Immediately set the commit status of the thread of
* SquashAfterPending. This forces the thread to stop
* committing instructions in this cycle. The last
* instruction to be committed in this cycle will be the
* SquashAfter instruction.
* <li>In the next cycle, commit() checks for the
* SquashAfterPending state and squashes <i>all</i>
* in-flight instructions. Since the SquashAfter instruction
* was the last instruction to be committed in the previous
* cycle, this causes all subsequent instructions to be
* squashed.
* </ol>
*
* @param tid ID of the thread to squash.
* @param head_inst Instruction that requested the squash.
*/
void squashAfter(ThreadID tid, DynInstPtr &head_inst,
uint64_t squash_after_seq_num);
void squashAfter(ThreadID tid, DynInstPtr &head_inst);
/** Handles processing an interrupt. */
void handleInterrupt();
@ -372,6 +398,15 @@ class DefaultCommit
/** Records if a thread has to squash this cycle due to an XC write. */
bool tcSquash[Impl::MaxThreads];
/**
* Instruction passed to squashAfter().
*
* The squash after implementation needs to buffer the instruction
* that caused a squash since this needs to be passed to the fetch
* stage once squashing starts.
*/
DynInstPtr squashAfterInst[Impl::MaxThreads];
/** Priority List used for Commit Policy */
std::list<ThreadID> priority_list;

View file

@ -144,6 +144,7 @@ DefaultCommit<Impl>::DefaultCommit(O3CPU *_cpu, DerivO3CPUParams *params)
tcSquash[tid] = false;
pc[tid].set(0);
lastCommitedSeqNum[tid] = 0;
squashAfterInst[tid] = NULL;
}
interrupt = NoFault;
}
@ -404,6 +405,7 @@ DefaultCommit<Impl>::takeOverFrom()
changedROBNumEntries[tid] = false;
trapSquash[tid] = false;
tcSquash[tid] = false;
squashAfterInst[tid] = NULL;
}
squashCounter = 0;
rob->takeOverFrom();
@ -587,31 +589,32 @@ DefaultCommit<Impl>::squashFromTC(ThreadID tid)
template <class Impl>
void
DefaultCommit<Impl>::squashAfter(ThreadID tid, DynInstPtr &head_inst,
uint64_t squash_after_seq_num)
DefaultCommit<Impl>::squashFromSquashAfter(ThreadID tid)
{
youngestSeqNum[tid] = squash_after_seq_num;
DPRINTF(Commit, "Squashing after squash after request, "
"restarting at PC %s\n", pc[tid]);
rob->squash(squash_after_seq_num, tid);
changedROBNumEntries[tid] = true;
squashAll(tid);
// Make sure to inform the fetch stage of which instruction caused
// the squash. It'll try to re-fetch an instruction executing in
// microcode unless this is set.
toIEW->commitInfo[tid].squashInst = squashAfterInst[tid];
squashAfterInst[tid] = NULL;
// Send back the sequence number of the squashed instruction.
toIEW->commitInfo[tid].doneSeqNum = squash_after_seq_num;
toIEW->commitInfo[tid].squashInst = head_inst;
// Send back the squash signal to tell stages that they should squash.
toIEW->commitInfo[tid].squash = true;
// Send back the rob squashing signal so other stages know that
// the ROB is in the process of squashing.
toIEW->commitInfo[tid].robSquashing = true;
toIEW->commitInfo[tid].mispredictInst = NULL;
toIEW->commitInfo[tid].pc = pc[tid];
DPRINTF(Commit, "Executing squash after for [tid:%i] inst [sn:%lli]\n",
tid, squash_after_seq_num);
commitStatus[tid] = ROBSquashing;
cpu->activityThisCycle();
}
template <class Impl>
void
DefaultCommit<Impl>::squashAfter(ThreadID tid, DynInstPtr &head_inst)
{
DPRINTF(Commit, "Executing squash after for [tid:%i] inst [sn:%lli]\n",
tid, head_inst->seqNum);
assert(!squashAfterInst[tid] || squashAfterInst[tid] == head_inst);
commitStatus[tid] = SquashAfterPending;
squashAfterInst[tid] = head_inst;
}
template <class Impl>
@ -797,6 +800,11 @@ DefaultCommit<Impl>::commit()
} else if (tcSquash[tid] == true) {
assert(commitStatus[tid] != TrapPending);
squashFromTC(tid);
} else if (commitStatus[tid] == SquashAfterPending) {
// A squash from the previous cycle of the commit stage (i.e.,
// commitInsts() called squashAfter) is pending. Squash the
// thread now.
squashFromSquashAfter(tid);
}
// Squashed sequence number must be older than youngest valid
@ -1008,7 +1016,7 @@ DefaultCommit<Impl>::commitInsts()
// If this is an instruction that doesn't play nicely with
// others squash everything and restart fetch
if (head_inst->isSquashAfter())
squashAfter(tid, head_inst, head_inst->seqNum);
squashAfter(tid, head_inst);
int count = 0;
Addr oldpc;