blob: 5e94c665fe6caff2de9378fba05e918d7ef1dc0a [file] [log] [blame]
/*
* 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++;
}
}
}