/*
- * Copyright (c) 2010 ARM Limited
+ * Copyright 2014 Google, Inc.
+ * Copyright (c) 2010-2014 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* Authors: Kevin Lim
* Korey Sewell
*/
+#ifndef __CPU_O3_COMMIT_IMPL_HH__
+#define __CPU_O3_COMMIT_IMPL_HH__
#include <algorithm>
+#include <set>
#include <string>
#include "arch/utility.hh"
-#include "base/cp_annotate.hh"
#include "base/loader/symtab.hh"
-#include "cpu/timebuf.hh"
-#include "config/full_system.hh"
+#include "base/cp_annotate.hh"
#include "config/the_isa.hh"
-#include "config/use_checker.hh"
-#include "cpu/exetrace.hh"
+#include "cpu/checker/cpu.hh"
#include "cpu/o3/commit.hh"
#include "cpu/o3/thread_state.hh"
+#include "cpu/base.hh"
+#include "cpu/exetrace.hh"
+#include "cpu/timebuf.hh"
+#include "debug/Activity.hh"
+#include "debug/Commit.hh"
+#include "debug/CommitRate.hh"
+#include "debug/Drain.hh"
+#include "debug/ExecFaulting.hh"
+#include "debug/O3PipeView.hh"
#include "params/DerivO3CPU.hh"
-
-#if USE_CHECKER
-#include "cpu/checker/cpu.hh"
-#endif
+#include "sim/faults.hh"
+#include "sim/full_system.hh"
using namespace std;
template <class Impl>
DefaultCommit<Impl>::TrapEvent::TrapEvent(DefaultCommit<Impl> *_commit,
ThreadID _tid)
- : Event(CPU_Tick_Pri), commit(_commit), tid(_tid)
+ : Event(CPU_Tick_Pri, AutoDelete), commit(_commit), tid(_tid)
{
- this->setFlags(AutoDelete);
}
template <class Impl>
template <class Impl>
DefaultCommit<Impl>::DefaultCommit(O3CPU *_cpu, DerivO3CPUParams *params)
: cpu(_cpu),
- squashCounter(0),
iewToCommitDelay(params->iewToCommitDelay),
commitToIEWDelay(params->commitToIEWDelay),
renameToROBDelay(params->renameToROBDelay),
commitWidth(params->commitWidth),
numThreads(params->numThreads),
drainPending(false),
- switchedOut(false),
- trapLatency(params->trapLatency)
+ drainImminent(false),
+ trapLatency(params->trapLatency),
+ canHandleInterrupts(true),
+ avoidQuiesceLiveLock(false)
{
+ if (commitWidth > Impl::MaxWidth)
+ fatal("commitWidth (%d) is larger than compiled limit (%d),\n"
+ "\tincrease MaxWidth in src/cpu/o3/impl.hh\n",
+ commitWidth, static_cast<int>(Impl::MaxWidth));
+
_status = Active;
_nextStatus = Inactive;
std::string policy = params->smtCommitPolicy;
if (policy == "aggressive"){
commitPolicy = Aggressive;
- DPRINTF(Commit,"Commit Policy set to Aggressive.");
+ DPRINTF(Commit,"Commit Policy set to Aggressive.\n");
} else if (policy == "roundrobin"){
commitPolicy = RoundRobin;
priority_list.push_back(tid);
}
- DPRINTF(Commit,"Commit Policy set to Round Robin.");
+ DPRINTF(Commit,"Commit Policy set to Round Robin.\n");
} else if (policy == "oldestready"){
commitPolicy = OldestReady;
tcSquash[tid] = false;
pc[tid].set(0);
lastCommitedSeqNum[tid] = 0;
+ squashAfterInst[tid] = NULL;
}
-#if FULL_SYSTEM
interrupt = NoFault;
-#endif
}
template <class Impl>
return cpu->name() + ".commit";
}
+template <class Impl>
+void
+DefaultCommit<Impl>::regProbePoints()
+{
+ ppCommit = new ProbePointArg<DynInstPtr>(cpu->getProbeManager(), "Commit");
+ ppCommitStall = new ProbePointArg<DynInstPtr>(cpu->getProbeManager(), "CommitStall");
+ ppSquash = new ProbePointArg<DynInstPtr>(cpu->getProbeManager(), "Squash");
+}
+
template <class Impl>
void
DefaultCommit<Impl>::regStats()
{
using namespace Stats;
- commitCommittedInsts
- .name(name() + ".commitCommittedInsts")
- .desc("The number of committed instructions")
- .prereq(commitCommittedInsts);
commitSquashedInsts
.name(name() + ".commitSquashedInsts")
.desc("The number of squashed insts skipped by commit")
.prereq(commitSquashedInsts);
- commitSquashEvents
- .name(name() + ".commitSquashEvents")
- .desc("The number of times commit is told to squash")
- .prereq(commitSquashEvents);
+
commitNonSpecStalls
.name(name() + ".commitNonSpecStalls")
.desc("The number of times commit has been forced to stall to "
"communicate backwards")
.prereq(commitNonSpecStalls);
+
branchMispredicts
.name(name() + ".branchMispredicts")
.desc("The number of times a branch was mispredicted")
.prereq(branchMispredicts);
+
numCommittedDist
.init(0,commitWidth,1)
- .name(name() + ".COM:committed_per_cycle")
+ .name(name() + ".committed_per_cycle")
.desc("Number of insts commited each cycle")
.flags(Stats::pdf)
;
- statComInst
+ instsCommitted
.init(cpu->numThreads)
- .name(name() + ".COM:count")
+ .name(name() + ".committedInsts")
.desc("Number of instructions committed")
.flags(total)
;
+ opsCommitted
+ .init(cpu->numThreads)
+ .name(name() + ".committedOps")
+ .desc("Number of ops (including micro ops) committed")
+ .flags(total)
+ ;
+
statComSwp
.init(cpu->numThreads)
- .name(name() + ".COM:swp_count")
+ .name(name() + ".swp_count")
.desc("Number of s/w prefetches committed")
.flags(total)
;
statComRefs
.init(cpu->numThreads)
- .name(name() + ".COM:refs")
+ .name(name() + ".refs")
.desc("Number of memory references committed")
.flags(total)
;
statComLoads
.init(cpu->numThreads)
- .name(name() + ".COM:loads")
+ .name(name() + ".loads")
.desc("Number of loads committed")
.flags(total)
;
statComMembars
.init(cpu->numThreads)
- .name(name() + ".COM:membars")
+ .name(name() + ".membars")
.desc("Number of memory barriers committed")
.flags(total)
;
statComBranches
.init(cpu->numThreads)
- .name(name() + ".COM:branches")
+ .name(name() + ".branches")
.desc("Number of branches committed")
.flags(total)
;
statComFloating
.init(cpu->numThreads)
- .name(name() + ".COM:fp_insts")
+ .name(name() + ".fp_insts")
.desc("Number of committed floating point instructions.")
.flags(total)
;
statComInteger
.init(cpu->numThreads)
- .name(name()+".COM:int_insts")
+ .name(name()+".int_insts")
.desc("Number of committed integer instructions.")
.flags(total)
;
statComFunctionCalls
.init(cpu->numThreads)
- .name(name()+".COM:function_calls")
+ .name(name()+".function_calls")
.desc("Number of function calls committed.")
.flags(total)
;
- commitEligible
- .init(cpu->numThreads)
- .name(name() + ".COM:bw_limited")
- .desc("number of insts not committed due to BW limits")
- .flags(total)
+ statCommittedInstType
+ .init(numThreads,Enums::Num_OpClass)
+ .name(name() + ".op_class")
+ .desc("Class of committed instruction")
+ .flags(total | pdf | dist)
;
+ statCommittedInstType.ysubnames(Enums::OpClassStrings);
commitEligibleSamples
- .name(name() + ".COM:bw_lim_events")
+ .name(name() + ".bw_lim_events")
.desc("number cycles where commit BW limit reached")
;
}
template <class Impl>
void
-DefaultCommit<Impl>::initStage()
+DefaultCommit<Impl>::startupStage()
{
rob->setActiveThreads(activeThreads);
rob->resetEntries();
cpu->activateStage(O3CPU::CommitIdx);
cpu->activityThisCycle();
- trapLatency = cpu->ticks(trapLatency);
}
template <class Impl>
-bool
+void
DefaultCommit<Impl>::drain()
{
drainPending = true;
-
- return false;
}
template <class Impl>
void
-DefaultCommit<Impl>::switchOut()
+DefaultCommit<Impl>::drainResume()
{
- switchedOut = true;
drainPending = false;
- rob->switchOut();
+ drainImminent = false;
}
template <class Impl>
void
-DefaultCommit<Impl>::resume()
+DefaultCommit<Impl>::drainSanityCheck() const
{
- drainPending = false;
+ assert(isDrained());
+ rob->drainSanityCheck();
+}
+
+template <class Impl>
+bool
+DefaultCommit<Impl>::isDrained() const
+{
+ /* Make sure no one is executing microcode. There are two reasons
+ * for this:
+ * - Hardware virtualized CPUs can't switch into the middle of a
+ * microcode sequence.
+ * - The current fetch implementation will most likely get very
+ * confused if it tries to start fetching an instruction that
+ * is executing in the middle of a ucode sequence that changes
+ * address mappings. This can happen on for example x86.
+ */
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ if (pc[tid].microPC() != 0)
+ return false;
+ }
+
+ /* Make sure that all instructions have finished committing before
+ * declaring the system as drained. We want the pipeline to be
+ * completely empty when we declare the CPU to be drained. This
+ * makes debugging easier since CPU handover and restoring from a
+ * checkpoint with a different CPU should have the same timing.
+ */
+ return rob->isEmpty() &&
+ interrupt == NoFault;
}
template <class Impl>
void
DefaultCommit<Impl>::takeOverFrom()
{
- switchedOut = false;
_status = Active;
_nextStatus = Inactive;
for (ThreadID tid = 0; tid < numThreads; tid++) {
changedROBNumEntries[tid] = false;
trapSquash[tid] = false;
tcSquash[tid] = false;
+ squashAfterInst[tid] = NULL;
}
- squashCounter = 0;
rob->takeOverFrom();
}
+template <class Impl>
+void
+DefaultCommit<Impl>::deactivateThread(ThreadID tid)
+{
+ list<ThreadID>::iterator thread_it = std::find(priority_list.begin(),
+ priority_list.end(), tid);
+
+ if (thread_it != priority_list.end()) {
+ priority_list.erase(thread_it);
+ }
+}
+
+
template <class Impl>
void
DefaultCommit<Impl>::updateStatus()
_status = _nextStatus;
}
-template <class Impl>
-void
-DefaultCommit<Impl>::setNextStatus()
-{
- int squashes = 0;
-
- list<ThreadID>::iterator threads = activeThreads->begin();
- list<ThreadID>::iterator end = activeThreads->end();
-
- while (threads != end) {
- ThreadID tid = *threads++;
-
- if (commitStatus[tid] == ROBSquashing) {
- squashes++;
- }
- }
-
- squashCounter = squashes;
-
- // If commit is currently squashing, then it will have activity for the
- // next cycle. Set its next status as active.
- if (squashCounter) {
- _nextStatus = Active;
- }
-}
-
template <class Impl>
bool
DefaultCommit<Impl>::changedROBEntries()
template <class Impl>
void
-DefaultCommit<Impl>::generateTrapEvent(ThreadID tid)
+DefaultCommit<Impl>::generateTrapEvent(ThreadID tid, Fault inst_fault)
{
DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid);
TrapEvent *trap = new TrapEvent(this, tid);
- cpu->schedule(trap, curTick() + trapLatency);
+ Cycles latency = dynamic_pointer_cast<SyscallRetryFault>(inst_fault) ?
+ cpu->syscallRetryLatency : trapLatency;
+
+ cpu->schedule(trap, cpu->clockEdge(latency));
trapInFlight[tid] = true;
+ thread[tid]->trapPending = true;
}
template <class Impl>
// then use one older sequence number.
// Hopefully this doesn't mess things up. Basically I want to squash
// all instructions of this thread.
- InstSeqNum squashed_inst = rob->isEmpty() ?
+ InstSeqNum squashed_inst = rob->isEmpty(tid) ?
lastCommitedSeqNum[tid] : rob->readHeadInst(tid)->seqNum - 1;
// All younger instructions will be squashed. Set the sequence
// the ROB is in the process of squashing.
toIEW->commitInfo[tid].robSquashing = true;
- toIEW->commitInfo[tid].branchMispredict = false;
toIEW->commitInfo[tid].mispredictInst = NULL;
+ toIEW->commitInfo[tid].squashInst = NULL;
toIEW->commitInfo[tid].pc = pc[tid];
}
DPRINTF(Commit, "Squashing from trap, restarting at PC %s\n", pc[tid]);
thread[tid]->trapPending = false;
- thread[tid]->inSyscall = false;
+ thread[tid]->noSquashFromTC = false;
trapInFlight[tid] = false;
trapSquash[tid] = false;
DPRINTF(Commit, "Squashing from TC, restarting at PC %s\n", pc[tid]);
- thread[tid]->inSyscall = false;
+ thread[tid]->noSquashFromTC = false;
assert(!thread[tid]->trapPending);
commitStatus[tid] = ROBSquashing;
template <class Impl>
void
-DefaultCommit<Impl>::squashAfter(ThreadID tid, uint64_t squash_after_seq_num)
+DefaultCommit<Impl>::squashFromSquashAfter(ThreadID tid)
{
- youngestSeqNum[tid] = squash_after_seq_num;
-
- rob->squash(squash_after_seq_num, tid);
- changedROBNumEntries[tid] = true;
+ DPRINTF(Commit, "Squashing after squash after request, "
+ "restarting at PC %s\n", pc[tid]);
- // Send back the sequence number of the squashed instruction.
- toIEW->commitInfo[tid].doneSeqNum = squash_after_seq_num;
-
- // 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;
+ 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;
- toIEW->commitInfo[tid].branchMispredict = false;
+ commitStatus[tid] = ROBSquashing;
+ cpu->activityThisCycle();
+}
- toIEW->commitInfo[tid].pc = pc[tid];
+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, squash_after_seq_num);
- commitStatus[tid] = ROBSquashing;
+ tid, head_inst->seqNum);
+
+ assert(!squashAfterInst[tid] || squashAfterInst[tid] == head_inst);
+ commitStatus[tid] = SquashAfterPending;
+ squashAfterInst[tid] = head_inst;
}
template <class Impl>
wroteToTimeBuffer = false;
_nextStatus = Inactive;
- if (drainPending && rob->isEmpty() && !iewStage->hasStoresToWB()) {
- cpu->signalDrained();
- drainPending = false;
- return;
- }
-
if (activeThreads->empty())
return;
} else if (!rob->isEmpty(tid)) {
DynInstPtr inst = rob->readHeadInst(tid);
+ ppCommitStall->notify(inst);
+
DPRINTF(Commit,"[tid:%i]: Can't commit, Instruction [sn:%lli] PC "
"%s is head of ROB and not ready\n",
tid, inst->seqNum, inst->pcState());
updateStatus();
}
-#if FULL_SYSTEM
template <class Impl>
void
DefaultCommit<Impl>::handleInterrupt()
"it got handled. Restart fetching from the orig path.\n");
toIEW->commitInfo[0].clearInterrupt = true;
interrupt = NoFault;
+ avoidQuiesceLiveLock = true;
return;
}
- // Wait until the ROB is empty and all stores have drained in
- // order to enter the interrupt.
- if (rob->isEmpty() && !iewStage->hasStoresToWB()) {
+ // Wait until all in flight instructions are finished before enterring
+ // the interrupt.
+ if (canHandleInterrupts && cpu->instList.empty()) {
// Squash or record that I need to squash this cycle if
// an interrupt needed to be handled.
DPRINTF(Commit, "Interrupt detected.\n");
// Clear the interrupt now that it's going to be handled
toIEW->commitInfo[0].clearInterrupt = true;
- assert(!thread[0]->inSyscall);
- thread[0]->inSyscall = true;
+ assert(!thread[0]->noSquashFromTC);
+ thread[0]->noSquashFromTC = true;
- // CPU will handle interrupt.
- cpu->processInterrupts(interrupt);
+ if (cpu->checker) {
+ cpu->checker->handlePendingInt();
+ }
+
+ // CPU will handle interrupt. Note that we ignore the local copy of
+ // interrupt. This is because the local copy may no longer be the
+ // interrupt that the interrupt controller thinks is being handled.
+ cpu->processInterrupts(cpu->getInterrupts());
- thread[0]->inSyscall = false;
+ thread[0]->noSquashFromTC = false;
commitStatus[0] = TrapPending;
+ interrupt = NoFault;
+
// Generate trap squash event.
- generateTrapEvent(0);
+ generateTrapEvent(0, interrupt);
- interrupt = NoFault;
+ avoidQuiesceLiveLock = false;
} else {
- DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n");
+ DPRINTF(Commit, "Interrupt pending: instruction is %sin "
+ "flight, ROB is %sempty\n",
+ canHandleInterrupts ? "not " : "",
+ cpu->instList.empty() ? "" : "not " );
}
}
void
DefaultCommit<Impl>::propagateInterrupt()
{
+ // Don't propagate intterupts if we are currently handling a trap or
+ // in draining and the last observable instruction has been committed.
if (commitStatus[0] == TrapPending || interrupt || trapSquash[0] ||
- tcSquash[0])
+ tcSquash[0] || drainImminent)
return;
// Process interrupts if interrupts are enabled, not in PAL
toIEW->commitInfo[0].interruptPending = true;
}
-#endif // FULL_SYSTEM
-
template <class Impl>
void
DefaultCommit<Impl>::commit()
{
-
-#if FULL_SYSTEM
- // Check for any interrupt that we've already squashed for and start processing it.
- if (interrupt != NoFault)
- handleInterrupt();
-
- // Check if we have a interrupt and get read to handle it
- if (cpu->checkInterrupts(cpu->tcBase(0)))
- propagateInterrupt();
-#endif // FULL_SYSTEM
+ if (FullSystem) {
+ // Check if we have a interrupt and get read to handle it
+ if (cpu->checkInterrupts(cpu->tcBase(0)))
+ propagateInterrupt();
+ }
////////////////////////////////////
// Check for any possible squashes, handle them first
list<ThreadID>::iterator threads = activeThreads->begin();
list<ThreadID>::iterator end = activeThreads->end();
+ int num_squashing_threads = 0;
+
while (threads != end) {
ThreadID tid = *threads++;
// Not sure which one takes priority. I think if we have
// both, that's a bad sign.
- if (trapSquash[tid] == true) {
+ if (trapSquash[tid]) {
assert(!tcSquash[tid]);
squashFromTrap(tid);
- } else if (tcSquash[tid] == true) {
+ } else if (tcSquash[tid]) {
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
commitStatus[tid] != TrapPending &&
fromIEW->squashedSeqNum[tid] <= youngestSeqNum[tid]) {
- DPRINTF(Commit, "[tid:%i]: Squashing due to PC %#x [sn:%i]\n",
+ if (fromIEW->mispredictInst[tid]) {
+ DPRINTF(Commit,
+ "[tid:%i]: Squashing due to branch mispred PC:%#x [sn:%i]\n",
tid,
- fromIEW->mispredPC[tid],
+ fromIEW->mispredictInst[tid]->instAddr(),
fromIEW->squashedSeqNum[tid]);
+ } else {
+ DPRINTF(Commit,
+ "[tid:%i]: Squashing due to order violation [sn:%i]\n",
+ tid, fromIEW->squashedSeqNum[tid]);
+ }
DPRINTF(Commit, "[tid:%i]: Redirecting to PC %#x\n",
tid,
// then use one older sequence number.
InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid];
- if (fromIEW->includeSquashInst[tid] == true) {
+ if (fromIEW->includeSquashInst[tid]) {
squashed_inst--;
}
// the ROB is in the process of squashing.
toIEW->commitInfo[tid].robSquashing = true;
- toIEW->commitInfo[tid].branchMispredict =
- fromIEW->branchMispredict[tid];
toIEW->commitInfo[tid].mispredictInst =
fromIEW->mispredictInst[tid];
toIEW->commitInfo[tid].branchTaken =
fromIEW->branchTaken[tid];
-
- toIEW->commitInfo[tid].pc = fromIEW->pc[tid];
-
- toIEW->commitInfo[tid].mispredPC = fromIEW->mispredPC[tid];
-
- if (toIEW->commitInfo[tid].branchMispredict) {
+ toIEW->commitInfo[tid].squashInst =
+ rob->findInst(tid, squashed_inst);
+ if (toIEW->commitInfo[tid].mispredictInst) {
+ if (toIEW->commitInfo[tid].mispredictInst->isUncondCtrl()) {
+ toIEW->commitInfo[tid].branchTaken = true;
+ }
++branchMispredicts;
}
+
+ toIEW->commitInfo[tid].pc = fromIEW->pc[tid];
}
+ if (commitStatus[tid] == ROBSquashing) {
+ num_squashing_threads++;
+ }
}
- setNextStatus();
+ // If commit is currently squashing, then it will have activity for the
+ // next cycle. Set its next status as active.
+ if (num_squashing_threads) {
+ _nextStatus = Active;
+ }
- if (squashCounter != numThreads) {
+ if (num_squashing_threads != numThreads) {
// If we're not currently squashing, then get instructions.
getInsts();
// Commit as many instructions as possible until the commit bandwidth
// limit is reached, or it becomes impossible to commit any more.
while (num_committed < commitWidth) {
- int commit_thread = getCommittingThread();
+ // Check for any interrupt that we've already squashed for
+ // and start processing it.
+ if (interrupt != NoFault)
+ handleInterrupt();
+
+ ThreadID commit_thread = getCommittingThread();
if (commit_thread == -1 || !rob->isHeadReady(commit_thread))
break;
rob->retireHead(commit_thread);
++commitSquashedInsts;
+ // Notify potential listeners that this instruction is squashed
+ ppSquash->notify(head_inst);
// Record that the number of ROB entries has changed.
changedROBNumEntries[tid] = true;
if (commit_success) {
++num_committed;
+ statCommittedInstType[tid][head_inst->opClass()]++;
+ ppCommit->notify(head_inst);
changedROBNumEntries[tid] = true;
// Set the doneSeqNum to the youngest committed instruction.
toIEW->commitInfo[tid].doneSeqNum = head_inst->seqNum;
- ++commitCommittedInsts;
-
- // To match the old model, don't count nops and instruction
- // prefetches towards the total commit count.
- if (!head_inst->isNop() && !head_inst->isInstPrefetch()) {
- cpu->instDone(tid);
+ if (tid == 0) {
+ canHandleInterrupts = (!head_inst->isDelayedCommit()) &&
+ ((THE_ISA != ALPHA_ISA) ||
+ (!(pc[0].instAddr() & 0x3)));
}
// Updates misc. registers.
head_inst->updateMiscRegs();
+ // Check instruction execution if it successfully commits and
+ // is not carrying a fault.
+ if (cpu->checker) {
+ cpu->checker->verify(head_inst);
+ }
+
+ cpu->traceFunctions(pc[tid].instAddr());
+
TheISA::advancePC(pc[tid], head_inst->staticInst);
// Keep track of the last sequence number commited
// 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->seqNum);
-
- int count = 0;
- Addr oldpc;
- // Debug statement. Checks to make sure we're not
- // currently updating state while handling PC events.
- assert(!thread[tid]->inSyscall && !thread[tid]->trapPending);
- do {
- oldpc = pc[tid].instAddr();
- cpu->system->pcEventQueue.service(thread[tid]->getTC());
- count++;
- } while (oldpc != pc[tid].instAddr());
- if (count > 1) {
- DPRINTF(Commit,
- "PC skip function event, stopping commit\n");
- break;
+ squashAfter(tid, head_inst);
+
+ if (drainPending) {
+ if (pc[tid].microPC() == 0 && interrupt == NoFault &&
+ !thread[tid]->trapPending) {
+ // Last architectually committed instruction.
+ // Squash the pipeline, stall fetch, and use
+ // drainImminent to disable interrupts
+ DPRINTF(Drain, "Draining: %i:%s\n", tid, pc[tid]);
+ squashAfter(tid, head_inst);
+ cpu->commitDrained(tid);
+ drainImminent = true;
+ }
+ }
+
+ bool onInstBoundary = !head_inst->isMicroop() ||
+ head_inst->isLastMicroop() ||
+ !head_inst->isDelayedCommit();
+
+ if (onInstBoundary) {
+ int count = 0;
+ Addr oldpc;
+ // Make sure we're not currently updating state while
+ // handling PC events.
+ assert(!thread[tid]->noSquashFromTC &&
+ !thread[tid]->trapPending);
+ do {
+ oldpc = pc[tid].instAddr();
+ cpu->system->pcEventQueue.service(thread[tid]->getTC());
+ count++;
+ } while (oldpc != pc[tid].instAddr());
+ if (count > 1) {
+ DPRINTF(Commit,
+ "PC skip function event, stopping commit\n");
+ break;
+ }
}
+
+ // Check if an instruction just enabled interrupts and we've
+ // previously had an interrupt pending that was not handled
+ // because interrupts were subsequently disabled before the
+ // pipeline reached a place to handle the interrupt. In that
+ // case squash now to make sure the interrupt is handled.
+ //
+ // If we don't do this, we might end up in a live lock situation
+ if (!interrupt && avoidQuiesceLiveLock &&
+ onInstBoundary && cpu->checkInterrupts(cpu->tcBase(0)))
+ squashAfter(tid, head_inst);
} else {
DPRINTF(Commit, "Unable to commit head instruction PC:%s "
"[tid:%i] [sn:%i].\n",
// and committed this instruction.
thread[tid]->funcExeInst--;
- if (head_inst->isNonSpeculative() ||
- head_inst->isStoreConditional() ||
- head_inst->isMemBarrier() ||
- head_inst->isWriteBarrier()) {
-
- DPRINTF(Commit, "Encountered a barrier or non-speculative "
- "instruction [sn:%lli] at the head of the ROB, PC %s.\n",
- head_inst->seqNum, head_inst->pcState());
-
- if (inst_num > 0 || iewStage->hasStoresToWB(tid)) {
- DPRINTF(Commit, "Waiting for all stores to writeback.\n");
- return false;
- }
-
- toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum;
+ // Make sure we are only trying to commit un-executed instructions we
+ // think are possible.
+ assert(head_inst->isNonSpeculative() || head_inst->isStoreConditional()
+ || head_inst->isMemBarrier() || head_inst->isWriteBarrier() ||
+ (head_inst->isLoad() && head_inst->strictlyOrdered()));
- // Change the instruction so it won't try to commit again until
- // it is executed.
- head_inst->clearCanCommit();
-
- ++commitNonSpecStalls;
+ DPRINTF(Commit, "Encountered a barrier or non-speculative "
+ "instruction [sn:%lli] at the head of the ROB, PC %s.\n",
+ head_inst->seqNum, head_inst->pcState());
+ if (inst_num > 0 || iewStage->hasStoresToWB(tid)) {
+ DPRINTF(Commit, "Waiting for all stores to writeback.\n");
return false;
- } else if (head_inst->isLoad()) {
- if (inst_num > 0 || iewStage->hasStoresToWB(tid)) {
- DPRINTF(Commit, "Waiting for all stores to writeback.\n");
- return false;
- }
-
- assert(head_inst->uncacheable());
- DPRINTF(Commit, "[sn:%lli]: Uncached load, PC %s.\n",
- head_inst->seqNum, head_inst->pcState());
+ }
- // Send back the non-speculative instruction's sequence
- // number. Tell the lsq to re-execute the load.
- toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum;
- toIEW->commitInfo[tid].uncached = true;
- toIEW->commitInfo[tid].uncachedLoad = head_inst;
+ toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum;
- head_inst->clearCanCommit();
+ // Change the instruction so it won't try to commit again until
+ // it is executed.
+ head_inst->clearCanCommit();
- return false;
+ if (head_inst->isLoad() && head_inst->strictlyOrdered()) {
+ DPRINTF(Commit, "[sn:%lli]: Strictly ordered load, PC %s.\n",
+ head_inst->seqNum, head_inst->pcState());
+ toIEW->commitInfo[tid].strictlyOrdered = true;
+ toIEW->commitInfo[tid].strictlyOrderedLoad = head_inst;
} else {
- panic("Trying to commit un-executed instruction "
- "of unknown type!\n");
+ ++commitNonSpecStalls;
}
+
+ return false;
}
if (head_inst->isThreadSync()) {
head_inst->setCompleted();
}
-#if USE_CHECKER
- // Use checker prior to updating anything due to traps or PC
- // based events.
- if (cpu->checker) {
- cpu->checker->verify(head_inst);
- }
-#endif
-
if (inst_fault != NoFault) {
DPRINTF(Commit, "Inst [sn:%lli] PC %s has a fault\n",
head_inst->seqNum, head_inst->pcState());
head_inst->setCompleted();
-#if USE_CHECKER
- if (cpu->checker && head_inst->isStore()) {
+ // If instruction has faulted, let the checker execute it and
+ // check if it sees the same fault and control flow.
+ if (cpu->checker) {
+ // Need to check the instruction before its fault is processed
cpu->checker->verify(head_inst);
}
-#endif
- assert(!thread[tid]->inSyscall);
+ assert(!thread[tid]->noSquashFromTC);
// Mark that we're in state update mode so that the trap's
// execution doesn't generate extra squashes.
- thread[tid]->inSyscall = true;
+ thread[tid]->noSquashFromTC = true;
// Execute the trap. Although it's slightly unrealistic in
// terms of timing (as it doesn't wait for the full timing of
cpu->trap(inst_fault, tid, head_inst->staticInst);
// Exit state update mode to avoid accidental updating.
- thread[tid]->inSyscall = false;
+ thread[tid]->noSquashFromTC = false;
commitStatus[tid] = TrapPending;
if (head_inst->traceData) {
if (DTRACE(ExecFaulting)) {
head_inst->traceData->setFetchSeq(head_inst->seqNum);
- head_inst->traceData->setCPSeq(thread[tid]->numInst);
+ head_inst->traceData->setCPSeq(thread[tid]->numOp);
head_inst->traceData->dump();
}
delete head_inst->traceData;
}
// Generate trap squash event.
- generateTrapEvent(tid);
+ generateTrapEvent(tid, inst_fault);
return false;
}
updateComInstStats(head_inst);
-#if FULL_SYSTEM
- if (thread[tid]->profile) {
- thread[tid]->profilePC = head_inst->instAddr();
- ProfileNode *node = thread[tid]->profile->consume(thread[tid]->getTC(),
- head_inst->staticInst);
+ if (FullSystem) {
+ if (thread[tid]->profile) {
+ thread[tid]->profilePC = head_inst->instAddr();
+ ProfileNode *node = thread[tid]->profile->consume(
+ thread[tid]->getTC(), head_inst->staticInst);
- if (node)
- thread[tid]->profileNode = node;
- }
- if (CPA::available()) {
- if (head_inst->isControl()) {
- ThreadContext *tc = thread[tid]->getTC();
- CPA::cpa()->swAutoBegin(tc, head_inst->nextInstAddr());
+ if (node)
+ thread[tid]->profileNode = node;
+ }
+ if (CPA::available()) {
+ if (head_inst->isControl()) {
+ ThreadContext *tc = thread[tid]->getTC();
+ CPA::cpa()->swAutoBegin(tc, head_inst->nextInstAddr());
+ }
}
}
-#endif
- DPRINTF(Commit, "Committing instruction with [sn:%lli]\n",
- head_inst->seqNum);
+ DPRINTF(Commit, "Committing instruction with [sn:%lli] PC %s\n",
+ head_inst->seqNum, head_inst->pcState());
if (head_inst->traceData) {
head_inst->traceData->setFetchSeq(head_inst->seqNum);
- head_inst->traceData->setCPSeq(thread[tid]->numInst);
+ head_inst->traceData->setCPSeq(thread[tid]->numOp);
head_inst->traceData->dump();
delete head_inst->traceData;
head_inst->traceData = NULL;
}
+ if (head_inst->isReturn()) {
+ DPRINTF(Commit,"Return Instruction Committed [sn:%lli] PC %s \n",
+ head_inst->seqNum, head_inst->pcState());
+ }
// Update the commit rename map
for (int i = 0; i < head_inst->numDestRegs(); i++) {
head_inst->renamedDestRegIdx(i));
}
- if (head_inst->isCopy())
- panic("Should not commit any copy instructions!");
-
// Finally clear the head ROB entry.
rob->retireHead(tid);
+#if TRACING_ON
+ if (DTRACE(O3PipeView)) {
+ head_inst->commitTick = curTick() - head_inst->fetchTick;
+ }
+#endif
+
// If this was a store, record it for this cycle.
if (head_inst->isStore())
committedStores[tid] = true;
}
}
-template <class Impl>
-void
-DefaultCommit<Impl>::skidInsert()
-{
- DPRINTF(Commit, "Attempting to any instructions from rename into "
- "skidBuffer.\n");
-
- for (int inst_num = 0; inst_num < fromRename->size; ++inst_num) {
- DynInstPtr inst = fromRename->insts[inst_num];
-
- if (!inst->isSquashed()) {
- DPRINTF(Commit, "Inserting PC %s [sn:%i] [tid:%i] into ",
- "skidBuffer.\n", inst->pcState(), inst->seqNum,
- inst->threadNumber);
- skidBuffer.push(inst);
- } else {
- DPRINTF(Commit, "Instruction PC %s [sn:%i] [tid:%i] was "
- "squashed, skipping.\n",
- inst->pcState(), inst->seqNum, inst->threadNumber);
- }
- }
-}
-
template <class Impl>
void
DefaultCommit<Impl>::markCompletedInsts()
{
// Grab completed insts out of the IEW instruction queue, and mark
// instructions completed within the ROB.
- for (int inst_num = 0;
- inst_num < fromIEW->size && fromIEW->insts[inst_num];
- ++inst_num)
- {
+ for (int inst_num = 0; inst_num < fromIEW->size; ++inst_num) {
+ assert(fromIEW->insts[inst_num]);
if (!fromIEW->insts[inst_num]->isSquashed()) {
DPRINTF(Commit, "[tid:%i]: Marking PC %s, [sn:%lli] ready "
"within ROB.\n",
}
}
-template <class Impl>
-bool
-DefaultCommit<Impl>::robDoneSquashing()
-{
- list<ThreadID>::iterator threads = activeThreads->begin();
- list<ThreadID>::iterator end = activeThreads->end();
-
- while (threads != end) {
- ThreadID tid = *threads++;
-
- if (!rob->isDoneSquashing(tid))
- return false;
- }
-
- return true;
-}
-
template <class Impl>
void
DefaultCommit<Impl>::updateComInstStats(DynInstPtr &inst)
{
ThreadID tid = inst->threadNumber;
- //
- // Pick off the software prefetches
- //
-#ifdef TARGET_ALPHA
- if (inst->isDataPrefetch()) {
- statComSwp[tid]++;
- } else {
- statComInst[tid]++;
+ if (!inst->isMicroop() || inst->isLastMicroop())
+ instsCommitted[tid]++;
+ opsCommitted[tid]++;
+
+ // To match the old model, don't count nops and instruction
+ // prefetches towards the total commit count.
+ if (!inst->isNop() && !inst->isInstPrefetch()) {
+ cpu->instDone(tid, inst);
}
-#else
- statComInst[tid]++;
-#endif
//
// Control Instructions
return InvalidThreadID;
}
}
+
+#endif//__CPU_O3_COMMIT_IMPL_HH__