summaryrefslogtreecommitdiff
path: root/cpu/beta_cpu/decode_impl.hh
diff options
context:
space:
mode:
Diffstat (limited to 'cpu/beta_cpu/decode_impl.hh')
-rw-r--r--cpu/beta_cpu/decode_impl.hh325
1 files changed, 325 insertions, 0 deletions
diff --git a/cpu/beta_cpu/decode_impl.hh b/cpu/beta_cpu/decode_impl.hh
new file mode 100644
index 000000000..ecf19b8ea
--- /dev/null
+++ b/cpu/beta_cpu/decode_impl.hh
@@ -0,0 +1,325 @@
+#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)
+{
+ DPRINTF(Decode, "Decode: decodeWidth=%i.\n", decodeWidth);
+ _status = Idle;
+}
+
+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(DynInst *inst)
+{
+ DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction "
+ "detected at decode.\n");
+ Addr new_PC = inst->nextPC;
+
+ toFetch->decodeInfo.predIncorrect = true;
+ toFetch->decodeInfo.squash = true;
+ toFetch->decodeInfo.nextPC = new_PC;
+
+ // 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) {
+ unblock();
+ }
+ } else if (_status == Blocked) {
+ if (fromFetch->insts[0] != NULL) {
+ 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) {
+ 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 (/* fromRename->renameInfo.squash || */
+ /* fromIEW->iewInfo.squash || */
+ 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->insts[0] == NULL && _status != Unblocking) {
+ DPRINTF(Decode, "Decode: Nothing to do, breaking out early.\n");
+ // Should I change the status to idle?
+ return;
+ }
+
+ DynInst *inst;
+ // Instead have a class member variable that records which instruction
+ // was the last one that was ended on. At the tick() stage, it can
+ // check if that's equal to 0. If not, then don't pop stuff off.
+ unsigned num_inst = 0;
+ bool insts_available = _status == Unblocking ?
+ skidBuffer.front().insts[num_inst] != NULL :
+ fromFetch->insts[num_inst] != NULL;
+
+ // 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] == NULL) {
+ DPRINTF(Decode, "Decode: No instructions available, fetch queue "
+ "empty.\n");
+ } else {
+ panic("Decode: No instructions available, unexpected condition!"
+ "\n");
+ }
+ }
+#endif
+
+ // Check to make sure that instructions coming from fetch are valid.
+ // Normally at this stage the branch target of PC-relative branches
+ // should be computed here. However in this simple model all
+ // computation will take place at execute. Hence doneTargCalc()
+ // will always be false.
+ while (num_inst < decodeWidth &&
+ insts_available)
+ {
+ DPRINTF(Decode, "Decode: Sending instruction to rename.\n");
+ // Might create some sort of accessor to get an instruction
+ // on a per thread basis. Or might be faster to just get
+ // a pointer to an array or list of instructions and use that
+ // within this code.
+ inst = _status == Unblocking ? skidBuffer.front().insts[num_inst] :
+ fromFetch->insts[num_inst];
+ DPRINTF(Decode, "Decode: Processing instruction %i with PC %#x\n",
+ inst, inst->readPC());
+
+ // 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[num_inst] = inst;
+
+ // Ensure that if it was predicted as a branch, it really is a
+ // branch. This case should never happen in this model.
+ if (inst->predTaken() && !inst->isControl()) {
+ panic("Instruction predicted as a branch!");
+
+ // Might want to set some sort of boolean and just do
+ // a check at the end
+ squash(inst);
+ break;
+ }
+
+ // Ensure that the predicted branch target is the actual branch
+ // target if possible (branches that are PC relative).
+ if (inst->isControl() && inst->doneTargCalc()) {
+ if (inst->mispredicted()) {
+ // Might want to set some sort of boolean and just do
+ // a check at the end
+ squash(inst);
+ break;
+ }
+ }
+
+ // 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.
+ if (inst->numSrcRegs() == 0) {
+ inst->setCanIssue();
+ }
+
+ // Increment which instruction we're looking at.
+ ++num_inst;
+
+ // Check whether or not there are instructions available.
+ // Either need to check within the skid buffer, or the fetch
+ // queue, depending if this stage is unblocking or not.
+ insts_available = _status == Unblocking ?
+ skidBuffer.front().insts[num_inst] == NULL :
+ fromFetch->insts[num_inst] == NULL;
+ }
+}
+
+#endif // __SIMPLE_DECODE_CC__