gem5/src/cpu/inorder/first_stage.cc
Nathan Binkert eddac53ff6 trace: reimplement the DTRACE function so it doesn't use a vector
At the same time, rename the trace flags to debug flags since they
have broader usage than simply tracing.  This means that
--trace-flags is now --debug-flags and --trace-help is now --debug-help
2011-04-15 10:44:32 -07:00

280 lines
8.7 KiB
C++

/*
* 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 "cpu/inorder/resources/resource_list.hh"
#include "cpu/inorder/cpu.hh"
#include "cpu/inorder/first_stage.hh"
#include "cpu/inorder/resource_pool.hh"
#include "debug/InOrderStage.hh"
#include "params/InOrderTrace.hh"
using namespace std;
using namespace ThePipeline;
FirstStage::FirstStage(Params *params, unsigned stage_num)
: PipelineStage(params, stage_num), numFetchingThreads(1),
fetchPolicy(FirstStage::RoundRobin)
{
for(ThreadID tid = 0; tid < this->numThreads; tid++) {
stageStatus[tid] = Running;
}
}
void
FirstStage::setCPU(InOrderCPU *cpu_ptr)
{
cpu = cpu_ptr;
fetchPriorityList = &cpu->fetchPriorityList;
DPRINTF(InOrderStage, "Set CPU pointer.\n");
}
void
FirstStage::squash(InstSeqNum squash_seq_num, ThreadID tid)
{
// Set status to squashing.
//stageStatus[tid] = Squashing;
// Clear the instruction list and skid buffer in case they have any
// insts in them.
DPRINTF(InOrderStage, "Removing instructions from stage instruction "
"list.\n");
while (!skidBuffer[tid].empty()) {
if (skidBuffer[tid].front()->seqNum <= squash_seq_num) {
DPRINTF(InOrderStage,"[tid:%i]: Cannot remove [sn:%i] because "
"it's <= squashing seqNum %i.\n",
tid,
skidBuffer[tid].front()->seqNum,
squash_seq_num);
DPRINTF(InOrderStage, "[tid:%i]: Cannot remove incoming "
"instructions before delay slot [sn:%i]. %i insts"
"left.\n", tid, squash_seq_num,
skidBuffer[tid].size());
break;
}
DPRINTF(InOrderStage, "[tid:%i]: Removing instruction, [sn:%i] "
"PC %s.\n", tid, skidBuffer[tid].front()->seqNum,
skidBuffer[tid].front()->pc);
skidBuffer[tid].pop_front();
}
// Now that squash has propagated to the first stage,
// Alert CPU to remove instructions from the CPU instruction list.
// @todo: Move this to the CPU object.
cpu->removeInstsUntil(squash_seq_num, tid);
}
void
FirstStage::squashDueToMemStall(InstSeqNum seq_num, ThreadID tid)
{
// Need to preserve the stalling instruction in first-stage
// since the squash() from first stage also removes
// the instruction from the CPU (removeInstsUntil). If that
// functionality gets changed then you can move this offset.
// (stalling instruction = seq_num + 1)
squash(seq_num+1, tid);
}
void
FirstStage::processStage(bool &status_change)
{
list<ThreadID>::iterator threads = activeThreads->begin();
//Check stall and squash signals.
while (threads != activeThreads->end()) {
ThreadID tid = *threads++;
status_change = checkSignalsAndUpdate(tid) || status_change;
}
while (instsProcessed < stageWidth) {
ThreadID tid = getFetchingThread(fetchPolicy);
if (tid >= 0) {
DPRINTF(InOrderStage, "Processing [tid:%i]\n",tid);
processThread(status_change, tid);
DPRINTF(InOrderStage, "Done Processing [tid:%i]\n",tid);
} else {
DPRINTF(InOrderStage, "No more threads to fetch from.\n");
break;
}
}
if (instsProcessed > 0) {
++runCycles;
idle = false;
} else {
++idleCycles;
idle = true;
}
}
//@TODO: Note in documentation, that when you make a pipeline stage change,
//then make sure you change the first stage too
void
FirstStage::processInsts(ThreadID tid)
{
bool all_reqs_completed = true;
for (int insts_fetched = instsProcessed;
insts_fetched < stageWidth;
insts_fetched++) {
DynInstPtr inst;
bool new_inst = false;
if (!skidBuffer[tid].empty()) {
inst = skidBuffer[tid].front();
} else {
// Get new instruction.
new_inst = true;
inst = new InOrderDynInst(cpu,
cpu->thread[tid],
cpu->nextInstSeqNum(tid),
tid,
tid);
#if TRACING_ON
inst->traceData =
tracer->getInstRecord(ThePipeline::NumStages,
cpu->stageTracing,
cpu->thread[tid]->getTC());
#else
inst->traceData = NULL;
#endif // TRACING_ON
// Add instruction to the CPU's list of instructions.
inst->setInstListIt(cpu->addInst(inst));
// Create Front-End Resource Schedule For Instruction
inst->setFrontSked(cpu->frontEndSked);
}
int reqs_processed = 0;
all_reqs_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++;
if (!all_reqs_completed || !sendInstToNextStage(inst)) {
if (new_inst) {
DPRINTF(InOrderStage, "[tid:%u]: [sn:%u] Did not finish all "
"requests for this stage. Keep in stage inst. "
"list.\n", tid, inst->seqNum);
skidBuffer[tid].push_back(inst);
}
block(tid);
break;
} else if (!skidBuffer[tid].empty()){
DPRINTF(InOrderStage, "[tid:%u]: [sn:%u] Finished all "
"requests for this stage.\n", tid, inst->seqNum);
skidBuffer[tid].pop_front();
}
}
// Record that stage has written to the time buffer for activity
// tracking.
if (instsProcessed) {
wroteToTimeBuffer = true;
}
}
ThreadID
FirstStage::getFetchingThread(FetchPriority &fetch_priority)
{
ThreadID num_active_threads = cpu->numActiveThreads();
if (num_active_threads > 1) {
switch (fetch_priority) {
case SingleThread:
return cpu->activeThreadId();
case RoundRobin:
return roundRobin();
default:
return InvalidThreadID;
}
} else if (num_active_threads == 1) {
ThreadID tid = *activeThreads->begin();
if (stageStatus[tid] == Running ||
stageStatus[tid] == Idle ||
stageStatus[tid] == Unblocking) {
return tid;
} else {
return InvalidThreadID;
}
} else {
return InvalidThreadID;
}
}
ThreadID
FirstStage::roundRobin()
{
list<ThreadID>::iterator pri_iter = fetchPriorityList->begin();
list<ThreadID>::iterator end = fetchPriorityList->end();
ThreadID high_pri;
while (pri_iter != end) {
high_pri = *pri_iter;
assert(high_pri <= numThreads);
if (stageStatus[high_pri] == Running ||
stageStatus[high_pri] == Idle ||
stageStatus[high_pri] == Unblocking){
fetchPriorityList->erase(pri_iter);
fetchPriorityList->push_back(high_pri);
return high_pri;
}
pri_iter++;
}
return InvalidThreadID;
}