| /* |
| * Copyright (c) 2007 MIPS Technologies, Inc. |
| * 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: Korey Sewell |
| * |
| */ |
| |
| #include "base/str.hh" |
| #include "config/the_isa.hh" |
| #include "cpu/inorder/cpu.hh" |
| #include "cpu/inorder/pipeline_stage.hh" |
| #include "cpu/inorder/resource_pool.hh" |
| #include "debug/Activity.hh" |
| #include "debug/InOrderStage.hh" |
| #include "debug/InOrderStall.hh" |
| #include "debug/Resource.hh" |
| #include "debug/ThreadModel.hh" |
| |
| using namespace std; |
| using namespace ThePipeline; |
| |
| PipelineStage::PipelineStage(Params *params, unsigned stage_num) |
| : stageNum(stage_num), stageWidth(params->stageWidth), |
| numThreads(ThePipeline::MaxThreads), _status(Inactive), |
| stageBufferMax(params->stageWidth), |
| prevStageValid(false), nextStageValid(false), idle(false) |
| { |
| init(params); |
| } |
| |
| PipelineStage::~PipelineStage() |
| { |
| for(ThreadID tid = 0; tid < numThreads; tid++) { |
| skidBuffer[tid].clear(); |
| stalls[tid].resources.clear(); |
| } |
| } |
| |
| void |
| PipelineStage::init(Params *params) |
| { |
| for(ThreadID tid = 0; tid < numThreads; tid++) { |
| stageStatus[tid] = Idle; |
| |
| for (int stNum = 0; stNum < NumStages; stNum++) { |
| stalls[tid].stage[stNum] = false; |
| } |
| stalls[tid].resources.clear(); |
| |
| if (stageNum < BackEndStartStage) |
| lastStallingStage[tid] = BackEndStartStage - 1; |
| else |
| lastStallingStage[tid] = NumStages - 1; |
| } |
| |
| if ((InOrderCPU::ThreadModel) params->threadModel == |
| InOrderCPU::SwitchOnCacheMiss) { |
| switchedOutBuffer.resize(ThePipeline::MaxThreads); |
| switchedOutValid.resize(ThePipeline::MaxThreads); |
| } |
| } |
| |
| |
| std::string |
| PipelineStage::name() const |
| { |
| return cpu->name() + ".stage" + to_string(stageNum); |
| } |
| |
| |
| void |
| PipelineStage::regStats() |
| { |
| idleCycles |
| .name(name() + ".idleCycles") |
| .desc("Number of cycles 0 instructions are processed."); |
| |
| runCycles |
| .name(name() + ".runCycles") |
| .desc("Number of cycles 1+ instructions are processed."); |
| |
| utilization |
| .name(name() + ".utilization") |
| .desc("Percentage of cycles stage was utilized (processing insts).") |
| .precision(6); |
| utilization = (runCycles / cpu->numCycles) * 100; |
| |
| } |
| |
| |
| void |
| PipelineStage::setCPU(InOrderCPU *cpu_ptr) |
| { |
| cpu = cpu_ptr; |
| |
| DPRINTF(InOrderStage, "Set CPU pointer.\n"); |
| |
| tracer = dynamic_cast<Trace::InOrderTrace *>(cpu->getTracer()); |
| } |
| |
| |
| void |
| PipelineStage::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) |
| { |
| DPRINTF(InOrderStage, "Setting time buffer pointer.\n"); |
| timeBuffer = tb_ptr; |
| |
| // Setup wire to write information back to fetch. |
| // @todo: should this be writing to the next stage => -1 and reading from is (0)??? |
| toPrevStages = timeBuffer->getWire(0); |
| |
| // Create wires to get information from proper places in time buffer. |
| fromNextStages = timeBuffer->getWire(-1); |
| } |
| |
| |
| void |
| PipelineStage::setPrevStageQueue(TimeBuffer<InterStageStruct> *prev_stage_ptr) |
| { |
| DPRINTF(InOrderStage, "Setting previous stage queue pointer.\n"); |
| prevStageQueue = prev_stage_ptr; |
| |
| // Setup wire to read information from fetch queue. |
| prevStage = prevStageQueue->getWire(-1); |
| |
| prevStageValid = true; |
| } |
| |
| |
| |
| void |
| PipelineStage::setNextStageQueue(TimeBuffer<InterStageStruct> *next_stage_ptr) |
| { |
| DPRINTF(InOrderStage, "Setting next stage pointer.\n"); |
| nextStageQueue = next_stage_ptr; |
| |
| // Setup wire to write information to proper place in stage queue. |
| nextStage = nextStageQueue->getWire(0); |
| nextStageValid = true; |
| } |
| |
| |
| |
| void |
| PipelineStage::setActiveThreads(list<ThreadID> *at_ptr) |
| { |
| DPRINTF(InOrderStage, "Setting active threads list pointer.\n"); |
| activeThreads = at_ptr; |
| } |
| |
| /*inline void |
| PipelineStage::switchToActive() |
| { |
| if (_status == Inactive) { |
| DPRINTF(Activity, "Activating stage.\n"); |
| |
| cpu->activateStage(stageNum); |
| |
| _status = Active; |
| } |
| }*/ |
| |
| void |
| PipelineStage::switchOut() |
| { |
| // Stage can immediately switch out. |
| panic("Switching Out of Stages Unimplemented"); |
| } |
| |
| |
| void |
| PipelineStage::takeOverFrom() |
| { |
| _status = Inactive; |
| |
| // Be sure to reset state and clear out any old instructions. |
| for (ThreadID tid = 0; tid < numThreads; ++tid) { |
| stageStatus[tid] = Idle; |
| |
| for (int stNum = 0; stNum < NumStages; stNum++) { |
| stalls[tid].stage[stNum] = false; |
| } |
| |
| stalls[tid].resources.clear(); |
| |
| skidBuffer[tid].clear(); |
| } |
| wroteToTimeBuffer = false; |
| } |
| |
| |
| |
| bool |
| PipelineStage::checkStall(ThreadID tid) const |
| { |
| bool ret_val = false; |
| |
| // Only check pipeline stall from stage directly following this stage |
| if (nextStageValid && stalls[tid].stage[stageNum + 1]) { |
| DPRINTF(InOrderStage,"[tid:%i]: Stall fom Stage %i detected.\n", |
| tid, stageNum + 1); |
| ret_val = true; |
| } |
| |
| if (!stalls[tid].resources.empty()) { |
| #if TRACING_ON |
| string stall_src; |
| |
| for (int i=0; i < stalls[tid].resources.size(); i++) { |
| stall_src += stalls[tid].resources[i]->res->name() + ":"; |
| } |
| |
| DPRINTF(InOrderStage,"[tid:%i]: Stall fom resources (%s) detected.\n", |
| tid, stall_src); |
| #endif |
| ret_val = true; |
| } |
| |
| return ret_val; |
| } |
| |
| |
| void |
| PipelineStage::removeStalls(ThreadID tid) |
| { |
| for (int st_num = 0; st_num < NumStages; st_num++) { |
| if (stalls[tid].stage[st_num]) { |
| DPRINTF(InOrderStage, "Removing stall from stage %i.\n", |
| st_num); |
| stalls[tid].stage[st_num] = false; |
| } |
| |
| if (toPrevStages->stageBlock[st_num][tid]) { |
| DPRINTF(InOrderStage, "Removing pending block from stage %i.\n", |
| st_num); |
| toPrevStages->stageBlock[st_num][tid] = false; |
| } |
| |
| if (fromNextStages->stageBlock[st_num][tid]) { |
| DPRINTF(InOrderStage, "Removing pending block from stage %i.\n", |
| st_num); |
| fromNextStages->stageBlock[st_num][tid] = false; |
| } |
| } |
| stalls[tid].resources.clear(); |
| } |
| |
| inline bool |
| PipelineStage::prevStageInstsValid() |
| { |
| return prevStage->insts.size() > 0; |
| } |
| |
| bool |
| PipelineStage::isBlocked(ThreadID tid) |
| { |
| return stageStatus[tid] == Blocked; |
| } |
| |
| bool |
| PipelineStage::block(ThreadID tid) |
| { |
| DPRINTF(InOrderStage, "[tid:%d]: Blocking, sending block signal back to " |
| "previous stages.\n", tid); |
| |
| // If the stage status is blocked or unblocking then stage has not yet |
| // signalled fetch to unblock. In that case, there is no need to tell |
| // fetch to block. |
| if (stageStatus[tid] != Blocked) { |
| if (stageStatus[tid] != Unblocking) { |
| wroteToTimeBuffer = true; |
| } |
| |
| stageStatus[tid] = Blocked; |
| |
| if (prevStageValid) { |
| DPRINTF(InOrderStage, "[tid:%d]: Stage %i setting block signal.\n", |
| tid, stageNum); |
| toPrevStages->stageBlock[stageNum][tid] = true; |
| } |
| |
| return true; |
| } |
| |
| |
| return false; |
| } |
| |
| void |
| PipelineStage::blockDueToBuffer(ThreadID tid) |
| { |
| DPRINTF(InOrderStage, "[tid:%d]: Blocking instructions from passing to " |
| "next stage.\n", tid); |
| |
| if (stageStatus[tid] != Blocked) { |
| if (stageStatus[tid] != Unblocking) { |
| wroteToTimeBuffer = true; |
| } |
| |
| // Set the status to Blocked. |
| stageStatus[tid] = Blocked; |
| } |
| } |
| |
| bool |
| PipelineStage::unblock(ThreadID tid) |
| { |
| // @todo: Shouldnt this be if any available slots are open??? |
| // Stage is done unblocking only if the skid buffer is empty. |
| if (skidBuffer[tid].empty()) { |
| DPRINTF(InOrderStage, "[tid:%u]: Done unblocking.\n", tid); |
| |
| if (prevStageValid) |
| toPrevStages->stageUnblock[stageNum][tid] = true; |
| |
| wroteToTimeBuffer = true; |
| |
| stageStatus[tid] = Running; |
| |
| return true; |
| } |
| |
| DPRINTF(InOrderStage, "[tid:%u]: Currently unblocking.\n", tid); |
| return false; |
| } |
| |
| void |
| PipelineStage::setupSquash(DynInstPtr inst, ThreadID tid) |
| { |
| if (cpu->lastSquashCycle[tid] == curTick() && |
| cpu->squashSeqNum[tid] < inst->seqNum){ |
| DPRINTF(Resource, "Ignoring [sn:%i] branch squash signal due to " |
| "another stage's squash signal for after [sn:%i].\n", |
| inst->seqNum, cpu->squashSeqNum[tid]); |
| } else { |
| InstSeqNum squash_seq_num = inst->squashSeqNum; |
| unsigned squash_stage = (nextStageValid) ? stageNum + 1 |
| : stageNum; |
| |
| toPrevStages->stageInfo[squash_stage][tid].squash = true; |
| toPrevStages->stageInfo[squash_stage][tid].doneSeqNum = |
| squash_seq_num; |
| |
| DPRINTF(InOrderStage, "[tid:%i]: Setting up squashing after " |
| "[sn:%i], due to [sn:%i] %s. Squash-Start-Stage:%i\n", |
| tid, squash_seq_num, inst->seqNum, inst->instName(), |
| squash_stage); |
| |
| // Save squash num for later stage use |
| cpu->lastSquashCycle[tid] = curTick(); |
| cpu->squashSeqNum[tid] = squash_seq_num; |
| } |
| } |
| |
| void |
| PipelineStage::squashDueToMemStall(InstSeqNum seq_num, ThreadID tid) |
| { |
| squash(seq_num, tid); |
| } |
| |
| void |
| PipelineStage::squashPrevStageInsts(InstSeqNum squash_seq_num, ThreadID tid) |
| { |
| DPRINTF(InOrderStage, "[tid:%i]: Removing instructions from " |
| "incoming stage queue.\n", tid); |
| |
| int insts_from_prev_stage = prevStage->insts.size(); |
| for (int i=0; i < insts_from_prev_stage; i++) { |
| if (prevStage->insts[i]->threadNumber == tid && |
| prevStage->insts[i]->seqNum > squash_seq_num) { |
| DPRINTF(InOrderStage, "[tid:%i]: Squashing instruction, " |
| "[sn:%i] PC %s.\n", |
| tid, |
| prevStage->insts[i]->seqNum, |
| prevStage->insts[i]->pcState()); |
| prevStage->insts[i]->setSquashed(); |
| |
| prevStage->insts[i] = cpu->dummyBufferInst; |
| } |
| } |
| } |
| |
| void |
| PipelineStage::squash(InstSeqNum squash_seq_num, ThreadID tid) |
| { |
| // Set status to squashing. |
| stageStatus[tid] = Squashing; |
| |
| squashPrevStageInsts(squash_seq_num, tid); |
| |
| DPRINTF(InOrderStage, "[tid:%i]: Removing instructions from incoming stage" |
| " skidbuffer.\n", tid); |
| //@TODO: Walk Through List Using iterator and remove |
| // all instructions over the value |
| std::list<DynInstPtr>::iterator cur_it = skidBuffer[tid].begin(); |
| std::list<DynInstPtr>::iterator end_it = skidBuffer[tid].end(); |
| |
| while (cur_it != end_it) { |
| if ((*cur_it)->seqNum <= squash_seq_num) { |
| DPRINTF(InOrderStage, "[tid:%i]: Cannot remove skidBuffer " |
| "instructions (starting w/[sn:%i]) before " |
| "[sn:%i]. %i insts left.\n", tid, |
| (*cur_it)->seqNum, squash_seq_num, |
| skidBuffer[tid].size()); |
| cur_it++; |
| } else { |
| DPRINTF(InOrderStage, "[tid:%i]: Removing instruction, [sn:%i] " |
| " PC %s.\n", tid, (*cur_it)->seqNum, (*cur_it)->pc); |
| (*cur_it)->setSquashed(); |
| cur_it = skidBuffer[tid].erase(cur_it); |
| } |
| |
| } |
| |
| } |
| |
| int |
| PipelineStage::stageBufferAvail() |
| { |
| unsigned total = 0; |
| |
| for (int i=0; i < ThePipeline::MaxThreads; i++) { |
| total += skidBuffer[i].size(); |
| } |
| |
| int avail = stageBufferMax - total; |
| assert(avail >= 0); |
| |
| return avail; |
| } |
| |
| bool |
| PipelineStage::canSendInstToStage(unsigned stage_num) |
| { |
| bool buffer_avail = false; |
| |
| if (cpu->pipelineStage[stage_num]->prevStageValid) { |
| buffer_avail = cpu->pipelineStage[stage_num]->stageBufferAvail() - |
| cpu->pipelineStage[stage_num-1]->nextStage->insts.size() >= 1; |
| } |
| |
| if (!buffer_avail && nextStageQueueValid(stage_num)) { |
| DPRINTF(InOrderStall, "STALL: No room in stage %i buffer.\n", |
| stageNum + 1); |
| } |
| |
| return buffer_avail; |
| } |
| |
| int |
| PipelineStage::skidSize() |
| { |
| int total = 0; |
| |
| for (int i=0; i < ThePipeline::MaxThreads; i++) { |
| total += skidBuffer[i].size(); |
| } |
| |
| return total; |
| } |
| |
| bool |
| PipelineStage::skidsEmpty() |
| { |
| list<ThreadID>::iterator threads = activeThreads->begin(); |
| |
| while (threads != activeThreads->end()) { |
| if (!skidBuffer[*threads++].empty()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| |
| void |
| PipelineStage::updateStatus() |
| { |
| bool any_unblocking = false; |
| |
| list<ThreadID>::iterator threads = activeThreads->begin(); |
| |
| while (threads != activeThreads->end()) { |
| ThreadID tid = *threads++; |
| |
| if (stageStatus[tid] == Unblocking) { |
| any_unblocking = true; |
| break; |
| } |
| } |
| |
| // Stage will have activity if it's unblocking. |
| if (any_unblocking) { |
| if (_status == Inactive) { |
| _status = Active; |
| |
| DPRINTF(Activity, "Activating stage.\n"); |
| |
| cpu->activateStage(stageNum); |
| } |
| } else { |
| // If it's not unblocking, then stage will not have any internal |
| // activity. Switch it to inactive. |
| if (_status == Active) { |
| _status = Inactive; |
| DPRINTF(Activity, "Deactivating stage.\n"); |
| |
| cpu->deactivateStage(stageNum); |
| } |
| } |
| } |
| |
| void |
| PipelineStage::activateThread(ThreadID tid) |
| { |
| if (cpu->threadModel == InOrderCPU::SwitchOnCacheMiss) { |
| if (!switchedOutValid[tid]) { |
| DPRINTF(InOrderStage, "[tid:%i] No instruction available in " |
| "switch out buffer.\n", tid); |
| } else { |
| DynInstPtr inst = switchedOutBuffer[tid]; |
| |
| DPRINTF(InOrderStage,"[tid:%i]: Re-Inserting [sn:%lli] PC:%s into" |
| " stage skidBuffer %i\n", tid, inst->seqNum, |
| inst->pcState(), inst->threadNumber); |
| |
| // Make instruction available for pipeline processing |
| skidBuffer[tid].push_back(inst); |
| |
| // Update PC so that we start fetching after this instruction to |
| // prevent "double"-execution of instructions |
| cpu->resPool->scheduleEvent((InOrderCPU::CPUEventType) |
| ResourcePool::UpdateAfterContextSwitch, |
| inst, Cycles(0), 0, tid); |
| |
| // Clear switchout buffer |
| switchedOutBuffer[tid] = NULL; |
| switchedOutValid[tid] = false; |
| |
| // Update any CPU stats based off context switches |
| cpu->updateContextSwitchStats(); |
| } |
| } |
| |
| } |
| |
| |
| void |
| PipelineStage::sortInsts() |
| { |
| if (prevStageValid) { |
| assert(prevStage->insts.size() <= stageWidth); |
| |
| int insts_from_prev_stage = prevStage->insts.size(); |
| int insts_from_cur_stage = skidSize(); |
| DPRINTF(InOrderStage, "%i insts available from stage buffer %i. Stage " |
| "currently has %i insts from last cycle.\n", |
| insts_from_prev_stage, prevStageQueue->id(), |
| insts_from_cur_stage); |
| |
| int inserted_insts = 0; |
| |
| for (int i = 0; i < insts_from_prev_stage; i++) { |
| if (prevStage->insts[i]->isSquashed()) { |
| DPRINTF(InOrderStage, "[tid:%i]: Ignoring squashed [sn:%i], " |
| "not inserting into stage buffer.\n", |
| prevStage->insts[i]->readTid(), |
| prevStage->insts[i]->seqNum); |
| continue; |
| } |
| |
| ThreadID tid = prevStage->insts[i]->threadNumber; |
| |
| if (inserted_insts + insts_from_cur_stage == stageWidth) { |
| DPRINTF(InOrderStage, "Stage %i has accepted all insts " |
| "possible for this tick. Placing [sn:%i] in stage %i skidBuffer\n", |
| stageNum, prevStage->insts[i]->seqNum, stageNum - 1); |
| cpu->pipelineStage[stageNum - 1]-> |
| skidBuffer[tid].push_front(prevStage->insts[i]); |
| |
| int prev_stage = stageNum - 1; |
| if (cpu->pipelineStage[prev_stage]->stageStatus[tid] == Running || |
| cpu->pipelineStage[prev_stage]->stageStatus[tid] == Idle) { |
| cpu->pipelineStage[prev_stage]->stageStatus[tid] = Unblocking; |
| } |
| } else { |
| DPRINTF(InOrderStage, "[tid:%i]: Inserting [sn:%i] into stage " |
| "buffer.\n", prevStage->insts[i]->readTid(), |
| prevStage->insts[i]->seqNum); |
| |
| skidBuffer[tid].push_back(prevStage->insts[i]); |
| } |
| |
| prevStage->insts[i] = cpu->dummyBufferInst; |
| |
| inserted_insts++; |
| } |
| } |
| } |
| |
| |
| |
| void |
| PipelineStage::readStallSignals(ThreadID tid) |
| { |
| for (int stage_idx = stageNum+1; stage_idx <= lastStallingStage[tid]; |
| stage_idx++) { |
| |
| DPRINTF(InOrderStage, "[tid:%i] Reading stall signals from Stage " |
| "%i. Block:%i Unblock:%i.\n", |
| tid, |
| stage_idx, |
| fromNextStages->stageBlock[stage_idx][tid], |
| fromNextStages->stageUnblock[stage_idx][tid]); |
| |
| // Check for Stage Blocking Signal |
| if (fromNextStages->stageBlock[stage_idx][tid]) { |
| DPRINTF(InOrderStage, "[tid:%i] Stall from stage %i set.\n", tid, |
| stage_idx); |
| stalls[tid].stage[stage_idx] = true; |
| } |
| |
| // Check for Stage Unblocking Signal |
| if (fromNextStages->stageUnblock[stage_idx][tid]) { |
| DPRINTF(InOrderStage, "[tid:%i] Stall from stage %i unset.\n", tid, |
| stage_idx); |
| stalls[tid].stage[stage_idx] = false; |
| } |
| } |
| } |
| |
| |
| |
| bool |
| PipelineStage::checkSignalsAndUpdate(ThreadID tid) |
| { |
| // Check if there's a squash signal, squash if there is. |
| // Check stall signals, block if necessary. |
| // If status was blocked |
| // Check if stall conditions have passed |
| // if so then go to unblocking |
| // If status was Squashing |
| // check if squashing is not high. Switch to running this cycle. |
| |
| // Update the per thread stall statuses. |
| readStallSignals(tid); |
| |
| // Check for squash from later pipeline stages |
| for (int stage_idx=stageNum; stage_idx < NumStages; stage_idx++) { |
| if (fromNextStages->stageInfo[stage_idx][tid].squash) { |
| DPRINTF(InOrderStage, "[tid:%u]: Squashing instructions due to " |
| "squash from stage %u.\n", tid, stage_idx); |
| InstSeqNum squash_seq_num = fromNextStages-> |
| stageInfo[stage_idx][tid].doneSeqNum; |
| squash(squash_seq_num, tid); |
| break; //return true; |
| } |
| } |
| |
| if (checkStall(tid)) { |
| return block(tid); |
| } |
| |
| if (stageStatus[tid] == Blocked) { |
| DPRINTF(InOrderStage, "[tid:%u]: Done blocking, switching to " |
| "unblocking.\n", tid); |
| |
| stageStatus[tid] = Unblocking; |
| |
| unblock(tid); |
| |
| return true; |
| } |
| |
| if (stageStatus[tid] == Squashing) { |
| if (!skidBuffer[tid].empty()) { |
| DPRINTF(InOrderStage, "[tid:%u]: Done squashing, switching to " |
| "unblocking.\n", tid); |
| |
| stageStatus[tid] = Unblocking; |
| } else { |
| // Switch status to running if stage isn't being told to block or |
| // squash this cycle. |
| DPRINTF(InOrderStage, "[tid:%u]: Done squashing, switching to " |
| "running.\n", tid); |
| |
| stageStatus[tid] = Running; |
| } |
| |
| return true; |
| } |
| |
| // If we've reached this point, we have not gotten any signals that |
| // cause stage to change its status. Stage remains the same as before.*/ |
| return false; |
| } |
| |
| |
| |
| void |
| PipelineStage::tick() |
| { |
| idle = false; |
| |
| wroteToTimeBuffer = false; |
| |
| bool status_change = false; |
| |
| sortInsts(); |
| |
| instsProcessed = 0; |
| |
| processStage(status_change); |
| |
| if (status_change) { |
| updateStatus(); |
| } |
| |
| if (wroteToTimeBuffer) { |
| DPRINTF(Activity, "Activity this cycle.\n"); |
| cpu->activityThisCycle(); |
| } |
| |
| DPRINTF(InOrderStage, "\n\n"); |
| } |
| |
| void |
| PipelineStage::setResStall(ResReqPtr res_req, ThreadID tid) |
| { |
| DPRINTF(InOrderStage, "Inserting stall from %s.\n", res_req->res->name()); |
| stalls[tid].resources.push_back(res_req); |
| } |
| |
| void |
| PipelineStage::unsetResStall(ResReqPtr res_req, ThreadID tid) |
| { |
| // Search through stalls to find stalling request and then |
| // remove it |
| vector<ResReqPtr>::iterator req_it = stalls[tid].resources.begin(); |
| vector<ResReqPtr>::iterator req_end = stalls[tid].resources.end(); |
| |
| while (req_it != req_end) { |
| if( (*req_it)->res == res_req->res && // Same Resource |
| (*req_it)->inst == res_req->inst && // Same Instruction |
| (*req_it)->getSlot() == res_req->getSlot()) { |
| DPRINTF(InOrderStage, "[tid:%u]: Clearing stall by %s.\n", |
| tid, res_req->res->name()); |
| stalls[tid].resources.erase(req_it); |
| break; |
| } |
| |
| req_it++; |
| } |
| |
| if (stalls[tid].resources.size() == 0) { |
| DPRINTF(InOrderStage, "[tid:%u]: There are no remaining resource" |
| "stalls.\n", tid); |
| } |
| } |
| |
| // @TODO: Update How we handled threads in CPU. Maybe threads shouldnt be |
| // handled one at a time, but instead first come first serve by instruction? |
| // Questions are how should a pipeline stage handle thread-specific stalls & |
| // pipeline squashes |
| void |
| PipelineStage::processStage(bool &status_change) |
| { |
| list<ThreadID>::iterator threads = activeThreads->begin(); |
| |
| //Check stall and squash signals. |
| while (threads != activeThreads->end()) { |
| ThreadID tid = *threads++; |
| |
| DPRINTF(InOrderStage,"Processing [tid:%i]\n",tid); |
| status_change = checkSignalsAndUpdate(tid) || status_change; |
| |
| processThread(status_change, tid); |
| } |
| |
| if (nextStageValid) { |
| DPRINTF(InOrderStage, "%i insts now available for stage %i.\n", |
| nextStage->insts.size(), stageNum + 1); |
| } |
| |
| if (instsProcessed > 0) { |
| ++runCycles; |
| idle = false; |
| } else { |
| ++idleCycles; |
| idle = true; |
| } |
| |
| DPRINTF(InOrderStage, "%i left in stage %i incoming buffer.\n", skidSize(), |
| stageNum); |
| |
| DPRINTF(InOrderStage, "%i available in stage %i incoming buffer.\n", |
| stageBufferAvail(), stageNum); |
| } |
| |
| void |
| PipelineStage::processThread(bool &status_change, ThreadID tid) |
| { |
| // If status is Running or idle, |
| // call processInsts() |
| // If status is Unblocking, |
| // buffer any instructions coming from fetch |
| // continue trying to empty skid buffer |
| // check if stall conditions have passed |
| |
| // Stage should try to process as many instructions as its bandwidth |
| // will allow, as long as it is not currently blocked. |
| if (stageStatus[tid] == Running || |
| stageStatus[tid] == Idle) { |
| DPRINTF(InOrderStage, "[tid:%u]: Not blocked, so attempting to run " |
| "stage.\n",tid); |
| |
| processInsts(tid); |
| } else if (stageStatus[tid] == Unblocking) { |
| // Make sure that the skid buffer has something in it if the |
| // status is unblocking. |
| assert(!skidsEmpty()); |
| |
| // If the status was unblocking, then instructions from the skid |
| // buffer were used. Remove those instructions and handle |
| // the rest of unblocking. |
| processInsts(tid); |
| |
| status_change = unblock(tid) || status_change; |
| } |
| } |
| |
| |
| void |
| PipelineStage::processInsts(ThreadID tid) |
| { |
| // Instructions can come either from the skid buffer or the list of |
| // instructions coming from fetch, depending on stage's status. |
| int insts_available = skidBuffer[tid].size(); |
| |
| std::list<DynInstPtr> &insts_to_stage = skidBuffer[tid]; |
| |
| if (insts_available == 0) { |
| DPRINTF(InOrderStage, "[tid:%u]: Nothing to do, breaking out" |
| " early.\n",tid); |
| return; |
| } |
| |
| DynInstPtr inst; |
| bool last_req_completed = true; |
| |
| while (insts_available > 0 && |
| instsProcessed < stageWidth && |
| last_req_completed) { |
| assert(!insts_to_stage.empty()); |
| |
| inst = insts_to_stage.front(); |
| |
| DPRINTF(InOrderStage, "[tid:%u]: Processing instruction [sn:%lli] " |
| "%s with PC %s\n", tid, inst->seqNum, |
| inst->instName(), |
| inst->pcState()); |
| |
| if (inst->isSquashed()) { |
| DPRINTF(InOrderStage, "[tid:%u]: Instruction %i with PC %s is " |
| "squashed, skipping.\n", |
| tid, inst->seqNum, inst->pcState()); |
| |
| insts_to_stage.pop_front(); |
| |
| --insts_available; |
| |
| continue; |
| } |
| |
| int reqs_processed = 0; |
| last_req_completed = processInstSchedule(inst, reqs_processed); |
| |
| // If the instruction isnt squashed & we've completed one request |
| // Then we can officially count this instruction toward the stage's |
| // bandwidth count |
| if (reqs_processed > 0) |
| instsProcessed++; |
| |
| // Don't let instruction pass to next stage if it hasnt completed |
| // all of it's requests for this stage. |
| if (!last_req_completed) |
| continue; |
| |
| // Send to Next Stage or Break Loop |
| if (nextStageValid && !sendInstToNextStage(inst)) { |
| DPRINTF(InOrderStage, "[tid:%i] [sn:%i] unable to proceed to stage" |
| " %i.\n", tid, inst->seqNum,inst->nextStage); |
| break; |
| } |
| |
| insts_to_stage.pop_front(); |
| |
| --insts_available; |
| } |
| |
| // If we didn't process all instructions, then we will need to block |
| // and put all those instructions into the skid buffer. |
| if (!insts_to_stage.empty()) { |
| blockDueToBuffer(tid); |
| } |
| |
| // Record that stage has written to the time buffer for activity |
| // tracking. |
| if (instsProcessed) { |
| wroteToTimeBuffer = true; |
| } |
| } |
| |
| bool |
| PipelineStage::processInstSchedule(DynInstPtr inst,int &reqs_processed) |
| { |
| bool last_req_completed = true; |
| ThreadID tid = inst->readTid(); |
| |
| if (inst->nextResStage() == stageNum) { |
| int res_stage_num = inst->nextResStage(); |
| |
| while (res_stage_num == stageNum) { |
| int res_num = inst->nextResource(); |
| |
| |
| DPRINTF(InOrderStage, "[tid:%i]: [sn:%i]: sending request to %s." |
| "\n", tid, inst->seqNum, cpu->resPool->name(res_num)); |
| |
| ResReqPtr req = cpu->resPool->request(res_num, inst); |
| assert(req->valid); |
| |
| bool req_completed = req->isCompleted(); |
| bool done_in_pipeline = false; |
| if (req_completed) { |
| DPRINTF(InOrderStage, "[tid:%i]: [sn:%i] request to %s " |
| "completed.\n", tid, inst->seqNum, |
| cpu->resPool->name(res_num)); |
| |
| reqs_processed++; |
| |
| req->stagePasses++; |
| |
| done_in_pipeline = inst->finishSkedEntry(); |
| if (done_in_pipeline) { |
| DPRINTF(InOrderDynInst, "[tid:%i]: [sn:%i] finished " |
| "in pipeline.\n", tid, inst->seqNum); |
| } |
| } else { |
| DPRINTF(InOrderStage, "[tid:%i]: [sn:%i] request to %s failed." |
| "\n", tid, inst->seqNum, cpu->resPool->name(res_num)); |
| |
| last_req_completed = false; |
| |
| if (req->isMemStall() && |
| cpu->threadModel == InOrderCPU::SwitchOnCacheMiss) { |
| // Save Stalling Instruction |
| DPRINTF(ThreadModel, "[tid:%i] [sn:%i] Detected cache " |
| "miss.\n", tid, inst->seqNum); |
| |
| DPRINTF(InOrderStage, "Inserting [tid:%i][sn:%i] into " |
| "switch out buffer.\n", tid, inst->seqNum); |
| |
| switchedOutBuffer[tid] = inst; |
| switchedOutValid[tid] = true; |
| |
| // Remove Thread From Pipeline & Resource Pool |
| inst->squashingStage = stageNum; |
| inst->squashSeqNum = inst->seqNum; |
| cpu->squashFromMemStall(inst, tid); |
| |
| // Switch On Cache Miss |
| //===================== |
| // Suspend Thread at end of cycle |
| DPRINTF(ThreadModel, "Suspending [tid:%i] due to cache " |
| "miss.\n", tid); |
| cpu->suspendContext(tid); |
| |
| // Activate Next Ready Thread at end of cycle |
| DPRINTF(ThreadModel, "Attempting to activate next ready " |
| "thread due to cache miss.\n"); |
| cpu->activateNextReadyContext(); |
| } |
| } |
| |
| // If this request is no longer needs to take up bandwidth in the |
| // resource, go ahead and free that bandwidth up |
| if (req->doneInResource) { |
| req->freeSlot(); |
| } |
| |
| // No longer need to process this instruction if the last |
| // request it had wasn't completed or if there is nothing |
| // else for it to do in the pipeline |
| if (done_in_pipeline || !req_completed) { |
| break; |
| } |
| |
| res_stage_num = inst->nextResStage(); |
| } |
| } else { |
| DPRINTF(InOrderStage, "[tid:%u]: Instruction [sn:%i] with PC %s " |
| " needed no resources in stage %i.\n", |
| tid, inst->seqNum, inst->pcState(), stageNum); |
| } |
| |
| return last_req_completed; |
| } |
| |
| bool |
| PipelineStage::nextStageQueueValid(int stage_num) |
| { |
| return cpu->pipelineStage[stage_num]->nextStageValid; |
| } |
| |
| |
| bool |
| PipelineStage::sendInstToNextStage(DynInstPtr inst) |
| { |
| // Update Next Stage Variable in Instruction |
| // NOTE: Some Resources will update this nextStage var. to |
| // for bypassing, so can't always assume nextStage=stageNum+1 |
| if (inst->nextStage == stageNum) |
| inst->nextStage++; |
| |
| bool success = false; |
| ThreadID tid = inst->readTid(); |
| int next_stage = inst->nextStage; |
| int prev_stage = next_stage - 1; |
| |
| assert(next_stage >= 1); |
| assert(prev_stage >= 0); |
| |
| DPRINTF(InOrderStage, "[tid:%u]: Attempting to send instructions to " |
| "stage %u.\n", tid, stageNum+1); |
| |
| if (!canSendInstToStage(inst->nextStage)) { |
| DPRINTF(InOrderStage, "[tid:%u]: Could not send instruction to " |
| "stage %u.\n", tid, stageNum+1); |
| return false; |
| } |
| |
| |
| if (nextStageQueueValid(inst->nextStage - 1)) { |
| if (inst->seqNum > cpu->squashSeqNum[tid] && |
| curTick() == cpu->lastSquashCycle[tid]) { |
| DPRINTF(InOrderStage, "[tid:%u]: [sn:%i]: squashed, skipping " |
| "insertion into stage %i queue.\n", tid, inst->seqNum, |
| inst->nextStage); |
| } else { |
| if (nextStageValid) { |
| DPRINTF(InOrderStage, "[tid:%u] %i slots available in next " |
| "stage buffer.\n", tid, |
| cpu->pipelineStage[next_stage]->stageBufferAvail()); |
| } |
| |
| DPRINTF(InOrderStage, "[tid:%u]: [sn:%i]: being placed into " |
| "index %i of stage buffer %i queue.\n", |
| tid, inst->seqNum, |
| cpu->pipelineStage[prev_stage]->nextStage->insts.size(), |
| cpu->pipelineStage[prev_stage]->nextStageQueue->id()); |
| |
| // Place instructions in inter-stage communication struct for next |
| // pipeline stage to read next cycle |
| cpu->pipelineStage[prev_stage]->nextStage->insts.push_back(inst); |
| |
| success = true; |
| |
| // Take note of trace data for this inst & stage |
| if (inst->traceData) { |
| //@todo: exec traces are broke. fix them |
| inst->traceData->setStageCycle(stageNum, curTick()); |
| } |
| |
| } |
| } |
| |
| return success; |
| } |
| |
| void |
| PipelineStage::dumpInsts() |
| { |
| cprintf("Insts in Stage %i skidbuffers\n",stageNum); |
| |
| for (ThreadID tid = 0; tid < ThePipeline::MaxThreads; tid++) { |
| std::list<DynInstPtr>::iterator cur_it = skidBuffer[tid].begin(); |
| std::list<DynInstPtr>::iterator end_it = skidBuffer[tid].end(); |
| |
| while (cur_it != end_it) { |
| DynInstPtr inst = (*cur_it); |
| |
| cprintf("Inst. PC:%s\n[tid:%i]\n[sn:%i]\n\n", |
| inst->pcState(), inst->threadNumber, inst->seqNum); |
| |
| cur_it++; |
| } |
| } |
| |
| } |