blob: 5f0d7b64769f83fac278cbe9cc4fbdc0971aabf8 [file] [log] [blame]
/*
* 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.
*/
// @todo: Fix the instantaneous communication among all the stages within
// iew. There's a clear delay between issue and execute, yet backwards
// communication happens simultaneously.
// Update the statuses for each stage.
#include <queue>
#include "base/timebuf.hh"
#include "cpu/o3/iew.hh"
template<class Impl>
SimpleIEW<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst,
SimpleIEW<Impl> *_iew)
: Event(&mainEventQueue, CPU_Tick_Pri), inst(_inst), iewStage(_iew)
{
this->setFlags(Event::AutoDelete);
}
template<class Impl>
void
SimpleIEW<Impl>::WritebackEvent::process()
{
DPRINTF(IEW, "IEW: WRITEBACK EVENT!!!!\n");
// Need to insert instruction into queue to commit
iewStage->instToCommit(inst);
// Need to execute second half of the instruction, do actual writing to
// registers and such
inst->execute();
}
template<class Impl>
const char *
SimpleIEW<Impl>::WritebackEvent::description()
{
return "LSQ writeback event";
}
template<class Impl>
SimpleIEW<Impl>::SimpleIEW(Params &params)
: // Just make this time buffer really big for now
issueToExecQueue(5, 5),
instQueue(params),
ldstQueue(params),
commitToIEWDelay(params.commitToIEWDelay),
renameToIEWDelay(params.renameToIEWDelay),
issueToExecuteDelay(params.issueToExecuteDelay),
issueReadWidth(params.issueWidth),
issueWidth(params.issueWidth),
executeWidth(params.executeWidth)
{
DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth);
_status = Idle;
_issueStatus = Idle;
_exeStatus = Idle;
_wbStatus = Idle;
// Setup wire to read instructions coming from issue.
fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay);
// Instruction queue needs the queue between issue and execute.
instQueue.setIssueToExecuteQueue(&issueToExecQueue);
ldstQueue.setIEW(this);
}
template <class Impl>
void
SimpleIEW<Impl>::regStats()
{
instQueue.regStats();
iewIdleCycles
.name(name() + ".iewIdleCycles")
.desc("Number of cycles IEW is idle");
iewSquashCycles
.name(name() + ".iewSquashCycles")
.desc("Number of cycles IEW is squashing");
iewBlockCycles
.name(name() + ".iewBlockCycles")
.desc("Number of cycles IEW is blocking");
iewUnblockCycles
.name(name() + ".iewUnblockCycles")
.desc("Number of cycles IEW is unblocking");
// iewWBInsts;
iewDispatchedInsts
.name(name() + ".iewDispatchedInsts")
.desc("Number of instructions dispatched to IQ");
iewDispSquashedInsts
.name(name() + ".iewDispSquashedInsts")
.desc("Number of squashed instructions skipped by dispatch");
iewDispLoadInsts
.name(name() + ".iewDispLoadInsts")
.desc("Number of dispatched load instructions");
iewDispStoreInsts
.name(name() + ".iewDispStoreInsts")
.desc("Number of dispatched store instructions");
iewDispNonSpecInsts
.name(name() + ".iewDispNonSpecInsts")
.desc("Number of dispatched non-speculative instructions");
iewIQFullEvents
.name(name() + ".iewIQFullEvents")
.desc("Number of times the IQ has become full, causing a stall");
iewExecutedInsts
.name(name() + ".iewExecutedInsts")
.desc("Number of executed instructions");
iewExecLoadInsts
.name(name() + ".iewExecLoadInsts")
.desc("Number of load instructions executed");
iewExecStoreInsts
.name(name() + ".iewExecStoreInsts")
.desc("Number of store instructions executed");
iewExecSquashedInsts
.name(name() + ".iewExecSquashedInsts")
.desc("Number of squashed instructions skipped in execute");
memOrderViolationEvents
.name(name() + ".memOrderViolationEvents")
.desc("Number of memory order violations");
predictedTakenIncorrect
.name(name() + ".predictedTakenIncorrect")
.desc("Number of branches that were predicted taken incorrectly");
}
template<class Impl>
void
SimpleIEW<Impl>::setCPU(FullCPU *cpu_ptr)
{
DPRINTF(IEW, "IEW: Setting CPU pointer.\n");
cpu = cpu_ptr;
instQueue.setCPU(cpu_ptr);
ldstQueue.setCPU(cpu_ptr);
}
template<class Impl>
void
SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
DPRINTF(IEW, "IEW: Setting time buffer pointer.\n");
timeBuffer = tb_ptr;
// Setup wire to read information from time buffer, from commit.
fromCommit = timeBuffer->getWire(-commitToIEWDelay);
// Setup wire to write information back to previous stages.
toRename = timeBuffer->getWire(0);
// Instruction queue also needs main time buffer.
instQueue.setTimeBuffer(tb_ptr);
}
template<class Impl>
void
SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
{
DPRINTF(IEW, "IEW: Setting rename queue pointer.\n");
renameQueue = rq_ptr;
// Setup wire to read information from rename queue.
fromRename = renameQueue->getWire(-renameToIEWDelay);
}
template<class Impl>
void
SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
{
DPRINTF(IEW, "IEW: Setting IEW queue pointer.\n");
iewQueue = iq_ptr;
// Setup wire to write instructions to commit.
toCommit = iewQueue->getWire(0);
}
template<class Impl>
void
SimpleIEW<Impl>::setRenameMap(RenameMap *rm_ptr)
{
DPRINTF(IEW, "IEW: Setting rename map pointer.\n");
renameMap = rm_ptr;
}
template<class Impl>
void
SimpleIEW<Impl>::squash()
{
DPRINTF(IEW, "IEW: Squashing all instructions.\n");
_status = Squashing;
// Tell the IQ to start squashing.
instQueue.squash();
// Tell the LDSTQ to start squashing.
ldstQueue.squash(fromCommit->commitInfo.doneSeqNum);
}
template<class Impl>
void
SimpleIEW<Impl>::squashDueToBranch(DynInstPtr &inst)
{
DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n",
inst->PC);
// Perhaps leave the squashing up to the ROB stage to tell it when to
// squash?
_status = Squashing;
// Tell rename to squash through the time buffer.
toCommit->squash = true;
// Also send PC update information back to prior stages.
toCommit->squashedSeqNum = inst->seqNum;
toCommit->mispredPC = inst->readPC();
toCommit->nextPC = inst->readNextPC();
toCommit->branchMispredict = true;
// Prediction was incorrect, so send back inverse.
toCommit->branchTaken = inst->readNextPC() !=
(inst->readPC() + sizeof(MachInst));
}
template<class Impl>
void
SimpleIEW<Impl>::squashDueToMem(DynInstPtr &inst)
{
DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n",
inst->PC);
// Perhaps leave the squashing up to the ROB stage to tell it when to
// squash?
_status = Squashing;
// Tell rename to squash through the time buffer.
toCommit->squash = true;
// Also send PC update information back to prior stages.
toCommit->squashedSeqNum = inst->seqNum;
toCommit->nextPC = inst->readNextPC();
}
template<class Impl>
void
SimpleIEW<Impl>::block()
{
DPRINTF(IEW, "IEW: Blocking.\n");
// Set the status to Blocked.
_status = Blocked;
// Add the current inputs to the skid buffer so they can be
// reprocessed when this stage unblocks.
skidBuffer.push(*fromRename);
// Note that this stage only signals previous stages to stall when
// it is the cause of the stall originates at this stage. Otherwise
// the previous stages are expected to check all possible stall signals.
}
template<class Impl>
inline void
SimpleIEW<Impl>::unblock()
{
// Check if there's information in the skid buffer. If there is, then
// set status to unblocking, otherwise set it directly to running.
DPRINTF(IEW, "IEW: Reading instructions out of the skid "
"buffer.\n");
// Remove the now processed instructions from the skid buffer.
skidBuffer.pop();
// If there's still information in the skid buffer, then
// continue to tell previous stages to stall. They will be
// able to restart once the skid buffer is empty.
if (!skidBuffer.empty()) {
toRename->iewInfo.stall = true;
} else {
DPRINTF(IEW, "IEW: Stage is done unblocking.\n");
_status = Running;
}
}
template<class Impl>
void
SimpleIEW<Impl>::wakeDependents(DynInstPtr &inst)
{
instQueue.wakeDependents(inst);
}
template<class Impl>
void
SimpleIEW<Impl>::instToCommit(DynInstPtr &inst)
{
}
template <class Impl>
void
SimpleIEW<Impl>::dispatchInsts()
{
////////////////////////////////////////
// DISPATCH/ISSUE stage
////////////////////////////////////////
//Put into its own function?
//Add instructions to IQ if there are any instructions there
// Check if there are any instructions coming from rename, and we're.
// not squashing.
if (fromRename->size > 0) {
int insts_to_add = fromRename->size;
// Loop through the instructions, putting them in the instruction
// queue.
for (int inst_num = 0; inst_num < insts_to_add; ++inst_num)
{
DynInstPtr inst = fromRename->insts[inst_num];
// Make sure there's a valid instruction there.
assert(inst);
DPRINTF(IEW, "IEW: Issue: Adding PC %#x to IQ.\n",
inst->readPC());
// Be sure to mark these instructions as ready so that the
// commit stage can go ahead and execute them, and mark
// them as issued so the IQ doesn't reprocess them.
if (inst->isSquashed()) {
++iewDispSquashedInsts;
continue;
} else if (instQueue.isFull()) {
DPRINTF(IEW, "IEW: Issue: IQ has become full.\n");
// Call function to start blocking.
block();
// Tell previous stage to stall.
toRename->iewInfo.stall = true;
++iewIQFullEvents;
break;
} else if (inst->isLoad()) {
DPRINTF(IEW, "IEW: Issue: Memory instruction "
"encountered, adding to LDSTQ.\n");
// Reserve a spot in the load store queue for this
// memory access.
ldstQueue.insertLoad(inst);
++iewDispLoadInsts;
} else if (inst->isStore()) {
ldstQueue.insertStore(inst);
++iewDispStoreInsts;
} else if (inst->isNonSpeculative()) {
DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction "
"encountered, skipping.\n");
// Same hack as with stores.
inst->setCanCommit();
// Specificall insert it as nonspeculative.
instQueue.insertNonSpec(inst);
++iewDispNonSpecInsts;
continue;
} else if (inst->isNop()) {
DPRINTF(IEW, "IEW: Issue: Nop instruction encountered "
", skipping.\n");
inst->setIssued();
inst->setExecuted();
inst->setCanCommit();
instQueue.advanceTail(inst);
continue;
} else if (inst->isExecuted()) {
assert(0 && "Instruction shouldn't be executed.\n");
DPRINTF(IEW, "IEW: Issue: Executed branch encountered, "
"skipping.\n");
inst->setIssued();
inst->setCanCommit();
instQueue.advanceTail(inst);
continue;
}
// If the instruction queue is not full, then add the
// instruction.
instQueue.insert(fromRename->insts[inst_num]);
++iewDispatchedInsts;
}
}
}
template <class Impl>
void
SimpleIEW<Impl>::executeInsts()
{
////////////////////////////////////////
//EXECUTE/WRITEBACK stage
////////////////////////////////////////
//Put into its own function?
//Similarly should probably have separate execution for int vs FP.
// Above comment is handled by the issue queue only issuing a valid
// mix of int/fp instructions.
//Actually okay to just have one execution, buuuuuut will need
//somewhere that defines the execution latency of all instructions.
// @todo: Move to the FU pool used in the current full cpu.
int fu_usage = 0;
bool fetch_redirect = false;
int inst_slot = 0;
int time_slot = 0;
// Execute/writeback any instructions that are available.
for (int inst_num = 0;
fu_usage < executeWidth && /* Haven't exceeded available FU's. */
inst_num < issueWidth &&
fromIssue->insts[inst_num];
++inst_num) {
DPRINTF(IEW, "IEW: Execute: Executing instructions from IQ.\n");
// Get instruction from issue's queue.
DynInstPtr inst = fromIssue->insts[inst_num];
DPRINTF(IEW, "IEW: Execute: Processing PC %#x.\n", inst->readPC());
// Check if the instruction is squashed; if so then skip it
// and don't count it towards the FU usage.
if (inst->isSquashed()) {
DPRINTF(IEW, "IEW: Execute: Instruction was squashed.\n");
// Consider this instruction executed so that commit can go
// ahead and retire the instruction.
inst->setExecuted();
toCommit->insts[inst_num] = inst;
++iewExecSquashedInsts;
continue;
}
inst->setExecuted();
// If an instruction is executed, then count it towards FU usage.
++fu_usage;
// Execute instruction.
// Note that if the instruction faults, it will be handled
// at the commit stage.
if (inst->isMemRef()) {
DPRINTF(IEW, "IEW: Execute: Calculating address for memory "
"reference.\n");
// Tell the LDSTQ to execute this instruction (if it is a load).
if (inst->isLoad()) {
ldstQueue.executeLoad(inst);
++iewExecLoadInsts;
} else if (inst->isStore()) {
ldstQueue.executeStore(inst);
++iewExecStoreInsts;
} else {
panic("IEW: Unexpected memory type!\n");
}
} else {
inst->execute();
++iewExecutedInsts;
}
// First check the time slot that this instruction will write
// to. If there are free write ports at the time, then go ahead
// and write the instruction to that time. If there are not,
// keep looking back to see where's the first time there's a
// free slot. What happens if you run out of free spaces?
// For now naively assume that all instructions take one cycle.
// Otherwise would have to look into the time buffer based on the
// latency of the instruction.
(*iewQueue)[time_slot].insts[inst_slot];
while ((*iewQueue)[time_slot].insts[inst_slot]) {
if (inst_slot < issueWidth) {
++inst_slot;
} else {
++time_slot;
inst_slot = 0;
}
assert(time_slot < 5);
}
// May actually have to work this out, especially with loads and stores
// Add finished instruction to queue to commit.
(*iewQueue)[time_slot].insts[inst_slot] = inst;
(*iewQueue)[time_slot].size++;
// Check if branch was correct. This check happens after the
// instruction is added to the queue because even if the branch
// is mispredicted, the branch instruction itself is still valid.
// Only handle this if there hasn't already been something that
// redirects fetch in this group of instructions.
if (!fetch_redirect) {
if (inst->mispredicted()) {
fetch_redirect = true;
DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n");
DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n",
inst->nextPC);
// If incorrect, then signal the ROB that it must be squashed.
squashDueToBranch(inst);
if (inst->predTaken()) {
predictedTakenIncorrect++;
}
} else if (ldstQueue.violation()) {
fetch_redirect = true;
// Get the DynInst that caused the violation.
DynInstPtr violator = ldstQueue.getMemDepViolator();
DPRINTF(IEW, "IEW: LDSTQ detected a violation. Violator PC: "
"%#x, inst PC: %#x. Addr is: %#x.\n",
violator->readPC(), inst->readPC(), inst->physEffAddr);
// Tell the instruction queue that a violation has occured.
instQueue.violation(inst, violator);
// Squash.
squashDueToMem(inst);
++memOrderViolationEvents;
}
}
}
}
template<class Impl>
void
SimpleIEW<Impl>::tick()
{
// Considering putting all the state-determining stuff in this section.
// Try to fill up issue queue with as many instructions as bandwidth
// allows.
// Decode should try to execute as many instructions as its bandwidth
// will allow, as long as it is not currently blocked.
// Check if the stage is in a running status.
if (_status != Blocked && _status != Squashing) {
DPRINTF(IEW, "IEW: Status is not blocked, attempting to run "
"stage.\n");
iew();
// If it's currently unblocking, check to see if it should switch
// to running.
if (_status == Unblocking) {
unblock();
++iewUnblockCycles;
}
} else if (_status == Squashing) {
DPRINTF(IEW, "IEW: Still squashing.\n");
// Check if stage should remain squashing. Stop squashing if the
// squash signal clears.
if (!fromCommit->commitInfo.squash &&
!fromCommit->commitInfo.robSquashing) {
DPRINTF(IEW, "IEW: Done squashing, changing status to "
"running.\n");
_status = Running;
instQueue.stopSquash();
} else {
instQueue.doSquash();
}
++iewSquashCycles;
} else if (_status == Blocked) {
// Continue to tell previous stage to stall.
toRename->iewInfo.stall = true;
// Check if possible stall conditions have cleared.
if (!fromCommit->commitInfo.stall &&
!instQueue.isFull()) {
DPRINTF(IEW, "IEW: Stall signals cleared, going to unblock.\n");
_status = Unblocking;
}
// If there's still instructions coming from rename, continue to
// put them on the skid buffer.
if (fromRename->size == 0) {
block();
}
if (fromCommit->commitInfo.squash ||
fromCommit->commitInfo.robSquashing) {
squash();
}
++iewBlockCycles;
}
// @todo: Maybe put these at the beginning, so if it's idle it can
// return early.
// Write back number of free IQ entries here.
toRename->iewInfo.freeIQEntries = instQueue.numFreeEntries();
ldstQueue.writebackStores();
// Check the committed load/store signals to see if there's a load
// or store to commit. Also check if it's being told to execute a
// nonspeculative instruction.
// This is pretty inefficient...
if (!fromCommit->commitInfo.squash &&
!fromCommit->commitInfo.robSquashing) {
ldstQueue.commitStores(fromCommit->commitInfo.doneSeqNum);
ldstQueue.commitLoads(fromCommit->commitInfo.doneSeqNum);
}
if (fromCommit->commitInfo.nonSpecSeqNum != 0) {
instQueue.scheduleNonSpec(fromCommit->commitInfo.nonSpecSeqNum);
}
DPRINTF(IEW, "IEW: IQ has %i free entries.\n",
instQueue.numFreeEntries());
}
template<class Impl>
void
SimpleIEW<Impl>::iew()
{
// Might want to put all state checks in the tick() function.
// Check if being told to stall from commit.
if (fromCommit->commitInfo.stall) {
block();
return;
} else if (fromCommit->commitInfo.squash ||
fromCommit->commitInfo.robSquashing) {
// Also check if commit is telling this stage to squash.
squash();
return;
}
dispatchInsts();
// Have the instruction queue try to schedule any ready instructions.
instQueue.scheduleReadyInsts();
executeInsts();
// Loop through the head of the time buffer and wake any dependents.
// These instructions are about to write back. In the simple model
// this loop can really happen within the previous loop, but when
// instructions have actual latencies, this loop must be separate.
// Also mark scoreboard that this instruction is finally complete.
// Either have IEW have direct access to rename map, or have this as
// part of backwards communication.
for (int inst_num = 0; inst_num < issueWidth &&
toCommit->insts[inst_num]; inst_num++)
{
DynInstPtr inst = toCommit->insts[inst_num];
DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n",
inst->readPC());
if(!inst->isSquashed()) {
instQueue.wakeDependents(inst);
for (int i = 0; i < inst->numDestRegs(); i++)
{
renameMap->markAsReady(inst->renamedDestRegIdx(i));
}
}
}
// Also should advance its own time buffers if the stage ran.
// Not the best place for it, but this works (hopefully).
issueToExecQueue.advance();
}
#ifndef FULL_SYSTEM
template<class Impl>
void
SimpleIEW<Impl>::lsqWriteback()
{
ldstQueue.writebackAllInsts();
}
#endif