| /* |
| * Copyright (c) 2004-2005 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. |
| */ |
| |
| #include "base/timebuf.hh" |
| #include "cpu/o3/commit.hh" |
| #include "cpu/exetrace.hh" |
| |
| template <class Impl> |
| SimpleCommit<Impl>::SimpleCommit(Params ¶ms) |
| : dcacheInterface(params.dcacheInterface), |
| iewToCommitDelay(params.iewToCommitDelay), |
| renameToROBDelay(params.renameToROBDelay), |
| renameWidth(params.renameWidth), |
| iewWidth(params.executeWidth), |
| commitWidth(params.commitWidth) |
| { |
| _status = Idle; |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::regStats() |
| { |
| commitCommittedInsts |
| .name(name() + ".commitCommittedInsts") |
| .desc("The number of committed instructions") |
| .prereq(commitCommittedInsts); |
| commitSquashedInsts |
| .name(name() + ".commitSquashedInsts") |
| .desc("The number of squashed insts skipped by commit") |
| .prereq(commitSquashedInsts); |
| commitSquashEvents |
| .name(name() + ".commitSquashEvents") |
| .desc("The number of times commit is told to squash") |
| .prereq(commitSquashEvents); |
| commitNonSpecStalls |
| .name(name() + ".commitNonSpecStalls") |
| .desc("The number of times commit has been forced to stall to " |
| "communicate backwards") |
| .prereq(commitNonSpecStalls); |
| commitCommittedBranches |
| .name(name() + ".commitCommittedBranches") |
| .desc("The number of committed branches") |
| .prereq(commitCommittedBranches); |
| commitCommittedLoads |
| .name(name() + ".commitCommittedLoads") |
| .desc("The number of committed loads") |
| .prereq(commitCommittedLoads); |
| commitCommittedMemRefs |
| .name(name() + ".commitCommittedMemRefs") |
| .desc("The number of committed memory references") |
| .prereq(commitCommittedMemRefs); |
| branchMispredicts |
| .name(name() + ".branchMispredicts") |
| .desc("The number of times a branch was mispredicted") |
| .prereq(branchMispredicts); |
| n_committed_dist |
| .init(0,commitWidth,1) |
| .name(name() + ".COM:committed_per_cycle") |
| .desc("Number of insts commited each cycle") |
| .flags(Stats::pdf) |
| ; |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr) |
| { |
| DPRINTF(Commit, "Commit: Setting CPU pointer.\n"); |
| cpu = cpu_ptr; |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) |
| { |
| DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); |
| timeBuffer = tb_ptr; |
| |
| // Setup wire to send information back to IEW. |
| toIEW = timeBuffer->getWire(0); |
| |
| // Setup wire to read data from IEW (for the ROB). |
| robInfoFromIEW = timeBuffer->getWire(-iewToCommitDelay); |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) |
| { |
| DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); |
| renameQueue = rq_ptr; |
| |
| // Setup wire to get instructions from rename (for the ROB). |
| fromRename = renameQueue->getWire(-renameToROBDelay); |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) |
| { |
| DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); |
| iewQueue = iq_ptr; |
| |
| // Setup wire to get instructions from IEW. |
| fromIEW = iewQueue->getWire(-iewToCommitDelay); |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::setROB(ROB *rob_ptr) |
| { |
| DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); |
| rob = rob_ptr; |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::tick() |
| { |
| // If the ROB is currently in its squash sequence, then continue |
| // to squash. In this case, commit does not do anything. Otherwise |
| // run commit. |
| if (_status == ROBSquashing) { |
| if (rob->isDoneSquashing()) { |
| _status = Running; |
| } else { |
| rob->doSquash(); |
| |
| // Send back sequence number of tail of ROB, so other stages |
| // can squash younger instructions. Note that really the only |
| // stage that this is important for is the IEW stage; other |
| // stages can just clear all their state as long as selective |
| // replay isn't used. |
| toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum(); |
| toIEW->commitInfo.robSquashing = true; |
| } |
| } else { |
| commit(); |
| } |
| |
| markCompletedInsts(); |
| |
| // Writeback number of free ROB entries here. |
| DPRINTF(Commit, "Commit: ROB has %d free entries.\n", |
| rob->numFreeEntries()); |
| toIEW->commitInfo.freeROBEntries = rob->numFreeEntries(); |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::commit() |
| { |
| ////////////////////////////////////// |
| // Check for interrupts |
| ////////////////////////////////////// |
| |
| // Process interrupts if interrupts are enabled and not in PAL mode. |
| // Take the PC from commit and write it to the IPR, then squash. The |
| // interrupt completing will take care of restoring the PC from that value |
| // in the IPR. Look at IPR[EXC_ADDR]; |
| // hwrei() is what resets the PC to the place where instruction execution |
| // beings again. |
| #if FULL_SYSTEM |
| if (//checkInterrupts && |
| cpu->check_interrupts() && |
| !cpu->inPalMode(readCommitPC())) { |
| // 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. |
| |
| // CPU will handle implementation of the interrupt. |
| cpu->processInterrupts(); |
| } |
| #endif // FULL_SYSTEM |
| |
| //////////////////////////////////// |
| // Check for squash signal, handle that first |
| //////////////////////////////////// |
| |
| // Want to mainly check if the IEW stage is telling the ROB to squash. |
| // Should I also check if the commit stage is telling the ROB to squah? |
| // This might be necessary to keep the same timing between the IQ and |
| // the ROB... |
| if (fromIEW->squash) { |
| DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n"); |
| |
| _status = ROBSquashing; |
| |
| InstSeqNum squashed_inst = fromIEW->squashedSeqNum; |
| |
| rob->squash(squashed_inst); |
| |
| // Send back the sequence number of the squashed instruction. |
| toIEW->commitInfo.doneSeqNum = squashed_inst; |
| |
| // Send back the squash signal to tell stages that they should squash. |
| toIEW->commitInfo.squash = true; |
| |
| // Send back the rob squashing signal so other stages know that the |
| // ROB is in the process of squashing. |
| toIEW->commitInfo.robSquashing = true; |
| |
| toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict; |
| |
| toIEW->commitInfo.branchTaken = fromIEW->branchTaken; |
| |
| toIEW->commitInfo.nextPC = fromIEW->nextPC; |
| |
| toIEW->commitInfo.mispredPC = fromIEW->mispredPC; |
| |
| if (toIEW->commitInfo.branchMispredict) { |
| ++branchMispredicts; |
| } |
| } |
| |
| if (_status != ROBSquashing) { |
| // If we're not currently squashing, then get instructions. |
| getInsts(); |
| |
| // Try to commit any instructions. |
| commitInsts(); |
| } |
| |
| // If the ROB is empty, we can set this stage to idle. Use this |
| // in the future when the Idle status will actually be utilized. |
| #if 0 |
| if (rob->isEmpty()) { |
| DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n"); |
| _status = Idle; |
| // Schedule an event so that commit will actually wake up |
| // once something gets put in the ROB. |
| } |
| #endif |
| } |
| |
| // Loop that goes through as many instructions in the ROB as possible and |
| // tries to commit them. The actual work for committing is done by the |
| // commitHead() function. |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::commitInsts() |
| { |
| //////////////////////////////////// |
| // Handle commit |
| // Note that commit will be handled prior to the ROB so that the ROB |
| // only tries to commit instructions it has in this current cycle, and |
| // not instructions it is writing in during this cycle. |
| // Can't commit and squash things at the same time... |
| //////////////////////////////////// |
| |
| if (rob->isEmpty()) |
| return; |
| |
| DynInstPtr head_inst = rob->readHeadInst(); |
| |
| unsigned num_committed = 0; |
| |
| // Commit as many instructions as possible until the commit bandwidth |
| // limit is reached, or it becomes impossible to commit any more. |
| while (!rob->isEmpty() && |
| head_inst->readyToCommit() && |
| num_committed < commitWidth) |
| { |
| DPRINTF(Commit, "Commit: Trying to commit head instruction.\n"); |
| |
| // If the head instruction is squashed, it is ready to retire at any |
| // time. However, we need to avoid updating any other state |
| // incorrectly if it's already been squashed. |
| if (head_inst->isSquashed()) { |
| |
| DPRINTF(Commit, "Commit: Retiring squashed instruction from " |
| "ROB.\n"); |
| |
| // Tell ROB to retire head instruction. This retires the head |
| // inst in the ROB without affecting any other stages. |
| rob->retireHead(); |
| |
| ++commitSquashedInsts; |
| |
| } else { |
| // Increment the total number of non-speculative instructions |
| // executed. |
| // Hack for now: it really shouldn't happen until after the |
| // commit is deemed to be successful, but this count is needed |
| // for syscalls. |
| cpu->funcExeInst++; |
| |
| // Try to commit the head instruction. |
| bool commit_success = commitHead(head_inst, num_committed); |
| |
| // Update what instruction we are looking at if the commit worked. |
| if (commit_success) { |
| ++num_committed; |
| |
| // Send back which instruction has been committed. |
| // @todo: Update this later when a wider pipeline is used. |
| // Hmm, can't really give a pointer here...perhaps the |
| // sequence number instead (copy). |
| toIEW->commitInfo.doneSeqNum = head_inst->seqNum; |
| |
| ++commitCommittedInsts; |
| |
| if (!head_inst->isNop()) { |
| cpu->instDone(); |
| } |
| } else { |
| break; |
| } |
| } |
| |
| // Update the pointer to read the next instruction in the ROB. |
| head_inst = rob->readHeadInst(); |
| } |
| |
| DPRINTF(CommitRate, "%i\n", num_committed); |
| n_committed_dist.sample(num_committed); |
| } |
| |
| template <class Impl> |
| bool |
| SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) |
| { |
| // Make sure instruction is valid |
| assert(head_inst); |
| |
| // If the instruction is not executed yet, then it is a non-speculative |
| // or store inst. Signal backwards that it should be executed. |
| if (!head_inst->isExecuted()) { |
| // Keep this number correct. We have not yet actually executed |
| // and committed this instruction. |
| cpu->funcExeInst--; |
| |
| if (head_inst->isNonSpeculative()) { |
| DPRINTF(Commit, "Commit: Encountered a store or non-speculative " |
| "instruction at the head of the ROB, PC %#x.\n", |
| head_inst->readPC()); |
| |
| toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum; |
| |
| // Change the instruction so it won't try to commit again until |
| // it is executed. |
| head_inst->clearCanCommit(); |
| |
| ++commitNonSpecStalls; |
| |
| return false; |
| } else { |
| panic("Commit: 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 (head_inst->isThreadSync() || |
| head_inst->isSerializing() || |
| head_inst->isMemBarrier() || |
| head_inst->isWriteBarrier() ) |
| { |
| // Not handled for now. Mem barriers and write barriers are safe |
| // to simply let commit as memory accesses only happen once they |
| // reach the head of commit. Not sure about the other two. |
| panic("Serializing or barrier instructions" |
| " are not handled yet.\n"); |
| } |
| |
| // Check if the instruction caused a fault. If so, trap. |
| Fault inst_fault = head_inst->getFault(); |
| |
| if (inst_fault != NoFault) { |
| if (!head_inst->isNop()) { |
| #if FULL_SYSTEM |
| cpu->trap(inst_fault); |
| #else // !FULL_SYSTEM |
| panic("fault (%d) detected @ PC %08p", inst_fault, |
| head_inst->PC); |
| #endif // FULL_SYSTEM |
| } |
| } |
| |
| // Check if we're really ready to commit. If not then return false. |
| // I'm pretty sure all instructions should be able to commit if they've |
| // reached this far. For now leave this in as a check. |
| if (!rob->isHeadReady()) { |
| panic("Commit: Unable to commit head instruction!\n"); |
| return false; |
| } |
| |
| // If it's a branch, then send back branch prediction update info |
| // to the fetch stage. |
| // This should be handled in the iew stage if a mispredict happens... |
| |
| if (head_inst->isControl()) { |
| |
| #if 0 |
| toIEW->nextPC = head_inst->readPC(); |
| //Maybe switch over to BTB incorrect. |
| toIEW->btbMissed = head_inst->btbMiss(); |
| toIEW->target = head_inst->nextPC; |
| //Maybe also include global history information. |
| //This simple version will have no branch prediction however. |
| #endif |
| |
| ++commitCommittedBranches; |
| } |
| |
| // Now that the instruction is going to be committed, finalize its |
| // trace data. |
| if (head_inst->traceData) { |
| head_inst->traceData->finalize(); |
| } |
| |
| //Finally clear the head ROB entry. |
| rob->retireHead(); |
| |
| // Return true to indicate that we have committed an instruction. |
| return true; |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::getInsts() |
| { |
| ////////////////////////////////////// |
| // Handle ROB functions |
| ////////////////////////////////////// |
| |
| // Read any issued instructions and place them into the ROB. Do this |
| // prior to squashing to avoid having instructions in the ROB that |
| // don't get squashed properly. |
| int insts_to_process = min((int)renameWidth, fromRename->size); |
| |
| for (int inst_num = 0; |
| inst_num < insts_to_process; |
| ++inst_num) |
| { |
| if (!fromRename->insts[inst_num]->isSquashed()) { |
| DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n", |
| fromRename->insts[inst_num]->readPC()); |
| rob->insertInst(fromRename->insts[inst_num]); |
| } else { |
| DPRINTF(Commit, "Commit: Instruction %i PC %#x was " |
| "squashed, skipping.\n", |
| fromRename->insts[inst_num]->seqNum, |
| fromRename->insts[inst_num]->readPC()); |
| } |
| } |
| } |
| |
| template <class Impl> |
| void |
| SimpleCommit<Impl>::markCompletedInsts() |
| { |
| // Grab completed insts out of the IEW instruction queue, and mark |
| // instructions completed within the ROB. |
| for (int inst_num = 0; |
| inst_num < fromIEW->size && fromIEW->insts[inst_num]; |
| ++inst_num) |
| { |
| DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n", |
| fromIEW->insts[inst_num]->readPC(), |
| fromIEW->insts[inst_num]->seqNum); |
| |
| // Mark the instruction as ready to commit. |
| fromIEW->insts[inst_num]->setCanCommit(); |
| } |
| } |
| |
| template <class Impl> |
| uint64_t |
| SimpleCommit<Impl>::readCommitPC() |
| { |
| return rob->readHeadPC(); |
| } |