Files
gem5/cpu/beta_cpu/decode_impl.hh
Kevin Lim 2fb632dbda Check in of various updates to the CPU. Mainly adds in stats, improves
branch prediction, and makes memory dependence work properly.

SConscript:
    Added return address stack, tournament predictor.
cpu/base_cpu.cc:
    Added debug break and print statements.
cpu/base_dyn_inst.cc:
cpu/base_dyn_inst.hh:
    Comment out possibly unneeded variables.
cpu/beta_cpu/2bit_local_pred.cc:
    2bit predictor no longer speculatively updates itself.
cpu/beta_cpu/alpha_dyn_inst.hh:
    Comment formatting.
cpu/beta_cpu/alpha_full_cpu.hh:
    Formatting
cpu/beta_cpu/alpha_full_cpu_builder.cc:
    Added new parameters for branch predictors, and IQ parameters.
cpu/beta_cpu/alpha_full_cpu_impl.hh:
    Register stats.
cpu/beta_cpu/alpha_params.hh:
    Added parameters for IQ, branch predictors, and store sets.
cpu/beta_cpu/bpred_unit.cc:
    Removed one class.
cpu/beta_cpu/bpred_unit.hh:
    Add in RAS, stats.  Changed branch predictor unit functionality
    so that it holds a history of past branches so it can update, and also
    hold a proper history of the RAS so it can be restored on branch
    mispredicts.
cpu/beta_cpu/bpred_unit_impl.hh:
    Added in stats, history of branches, RAS.  Now bpred unit actually
    modifies the instruction's predicted next PC.
cpu/beta_cpu/btb.cc:
    Add in sanity checks.
cpu/beta_cpu/comm.hh:
    Add in communication where needed, remove it where it's not.
cpu/beta_cpu/commit.hh:
cpu/beta_cpu/rename.hh:
cpu/beta_cpu/rename_impl.hh:
    Add in stats.
cpu/beta_cpu/commit_impl.hh:
    Stats, update what is sent back on branch mispredict.
cpu/beta_cpu/cpu_policy.hh:
    Change the bpred unit being used.
cpu/beta_cpu/decode.hh:
cpu/beta_cpu/decode_impl.hh:
    Stats.
cpu/beta_cpu/fetch.hh:
    Stats, change squash so it can handle squashes from decode differently
    than squashes from commit.
cpu/beta_cpu/fetch_impl.hh:
    Add in stats.  Change how a cache line is fetched.  Update to work with
    caches.  Also have separate functions for different behavior if squash
    is coming from decode vs commit.
cpu/beta_cpu/free_list.hh:
    Remove some old comments.
cpu/beta_cpu/full_cpu.cc:
cpu/beta_cpu/full_cpu.hh:
    Added function to remove instructions from back of instruction list
    until a certain sequence number.
cpu/beta_cpu/iew.hh:
    Stats, separate squashing behavior due to branches vs memory.
cpu/beta_cpu/iew_impl.hh:
    Stats, separate squashing behavior for branches vs memory.
cpu/beta_cpu/inst_queue.cc:
    Debug stuff
cpu/beta_cpu/inst_queue.hh:
    Stats, change how mem dep unit works, debug stuff
cpu/beta_cpu/inst_queue_impl.hh:
    Stats, change how mem dep unit works, debug stuff.  Also add in
    parameters that used to be hardcoded.
cpu/beta_cpu/mem_dep_unit.hh:
cpu/beta_cpu/mem_dep_unit_impl.hh:
    Add in stats, change how memory dependence unit works.  It now holds
    the memory instructions that are waiting for their memory dependences
    to resolve.  It provides which instructions are ready directly to the
    IQ.
cpu/beta_cpu/regfile.hh:
    Fix up sanity checks.
cpu/beta_cpu/rename_map.cc:
    Fix loop variable type.
cpu/beta_cpu/rob_impl.hh:
    Remove intermediate DynInstPtr
cpu/beta_cpu/store_set.cc:
    Add in debugging statements.
cpu/beta_cpu/store_set.hh:
    Reorder function arguments to match the rest of the calls.

--HG--
extra : convert_revision : aabf9b1fecd1d743265dfc3b174d6159937c6f44
2004-10-21 18:02:36 -04:00

398 lines
12 KiB
C++

