2009-02-11 00:49:29 +01:00
|
|
|
/*
|
|
|
|
* 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/first_stage.hh"
|
|
|
|
#include "cpu/inorder/resources/resource_list.hh"
|
|
|
|
#include "cpu/inorder/resource_pool.hh"
|
|
|
|
#include "cpu/inorder/cpu.hh"
|
|
|
|
#include "params/InOrderTrace.hh"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace ThePipeline;
|
|
|
|
|
|
|
|
FirstStage::FirstStage(Params *params, unsigned stage_num)
|
2009-03-05 04:37:45 +01:00
|
|
|
: PipelineStage(params, stage_num), numFetchingThreads(1),
|
|
|
|
fetchPolicy(FirstStage::RoundRobin)
|
2009-02-11 00:49:29 +01:00
|
|
|
{
|
2009-05-26 18:23:13 +02:00
|
|
|
for(ThreadID tid = 0; tid < this->numThreads; tid++) {
|
2009-02-11 00:49:29 +01:00
|
|
|
stageStatus[tid] = Running;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FirstStage::setCPU(InOrderCPU *cpu_ptr)
|
|
|
|
{
|
|
|
|
cpu = cpu_ptr;
|
|
|
|
|
|
|
|
fetchPriorityList = &cpu->fetchPriorityList;
|
|
|
|
|
|
|
|
DPRINTF(InOrderStage, "Set CPU pointer.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2009-05-26 18:23:13 +02:00
|
|
|
FirstStage::squash(InstSeqNum squash_seq_num, ThreadID tid)
|
2009-02-11 00:49:29 +01:00
|
|
|
{
|
|
|
|
// Set status to squashing.
|
|
|
|
//stageStatus[tid] = Squashing;
|
|
|
|
|
|
|
|
// Clear the instruction list and skid buffer in case they have any
|
|
|
|
// insts in them.
|
2010-02-01 00:26:13 +01:00
|
|
|
DPRINTF(InOrderStage, "Removing instructions from stage instruction "
|
|
|
|
"list.\n");
|
2009-02-11 00:49:29 +01:00
|
|
|
while (!insts[tid].empty()) {
|
|
|
|
if (insts[tid].front()->seqNum <= squash_seq_num) {
|
2010-02-01 00:26:13 +01:00
|
|
|
DPRINTF(InOrderStage,"[tid:%i]: Cannot remove [sn:%i] because "
|
|
|
|
"it's <= squashing seqNum %i.\n",
|
2009-02-11 00:49:29 +01:00
|
|
|
tid,
|
|
|
|
insts[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,
|
|
|
|
insts[tid].size());
|
|
|
|
break;
|
|
|
|
}
|
2010-02-01 00:26:13 +01:00
|
|
|
DPRINTF(InOrderStage, "[tid:%i]: Removing instruction, [sn:%i] "
|
|
|
|
"PC %08p.\n", tid, insts[tid].front()->seqNum,
|
|
|
|
insts[tid].front()->PC);
|
2009-02-11 00:49:29 +01:00
|
|
|
insts[tid].pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2010-02-01 00:26:13 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-11 00:49:29 +01:00
|
|
|
void
|
|
|
|
FirstStage::processStage(bool &status_change)
|
|
|
|
{
|
2009-05-26 18:23:13 +02:00
|
|
|
list<ThreadID>::iterator threads = activeThreads->begin();
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
//Check stall and squash signals.
|
2009-05-26 18:23:13 +02:00
|
|
|
while (threads != activeThreads->end()) {
|
|
|
|
ThreadID tid = *threads++;
|
2009-02-11 00:49:29 +01:00
|
|
|
status_change = checkSignalsAndUpdate(tid) || status_change;
|
|
|
|
}
|
|
|
|
|
2010-02-01 00:28:51 +01:00
|
|
|
for (int insts_fetched = 0;
|
|
|
|
insts_fetched < stageWidth && canSendInstToStage(1);
|
|
|
|
insts_fetched++) {
|
2009-05-26 18:23:13 +02:00
|
|
|
ThreadID tid = getFetchingThread(fetchPolicy);
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
if (tid >= 0) {
|
|
|
|
DPRINTF(InOrderStage, "Processing [tid:%i]\n",tid);
|
|
|
|
processThread(status_change, tid);
|
|
|
|
} else {
|
|
|
|
DPRINTF(InOrderStage, "No more threads to fetch from.\n");
|
|
|
|
}
|
|
|
|
}
|
2010-02-01 00:28:51 +01:00
|
|
|
|
|
|
|
if (instsProcessed > 0) {
|
|
|
|
++runCycles;
|
|
|
|
} else {
|
|
|
|
++idleCycles;
|
|
|
|
}
|
|
|
|
|
2009-02-11 00:49:29 +01:00
|
|
|
}
|
|
|
|
|
2010-02-01 00:26:13 +01:00
|
|
|
//@TODO: Note in documentation, that when you make a pipeline stage change,
|
|
|
|
//then make sure you change the first stage too
|
2009-02-11 00:49:29 +01:00
|
|
|
void
|
2009-05-26 18:23:13 +02:00
|
|
|
FirstStage::processInsts(ThreadID tid)
|
2009-02-11 00:49:29 +01:00
|
|
|
{
|
|
|
|
bool all_reqs_completed = true;
|
|
|
|
|
2010-02-01 00:26:13 +01:00
|
|
|
for (int insts_fetched = 0;
|
|
|
|
insts_fetched < stageWidth && canSendInstToStage(1);
|
|
|
|
insts_fetched++) {
|
|
|
|
|
2009-02-11 00:49:29 +01:00
|
|
|
DynInstPtr inst;
|
|
|
|
bool new_inst = false;
|
|
|
|
|
|
|
|
if (!insts[tid].empty()) {
|
|
|
|
inst = insts[tid].front();
|
|
|
|
} else {
|
|
|
|
// Get new instruction.
|
|
|
|
new_inst = true;
|
|
|
|
|
|
|
|
inst = new InOrderDynInst(cpu,
|
2009-05-12 21:01:15 +02:00
|
|
|
cpu->thread[tid],
|
|
|
|
cpu->nextInstSeqNum(tid),
|
|
|
|
tid,
|
|
|
|
tid);
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
#if TRACING_ON
|
|
|
|
inst->traceData =
|
|
|
|
tracer->getInstRecord(ThePipeline::NumStages,
|
|
|
|
cpu->stageTracing,
|
|
|
|
cpu->thread[tid]->getTC());
|
|
|
|
|
2009-05-12 21:01:15 +02:00
|
|
|
#else
|
|
|
|
inst->traceData = NULL;
|
2009-02-11 00:49:29 +01:00
|
|
|
#endif // TRACING_ON
|
|
|
|
|
|
|
|
// Add instruction to the CPU's list of instructions.
|
|
|
|
inst->setInstListIt(cpu->addInst(inst));
|
|
|
|
|
|
|
|
// Create Front-End Resource Schedule For Instruction
|
|
|
|
ThePipeline::createFrontEndSchedule(inst);
|
|
|
|
}
|
|
|
|
|
2010-02-01 00:28:31 +01:00
|
|
|
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++;
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
if (!all_reqs_completed) {
|
|
|
|
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);
|
|
|
|
insts[tid].push(inst);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} else if (!insts[tid].empty()){
|
|
|
|
insts[tid].pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
sendInstToNextStage(inst);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Record that stage has written to the time buffer for activity
|
|
|
|
// tracking.
|
|
|
|
if (toNextStageIndex) {
|
|
|
|
wroteToTimeBuffer = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-26 18:23:13 +02:00
|
|
|
ThreadID
|
2009-02-11 00:49:29 +01:00
|
|
|
FirstStage::getFetchingThread(FetchPriority &fetch_priority)
|
|
|
|
{
|
2010-02-01 00:26:54 +01:00
|
|
|
ThreadID num_active_threads = cpu->numActiveThreads();
|
2009-02-11 00:49:29 +01:00
|
|
|
|
2010-02-01 00:26:54 +01:00
|
|
|
if (num_active_threads > 1) {
|
|
|
|
switch (fetch_priority) {
|
2009-02-11 00:49:29 +01:00
|
|
|
case SingleThread:
|
2010-02-01 00:26:54 +01:00
|
|
|
return cpu->activeThreadId();
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
case RoundRobin:
|
|
|
|
return roundRobin();
|
|
|
|
|
|
|
|
default:
|
2009-05-26 18:23:13 +02:00
|
|
|
return InvalidThreadID;
|
2009-02-11 00:49:29 +01:00
|
|
|
}
|
2010-02-01 00:26:54 +01:00
|
|
|
} else if (num_active_threads == 1) {
|
2009-05-26 18:23:13 +02:00
|
|
|
ThreadID tid = *activeThreads->begin();
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
if (stageStatus[tid] == Running ||
|
|
|
|
stageStatus[tid] == Idle) {
|
|
|
|
return tid;
|
|
|
|
} else {
|
2009-05-26 18:23:13 +02:00
|
|
|
return InvalidThreadID;
|
2009-02-11 00:49:29 +01:00
|
|
|
}
|
2010-02-01 00:26:54 +01:00
|
|
|
} else {
|
|
|
|
return InvalidThreadID;
|
|
|
|
}
|
2009-02-11 00:49:29 +01:00
|
|
|
}
|
|
|
|
|
2009-05-26 18:23:13 +02:00
|
|
|
ThreadID
|
2009-02-11 00:49:29 +01:00
|
|
|
FirstStage::roundRobin()
|
|
|
|
{
|
2009-05-26 18:23:13 +02:00
|
|
|
list<ThreadID>::iterator pri_iter = fetchPriorityList->begin();
|
|
|
|
list<ThreadID>::iterator end = fetchPriorityList->end();
|
2009-02-11 00:49:29 +01:00
|
|
|
|
2009-05-26 18:23:13 +02:00
|
|
|
ThreadID high_pri;
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
while (pri_iter != end) {
|
|
|
|
high_pri = *pri_iter;
|
|
|
|
|
|
|
|
assert(high_pri <= numThreads);
|
|
|
|
|
|
|
|
if (stageStatus[high_pri] == Running ||
|
|
|
|
stageStatus[high_pri] == Idle) {
|
|
|
|
|
2009-05-26 18:23:13 +02:00
|
|
|
fetchPriorityList->erase(pri_iter);
|
|
|
|
fetchPriorityList->push_back(high_pri);
|
2009-02-11 00:49:29 +01:00
|
|
|
|
|
|
|
return high_pri;
|
|
|
|
}
|
|
|
|
|
|
|
|
pri_iter++;
|
|
|
|
}
|
|
|
|
|
2009-05-26 18:23:13 +02:00
|
|
|
return InvalidThreadID;
|
2009-02-11 00:49:29 +01:00
|
|
|
}
|