/*
 * 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<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>
inline bool
SimpleDecode<Impl>::fetchInstsValid()
{
    return fromFetch->size > 0;
}

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->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<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 (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<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 (!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;
}