| /* |
| * 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/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 |
| |
| thread->setInst( |
| static_cast<TheISA::MachInst>(inst->staticInst->machInst)); |
| |
| 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; |
| } |
| |
| if (inst->isCopy()) |
| panic("Should not commit any copy instructions!"); |
| |
| 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"); |
| } |