#ifndef __SIMPLE_DECODE_CC__ #define __SIMPLE_DECODE_CC__ #include "cpu/beta_cpu/decode.hh" template SimpleDecode::SimpleDecode(Params ¶ms) : 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 void SimpleDecode::setCPU(FullCPU *cpu_ptr) { DPRINTF(Decode, "Decode: Setting CPU pointer.\n"); cpu = cpu_ptr; } template void SimpleDecode::setTimeBuffer(TimeBuffer *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 void SimpleDecode::setDecodeQueue(TimeBuffer *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 void SimpleDecode::setFetchQueue(TimeBuffer *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 void SimpleDecode::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 inline void SimpleDecode::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 void SimpleDecode::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 void SimpleDecode::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 void SimpleDecode::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 void SimpleDecode::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__