gem5/src/cpu/ozone/lw_back_end_impl.hh
Ali Saidi 5146a69835 cpu: rename the misleading inSyscall to noSquashFromTC
isSyscall was originally created because during handling of a syscall in SE
mode the threadcontext had to be updated. However, in many places this is used
in FS mode (e.g. fault handlers) and the name doesn't make much sense. The
boolean actually stops gem5 from squashing speculative and non-committed state
when a write to a threadcontext happens, so re-name the variable to something
more appropriate
2013-01-07 13:05:33 -05:00

1675 lines
47 KiB
C++

/*
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Kevin Lim
*/
#include "config/the_isa.hh"
#include "cpu/checker/cpu.hh"
#include "cpu/ozone/lw_back_end.hh"
#include "cpu/op_class.hh"
template <class Impl>
void
LWBackEnd<Impl>::generateTrapEvent(Tick latency)
{
DPRINTF(BE, "Generating trap event\n");
TrapEvent *trap = new TrapEvent(this);
trap->schedule(curTick() + cpu->ticks(latency));
thread->trapPending = true;
}
template <class Impl>
int
LWBackEnd<Impl>::wakeDependents(DynInstPtr &inst, bool memory_deps)
{
assert(!inst->isSquashed());
std::vector<DynInstPtr> &dependents = memory_deps ? inst->getMemDeps() :
inst->getDependents();
int num_outputs = dependents.size();
DPRINTF(BE, "Waking instruction [sn:%lli] dependents in IQ\n", inst->seqNum);
for (int i = 0; i < num_outputs; i++) {
DynInstPtr dep_inst = dependents[i];
if (!memory_deps) {
dep_inst->markSrcRegReady();
} else {
if (!dep_inst->isSquashed())
dep_inst->markMemInstReady(inst.get());
}
DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum);
if (dep_inst->readyToIssue() && dep_inst->isInROB() &&
!dep_inst->isNonSpeculative() && !dep_inst->isStoreConditional() &&
dep_inst->memDepReady() && !dep_inst->isMemBarrier() &&
!dep_inst->isWriteBarrier()) {
DPRINTF(BE, "Adding instruction to exeList [sn:%lli]\n",
dep_inst->seqNum);
exeList.push(dep_inst);
if (dep_inst->iqItValid) {
DPRINTF(BE, "Removing instruction from waiting list\n");
waitingList.erase(dep_inst->iqIt);
waitingInsts--;
dep_inst->iqItValid = false;
assert(waitingInsts >= 0);
}
if (dep_inst->isMemRef()) {
removeWaitingMemOp(dep_inst);
DPRINTF(BE, "Issued a waiting mem op [sn:%lli]\n",
dep_inst->seqNum);
}
}
}
return num_outputs;
}
template <class Impl>
void
LWBackEnd<Impl>::rescheduleMemInst(DynInstPtr &inst)
{
replayList.push_front(inst);
}
template <class Impl>
LWBackEnd<Impl>::TrapEvent::TrapEvent(LWBackEnd<Impl> *_be)
: Event(&mainEventQueue, CPU_Tick_Pri), be(_be)
{
this->setFlags(Event::AutoDelete);
}
template <class Impl>
void
LWBackEnd<Impl>::TrapEvent::process()
{
be->trapSquash = true;
}
template <class Impl>
const char *
LWBackEnd<Impl>::TrapEvent::description() const
{
return "Trap";
}
template <class Impl>
void
LWBackEnd<Impl>::replayMemInst(DynInstPtr &inst)
{
bool found_inst = false;
while (!replayList.empty()) {
exeList.push(replayList.front());
if (replayList.front() == inst) {
found_inst = true;
}
replayList.pop_front();
}
assert(found_inst);
}
template <class Impl>
LWBackEnd<Impl>::LWBackEnd(Params *params)
: d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(params->backEndLatency, 0),
trapSquash(false), tcSquash(false),
latency(params->backEndLatency),
width(params->backEndWidth), lsqLimits(params->lsqLimits),
exactFullStall(true)
{
numROBEntries = params->numROBEntries;
numInsts = 0;
maxOutstandingMemOps = params->maxOutstandingMemOps;
numWaitingMemOps = 0;
waitingInsts = 0;
switchedOut = false;
switchPending = false;
LSQ.setBE(this);
// Setup IQ and LSQ with their parameters here.
instsToDispatch = d2i.getWire(-1);
instsToExecute = i2e.getWire(-1);
dispatchWidth = params->dispatchWidth ? params->dispatchWidth : width;
issueWidth = params->issueWidth ? params->issueWidth : width;
wbWidth = params->wbWidth ? params->wbWidth : width;
commitWidth = params->commitWidth ? params->commitWidth : width;
LSQ.init(params, params->LQEntries, params->SQEntries, 0);
dispatchStatus = Running;
commitStatus = Running;
}
template <class Impl>
std::string
LWBackEnd<Impl>::name() const
{
return cpu->name() + ".backend";
}
template <class Impl>
void
LWBackEnd<Impl>::regStats()
{
using namespace Stats;
LSQ.regStats();
robCapEvents
.init(cpu->numThreads)
.name(name() + ".ROB:cap_events")
.desc("number of cycles where ROB cap was active")
.flags(total)
;
robCapInstCount
.init(cpu->numThreads)
.name(name() + ".ROB:cap_inst")
.desc("number of instructions held up by ROB cap")
.flags(total)
;
iqCapEvents
.init(cpu->numThreads)
.name(name() +".IQ:cap_events" )
.desc("number of cycles where IQ cap was active")
.flags(total)
;
iqCapInstCount
.init(cpu->numThreads)
.name(name() + ".IQ:cap_inst")
.desc("number of instructions held up by IQ cap")
.flags(total)
;
exeInst
.init(cpu->numThreads)
.name(name() + ".ISSUE:count")
.desc("number of insts issued")
.flags(total)
;
exeSwp
.init(cpu->numThreads)
.name(name() + ".ISSUE:swp")
.desc("number of swp insts issued")
.flags(total)
;
exeNop
.init(cpu->numThreads)
.name(name() + ".ISSUE:nop")
.desc("number of nop insts issued")
.flags(total)
;
exeRefs
.init(cpu->numThreads)
.name(name() + ".ISSUE:refs")
.desc("number of memory reference insts issued")
.flags(total)
;
exeLoads
.init(cpu->numThreads)
.name(name() + ".ISSUE:loads")
.desc("number of load insts issued")
.flags(total)
;
exeBranches
.init(cpu->numThreads)
.name(name() + ".ISSUE:branches")
.desc("Number of branches issued")
.flags(total)
;
issuedOps
.init(cpu->numThreads)
.name(name() + ".ISSUE:op_count")
.desc("number of insts issued")
.flags(total)
;
/*
for (int i=0; i<Num_OpClasses; ++i) {
stringstream subname;
subname << opClassStrings[i] << "_delay";
issue_delay_dist.subname(i, subname.str());
}
*/
//
// Other stats
//
lsqForwLoads
.init(cpu->numThreads)
.name(name() + ".LSQ:forw_loads")
.desc("number of loads forwarded via LSQ")
.flags(total)
;
invAddrLoads
.init(cpu->numThreads)
.name(name() + ".ISSUE:addr_loads")
.desc("number of invalid-address loads")
.flags(total)
;
invAddrSwpfs
.init(cpu->numThreads)
.name(name() + ".ISSUE:addr_swpfs")
.desc("number of invalid-address SW prefetches")
.flags(total)
;
lsqBlockedLoads
.init(cpu->numThreads)
.name(name() + ".LSQ:blocked_loads")
.desc("number of ready loads not issued due to memory disambiguation")
.flags(total)
;
lsqInversion
.name(name() + ".ISSUE:lsq_invert")
.desc("Number of times LSQ instruction issued early")
;
nIssuedDist
.init(issueWidth + 1)
.name(name() + ".ISSUE:issued_per_cycle")
.desc("Number of insts issued each cycle")
.flags(total | pdf | dist)
;
/*
issueDelayDist
.init(Num_OpClasses,0,99,2)
.name(name() + ".ISSUE:")
.desc("cycles from operands ready to issue")
.flags(pdf | cdf)
;
queueResDist
.init(Num_OpClasses, 0, 99, 2)
.name(name() + ".IQ:residence:")
.desc("cycles from dispatch to issue")
.flags(total | pdf | cdf )
;
for (int i = 0; i < Num_OpClasses; ++i) {
queueResDist.subname(i, opClassStrings[i]);
}
*/
writebackCount
.init(cpu->numThreads)
.name(name() + ".WB:count")
.desc("cumulative count of insts written-back")
.flags(total)
;
producerInst
.init(cpu->numThreads)
.name(name() + ".WB:producers")
.desc("num instructions producing a value")
.flags(total)
;
consumerInst
.init(cpu->numThreads)
.name(name() + ".WB:consumers")
.desc("num instructions consuming a value")
.flags(total)
;
wbPenalized
.init(cpu->numThreads)
.name(name() + ".WB:penalized")
.desc("number of instrctions required to write to 'other' IQ")
.flags(total)
;
wbPenalizedRate
.name(name() + ".WB:penalized_rate")
.desc ("fraction of instructions written-back that wrote to 'other' IQ")
.flags(total)
;
wbPenalizedRate = wbPenalized / writebackCount;
wbFanout
.name(name() + ".WB:fanout")
.desc("average fanout of values written-back")
.flags(total)
;
wbFanout = producerInst / consumerInst;
wbRate
.name(name() + ".WB:rate")
.desc("insts written-back per cycle")
.flags(total)
;
wbRate = writebackCount / cpu->numCycles;
statComInst
.init(cpu->numThreads)
.name(name() + ".COM:count")
.desc("Number of instructions committed")
.flags(total)
;
statComSwp
.init(cpu->numThreads)
.name(name() + ".COM:swp_count")
.desc("Number of s/w prefetches committed")
.flags(total)
;
statComRefs
.init(cpu->numThreads)
.name(name() + ".COM:refs")
.desc("Number of memory references committed")
.flags(total)
;
statComLoads
.init(cpu->numThreads)
.name(name() + ".COM:loads")
.desc("Number of loads committed")
.flags(total)
;
statComMembars
.init(cpu->numThreads)
.name(name() + ".COM:membars")
.desc("Number of memory barriers committed")
.flags(total)
;
statComBranches
.init(cpu->numThreads)
.name(name() + ".COM:branches")
.desc("Number of branches committed")
.flags(total)
;
nCommittedDist
.init(0,commitWidth,1)
.name(name() + ".COM:committed_per_cycle")
.desc("Number of insts commited each cycle")
.flags(pdf)
;
//
// Commit-Eligible instructions...
//
// -> The number of instructions eligible to commit in those
// cycles where we reached our commit BW limit (less the number
// actually committed)
//
// -> The average value is computed over ALL CYCLES... not just
// the BW limited cycles
//
// -> The standard deviation is computed only over cycles where
// we reached the BW limit
//
commitEligible
.init(cpu->numThreads)
.name(name() + ".COM:bw_limited")
.desc("number of insts not committed due to BW limits")
.flags(total)
;
commitEligibleSamples
.name(name() + ".COM:bw_lim_events")
.desc("number cycles where commit BW limit reached")
;
squashedInsts
.init(cpu->numThreads)
.name(name() + ".COM:squashed_insts")
.desc("Number of instructions removed from inst list")
;
ROBSquashedInsts
.init(cpu->numThreads)
.name(name() + ".COM:rob_squashed_insts")
.desc("Number of instructions removed from inst list when they reached the head of the ROB")
;
ROBFcount
.name(name() + ".ROB:full_count")
.desc("number of cycles where ROB was full")
;
ROBCount
.init(cpu->numThreads)
.name(name() + ".ROB:occupancy")
.desc(name() + ".ROB occupancy (cumulative)")
.flags(total)
;
ROBFullRate
.name(name() + ".ROB:full_rate")
.desc("ROB full per cycle")
;
ROBFullRate = ROBFcount / cpu->numCycles;
ROBOccRate
.name(name() + ".ROB:occ_rate")
.desc("ROB occupancy rate")
.flags(total)
;
ROBOccRate = ROBCount / cpu->numCycles;
/*
ROBOccDist
.init(cpu->numThreads, 0, numROBEntries, 2)
.name(name() + ".ROB:occ_dist")
.desc("ROB Occupancy per cycle")
.flags(total | cdf)
;
*/
}
template <class Impl>
void
LWBackEnd<Impl>::setCPU(OzoneCPU *cpu_ptr)
{
cpu = cpu_ptr;
LSQ.setCPU(cpu_ptr);
checker = cpu->checker;
}
template <class Impl>
void
LWBackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm)
{
comm = _comm;
toIEW = comm->getWire(0);
fromCommit = comm->getWire(-1);
}
template <class Impl>
void
LWBackEnd<Impl>::checkInterrupts()
{
if (cpu->checkInterrupts(tc) && !trapSquash && !tcSquash) {
frontEnd->interruptPending = true;
if (robEmpty() && !LSQ.hasStoresToWB()) {
// Will need to squash all instructions currently in flight and have
// the interrupt handler restart at the last non-committed inst.
// Most of that can be handled through the trap() function. The
// processInterrupts() function really just checks for interrupts
// and then calls trap() if there is an interrupt present.
// Not sure which thread should be the one to interrupt. For now
// always do thread 0.
assert(!thread->noSquashFromTC);
thread->noSquashFromTC = true;
// CPU will handle implementation of the interrupt.
cpu->processInterrupts();
// Now squash or record that I need to squash this cycle.
commitStatus = TrapPending;
// Exit state update mode to avoid accidental updating.
thread->noSquashFromTC = false;
// Generate trap squash event.
generateTrapEvent();
DPRINTF(BE, "Interrupt detected.\n");
} else {
DPRINTF(BE, "Interrupt must wait for ROB to drain.\n");
}
}
}
template <class Impl>
void
LWBackEnd<Impl>::handleFault(Fault &fault, Tick latency)
{
DPRINTF(BE, "Handling fault!\n");
assert(!thread->noSquashFromTC);
thread->noSquashFromTC = true;
// Consider holding onto the trap and waiting until the trap event
// happens for this to be executed.
fault->invoke(thread->getTC());
// Exit state update mode to avoid accidental updating.
thread->noSquashFromTC = false;
commitStatus = TrapPending;
// Generate trap squash event.
generateTrapEvent(latency);
}
template <class Impl>
void
LWBackEnd<Impl>::tick()
{
DPRINTF(BE, "Ticking back end\n");
// Read in any done instruction information and update the IQ or LSQ.
updateStructures();
if (switchPending && robEmpty() && !LSQ.hasStoresToWB()) {
cpu->signalSwitched();
return;
}
readyInstsForCommit();
numInstsToWB.advance();
ROBCount[0]+= numInsts;
wbCycle = 0;
checkInterrupts();
if (trapSquash) {
assert(!tcSquash);
squashFromTrap();
} else if (tcSquash) {
squashFromTC();
}
if (dispatchStatus != Blocked) {
dispatchInsts();
} else {
checkDispatchStatus();
}
if (commitStatus != TrapPending) {
executeInsts();
commitInsts();
}
LSQ.writebackStores();
DPRINTF(BE, "Waiting insts: %i, mem ops: %i, ROB entries in use: %i, "
"LSQ loads: %i, LSQ stores: %i\n",
waitingInsts, numWaitingMemOps, numInsts,
LSQ.numLoads(), LSQ.numStores());
#ifdef DEBUG
assert(numInsts == instList.size());
assert(waitingInsts == waitingList.size());
assert(numWaitingMemOps == waitingMemOps.size());
assert(!switchedOut);
#endif
}
template <class Impl>
void
LWBackEnd<Impl>::updateStructures()
{
if (fromCommit->doneSeqNum) {
LSQ.commitLoads(fromCommit->doneSeqNum);
LSQ.commitStores(fromCommit->doneSeqNum);
}
if (fromCommit->nonSpecSeqNum) {
if (fromCommit->uncached) {
// LSQ.executeLoad(fromCommit->lqIdx);
} else {
// IQ.scheduleNonSpec(
// fromCommit->nonSpecSeqNum);
}
}
}
template <class Impl>
void
LWBackEnd<Impl>::addToLSQ(DynInstPtr &inst)
{
// Do anything LSQ specific here?
LSQ.insert(inst);
}
template <class Impl>
void
LWBackEnd<Impl>::dispatchInsts()
{
DPRINTF(BE, "Trying to dispatch instructions.\n");
while (numInsts < numROBEntries &&
numWaitingMemOps < maxOutstandingMemOps) {
// Get instruction from front of time buffer
if (lsqLimits && LSQ.isFull()) {
break;
}
DynInstPtr inst = frontEnd->getInst();
if (!inst) {
break;
} else if (inst->isSquashed()) {
continue;
}
++numInsts;
instList.push_front(inst);
inst->setInROB();
DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n",
inst->seqNum, inst->readPC());
for (int i = 0; i < inst->numDestRegs(); ++i)
renameTable[inst->destRegIdx(i)] = inst;
if (inst->isMemBarrier() || inst->isWriteBarrier()) {
if (memBarrier) {
DPRINTF(BE, "Instruction [sn:%lli] is waiting on "
"barrier [sn:%lli].\n",
inst->seqNum, memBarrier->seqNum);
memBarrier->addMemDependent(inst);
inst->addSrcMemInst(memBarrier);
}
memBarrier = inst;
inst->setCanCommit();
} else if (inst->readyToIssue() &&
!inst->isNonSpeculative() &&
!inst->isStoreConditional()) {
if (inst->isMemRef()) {
LSQ.insert(inst);
if (memBarrier) {
DPRINTF(BE, "Instruction [sn:%lli] is waiting on "
"barrier [sn:%lli].\n",
inst->seqNum, memBarrier->seqNum);
memBarrier->addMemDependent(inst);
inst->addSrcMemInst(memBarrier);
addWaitingMemOp(inst);
waitingList.push_front(inst);
inst->iqIt = waitingList.begin();
inst->iqItValid = true;
waitingInsts++;
} else {
DPRINTF(BE, "Instruction [sn:%lli] ready, addding to "
"exeList.\n",
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();
numInstsToWB[0]++;
} else {
DPRINTF(BE, "Instruction [sn:%lli] ready, addding to "
"exeList.\n",
inst->seqNum);
exeList.push(inst);
}
} else {
if (inst->isNonSpeculative() || inst->isStoreConditional()) {
inst->setCanCommit();
DPRINTF(BE, "Adding non speculative instruction\n");
}
if (inst->isMemRef()) {
addWaitingMemOp(inst);
LSQ.insert(inst);
if (memBarrier) {
memBarrier->addMemDependent(inst);
inst->addSrcMemInst(memBarrier);
DPRINTF(BE, "Instruction [sn:%lli] is waiting on "
"barrier [sn:%lli].\n",
inst->seqNum, memBarrier->seqNum);
}
}
DPRINTF(BE, "Instruction [sn:%lli] not ready, addding to "
"waitingList.\n",
inst->seqNum);
waitingList.push_front(inst);
inst->iqIt = waitingList.begin();
inst->iqItValid = true;
waitingInsts++;
}
}
// Check if IQ or LSQ is full. If so we'll need to break and stop
// removing instructions. Also update the number of insts to remove
// from the queue. Check here if we don't care about exact stall
// conditions.
/*
bool stall = false;
if (IQ.isFull()) {
DPRINTF(BE, "IQ is full!\n");
stall = true;
} else if (LSQ.isFull()) {
DPRINTF(BE, "LSQ is full!\n");
stall = true;
} else if (isFull()) {
DPRINTF(BE, "ROB is full!\n");
stall = true;
ROB_fcount++;
}
if (stall) {
d2i.advance();
dispatchStall();
return;
}
*/
}
template <class Impl>
void
LWBackEnd<Impl>::dispatchStall()
{
dispatchStatus = Blocked;
if (!cpu->decoupledFrontEnd) {
// Tell front end to stall here through a timebuffer, or just tell
// it directly.
}
}
template <class Impl>
void
LWBackEnd<Impl>::checkDispatchStatus()
{
DPRINTF(BE, "Checking dispatch status\n");
assert(dispatchStatus == Blocked);
if (!LSQ.isFull() && !isFull()) {
DPRINTF(BE, "Dispatch no longer blocked\n");
dispatchStatus = Running;
dispatchInsts();
}
}
template <class Impl>
void
LWBackEnd<Impl>::executeInsts()
{
DPRINTF(BE, "Trying to execute instructions\n");
int num_executed = 0;
while (!exeList.empty() && num_executed < issueWidth) {
DynInstPtr inst = exeList.top();
DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n",
inst->seqNum, inst->readPC());
// Check if the instruction is squashed; if so then skip it
// and don't count it towards the FU usage.
if (inst->isSquashed()) {
DPRINTF(BE, "Execute: Instruction was squashed.\n");
// Not sure how to handle this plus the method of sending # of
// instructions to use. Probably will just have to count it
// towards the bandwidth usage, but not the FU usage.
++num_executed;
// Consider this instruction executed so that commit can go
// ahead and retire the instruction.
inst->setExecuted();
// Not sure if I should set this here or just let commit try to
// commit any squashed instructions. I like the latter a bit more.
inst->setCanCommit();
// ++iewExecSquashedInsts;
exeList.pop();
continue;
}
Fault fault = NoFault;
// Execute instruction.
// Note that if the instruction faults, it will be handled
// at the commit stage.
if (inst->isMemRef() &&
(!inst->isDataPrefetch() && !inst->isInstPrefetch())) {
DPRINTF(BE, "Execute: Initiating access for memory "
"reference.\n");
if (inst->isLoad()) {
LSQ.executeLoad(inst);
} else if (inst->isStore()) {
Fault fault = LSQ.executeStore(inst);
if (!inst->isStoreConditional() && fault == NoFault) {
inst->setExecuted();
instToCommit(inst);
} else if (fault != NoFault) {
// If the instruction faulted, then we need to send it along to commit
// without the instruction completing.
// Send this instruction to commit, also make sure iew stage
// realizes there is activity.
inst->setExecuted();
instToCommit(inst);
}
} else {
panic("Unknown mem type!");
}
} else {
inst->execute();
inst->setExecuted();
instToCommit(inst);
}
updateExeInstStats(inst);
++funcExeInst;
++num_executed;
exeList.pop();
if (inst->mispredicted()) {
squashDueToBranch(inst);
break;
} else if (LSQ.violation()) {
// Get the DynInst that caused the violation. Note that this
// clears the violation signal.
DynInstPtr violator;
violator = LSQ.getMemDepViolator();
DPRINTF(BE, "LDSTQ detected a violation. Violator PC: "
"%#x, inst PC: %#x. Addr is: %#x.\n",
violator->readPC(), inst->readPC(), inst->physEffAddr);
// Squash.
squashDueToMemViolation(inst);
}
}
issuedOps[0]+= num_executed;
nIssuedDist[num_executed]++;
}
template<class Impl>
void
LWBackEnd<Impl>::instToCommit(DynInstPtr &inst)
{
DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n",
inst->seqNum, inst->readPC());
if (!inst->isSquashed()) {
if (inst->isExecuted()) {
inst->setResultReady();
int dependents = wakeDependents(inst);
if (dependents) {
producerInst[0]++;
consumerInst[0]+= dependents;
}
}
}
writeback.push_back(inst);
numInstsToWB[0]++;
writebackCount[0]++;
}
template <class Impl>
void
LWBackEnd<Impl>::readyInstsForCommit()
{
for (int i = numInstsToWB[-latency];
!writeback.empty() && i;
--i)
{
DynInstPtr inst = writeback.front();
writeback.pop_front();
if (!inst->isSquashed()) {
DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n",
inst->seqNum, inst->readPC());
inst->setCanCommit();
}
}
}
#if 0
template <class Impl>
void
LWBackEnd<Impl>::writebackInsts()
{
int wb_width = wbWidth;
// Using this method I'm not quite sure how to prevent an
// instruction from waking its own dependents multiple times,
// without the guarantee that commit always has enough bandwidth
// to accept all instructions being written back. This guarantee
// might not be too unrealistic.
InstListIt wb_inst_it = writeback.begin();
InstListIt wb_end_it = writeback.end();
int inst_num = 0;
int consumer_insts = 0;
for (; inst_num < wb_width &&
wb_inst_it != wb_end_it; inst_num++) {
DynInstPtr inst = (*wb_inst_it);
// Some instructions will be sent to commit without having
// executed because they need commit to handle them.
// E.g. Uncached loads have not actually executed when they
// are first sent to commit. Instead commit must tell the LSQ
// when it's ready to execute the uncached load.
if (!inst->isSquashed()) {
DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n",
inst->seqNum, inst->readPC());
inst->setCanCommit();
inst->setResultReady();
if (inst->isExecuted()) {
int dependents = wakeDependents(inst);
if (dependents) {
producer_inst[0]++;
consumer_insts+= dependents;
}
}
}
writeback.erase(wb_inst_it++);
}
LSQ.writebackStores();
consumer_inst[0]+= consumer_insts;
writeback_count[0]+= inst_num;
}
#endif
template <class Impl>
bool
LWBackEnd<Impl>::commitInst(int inst_num)
{
// Read instruction from the head of the ROB
DynInstPtr inst = instList.back();
// Make sure instruction is valid
assert(inst);
if (!inst->readyToCommit())
return false;
DPRINTF(BE, "Trying to commit instruction [sn:%lli] PC:%#x\n",
inst->seqNum, inst->readPC());
thread->setPC(inst->readPC());
thread->setNextPC(inst->readNextPC());
inst->setAtCommit();
// If the instruction is not executed yet, then it is a non-speculative
// or store inst. Signal backwards that it should be executed.
if (!inst->isExecuted()) {
if (inst->isNonSpeculative() ||
(inst->isStoreConditional() && inst->getFault() == NoFault) ||
inst->isMemBarrier() ||
inst->isWriteBarrier()) {
if ((inst->isMemBarrier() || inst->isWriteBarrier() ||
inst->isQuiesce()) && LSQ.hasStoresToWB())
{
DPRINTF(BE, "Waiting for all stores to writeback.\n");
return false;
}
DPRINTF(BE, "Encountered a store or non-speculative "
"instruction at the head of the ROB, PC %#x.\n",
inst->readPC());
if (inst->isMemBarrier() || inst->isWriteBarrier()) {
DPRINTF(BE, "Waking dependents on barrier [sn:%lli]\n",
inst->seqNum);
assert(memBarrier);
wakeDependents(inst, true);
if (memBarrier == inst)
memBarrier = NULL;
inst->clearMemDependents();
}
// Send back the non-speculative instruction's sequence number.
if (inst->iqItValid) {
DPRINTF(BE, "Removing instruction from waiting list\n");
waitingList.erase(inst->iqIt);
inst->iqItValid = false;
waitingInsts--;
assert(waitingInsts >= 0);
if (inst->isStore())
removeWaitingMemOp(inst);
}
exeList.push(inst);
// Change the instruction so it won't try to commit again until
// it is executed.
inst->clearCanCommit();
// ++commitNonSpecStalls;
return false;
} else if (inst->isLoad()) {
DPRINTF(BE, "[sn:%lli]: Uncached load, PC %#x.\n",
inst->seqNum, inst->readPC());
// Send back the non-speculative instruction's sequence
// number. Maybe just tell the lsq to re-execute the load.
// Send back the non-speculative instruction's sequence number.
if (inst->iqItValid) {
DPRINTF(BE, "Removing instruction from waiting list\n");
waitingList.erase(inst->iqIt);
inst->iqItValid = false;
waitingInsts--;
assert(waitingInsts >= 0);
removeWaitingMemOp(inst);
}
replayMemInst(inst);
inst->clearCanCommit();
return false;
} else {
panic("Trying to commit un-executed instruction "
"of unknown type!\n");
}
}
// Not handled for now.
assert(!inst->isThreadSync());
assert(inst->memDepReady());
// Stores will mark themselves as totally completed as they need
// to wait to writeback to memory. @todo: Hack...attempt to fix
// having the checker be forced to wait until a store completes in
// order to check all of the instructions. If the store at the
// head of the check list misses, but a later store hits, then
// loads in the checker may see the younger store values instead
// of the store they should see. Either the checker needs its own
// memory (annoying to update), its own store buffer (how to tell
// which value is correct?), or something else...
if (!inst->isStore()) {
inst->setCompleted();
}
// Check if the instruction caused a fault. If so, trap.
Fault inst_fault = inst->getFault();
// Use checker prior to updating anything due to traps or PC
// based events.
if (checker) {
checker->verify(inst);
}
if (inst_fault != NoFault) {
DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n",
inst->seqNum, inst->readPC());
// Instruction is completed as it has a fault.
inst->setCompleted();
if (LSQ.hasStoresToWB()) {
DPRINTF(BE, "Stores still in flight, will wait until drained.\n");
return false;
} else if (inst_num != 0) {
DPRINTF(BE, "Will wait until instruction is head of commit group.\n");
return false;
}
else if (checker && inst->isStore()) {
checker->verify(inst);
}
handleFault(inst_fault);
return false;
}
int freed_regs = 0;
for (int i = 0; i < inst->numDestRegs(); ++i) {
DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n",
(int)inst->destRegIdx(i), inst->seqNum);
thread->renameTable[inst->destRegIdx(i)] = inst;
++freed_regs;
}
if (FullSystem && thread->profile) {
thread->profilePC = inst->readPC();
ProfileNode *node = thread->profile->consume(thread->getTC(),
inst->staticInst);
if (node)
thread->profileNode = node;
}
if (inst->traceData) {
inst->traceData->setFetchSeq(inst->seqNum);
inst->traceData->setCPSeq(thread->numInst);
inst->traceData->finalize();
inst->traceData = NULL;
}
inst->clearDependents();
frontEnd->addFreeRegs(freed_regs);
instList.pop_back();
--numInsts;
++thread->funcExeInst;
// Maybe move this to where the fault is handled; if the fault is
// handled, don't try to set this myself as the fault will set it.
// If not, then I set thread->PC = thread->nextPC and
// thread->nextPC = thread->nextPC + 4.
thread->setPC(thread->readNextPC());
thread->setNextPC(thread->readNextPC() + sizeof(TheISA::MachInst));
updateComInstStats(inst);
// Write the done sequence number here.
toIEW->doneSeqNum = inst->seqNum;
lastCommitCycle = curTick();
if (FullSystem) {
int count = 0;
Addr oldpc;
do {
if (count == 0)
assert(!thread->noSquashFromTC && !thread->trapPending);
oldpc = thread->readPC();
cpu->system->pcEventQueue.service(
thread->getTC());
count++;
} while (oldpc != thread->readPC());
if (count > 1) {
DPRINTF(BE, "PC skip function event, stopping commit\n");
tcSquash = true;
return false;
}
}
return true;
}
template <class Impl>
void
LWBackEnd<Impl>::commitInsts()
{
// Not sure this should be a loop or not.
int inst_num = 0;
while (!instList.empty() && inst_num < commitWidth) {
if (instList.back()->isSquashed()) {
instList.back()->clearDependents();
ROBSquashedInsts[instList.back()->threadNumber]++;
instList.pop_back();
--numInsts;
continue;
}
if (!commitInst(inst_num++)) {
DPRINTF(BE, "Can't commit, Instruction [sn:%lli] PC "
"%#x is head of ROB and not ready\n",
instList.back()->seqNum, instList.back()->readPC());
--inst_num;
break;
}
}
nCommittedDist.sample(inst_num);
}
template <class Impl>
void
LWBackEnd<Impl>::squash(const InstSeqNum &sn)
{
LSQ.squash(sn);
int freed_regs = 0;
InstListIt insts_end_it = waitingList.end();
InstListIt insts_it = waitingList.begin();
while (insts_it != insts_end_it && (*insts_it)->seqNum > sn)
{
if ((*insts_it)->isSquashed()) {
++insts_it;
continue;
}
DPRINTF(BE, "Squashing instruction on waitingList PC %#x, [sn:%lli].\n",
(*insts_it)->readPC(),
(*insts_it)->seqNum);
if ((*insts_it)->isMemRef()) {
DPRINTF(BE, "Squashing a waiting mem op [sn:%lli]\n",
(*insts_it)->seqNum);
removeWaitingMemOp((*insts_it));
}
waitingList.erase(insts_it++);
waitingInsts--;
}
assert(waitingInsts >= 0);
insts_it = instList.begin();
while (!instList.empty() && (*insts_it)->seqNum > sn)
{
if ((*insts_it)->isSquashed()) {
panic("Instruction should not be already squashed and on list!");
++insts_it;
continue;
}
DPRINTF(BE, "Squashing instruction on inst list PC %#x, [sn:%lli].\n",
(*insts_it)->readPC(),
(*insts_it)->seqNum);
// Mark the instruction as squashed, and ready to commit so that
// it can drain out of the pipeline.
(*insts_it)->setSquashed();
(*insts_it)->setCanCommit();
(*insts_it)->clearInROB();
for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) {
DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i);
DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n",
(int)(*insts_it)->destRegIdx(i), prev_dest->seqNum);
renameTable[(*insts_it)->destRegIdx(i)] = prev_dest;
++freed_regs;
}
(*insts_it)->clearDependents();
squashedInsts[(*insts_it)->threadNumber]++;
instList.erase(insts_it++);
--numInsts;
}
while (memBarrier && memBarrier->seqNum > sn) {
DPRINTF(BE, "[sn:%lli] Memory barrier squashed (or previously "
"squashed)\n", memBarrier->seqNum);
memBarrier->clearMemDependents();
if (memBarrier->memDepReady()) {
DPRINTF(BE, "No previous barrier\n");
memBarrier = NULL;
} else {
std::list<DynInstPtr> &srcs = memBarrier->getMemSrcs();
memBarrier = srcs.front();
srcs.pop_front();
assert(srcs.empty());
DPRINTF(BE, "Previous barrier: [sn:%lli]\n",
memBarrier->seqNum);
}
}
insts_it = replayList.begin();
insts_end_it = replayList.end();
while (!replayList.empty() && insts_it != insts_end_it) {
if ((*insts_it)->seqNum < sn) {
++insts_it;
continue;
}
assert((*insts_it)->isSquashed());
replayList.erase(insts_it++);
}
frontEnd->addFreeRegs(freed_regs);
}
template <class Impl>
void
LWBackEnd<Impl>::squashFromTC()
{
InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1;
squash(squashed_inst);
frontEnd->squash(squashed_inst, thread->readPC(),
false, false);
frontEnd->interruptPending = false;
thread->trapPending = false;
thread->noSquashFromTC = false;
tcSquash = false;
commitStatus = Running;
}
template <class Impl>
void
LWBackEnd<Impl>::squashFromTrap()
{
InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1;
squash(squashed_inst);
frontEnd->squash(squashed_inst, thread->readPC(),
false, false);
frontEnd->interruptPending = false;
thread->trapPending = false;
thread->noSquashFromTC = false;
trapSquash = false;
commitStatus = Running;
}
template <class Impl>
void
LWBackEnd<Impl>::squashDueToBranch(DynInstPtr &inst)
{
// Update the branch predictor state I guess
DPRINTF(BE, "Squashing due to branch [sn:%lli], will restart at PC %#x\n",
inst->seqNum, inst->readNextPC());
squash(inst->seqNum);
frontEnd->squash(inst->seqNum, inst->readNextPC(),
true, inst->mispredicted());
}
template <class Impl>
void
LWBackEnd<Impl>::squashDueToMemViolation(DynInstPtr &inst)
{
// Update the branch predictor state I guess
DPRINTF(BE, "Squashing due to violation [sn:%lli], will restart at PC %#x\n",
inst->seqNum, inst->readNextPC());
squash(inst->seqNum);
frontEnd->squash(inst->seqNum, inst->readNextPC(),
false, inst->mispredicted());
}
template <class Impl>
void
LWBackEnd<Impl>::squashDueToMemBlocked(DynInstPtr &inst)
{
DPRINTF(IEW, "Memory blocked, squashing load and younger insts, "
"PC: %#x [sn:%i].\n", inst->readPC(), inst->seqNum);
squash(inst->seqNum - 1);
frontEnd->squash(inst->seqNum - 1, inst->readPC());
}
template <class Impl>
void
LWBackEnd<Impl>::switchOut()
{
switchPending = true;
}
template <class Impl>
void
LWBackEnd<Impl>::doSwitchOut()
{
switchedOut = true;
switchPending = false;
// Need to get rid of all committed, non-speculative state and write it
// to memory/TC. In this case this is stores that have committed and not
// yet written back.
assert(robEmpty());
assert(!LSQ.hasStoresToWB());
writeback.clear();
for (int i = 0; i < numInstsToWB.getSize() + 1; ++i)
numInstsToWB.advance();
// squash(0);
assert(waitingList.empty());
assert(instList.empty());
assert(replayList.empty());
assert(writeback.empty());
LSQ.switchOut();
}
template <class Impl>
void
LWBackEnd<Impl>::takeOverFrom(ThreadContext *old_tc)
{
assert(!squashPending);
squashSeqNum = 0;
squashNextPC = 0;
tcSquash = false;
trapSquash = false;
numInsts = 0;
numWaitingMemOps = 0;
waitingMemOps.clear();
waitingInsts = 0;
switchedOut = false;
dispatchStatus = Running;
commitStatus = Running;
LSQ.takeOverFrom(old_tc);
}
template <class Impl>
void
LWBackEnd<Impl>::updateExeInstStats(DynInstPtr &inst)
{
ThreadID tid = inst->threadNumber;
exeInst[tid]++;
//
// Control operations
//
if (inst->isControl())
exeBranches[tid]++;
//
// Memory operations
//
if (inst->isMemRef()) {
exeRefs[tid]++;
if (inst->isLoad())
exeLoads[tid]++;
}
}
template <class Impl>
void
LWBackEnd<Impl>::updateComInstStats(DynInstPtr &inst)
{
ThreadID tid = inst->threadNumber;
// keep an instruction count
thread->numInst++;
thread->numInsts++;
cpu->numInst++;
//
// Pick off the software prefetches
//
#ifdef TARGET_ALPHA
if (inst->isDataPrefetch()) {
statComSwp[tid]++;
} else {
statComInst[tid]++;
}
#else
statComInst[tid]++;
#endif
//
// Control Instructions
//
if (inst->isControl())
statComBranches[tid]++;
//
// Memory references
//
if (inst->isMemRef()) {
statComRefs[tid]++;
if (inst->isLoad()) {
statComLoads[tid]++;
}
}
if (inst->isMemBarrier()) {
statComMembars[tid]++;
}
}
template <class Impl>
void
LWBackEnd<Impl>::dumpInsts()
{
int num = 0;
int valid_num = 0;
InstListIt inst_list_it = --(instList.end());
cprintf("ExeList size: %i\n", exeList.size());
cprintf("Inst list size: %i\n", instList.size());
while (inst_list_it != instList.end())
{
cprintf("Instruction:%i\n",
num);
if (!(*inst_list_it)->isSquashed()) {
if (!(*inst_list_it)->isIssued()) {
++valid_num;
cprintf("Count:%i\n", valid_num);
} else if ((*inst_list_it)->isMemRef() &&
!(*inst_list_it)->memOpDone) {
// Loads that have not been marked as executed still count
// towards the total instructions.
++valid_num;
cprintf("Count:%i\n", valid_num);
}
}
cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
"Issued:%i\nSquashed:%i\n",
(*inst_list_it)->readPC(),
(*inst_list_it)->seqNum,
(*inst_list_it)->threadNumber,
(*inst_list_it)->isIssued(),
(*inst_list_it)->isSquashed());
if ((*inst_list_it)->isMemRef()) {
cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone);
}
cprintf("\n");
inst_list_it--;
++num;
}
inst_list_it = --(writeback.end());
cprintf("Writeback list size: %i\n", writeback.size());
while (inst_list_it != writeback.end())
{
cprintf("Instruction:%i\n",
num);
if (!(*inst_list_it)->isSquashed()) {
if (!(*inst_list_it)->isIssued()) {
++valid_num;
cprintf("Count:%i\n", valid_num);
} else if ((*inst_list_it)->isMemRef() &&
!(*inst_list_it)->memOpDone) {
// Loads that have not been marked as executed still count
// towards the total instructions.
++valid_num;
cprintf("Count:%i\n", valid_num);
}
}
cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
"Issued:%i\nSquashed:%i\n",
(*inst_list_it)->readPC(),
(*inst_list_it)->seqNum,
(*inst_list_it)->threadNumber,
(*inst_list_it)->isIssued(),
(*inst_list_it)->isSquashed());
if ((*inst_list_it)->isMemRef()) {
cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone);
}
cprintf("\n");
inst_list_it--;
++num;
}
cprintf("Waiting list size: %i\n", waitingList.size());
inst_list_it = --(waitingList.end());
while (inst_list_it != waitingList.end())
{
cprintf("Instruction:%i\n",
num);
if (!(*inst_list_it)->isSquashed()) {
if (!(*inst_list_it)->isIssued()) {
++valid_num;
cprintf("Count:%i\n", valid_num);
} else if ((*inst_list_it)->isMemRef() &&
!(*inst_list_it)->memOpDone) {
// Loads that have not been marked as executed still count
// towards the total instructions.
++valid_num;
cprintf("Count:%i\n", valid_num);
}
}
cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
"Issued:%i\nSquashed:%i\n",
(*inst_list_it)->readPC(),
(*inst_list_it)->seqNum,
(*inst_list_it)->threadNumber,
(*inst_list_it)->isIssued(),
(*inst_list_it)->isSquashed());
if ((*inst_list_it)->isMemRef()) {
cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone);
}
cprintf("\n");
inst_list_it--;
++num;
}
cprintf("waitingMemOps list size: %i\n", waitingMemOps.size());
MemIt waiting_it = waitingMemOps.begin();
while (waiting_it != waitingMemOps.end())
{
cprintf("[sn:%lli] ", (*waiting_it));
waiting_it++;
++num;
}
cprintf("\n");
}