gem5/src/cpu/ozone/lw_back_end_impl.hh

1709 lines
48 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 "config/use_checker.hh"
#include "cpu/ozone/lw_back_end.hh"
#include "cpu/op_class.hh"
#if USE_CHECKER
#include "cpu/checker/cpu.hh"
#endif
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);
}
#if FULL_SYSTEM
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->inSyscall);
thread->inSyscall = 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->inSyscall = false;
// Generate trap squash event.
generateTrapEvent();
DPRINTF(BE, "Interrupt detected.\n");
} else {
DPRINTF(BE, "Interrupt must wait for ROB to drain.\n");
}
}
}
#endif
template <class Impl>
void
LWBackEnd<Impl>::handleFault(Fault &fault, Tick latency)
{
DPRINTF(BE, "Handling fault!\n");
assert(!thread->inSyscall);
thread->inSyscall = 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->inSyscall = 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;
#if FULL_SYSTEM
checkInterrupts();
#endif
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 !FULL_SYSTEM
// Hack to make sure syscalls aren't executed until all stores
// write back their data. This direct communication shouldn't
// be used for anything other than this.
if (inst_num > 0 || LSQ.hasStoresToWB())
#else
if ((inst->isMemBarrier() || inst->isWriteBarrier() ||
inst->isQuiesce()) &&
LSQ.hasStoresToWB())
#endif
{
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 USE_CHECKER
if (checker) {
checker->verify(inst);
}
#endif
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;
}
#if USE_CHECKER
else if (checker && inst->isStore()) {
checker->verify(inst);
}
#endif
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 FULL_SYSTEM
if (thread->profile) {
// bool usermode =
// (xc->readMiscRegNoEffect(AlphaISA::IPR_DTB_CM) & 0x18) != 0;
// thread->profilePC = usermode ? 1 : inst->readPC();
thread->profilePC = inst->readPC();
ProfileNode *node = thread->profile->consume(thread->getTC(),
inst->staticInst);
if (node)
thread->profileNode = node;
}
#endif
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 FULL_SYSTEM
int count = 0;
Addr oldpc;
do {
if (count == 0)
assert(!thread->inSyscall && !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;
}
#endif
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->inSyscall = 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->inSyscall = 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;
//
// Pick off the software prefetches
//
#ifdef TARGET_ALPHA
if (inst->isDataPrefetch())
exeSwp[tid]++;
else
exeInst[tid]++;
#else
exeInst[tid]++;
#endif
//
// 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");
}