/* * 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. */ #include "cpu/o3/decode.hh" template SimpleDecode::SimpleDecode(Params ¶ms) : 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 void SimpleDecode::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 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 inline bool SimpleDecode::fetchInstsValid() { return fromFetch->size > 0; } 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(DynInstPtr &inst) { DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction " "detected at decode.\n"); Addr new_PC = inst->readNextPC(); 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; // Clear the skid buffer in case it has any data in it. while (!skidBuffer.empty()) { skidBuffer.pop(); } // Squash instructions up until this one // Slightly unrealistic! cpu->removeInstsUntil(inst->seqNum); } 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) { ++decodeUnblockCycles; if (fetchInstsValid()) { // 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 (fetchInstsValid()) { 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) { ++decodeSquashCycles; squash(); } } } template void SimpleDecode::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 (!fetchInstsValid() && _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 - numInst : 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; } // 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(); } // 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->setNextPC(inst->branchTarget()); 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. // Increment which instruction we're looking at. ++numInst; ++to_rename_index; ++decodeDecodedInsts; --insts_available; } numInst = 0; }