#ifndef __SIMPLE_DECODE_CC__
#define __SIMPLE_DECODE_CC__
#include "cpu/beta_cpu/decode.hh"
template<class Impl>
SimpleDecode<Impl>::SimpleDecode(Params &params)
: renameToDecodeDelay(params.renameToDecodeDelay),
iewToDecodeDelay(params.iewToDecodeDelay),
commitToDecodeDelay(params.commitToDecodeDelay),
fetchToDecodeDelay(params.fetchToDecodeDelay),
decodeWidth(params.decodeWidth),
numInst(0)
{
DPRINTF(Decode, "Decode: decodeWidth=%i.\n", decodeWidth);
_status = Idle;
}
template <class Impl>
void
SimpleDecode<Impl>::regStats()
{
decodeIdleCycles
.name(name() + ".decodeIdleCycles")
.desc("Number of cycles decode is idle")
.prereq(decodeIdleCycles);
decodeBlockedCycles
.name(name() + ".decodeBlockedCycles")
.desc("Number of cycles decode is blocked")
.prereq(decodeBlockedCycles);
decodeUnblockCycles
.name(name() + ".decodeUnblockCycles")
.desc("Number of cycles decode is unblocking")
.prereq(decodeUnblockCycles);
decodeSquashCycles
.name(name() + ".decodeSquashCycles")
.desc("Number of cycles decode is squashing")
.prereq(decodeSquashCycles);
decodeBranchMispred
.name(name() + ".decodeBranchMispred")
.desc("Number of times decode detected a branch misprediction")
.prereq(decodeBranchMispred);
decodeControlMispred
.name(name() + ".decodeControlMispred")
.desc("Number of times decode detected an instruction incorrectly"
" predicted as a control")
.prereq(decodeControlMispred);
decodeDecodedInsts
.name(name() + ".decodeDecodedInsts")
.desc("Number of instructions handled by decode")
.prereq(decodeDecodedInsts);
decodeSquashedInsts
.name(name() + ".decodeSquashedInsts")
.desc("Number of squashed instructions handled by decode")
.prereq(decodeSquashedInsts);
}
template<class Impl>
void
SimpleDecode<Impl>::setCPU(FullCPU *cpu_ptr)
{
DPRINTF(Decode, "Decode: Setting CPU pointer.\n");
cpu = cpu_ptr;
}
template<class Impl>
void
SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
DPRINTF(Decode, "Decode: Setting time buffer pointer.\n");
timeBuffer = tb_ptr;
// Setup wire to write information back to fetch.
toFetch = timeBuffer->getWire(0);
// Create wires to get information from proper places in time buffer.
fromRename = timeBuffer->getWire(-renameToDecodeDelay);
fromIEW = timeBuffer->getWire(-iewToDecodeDelay);
fromCommit = timeBuffer->getWire(-commitToDecodeDelay);
}
template<class Impl>
void
SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
{
DPRINTF(Decode, "Decode: Setting decode queue pointer.\n");
decodeQueue = dq_ptr;
// Setup wire to write information to proper place in decode queue.
toRename = decodeQueue->getWire(0);
}
template<class Impl>
void
SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
{
DPRINTF(Decode, "Decode: Setting fetch queue pointer.\n");
fetchQueue = fq_ptr;
// Setup wire to read information from fetch queue.
fromFetch = fetchQueue->getWire(-fetchToDecodeDelay);
}
template<class Impl>
void
SimpleDecode<Impl>::block()
{
DPRINTF(Decode, "Decode: 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(*fromFetch);
// 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
SimpleDecode<Impl>::unblock()
{
DPRINTF(Decode, "Decode: Unblocking, going to remove "
"instructions from 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()) {
toFetch->decodeInfo.stall = true;
} else {
DPRINTF(Decode, "Decode: Finished unblocking.\n");
_status = Running;
}
}
// This squash is specifically for when Decode detects a PC-relative branch
// was predicted incorrectly.
template<class Impl>
void
SimpleDecode<Impl>::squash(DynInstPtr &inst)
{
DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction "
"detected at decode.\n");
Addr new_PC = inst->nextPC;
toFetch->decodeInfo.branchMispredict = true;
toFetch->decodeInfo.doneSeqNum = inst->seqNum;
toFetch->decodeInfo.predIncorrect = true;
toFetch->decodeInfo.squash = true;
toFetch->decodeInfo.nextPC = new_PC;
toFetch->decodeInfo.branchTaken = true;
// Set status to squashing.
_status = Squashing;
// Maybe advance the time buffer? Not sure what to do in the normal
// case.
// Clear the skid buffer in case it has any data in it.
while (!skidBuffer.empty())
{
skidBuffer.pop();
}
}
template<class Impl>
void
SimpleDecode<Impl>::squash()
{
DPRINTF(Decode, "Decode: Squashing.\n");
// Set status to squashing.
_status = Squashing;
// Maybe advance the time buffer? Not sure what to do in the normal
// case.
// Clear the skid buffer in case it has any data in it.
while (!skidBuffer.empty())
{
skidBuffer.pop();
}
}
template<class Impl>
void
SimpleDecode<Impl>::tick()
{
// Decode should try to execute as many instructions as its bandwidth
// will allow, as long as it is not currently blocked.
if (_status != Blocked && _status != Squashing) {
DPRINTF(Decode, "Decode: Not blocked, so attempting to run "
"stage.\n");
// Make sure that the skid buffer has something in it if the
// status is unblocking.
assert(_status == Unblocking ? !skidBuffer.empty() : 1);
decode();
// If the status was unblocking, then instructions from the skid
// buffer were used. Remove those instructions and handle
// the rest of unblocking.
if (_status == Unblocking) {
++decodeUnblockCycles;
if (fromFetch->size > 0) {
// Add the current inputs to the skid buffer so they can be
// reprocessed when this stage unblocks.
skidBuffer.push(*fromFetch);
}
unblock();
}
} else if (_status == Blocked) {
++decodeBlockedCycles;
if (fromFetch->size > 0) {
block();
}
if (!fromRename->renameInfo.stall &&
!fromIEW->iewInfo.stall &&
!fromCommit->commitInfo.stall) {
DPRINTF(Decode, "Decode: Stall signals cleared, going to "
"unblock.\n");
_status = Unblocking;
// Continue to tell previous stage to block until this
// stage is done unblocking.
toFetch->decodeInfo.stall = true;
} else {
DPRINTF(Decode, "Decode: Still blocked.\n");
toFetch->decodeInfo.stall = true;
}
if (fromCommit->commitInfo.squash ||
fromCommit->commitInfo.robSquashing) {
squash();
}
} else if (_status == Squashing) {
++decodeSquashCycles;
if (!fromCommit->commitInfo.squash &&
!fromCommit->commitInfo.robSquashing) {
_status = Running;
} else if (fromCommit->commitInfo.squash) {
squash();
}
}
}
template<class Impl>
void
SimpleDecode<Impl>::decode()
{
// Check time buffer if being told to squash.
if (fromCommit->commitInfo.squash) {
squash();
return;
}
// Check time buffer if being told to stall.
if (fromRename->renameInfo.stall ||
fromIEW->iewInfo.stall ||
fromCommit->commitInfo.stall)
{
block();
return;
}
// Check fetch queue to see if instructions are available.
// If no available instructions, do nothing, unless this stage is
// currently unblocking.
if (fromFetch->size == 0 && _status != Unblocking) {
DPRINTF(Decode, "Decode: Nothing to do, breaking out early.\n");
// Should I change the status to idle?
++decodeIdleCycles;
return;
}
// Might be better to use a base DynInst * instead?
DynInstPtr inst;
unsigned to_rename_index = 0;
int insts_available = _status == Unblocking ?
skidBuffer.front().size :
fromFetch->size;
// Debug block...
#if 0
if (insts_available) {
DPRINTF(Decode, "Decode: Instructions available.\n");
} else {
if (_status == Unblocking && skidBuffer.empty()) {
DPRINTF(Decode, "Decode: No instructions available, skid buffer "
"empty.\n");
} else if (_status != Unblocking &&
!fromFetch->insts[0]) {
DPRINTF(Decode, "Decode: No instructions available, fetch queue "
"empty.\n");
} else {
panic("Decode: No instructions available, unexpected condition!"
"\n");
}
}
#endif
while (insts_available > 0)
{
DPRINTF(Decode, "Decode: Sending instruction to rename.\n");
inst = _status == Unblocking ? skidBuffer.front().insts[numInst] :
fromFetch->insts[numInst];
DPRINTF(Decode, "Decode: Processing instruction %i with PC %#x\n",
inst->seqNum, inst->readPC());
if (inst->isSquashed()) {
DPRINTF(Decode, "Decode: Instruction %i with PC %#x is "
"squashed, skipping.\n",
inst->seqNum, inst->readPC());
++decodeSquashedInsts;
++numInst;
--insts_available;
continue;
}
// This current instruction is valid, so add it into the decode
// queue. The next instruction may not be valid, so check to
// see if branches were predicted correctly.
toRename->insts[to_rename_index] = inst;
++(toRename->size);
// Ensure that if it was predicted as a branch, it really is a
// branch.
if (inst->predTaken() && !inst->isControl()) {
panic("Instruction predicted as a branch!");
++decodeControlMispred;
// Might want to set some sort of boolean and just do
// a check at the end
squash(inst);
break;
}
// Go ahead and compute any PC-relative branches.
if (inst->isDirectCtrl() && inst->isUncondCtrl() &&
inst->numDestRegs() == 0 && inst->numSrcRegs() == 0) {
inst->execute();
inst->setExecuted();
if (inst->mispredicted()) {
++decodeBranchMispred;
// Might want to set some sort of boolean and just do
// a check at the end
squash(inst);
break;
}
}
// Normally can check if a direct branch has the right target
// addr (either the immediate, or the branch PC + 4) and redirect
// fetch if it's incorrect.
// Also check if instructions have no source registers. Mark
// them as ready to issue at any time. Not sure if this check
// should exist here or at a later stage; however it doesn't matter
// too much for function correctness.
// Isn't this handled by the inst queue?
if (inst->numSrcRegs() == 0) {
inst->setCanIssue();
}
// Increment which instruction we're looking at.
++numInst;
++to_rename_index;
++decodeDecodedInsts;
--insts_available;
}
numInst = 0;
}
#endif // __SIMPLE_DECODE_CC__