| /* |
| * 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 "cpu/ozone/back_end.hh" |
| #include "encumbered/cpu/full/op_class.hh" |
| |
| template <class Impl> |
| BackEnd<Impl>::InstQueue::InstQueue(Params *params) |
| : size(params->numIQEntries), numInsts(0), width(params->issueWidth) |
| { |
| } |
| |
| template <class Impl> |
| std::string |
| BackEnd<Impl>::InstQueue::name() const |
| { |
| return be->name() + ".iq"; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::regStats() |
| { |
| using namespace Stats; |
| |
| occ_dist |
| .init(1, 0, size, 2) |
| .name(name() + "occ_dist") |
| .desc("IQ Occupancy per cycle") |
| .flags(total | cdf) |
| ; |
| |
| inst_count |
| .init(1) |
| .name(name() + "cum_num_insts") |
| .desc("Total occupancy") |
| .flags(total) |
| ; |
| |
| peak_inst_count |
| .init(1) |
| .name(name() + "peak_occupancy") |
| .desc("Peak IQ occupancy") |
| .flags(total) |
| ; |
| |
| current_count |
| .name(name() + "current_count") |
| .desc("Occupancy this cycle") |
| ; |
| |
| empty_count |
| .name(name() + "empty_count") |
| .desc("Number of empty cycles") |
| ; |
| |
| fullCount |
| .name(name() + "full_count") |
| .desc("Number of full cycles") |
| ; |
| |
| |
| occ_rate |
| .name(name() + "occ_rate") |
| .desc("Average occupancy") |
| .flags(total) |
| ; |
| occ_rate = inst_count / be->cpu->numCycles; |
| |
| avg_residency |
| .name(name() + "avg_residency") |
| .desc("Average IQ residency") |
| .flags(total) |
| ; |
| avg_residency = occ_rate / be->cpu->numCycles; |
| |
| empty_rate |
| .name(name() + "empty_rate") |
| .desc("Fraction of cycles empty") |
| ; |
| empty_rate = 100 * empty_count / be->cpu->numCycles; |
| |
| full_rate |
| .name(name() + "full_rate") |
| .desc("Fraction of cycles full") |
| ; |
| full_rate = 100 * fullCount / be->cpu->numCycles; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue) |
| { |
| i2e = i2e_queue; |
| numIssued = i2e->getWire(0); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::insert(DynInstPtr &inst) |
| { |
| numInsts++; |
| inst_count[0]++; |
| if (!inst->isNonSpeculative()) { |
| DPRINTF(BE, "Instruction [sn:%lli] added to IQ\n", inst->seqNum); |
| if (inst->readyToIssue()) { |
| toBeScheduled.push_front(inst); |
| inst->iqIt = toBeScheduled.begin(); |
| inst->iqItValid = true; |
| } else { |
| iq.push_front(inst); |
| inst->iqIt = iq.begin(); |
| inst->iqItValid = true; |
| } |
| } else { |
| DPRINTF(BE, "Nonspeculative instruction [sn:%lli] added to IQ\n", inst->seqNum); |
| nonSpec.push_front(inst); |
| inst->iqIt = nonSpec.begin(); |
| inst->iqItValid = true; |
| } |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::scheduleReadyInsts() |
| { |
| int scheduled = numIssued->size; |
| InstListIt iq_it = --toBeScheduled.end(); |
| InstListIt iq_end_it = toBeScheduled.end(); |
| |
| while (iq_it != iq_end_it && scheduled < width) { |
| // if ((*iq_it)->readyToIssue()) { |
| DPRINTF(BE, "Instruction [sn:%lli] PC:%#x is ready\n", |
| (*iq_it)->seqNum, (*iq_it)->readPC()); |
| readyQueue.push(*iq_it); |
| readyList.push_front(*iq_it); |
| |
| (*iq_it)->iqIt = readyList.begin(); |
| |
| toBeScheduled.erase(iq_it--); |
| |
| ++scheduled; |
| // } else { |
| // iq_it++; |
| // } |
| } |
| |
| numIssued->size+= scheduled; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::scheduleNonSpec(const InstSeqNum &sn) |
| { |
| /* |
| InstListIt non_spec_it = nonSpec.begin(); |
| InstListIt non_spec_end_it = nonSpec.end(); |
| |
| while ((*non_spec_it)->seqNum != sn) { |
| non_spec_it++; |
| assert(non_spec_it != non_spec_end_it); |
| } |
| */ |
| DynInstPtr inst = nonSpec.back(); |
| |
| DPRINTF(BE, "Nonspeculative instruction [sn:%lli] scheduled\n", inst->seqNum); |
| |
| assert(inst->seqNum == sn); |
| |
| assert(find(NonSpec, inst->iqIt)); |
| nonSpec.erase(inst->iqIt); |
| readyList.push_front(inst); |
| inst->iqIt = readyList.begin(); |
| readyQueue.push(inst); |
| numIssued->size++; |
| } |
| |
| template <class Impl> |
| typename Impl::DynInstPtr |
| BackEnd<Impl>::InstQueue::getReadyInst() |
| { |
| assert(!readyList.empty()); |
| |
| DynInstPtr inst = readyQueue.top(); |
| readyQueue.pop(); |
| assert(find(ReadyList, inst->iqIt)); |
| readyList.erase(inst->iqIt); |
| inst->iqItValid = false; |
| // if (!inst->isMemRef()) |
| --numInsts; |
| return inst; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::squash(const InstSeqNum &sn) |
| { |
| InstListIt iq_it = iq.begin(); |
| InstListIt iq_end_it = iq.end(); |
| |
| while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { |
| DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); |
| (*iq_it)->iqItValid = false; |
| iq.erase(iq_it++); |
| --numInsts; |
| } |
| |
| iq_it = nonSpec.begin(); |
| iq_end_it = nonSpec.end(); |
| |
| while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { |
| DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); |
| (*iq_it)->iqItValid = false; |
| nonSpec.erase(iq_it++); |
| --numInsts; |
| } |
| |
| iq_it = replayList.begin(); |
| iq_end_it = replayList.end(); |
| |
| while (iq_it != iq_end_it) { |
| if ((*iq_it)->seqNum > sn) { |
| DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); |
| (*iq_it)->iqItValid = false; |
| replayList.erase(iq_it++); |
| --numInsts; |
| } else { |
| iq_it++; |
| } |
| } |
| |
| assert(numInsts >= 0); |
| /* |
| InstListIt ready_it = readyList.begin(); |
| InstListIt ready_end_it = readyList.end(); |
| |
| while (ready_it != ready_end_it) { |
| if ((*ready_it)->seqNum > sn) { |
| readyList.erase(ready_it++); |
| } else { |
| ready_it++; |
| } |
| } |
| */ |
| } |
| |
| template <class Impl> |
| int |
| BackEnd<Impl>::InstQueue::wakeDependents(DynInstPtr &inst) |
| { |
| assert(!inst->isSquashed()); |
| std::vector<DynInstPtr> &dependents = 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]; |
| dep_inst->markSrcRegReady(); |
| DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum); |
| |
| if (dep_inst->readyToIssue() && dep_inst->iqItValid) { |
| if (dep_inst->isNonSpeculative()) { |
| assert(find(NonSpec, dep_inst->iqIt)); |
| nonSpec.erase(dep_inst->iqIt); |
| } else { |
| assert(find(IQ, dep_inst->iqIt)); |
| iq.erase(dep_inst->iqIt); |
| } |
| |
| toBeScheduled.push_front(dep_inst); |
| dep_inst->iqIt = toBeScheduled.begin(); |
| } |
| } |
| return num_outputs; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::rescheduleMemInst(DynInstPtr &inst) |
| { |
| DPRINTF(BE, "Rescheduling memory instruction [sn:%lli]\n", inst->seqNum); |
| assert(!inst->iqItValid); |
| replayList.push_front(inst); |
| inst->iqIt = replayList.begin(); |
| inst->iqItValid = true; |
| ++numInsts; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::replayMemInst(DynInstPtr &inst) |
| { |
| DPRINTF(BE, "Replaying memory instruction [sn:%lli]\n", inst->seqNum); |
| assert(find(ReplayList, inst->iqIt)); |
| InstListIt iq_it = --replayList.end(); |
| InstListIt iq_end_it = replayList.end(); |
| while (iq_it != iq_end_it) { |
| DynInstPtr rescheduled_inst = (*iq_it); |
| |
| DPRINTF(BE, "Memory instruction [sn:%lli] also replayed\n", inst->seqNum); |
| replayList.erase(iq_it--); |
| toBeScheduled.push_front(rescheduled_inst); |
| rescheduled_inst->iqIt = toBeScheduled.begin(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::completeMemInst(DynInstPtr &inst) |
| { |
| panic("Not implemented."); |
| } |
| |
| template <class Impl> |
| bool |
| BackEnd<Impl>::InstQueue::find(queue q, InstListIt it) |
| { |
| InstListIt iq_it, iq_end_it; |
| switch(q) { |
| case NonSpec: |
| iq_it = nonSpec.begin(); |
| iq_end_it = nonSpec.end(); |
| break; |
| case IQ: |
| iq_it = iq.begin(); |
| iq_end_it = iq.end(); |
| break; |
| case ToBeScheduled: |
| iq_it = toBeScheduled.begin(); |
| iq_end_it = toBeScheduled.end(); |
| break; |
| case ReadyList: |
| iq_it = readyList.begin(); |
| iq_end_it = readyList.end(); |
| break; |
| case ReplayList: |
| iq_it = replayList.begin(); |
| iq_end_it = replayList.end(); |
| } |
| |
| while (iq_it != it && iq_it != iq_end_it) { |
| iq_it++; |
| } |
| if (iq_it == it) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::InstQueue::dumpInsts() |
| { |
| cprintf("IQ size: %i\n", iq.size()); |
| |
| InstListIt inst_list_it = --iq.end(); |
| |
| int num = 0; |
| int valid_num = 0; |
| while (inst_list_it != iq.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("nonSpec size: %i\n", nonSpec.size()); |
| |
| inst_list_it = --nonSpec.end(); |
| |
| while (inst_list_it != nonSpec.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("toBeScheduled size: %i\n", toBeScheduled.size()); |
| |
| inst_list_it = --toBeScheduled.end(); |
| |
| while (inst_list_it != toBeScheduled.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("readyList size: %i\n", readyList.size()); |
| |
| inst_list_it = --readyList.end(); |
| |
| while (inst_list_it != readyList.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; |
| } |
| } |
| |
| template<class Impl> |
| BackEnd<Impl>::LdWritebackEvent::LdWritebackEvent(DynInstPtr &_inst, |
| BackEnd<Impl> *_be) |
| : Event(&mainEventQueue), inst(_inst), be(_be) |
| { |
| this->setFlags(Event::AutoDelete); |
| } |
| |
| template<class Impl> |
| void |
| BackEnd<Impl>::LdWritebackEvent::process() |
| { |
| DPRINTF(BE, "Load writeback event [sn:%lli]\n", inst->seqNum); |
| // DPRINTF(Activity, "Activity: Ld Writeback event [sn:%lli]\n", inst->seqNum); |
| |
| //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); |
| |
| // iewStage->wakeCPU(); |
| |
| if (inst->isSquashed()) { |
| inst = NULL; |
| return; |
| } |
| |
| if (!inst->isExecuted()) { |
| inst->setExecuted(); |
| |
| // Execute again to copy data to proper place. |
| inst->completeAcc(); |
| } |
| |
| // Need to insert instruction into queue to commit |
| be->instToCommit(inst); |
| |
| //wroteToTimeBuffer = true; |
| // iewStage->activityThisCycle(); |
| |
| inst = NULL; |
| } |
| |
| template<class Impl> |
| const char * |
| BackEnd<Impl>::LdWritebackEvent::description() const |
| { |
| return "Load writeback"; |
| } |
| |
| |
| template <class Impl> |
| BackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent(BackEnd *_be) |
| : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) |
| { |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::DCacheCompletionEvent::process() |
| { |
| } |
| |
| template <class Impl> |
| const char * |
| BackEnd<Impl>::DCacheCompletionEvent::description() const |
| { |
| return "Cache completion"; |
| } |
| |
| template <class Impl> |
| BackEnd<Impl>::BackEnd(Params *params) |
| : d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(5, 5), |
| xcSquash(false), IQ(params), |
| cacheCompletionEvent(this), width(params->backEndWidth), |
| exactFullStall(true) |
| { |
| numROBEntries = params->numROBEntries; |
| numInsts = 0; |
| numDispatchEntries = 32; |
| IQ.setBE(this); |
| LSQ.setBE(this); |
| |
| // Setup IQ and LSQ with their parameters here. |
| instsToDispatch = d2i.getWire(-1); |
| |
| instsToExecute = i2e.getWire(-1); |
| |
| IQ.setIssueExecQueue(&i2e); |
| |
| 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; |
| } |
| |
| template <class Impl> |
| std::string |
| BackEnd<Impl>::name() const |
| { |
| return cpu->name() + ".backend"; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::regStats() |
| { |
| using namespace Stats; |
| rob_cap_events |
| .init(cpu->numThreads) |
| .name(name() + ".ROB:cap_events") |
| .desc("number of cycles where ROB cap was active") |
| .flags(total) |
| ; |
| |
| rob_cap_inst_count |
| .init(cpu->numThreads) |
| .name(name() + ".ROB:cap_inst") |
| .desc("number of instructions held up by ROB cap") |
| .flags(total) |
| ; |
| |
| iq_cap_events |
| .init(cpu->numThreads) |
| .name(name() +".IQ:cap_events" ) |
| .desc("number of cycles where IQ cap was active") |
| .flags(total) |
| ; |
| |
| iq_cap_inst_count |
| .init(cpu->numThreads) |
| .name(name() + ".IQ:cap_inst") |
| .desc("number of instructions held up by IQ cap") |
| .flags(total) |
| ; |
| |
| |
| exe_inst |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:count") |
| .desc("number of insts issued") |
| .flags(total) |
| ; |
| |
| exe_swp |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:swp") |
| .desc("number of swp insts issued") |
| .flags(total) |
| ; |
| |
| exe_nop |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:nop") |
| .desc("number of nop insts issued") |
| .flags(total) |
| ; |
| |
| exe_refs |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:refs") |
| .desc("number of memory reference insts issued") |
| .flags(total) |
| ; |
| |
| exe_loads |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:loads") |
| .desc("number of load insts issued") |
| .flags(total) |
| ; |
| |
| exe_branches |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:branches") |
| .desc("Number of branches issued") |
| .flags(total) |
| ; |
| |
| issued_ops |
| .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 |
| // |
| lsq_forw_loads |
| .init(cpu->numThreads) |
| .name(name() + ".LSQ:forw_loads") |
| .desc("number of loads forwarded via LSQ") |
| .flags(total) |
| ; |
| |
| inv_addr_loads |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:addr_loads") |
| .desc("number of invalid-address loads") |
| .flags(total) |
| ; |
| |
| inv_addr_swpfs |
| .init(cpu->numThreads) |
| .name(name() + ".ISSUE:addr_swpfs") |
| .desc("number of invalid-address SW prefetches") |
| .flags(total) |
| ; |
| |
| lsq_blocked_loads |
| .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") |
| ; |
| |
| n_issued_dist |
| .init(issueWidth + 1) |
| .name(name() + ".ISSUE:issued_per_cycle") |
| .desc("Number of insts issued each cycle") |
| .flags(total | pdf | dist) |
| ; |
| issue_delay_dist |
| .init(Num_OpClasses,0,99,2) |
| .name(name() + ".ISSUE:") |
| .desc("cycles from operands ready to issue") |
| .flags(pdf | cdf) |
| ; |
| |
| queue_res_dist |
| .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) { |
| queue_res_dist.subname(i, opClassStrings[i]); |
| } |
| |
| writeback_count |
| .init(cpu->numThreads) |
| .name(name() + ".WB:count") |
| .desc("cumulative count of insts written-back") |
| .flags(total) |
| ; |
| |
| producer_inst |
| .init(cpu->numThreads) |
| .name(name() + ".WB:producers") |
| .desc("num instructions producing a value") |
| .flags(total) |
| ; |
| |
| consumer_inst |
| .init(cpu->numThreads) |
| .name(name() + ".WB:consumers") |
| .desc("num instructions consuming a value") |
| .flags(total) |
| ; |
| |
| wb_penalized |
| .init(cpu->numThreads) |
| .name(name() + ".WB:penalized") |
| .desc("number of instrctions required to write to 'other' IQ") |
| .flags(total) |
| ; |
| |
| |
| wb_penalized_rate |
| .name(name() + ".WB:penalized_rate") |
| .desc ("fraction of instructions written-back that wrote to 'other' IQ") |
| .flags(total) |
| ; |
| |
| wb_penalized_rate = wb_penalized / writeback_count; |
| |
| wb_fanout |
| .name(name() + ".WB:fanout") |
| .desc("average fanout of values written-back") |
| .flags(total) |
| ; |
| |
| wb_fanout = producer_inst / consumer_inst; |
| |
| wb_rate |
| .name(name() + ".WB:rate") |
| .desc("insts written-back per cycle") |
| .flags(total) |
| ; |
| wb_rate = writeback_count / cpu->numCycles; |
| |
| stat_com_inst |
| .init(cpu->numThreads) |
| .name(name() + ".COM:count") |
| .desc("Number of instructions committed") |
| .flags(total) |
| ; |
| |
| stat_com_swp |
| .init(cpu->numThreads) |
| .name(name() + ".COM:swp_count") |
| .desc("Number of s/w prefetches committed") |
| .flags(total) |
| ; |
| |
| stat_com_refs |
| .init(cpu->numThreads) |
| .name(name() + ".COM:refs") |
| .desc("Number of memory references committed") |
| .flags(total) |
| ; |
| |
| stat_com_loads |
| .init(cpu->numThreads) |
| .name(name() + ".COM:loads") |
| .desc("Number of loads committed") |
| .flags(total) |
| ; |
| |
| stat_com_membars |
| .init(cpu->numThreads) |
| .name(name() + ".COM:membars") |
| .desc("Number of memory barriers committed") |
| .flags(total) |
| ; |
| |
| stat_com_branches |
| .init(cpu->numThreads) |
| .name(name() + ".COM:branches") |
| .desc("Number of branches committed") |
| .flags(total) |
| ; |
| n_committed_dist |
| .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 |
| // |
| commit_eligible |
| .init(cpu->numThreads) |
| .name(name() + ".COM:bw_limited") |
| .desc("number of insts not committed due to BW limits") |
| .flags(total) |
| ; |
| |
| commit_eligible_samples |
| .name(name() + ".COM:bw_lim_events") |
| .desc("number cycles where commit BW limit reached") |
| ; |
| |
| ROB_fcount |
| .name(name() + ".ROB:full_count") |
| .desc("number of cycles where ROB was full") |
| ; |
| |
| ROB_count |
| .init(cpu->numThreads) |
| .name(name() + ".ROB:occupancy") |
| .desc(name() + ".ROB occupancy (cumulative)") |
| .flags(total) |
| ; |
| |
| ROB_full_rate |
| .name(name() + ".ROB:full_rate") |
| .desc("ROB full per cycle") |
| ; |
| ROB_full_rate = ROB_fcount / cpu->numCycles; |
| |
| ROB_occ_rate |
| .name(name() + ".ROB:occ_rate") |
| .desc("ROB occupancy rate") |
| .flags(total) |
| ; |
| ROB_occ_rate = ROB_count / cpu->numCycles; |
| |
| ROB_occ_dist |
| .init(cpu->numThreads, 0, numROBEntries, 2) |
| .name(name() + ".ROB:occ_dist") |
| .desc("ROB Occupancy per cycle") |
| .flags(total | cdf) |
| ; |
| |
| IQ.regStats(); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) |
| { |
| comm = _comm; |
| toIEW = comm->getWire(0); |
| fromCommit = comm->getWire(-1); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::tick() |
| { |
| DPRINTF(BE, "Ticking back end\n"); |
| |
| ROB_count[0]+= numInsts; |
| |
| wbCycle = 0; |
| |
| if (xcSquash) { |
| squashFromXC(); |
| } |
| |
| // Read in any done instruction information and update the IQ or LSQ. |
| updateStructures(); |
| |
| if (dispatchStatus != Blocked) { |
| d2i.advance(); |
| dispatchInsts(); |
| } else { |
| checkDispatchStatus(); |
| } |
| |
| i2e.advance(); |
| scheduleReadyInsts(); |
| |
| e2c.advance(); |
| executeInsts(); |
| |
| numInstsToWB.advance(); |
| writebackInsts(); |
| |
| commitInsts(); |
| |
| DPRINTF(BE, "IQ entries in use: %i, ROB entries in use: %i, LSQ loads: %i, LSQ stores: %i\n", |
| IQ.numInsts, numInsts, LSQ.numLoads(), LSQ.numStores()); |
| |
| assert(numInsts == instList.size()); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::updateStructures() |
| { |
| if (fromCommit->doneSeqNum) { |
| IQ.commit(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 |
| BackEnd<Impl>::addToIQ(DynInstPtr &inst) |
| { |
| // Do anything IQ specific here? |
| IQ.insert(inst); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::addToLSQ(DynInstPtr &inst) |
| { |
| // Do anything LSQ specific here? |
| LSQ.insert(inst); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::dispatchInsts() |
| { |
| DPRINTF(BE, "Trying to dispatch instructions.\n"); |
| |
| // Pull instructions out of the front end. |
| int disp_width = dispatchWidth ? dispatchWidth : width; |
| |
| // Could model dispatching time, but in general 1 cycle is probably |
| // good enough. |
| |
| if (dispatchSize < numDispatchEntries) { |
| for (int i = 0; i < disp_width; i++) { |
| // Get instructions |
| DynInstPtr inst = frontEnd->getInst(); |
| |
| if (!inst) { |
| // No more instructions to get |
| break; |
| } |
| |
| DPRINTF(BE, "Processing instruction [sn:%lli] PC:%#x\n", |
| inst->seqNum, inst->readPC()); |
| |
| for (int i = 0; i < inst->numDestRegs(); ++i) |
| renameTable[inst->destRegIdx(i)] = inst; |
| |
| // Add to queue to be dispatched. |
| dispatch.push_back(inst); |
| |
| d2i[0].size++; |
| ++dispatchSize; |
| } |
| } |
| |
| assert(dispatch.size() < 64); |
| |
| for (int i = 0; i < instsToDispatch->size; ++i) { |
| assert(!dispatch.empty()); |
| // Get instruction from front of time buffer |
| DynInstPtr inst = dispatch.front(); |
| dispatch.pop_front(); |
| --dispatchSize; |
| |
| if (inst->isSquashed()) |
| continue; |
| |
| ++numInsts; |
| instList.push_back(inst); |
| |
| DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n", |
| inst->seqNum, inst->readPC()); |
| |
| addToIQ(inst); |
| |
| if (inst->isMemRef()) { |
| addToLSQ(inst); |
| } |
| |
| if (inst->isNonSpeculative()) { |
| inst->setCanCommit(); |
| } |
| |
| // 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. |
| if (exactFullStall) { |
| 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) { |
| instsToDispatch->size-= i+1; |
| dispatchStall(); |
| return; |
| } |
| } |
| } |
| |
| // 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 |
| BackEnd<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 |
| BackEnd<Impl>::checkDispatchStatus() |
| { |
| DPRINTF(BE, "Checking dispatch status\n"); |
| assert(dispatchStatus == Blocked); |
| if (!IQ.isFull() && !LSQ.isFull() && !isFull()) { |
| DPRINTF(BE, "Dispatch no longer blocked\n"); |
| dispatchStatus = Running; |
| dispatchInsts(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::scheduleReadyInsts() |
| { |
| // Tell IQ to put any ready instructions into the instruction list. |
| // Probably want to have a list of DynInstPtrs returned here. Then I |
| // can choose to either put them into a time buffer to simulate |
| // IQ scheduling time, or hand them directly off to the next stage. |
| // Do you ever want to directly hand it off to the next stage? |
| DPRINTF(BE, "Trying to schedule ready instructions\n"); |
| IQ.scheduleReadyInsts(); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::executeInsts() |
| { |
| int insts_to_execute = instsToExecute->size; |
| |
| issued_ops[0]+= insts_to_execute; |
| n_issued_dist[insts_to_execute]++; |
| |
| DPRINTF(BE, "Trying to execute %i instructions\n", insts_to_execute); |
| |
| fetchRedirect[0] = false; |
| |
| while (insts_to_execute > 0) { |
| // Get ready instruction from the IQ (or queue coming out of IQ) |
| // Execute the ready instruction. |
| // Wakeup any dependents if it's done. |
| DynInstPtr inst = IQ.getReadyInst(); |
| |
| DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n", |
| inst->seqNum, inst->readPC()); |
| |
| ++funcExeInst; |
| |
| // 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. |
| --insts_to_execute; |
| |
| // 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; |
| |
| 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"); |
| |
| // Tell the LDSTQ to execute this instruction (if it is a load). |
| if (inst->isLoad()) { |
| // Loads will mark themselves as executed, and their writeback |
| // event adds the instruction to the queue to commit |
| fault = LSQ.executeLoad(inst); |
| |
| // ++iewExecLoadInsts; |
| } else if (inst->isStore()) { |
| LSQ.executeStore(inst); |
| |
| // ++iewExecStoreInsts; |
| |
| if (!(inst->req->isLLSC())) { |
| inst->setExecuted(); |
| |
| instToCommit(inst); |
| } |
| // Store conditionals will mark themselves as executed, and |
| // their writeback event will add the instruction to the queue |
| // to commit. |
| } else { |
| panic("Unexpected memory type!\n"); |
| } |
| |
| } else { |
| inst->execute(); |
| |
| // ++iewExecutedInsts; |
| |
| inst->setExecuted(); |
| |
| instToCommit(inst); |
| } |
| |
| updateExeInstStats(inst); |
| |
| // Probably should have some sort of function for this. |
| // More general question of how to handle squashes? Have some sort of |
| // squash unit that controls it? Probably... |
| // Check if branch was correct. This check happens after the |
| // instruction is added to the queue because even if the branch |
| // is mispredicted, the branch instruction itself is still valid. |
| // Only handle this if there hasn't already been something that |
| // redirects fetch in this group of instructions. |
| |
| // This probably needs to prioritize the redirects if a different |
| // scheduler is used. Currently the scheduler schedules the oldest |
| // instruction first, so the branch resolution order will be correct. |
| ThreadID tid = inst->threadNumber; |
| |
| if (!fetchRedirect[tid]) { |
| |
| if (inst->mispredicted()) { |
| fetchRedirect[tid] = true; |
| |
| DPRINTF(BE, "Execute: Branch mispredict detected.\n"); |
| DPRINTF(BE, "Execute: Redirecting fetch to PC: %#x.\n", |
| inst->nextPC); |
| |
| // If incorrect, then signal the ROB that it must be squashed. |
| squashDueToBranch(inst); |
| |
| if (inst->predTaken()) { |
| // predictedTakenIncorrect++; |
| } else { |
| // predictedNotTakenIncorrect++; |
| } |
| } else if (LSQ.violation()) { |
| fetchRedirect[tid] = true; |
| |
| // 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); |
| |
| // Tell the instruction queue that a violation has occured. |
| // IQ.violation(inst, violator); |
| |
| // Squash. |
| // squashDueToMemOrder(inst,tid); |
| squashDueToBranch(inst); |
| |
| // ++memOrderViolationEvents; |
| } else if (LSQ.loadBlocked()) { |
| fetchRedirect[tid] = true; |
| |
| DPRINTF(BE, "Load operation couldn't execute because the " |
| "memory system is blocked. PC: %#x [sn:%lli]\n", |
| inst->readPC(), inst->seqNum); |
| |
| squashDueToMemBlocked(inst); |
| } |
| } |
| |
| // instList.pop_front(); |
| |
| --insts_to_execute; |
| |
| // keep an instruction count |
| thread->numInst++; |
| thread->numInsts++; |
| } |
| |
| assert(insts_to_execute >= 0); |
| } |
| |
| template<class Impl> |
| void |
| BackEnd<Impl>::instToCommit(DynInstPtr &inst) |
| { |
| int wb_width = wbWidth; |
| // First check the time slot that this instruction will write |
| // to. If there are free write ports at the time, then go ahead |
| // and write the instruction to that time. If there are not, |
| // keep looking back to see where's the first time there's a |
| // free slot. What happens if you run out of free spaces? |
| // For now naively assume that all instructions take one cycle. |
| // Otherwise would have to look into the time buffer based on the |
| // latency of the instruction. |
| |
| DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n", |
| inst->seqNum, inst->readPC()); |
| |
| while (numInstsToWB[wbCycle].size >= wb_width) { |
| ++wbCycle; |
| |
| assert(wbCycle < 5); |
| } |
| |
| // Add finished instruction to queue to commit. |
| writeback.push_back(inst); |
| numInstsToWB[wbCycle].size++; |
| |
| if (wbCycle) |
| wb_penalized[0]++; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<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 = IQ.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; |
| } |
| |
| template <class Impl> |
| bool |
| BackEnd<Impl>::commitInst(int inst_num) |
| { |
| // Read instruction from the head of the ROB |
| DynInstPtr inst = instList.front(); |
| |
| // 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()); |
| |
| // 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()) { |
| // Keep this number correct. We have not yet actually executed |
| // and committed this instruction. |
| // thread->funcExeInst--; |
| |
| if (inst->isNonSpeculative()) { |
| #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()) { |
| DPRINTF(BE, "Waiting for all stores to writeback.\n"); |
| return false; |
| } |
| #endif |
| |
| DPRINTF(BE, "Encountered a store or non-speculative " |
| "instruction at the head of the ROB, PC %#x.\n", |
| inst->readPC()); |
| |
| // Send back the non-speculative instruction's sequence number. |
| toIEW->nonSpecSeqNum = inst->seqNum; |
| |
| // 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. |
| toIEW->nonSpecSeqNum = inst->seqNum; |
| toIEW->uncached = true; |
| toIEW->lqIdx = inst->lqIdx; |
| |
| inst->clearCanCommit(); |
| |
| return false; |
| } else { |
| panic("Trying to commit un-executed instruction " |
| "of unknown type!\n"); |
| } |
| } |
| |
| // Now check if it's one of the special trap or barrier or |
| // serializing instructions. |
| if (inst->isThreadSync()) |
| { |
| // Not handled for now. |
| panic("Barrier instructions are not handled yet.\n"); |
| } |
| |
| // Check if the instruction caused a fault. If so, trap. |
| Fault inst_fault = inst->getFault(); |
| |
| if (inst_fault != NoFault) { |
| if (!inst->isNop()) { |
| #if FULL_SYSTEM |
| DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n", |
| inst->seqNum, inst->readPC()); |
| |
| // assert(!thread->inSyscall); |
| |
| // thread->inSyscall = true; |
| |
| // Consider holding onto the trap and waiting until the trap event |
| // happens for this to be executed. |
| inst_fault->invoke(thread->getXCProxy()); |
| |
| // Exit state update mode to avoid accidental updating. |
| // thread->inSyscall = false; |
| |
| // commitStatus = TrapPending; |
| |
| // Generate trap squash event. |
| // generateTrapEvent(); |
| |
| return false; |
| #else // !FULL_SYSTEM |
| panic("fault (%d) detected @ PC %08p", inst_fault, |
| inst->PC); |
| #endif // FULL_SYSTEM |
| } |
| } |
| |
| if (inst->isControl()) { |
| // ++commitCommittedBranches; |
| } |
| |
| int freed_regs = 0; |
| |
| for (int i = 0; i < inst->numDestRegs(); ++i) { |
| DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", |
| (int)inst->destRegIdx(i), inst->seqNum); |
| thread->renameTable[inst->destRegIdx(i)] = inst; |
| ++freed_regs; |
| } |
| |
| if (inst->traceData) { |
| inst->traceData->finalize(); |
| inst->traceData = NULL; |
| } |
| |
| inst->clearDependents(); |
| |
| frontEnd->addFreeRegs(freed_regs); |
| |
| instList.pop_front(); |
| |
| --numInsts; |
| cpu->numInst++; |
| thread->numInsts++; |
| ++thread->funcExeInst; |
| thread->PC = inst->readNextPC(); |
| updateComInstStats(inst); |
| |
| // Write the done sequence number here. |
| toIEW->doneSeqNum = inst->seqNum; |
| |
| #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->getXCProxy()); |
| count++; |
| } while (oldpc != thread->readPC()); |
| if (count > 1) { |
| DPRINTF(BE, "PC skip function event, stopping commit\n"); |
| // completed_last_inst = false; |
| // squashPending = true; |
| return false; |
| } |
| #endif |
| return true; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::commitInsts() |
| { |
| int commit_width = commitWidth ? commitWidth : width; |
| |
| // Not sure this should be a loop or not. |
| int inst_num = 0; |
| while (!instList.empty() && inst_num < commit_width) { |
| if (instList.front()->isSquashed()) { |
| panic("No squashed insts should still be on the list!"); |
| instList.front()->clearDependents(); |
| instList.pop_front(); |
| continue; |
| } |
| |
| if (!commitInst(inst_num++)) { |
| break; |
| } |
| } |
| n_committed_dist.sample(inst_num); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::squash(const InstSeqNum &sn) |
| { |
| IQ.squash(sn); |
| LSQ.squash(sn); |
| |
| int freed_regs = 0; |
| InstListIt dispatch_end = dispatch.end(); |
| InstListIt insts_it = dispatch.end(); |
| insts_it--; |
| |
| while (insts_it != dispatch_end && (*insts_it)->seqNum > sn) |
| { |
| if ((*insts_it)->isSquashed()) { |
| --insts_it; |
| continue; |
| } |
| DPRINTF(BE, "Squashing instruction on dispatch 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(); |
| |
| // Be careful with IPRs and such here |
| for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { |
| DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); |
| DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", |
| (int)(*insts_it)->destRegIdx(i), prev_dest); |
| renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; |
| ++freed_regs; |
| } |
| |
| (*insts_it)->clearDependents(); |
| |
| --insts_it; |
| } |
| |
| insts_it = instList.end(); |
| insts_it--; |
| |
| while (!instList.empty() && (*insts_it)->seqNum > sn) |
| { |
| if ((*insts_it)->isSquashed()) { |
| --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(); |
| |
| for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { |
| DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); |
| DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", |
| (int)(*insts_it)->destRegIdx(i), prev_dest); |
| renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; |
| ++freed_regs; |
| } |
| |
| (*insts_it)->clearDependents(); |
| |
| instList.erase(insts_it--); |
| --numInsts; |
| } |
| |
| frontEnd->addFreeRegs(freed_regs); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::squashFromXC() |
| { |
| xcSquash = true; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::squashDueToBranch(DynInstPtr &inst) |
| { |
| // Update the branch predictor state I guess |
| squash(inst->seqNum); |
| frontEnd->squash(inst->seqNum, inst->readNextPC(), |
| true, inst->mispredicted()); |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<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 |
| BackEnd<Impl>::fetchFault(Fault &fault) |
| { |
| faultFromFetch = fault; |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::updateExeInstStats(DynInstPtr &inst) |
| { |
| ThreadID tid = inst->threadNumber; |
| |
| // |
| // Pick off the software prefetches |
| // |
| #ifdef TARGET_ALPHA |
| if (inst->isDataPrefetch()) |
| exe_swp[tid]++; |
| else |
| exe_inst[tid]++; |
| #else |
| exe_inst[tid]++; |
| #endif |
| |
| // |
| // Control operations |
| // |
| if (inst->isControl()) |
| exe_branches[tid]++; |
| |
| // |
| // Memory operations |
| // |
| if (inst->isMemRef()) { |
| exe_refs[tid]++; |
| |
| if (inst->isLoad()) |
| exe_loads[tid]++; |
| } |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::updateComInstStats(DynInstPtr &inst) |
| { |
| ThreadID tid = inst->threadNumber; |
| |
| // |
| // Pick off the software prefetches |
| // |
| #ifdef TARGET_ALPHA |
| if (inst->isDataPrefetch()) { |
| stat_com_swp[tid]++; |
| } else { |
| stat_com_inst[tid]++; |
| } |
| #else |
| stat_com_inst[tid]++; |
| #endif |
| |
| // |
| // Control Instructions |
| // |
| if (inst->isControl()) |
| stat_com_branches[tid]++; |
| |
| // |
| // Memory references |
| // |
| if (inst->isMemRef()) { |
| stat_com_refs[tid]++; |
| |
| if (inst->isLoad()) { |
| stat_com_loads[tid]++; |
| } |
| } |
| |
| if (inst->isMemBarrier()) { |
| stat_com_membars[tid]++; |
| } |
| } |
| |
| template <class Impl> |
| void |
| BackEnd<Impl>::dumpInsts() |
| { |
| int num = 0; |
| int valid_num = 0; |
| |
| InstListIt inst_list_it = instList.begin(); |
| |
| 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; |
| } |
| |
| cprintf("Dispatch list size: %i\n", dispatch.size()); |
| |
| inst_list_it = dispatch.begin(); |
| |
| while (inst_list_it != dispatch.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("Writeback list size: %i\n", writeback.size()); |
| |
| inst_list_it = writeback.begin(); |
| |
| 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; |
| } |
| } |