/*
- * Copyright (c) 2010-2013 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
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),
+ drainImminent(false),
trapLatency(params->trapLatency),
canHandleInterrupts(true),
avoidQuiesceLiveLock(false)
{
ppCommit = new ProbePointArg<DynInstPtr>(cpu->getProbeManager(), "Commit");
ppCommitStall = new ProbePointArg<DynInstPtr>(cpu->getProbeManager(), "CommitStall");
+ ppSquash = new ProbePointArg<DynInstPtr>(cpu->getProbeManager(), "Squash");
}
template <class Impl>
.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() + ".committed_per_cycle")
.flags(total)
;
- commitEligible
- .init(cpu->numThreads)
- .name(name() + ".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() + ".bw_lim_events")
DefaultCommit<Impl>::drainResume()
{
drainPending = false;
+ drainImminent = false;
}
template <class Impl>
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, cpu->clockEdge(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;
}
commitStatus[0] = TrapPending;
+ interrupt = NoFault;
+
// Generate trap squash event.
- generateTrapEvent(0);
+ generateTrapEvent(0, interrupt);
- interrupt = NoFault;
avoidQuiesceLiveLock = false;
} else {
DPRINTF(Commit, "Interrupt pending: instruction is %sin "
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
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) {
// then use one older sequence number.
InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid];
- if (fromIEW->includeSquashInst[tid] == true) {
+ if (fromIEW->includeSquashInst[tid]) {
squashed_inst--;
}
if (toIEW->commitInfo[tid].mispredictInst->isUncondCtrl()) {
toIEW->commitInfo[tid].branchTaken = true;
}
+ ++branchMispredicts;
}
toIEW->commitInfo[tid].pc = fromIEW->pc[tid];
-
- if (toIEW->commitInfo[tid].mispredictInst) {
- ++branchMispredicts;
- }
}
+ 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();
if (interrupt != NoFault)
handleInterrupt();
- int commit_thread = getCommittingThread();
+ 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;
squashAfter(tid, head_inst);
if (drainPending) {
- DPRINTF(Drain, "Draining: %i:%s\n", tid, pc[tid]);
- if (pc[tid].microPC() == 0 && interrupt == NoFault) {
+ 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;
}
}
- int count = 0;
- Addr oldpc;
- // Debug statement. Checks to 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;
+ 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
// 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 &&
- (!head_inst->isMicroop() || head_inst->isLastMicroop()) &&
- cpu->checkInterrupts(cpu->tcBase(0)))
+ if (!interrupt && avoidQuiesceLiveLock &&
+ onInstBoundary && cpu->checkInterrupts(cpu->tcBase(0)))
squashAfter(tid, head_inst);
} else {
DPRINTF(Commit, "Unable to commit head instruction PC:%s "
// think are possible.
assert(head_inst->isNonSpeculative() || head_inst->isStoreConditional()
|| head_inst->isMemBarrier() || head_inst->isWriteBarrier() ||
- (head_inst->isLoad() && head_inst->uncacheable()));
+ (head_inst->isLoad() && head_inst->strictlyOrdered()));
DPRINTF(Commit, "Encountered a barrier or non-speculative "
"instruction [sn:%lli] at the head of the ROB, PC %s.\n",
// it is executed.
head_inst->clearCanCommit();
- if (head_inst->isLoad() && head_inst->uncacheable()) {
- DPRINTF(Commit, "[sn:%lli]: Uncached load, PC %s.\n",
+ 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].uncached = true;
- toIEW->commitInfo[tid].uncachedLoad = head_inst;
+ toIEW->commitInfo[tid].strictlyOrdered = true;
+ toIEW->commitInfo[tid].strictlyOrderedLoad = head_inst;
} else {
++commitNonSpecStalls;
}
}
// Generate trap squash event.
- generateTrapEvent(tid);
+ generateTrapEvent(tid, inst_fault);
return false;
}
}
}
-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)