/*
 * 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.
 */

// @todo: Fix the instantaneous communication among all the stages within
// iew.  There's a clear delay between issue and execute, yet backwards
// communication happens simultaneously.
// Update the statuses for each stage.

#include <queue>

#include "base/timebuf.hh"
#include "cpu/o3/iew.hh"

template<class Impl>
SimpleIEW<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst,
                                                SimpleIEW<Impl> *_iew)
    : Event(&mainEventQueue, CPU_Tick_Pri), inst(_inst), iewStage(_iew)
{
    this->setFlags(Event::AutoDelete);
}

template<class Impl>
void
SimpleIEW<Impl>::WritebackEvent::process()
{
    DPRINTF(IEW, "IEW: WRITEBACK EVENT!!!!\n");

    // Need to insert instruction into queue to commit
    iewStage->instToCommit(inst);
    // Need to execute second half of the instruction, do actual writing to
    // registers and such
    inst->execute();
}

template<class Impl>
const char *
SimpleIEW<Impl>::WritebackEvent::description()
{
    return "LSQ writeback event";
}

template<class Impl>
SimpleIEW<Impl>::SimpleIEW(Params &params)
    : // Just make this time buffer really big for now
      issueToExecQueue(5, 5),
      instQueue(params),
      ldstQueue(params),
      commitToIEWDelay(params.commitToIEWDelay),
      renameToIEWDelay(params.renameToIEWDelay),
      issueToExecuteDelay(params.issueToExecuteDelay),
      issueReadWidth(params.issueWidth),
      issueWidth(params.issueWidth),
      executeWidth(params.executeWidth)
{
    DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth);
    _status = Idle;
    _issueStatus = Idle;
    _exeStatus = Idle;
    _wbStatus = Idle;

    // Setup wire to read instructions coming from issue.
    fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay);

    // Instruction queue needs the queue between issue and execute.
    instQueue.setIssueToExecuteQueue(&issueToExecQueue);

    ldstQueue.setIEW(this);
}

template <class Impl>
void
SimpleIEW<Impl>::regStats()
{
    instQueue.regStats();

    iewIdleCycles
        .name(name() + ".iewIdleCycles")
        .desc("Number of cycles IEW is idle");

    iewSquashCycles
        .name(name() + ".iewSquashCycles")
        .desc("Number of cycles IEW is squashing");

    iewBlockCycles
        .name(name() + ".iewBlockCycles")
        .desc("Number of cycles IEW is blocking");

    iewUnblockCycles
        .name(name() + ".iewUnblockCycles")
        .desc("Number of cycles IEW is unblocking");

//    iewWBInsts;

    iewDispatchedInsts
        .name(name() + ".iewDispatchedInsts")
        .desc("Number of instructions dispatched to IQ");

    iewDispSquashedInsts
        .name(name() + ".iewDispSquashedInsts")
        .desc("Number of squashed instructions skipped by dispatch");

    iewDispLoadInsts
        .name(name() + ".iewDispLoadInsts")
        .desc("Number of dispatched load instructions");

    iewDispStoreInsts
        .name(name() + ".iewDispStoreInsts")
        .desc("Number of dispatched store instructions");

    iewDispNonSpecInsts
        .name(name() + ".iewDispNonSpecInsts")
        .desc("Number of dispatched non-speculative instructions");

    iewIQFullEvents
        .name(name() + ".iewIQFullEvents")
        .desc("Number of times the IQ has become full, causing a stall");

    iewExecutedInsts
        .name(name() + ".iewExecutedInsts")
        .desc("Number of executed instructions");

    iewExecLoadInsts
        .name(name() + ".iewExecLoadInsts")
        .desc("Number of load instructions executed");

    iewExecStoreInsts
        .name(name() + ".iewExecStoreInsts")
        .desc("Number of store instructions executed");

    iewExecSquashedInsts
        .name(name() + ".iewExecSquashedInsts")
        .desc("Number of squashed instructions skipped in execute");

    memOrderViolationEvents
        .name(name() + ".memOrderViolationEvents")
        .desc("Number of memory order violations");

    predictedTakenIncorrect
        .name(name() + ".predictedTakenIncorrect")
        .desc("Number of branches that were predicted taken incorrectly");
}

template<class Impl>
void
SimpleIEW<Impl>::setCPU(FullCPU *cpu_ptr)
{
    DPRINTF(IEW, "IEW: Setting CPU pointer.\n");
    cpu = cpu_ptr;

    instQueue.setCPU(cpu_ptr);
    ldstQueue.setCPU(cpu_ptr);
}

template<class Impl>
void
SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
    DPRINTF(IEW, "IEW: Setting time buffer pointer.\n");
    timeBuffer = tb_ptr;

    // Setup wire to read information from time buffer, from commit.
    fromCommit = timeBuffer->getWire(-commitToIEWDelay);

    // Setup wire to write information back to previous stages.
    toRename = timeBuffer->getWire(0);

    // Instruction queue also needs main time buffer.
    instQueue.setTimeBuffer(tb_ptr);
}

template<class Impl>
void
SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
{
    DPRINTF(IEW, "IEW: Setting rename queue pointer.\n");
    renameQueue = rq_ptr;

    // Setup wire to read information from rename queue.
    fromRename = renameQueue->getWire(-renameToIEWDelay);
}

template<class Impl>
void
SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
{
    DPRINTF(IEW, "IEW: Setting IEW queue pointer.\n");
    iewQueue = iq_ptr;

    // Setup wire to write instructions to commit.
    toCommit = iewQueue->getWire(0);
}

template<class Impl>
void
SimpleIEW<Impl>::setRenameMap(RenameMap *rm_ptr)
{
    DPRINTF(IEW, "IEW: Setting rename map pointer.\n");
    renameMap = rm_ptr;
}

template<class Impl>
void
SimpleIEW<Impl>::squash()
{
    DPRINTF(IEW, "IEW: Squashing all instructions.\n");
    _status = Squashing;

    // Tell the IQ to start squashing.
    instQueue.squash();

    // Tell the LDSTQ to start squashing.
    ldstQueue.squash(fromCommit->commitInfo.doneSeqNum);
}

template<class Impl>
void
SimpleIEW<Impl>::squashDueToBranch(DynInstPtr &inst)
{
    DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n",
            inst->PC);
    // Perhaps leave the squashing up to the ROB stage to tell it when to
    // squash?
    _status = Squashing;

    // Tell rename to squash through the time buffer.
    toCommit->squash = true;
    // Also send PC update information back to prior stages.
    toCommit->squashedSeqNum = inst->seqNum;
    toCommit->mispredPC = inst->readPC();
    toCommit->nextPC = inst->readNextPC();
    toCommit->branchMispredict = true;
    // Prediction was incorrect, so send back inverse.
    toCommit->branchTaken = inst->readNextPC() !=
        (inst->readPC() + sizeof(MachInst));
}

template<class Impl>
void
SimpleIEW<Impl>::squashDueToMem(DynInstPtr &inst)
{
    DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n",
            inst->PC);
    // Perhaps leave the squashing up to the ROB stage to tell it when to
    // squash?
    _status = Squashing;

    // Tell rename to squash through the time buffer.
    toCommit->squash = true;
    // Also send PC update information back to prior stages.
    toCommit->squashedSeqNum = inst->seqNum;
    toCommit->nextPC = inst->readNextPC();
}

template<class Impl>
void
SimpleIEW<Impl>::block()
{
    DPRINTF(IEW, "IEW: 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(*fromRename);

    // 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
SimpleIEW<Impl>::unblock()
{
    // Check if there's information in the skid buffer.  If there is, then
    // set status to unblocking, otherwise set it directly to running.
    DPRINTF(IEW, "IEW: Reading instructions out of the 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()) {
        toRename->iewInfo.stall = true;
    } else {
        DPRINTF(IEW, "IEW: Stage is done unblocking.\n");
        _status = Running;
    }
}

template<class Impl>
void
SimpleIEW<Impl>::wakeDependents(DynInstPtr &inst)
{
    instQueue.wakeDependents(inst);
}


template<class Impl>
void
SimpleIEW<Impl>::instToCommit(DynInstPtr &inst)
{

}

template <class Impl>
void
SimpleIEW<Impl>::dispatchInsts()
{
    ////////////////////////////////////////
    // DISPATCH/ISSUE stage
    ////////////////////////////////////////

    //Put into its own function?
    //Add instructions to IQ if there are any instructions there

    // Check if there are any instructions coming from rename, and we're.
    // not squashing.
    if (fromRename->size > 0) {
        int insts_to_add = fromRename->size;

        // Loop through the instructions, putting them in the instruction
        // queue.
        for (int inst_num = 0; inst_num < insts_to_add; ++inst_num)
        {
            DynInstPtr inst = fromRename->insts[inst_num];

            // Make sure there's a valid instruction there.
            assert(inst);

            DPRINTF(IEW, "IEW: Issue: Adding PC %#x to IQ.\n",
                    inst->readPC());

            // Be sure to mark these instructions as ready so that the
            // commit stage can go ahead and execute them, and mark
            // them as issued so the IQ doesn't reprocess them.
            if (inst->isSquashed()) {
                ++iewDispSquashedInsts;
                continue;
            } else if (instQueue.isFull()) {
                DPRINTF(IEW, "IEW: Issue: IQ has become full.\n");
                // Call function to start blocking.
                block();
                // Tell previous stage to stall.
                toRename->iewInfo.stall = true;

                ++iewIQFullEvents;
                break;
            } else if (inst->isLoad()) {
                DPRINTF(IEW, "IEW: Issue: Memory instruction "
                        "encountered, adding to LDSTQ.\n");

                // Reserve a spot in the load store queue for this
                // memory access.
                ldstQueue.insertLoad(inst);

                ++iewDispLoadInsts;
            } else if (inst->isStore()) {
                ldstQueue.insertStore(inst);

                ++iewDispStoreInsts;
            } else if (inst->isNonSpeculative()) {
                DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction "
                        "encountered, skipping.\n");

                // Same hack as with stores.
                inst->setCanCommit();

                // Specificall insert it as nonspeculative.
                instQueue.insertNonSpec(inst);

                ++iewDispNonSpecInsts;

                continue;
            } else if (inst->isNop()) {
                DPRINTF(IEW, "IEW: Issue: Nop instruction encountered "
                        ", skipping.\n");

                inst->setIssued();
                inst->setExecuted();
                inst->setCanCommit();

                instQueue.advanceTail(inst);

                continue;
            } else if (inst->isExecuted()) {
                assert(0 && "Instruction shouldn't be executed.\n");
                DPRINTF(IEW, "IEW: Issue: Executed branch encountered, "
                        "skipping.\n");

                inst->setIssued();
                inst->setCanCommit();

                instQueue.advanceTail(inst);

                continue;
            }

            // If the instruction queue is not full, then add the
            // instruction.
            instQueue.insert(fromRename->insts[inst_num]);

            ++iewDispatchedInsts;
        }
    }
}

template <class Impl>
void
SimpleIEW<Impl>::executeInsts()
{
    ////////////////////////////////////////
    //EXECUTE/WRITEBACK stage
    ////////////////////////////////////////

    //Put into its own function?
    //Similarly should probably have separate execution for int vs FP.
    // Above comment is handled by the issue queue only issuing a valid
    // mix of int/fp instructions.
    //Actually okay to just have one execution, buuuuuut will need
    //somewhere that defines the execution latency of all instructions.
    // @todo: Move to the FU pool used in the current full cpu.

    int fu_usage = 0;
    bool fetch_redirect = false;
    int inst_slot = 0;
    int time_slot = 0;

    // Execute/writeback any instructions that are available.
    for (int inst_num = 0;
         fu_usage < executeWidth && /* Haven't exceeded available FU's. */
             inst_num < issueWidth &&
             fromIssue->insts[inst_num];
         ++inst_num) {

        DPRINTF(IEW, "IEW: Execute: Executing instructions from IQ.\n");

        // Get instruction from issue's queue.
        DynInstPtr inst = fromIssue->insts[inst_num];

        DPRINTF(IEW, "IEW: Execute: Processing PC %#x.\n", inst->readPC());

        // Check if the instruction is squashed; if so then skip it
        // and don't count it towards the FU usage.
        if (inst->isSquashed()) {
            DPRINTF(IEW, "IEW: Execute: Instruction was squashed.\n");

            // Consider this instruction executed so that commit can go
            // ahead and retire the instruction.
            inst->setExecuted();

            toCommit->insts[inst_num] = inst;

            ++iewExecSquashedInsts;

            continue;
        }

        inst->setExecuted();

        // If an instruction is executed, then count it towards FU usage.
        ++fu_usage;

        // Execute instruction.
        // Note that if the instruction faults, it will be handled
        // at the commit stage.
        if (inst->isMemRef()) {
            DPRINTF(IEW, "IEW: Execute: Calculating address for memory "
                    "reference.\n");

            // Tell the LDSTQ to execute this instruction (if it is a load).
            if (inst->isLoad()) {
                ldstQueue.executeLoad(inst);

                ++iewExecLoadInsts;
            } else if (inst->isStore()) {
                ldstQueue.executeStore(inst);

                ++iewExecStoreInsts;
            } else {
                panic("IEW: Unexpected memory type!\n");
            }

        } else {
            inst->execute();

            ++iewExecutedInsts;
        }

        // First check the time slot that this instruction will write
        // to.  If there are free write ports at the time, then go ahead
        // and write the instruction to that time.  If there are not,
        // keep looking back to see where's the first time there's a
        // free slot.  What happens if you run out of free spaces?
        // For now naively assume that all instructions take one cycle.
        // Otherwise would have to look into the time buffer based on the
        // latency of the instruction.
        (*iewQueue)[time_slot].insts[inst_slot];
        while ((*iewQueue)[time_slot].insts[inst_slot]) {
            if (inst_slot < issueWidth) {
                ++inst_slot;
            } else {
                ++time_slot;
                inst_slot = 0;
            }

            assert(time_slot < 5);
        }

        // May actually have to work this out, especially with loads and stores

        // Add finished instruction to queue to commit.
        (*iewQueue)[time_slot].insts[inst_slot] = inst;
        (*iewQueue)[time_slot].size++;

        // Check if branch was correct.  This check happens after the
        // instruction is added to the queue because even if the branch
        // is mispredicted, the branch instruction itself is still valid.
        // Only handle this if there hasn't already been something that
        // redirects fetch in this group of instructions.
        if (!fetch_redirect) {
            if (inst->mispredicted()) {
                fetch_redirect = true;

                DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n");
                DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n",
                        inst->nextPC);

                // If incorrect, then signal the ROB that it must be squashed.
                squashDueToBranch(inst);

                if (inst->predTaken()) {
                    predictedTakenIncorrect++;
                }
            } else if (ldstQueue.violation()) {
                fetch_redirect = true;

                // Get the DynInst that caused the violation.
                DynInstPtr violator = ldstQueue.getMemDepViolator();

                DPRINTF(IEW, "IEW: LDSTQ detected a violation.  Violator PC: "
                        "%#x, inst PC: %#x.  Addr is: %#x.\n",
                        violator->readPC(), inst->readPC(), inst->physEffAddr);

                // Tell the instruction queue that a violation has occured.
                instQueue.violation(inst, violator);

                // Squash.
                squashDueToMem(inst);

                ++memOrderViolationEvents;
            }
        }
    }
}

template<class Impl>
void
SimpleIEW<Impl>::tick()
{
    // Considering putting all the state-determining stuff in this section.

    // Try to fill up issue queue with as many instructions as bandwidth
    // allows.
    // Decode should try to execute as many instructions as its bandwidth
    // will allow, as long as it is not currently blocked.

    // Check if the stage is in a running status.
    if (_status != Blocked && _status != Squashing) {
        DPRINTF(IEW, "IEW: Status is not blocked, attempting to run "
                     "stage.\n");
        iew();

        // If it's currently unblocking, check to see if it should switch
        // to running.
        if (_status == Unblocking) {
            unblock();

            ++iewUnblockCycles;
        }
    } else if (_status == Squashing) {

        DPRINTF(IEW, "IEW: Still squashing.\n");

        // Check if stage should remain squashing.  Stop squashing if the
        // squash signal clears.
        if (!fromCommit->commitInfo.squash &&
            !fromCommit->commitInfo.robSquashing) {
            DPRINTF(IEW, "IEW: Done squashing, changing status to "
                    "running.\n");

            _status = Running;
            instQueue.stopSquash();
        } else {
            instQueue.doSquash();
        }

        ++iewSquashCycles;
    } else if (_status == Blocked) {
        // Continue to tell previous stage to stall.
        toRename->iewInfo.stall = true;

        // Check if possible stall conditions have cleared.
        if (!fromCommit->commitInfo.stall &&
            !instQueue.isFull()) {
            DPRINTF(IEW, "IEW: Stall signals cleared, going to unblock.\n");
            _status = Unblocking;
        }

        // If there's still instructions coming from rename, continue to
        // put them on the skid buffer.
        if (fromRename->size == 0) {
            block();
        }

        if (fromCommit->commitInfo.squash ||
            fromCommit->commitInfo.robSquashing) {
            squash();
        }

        ++iewBlockCycles;
    }

    // @todo: Maybe put these at the beginning, so if it's idle it can
    // return early.
    // Write back number of free IQ entries here.
    toRename->iewInfo.freeIQEntries = instQueue.numFreeEntries();

    ldstQueue.writebackStores();

    // Check the committed load/store signals to see if there's a load
    // or store to commit.  Also check if it's being told to execute a
    // nonspeculative instruction.
    // This is pretty inefficient...
    if (!fromCommit->commitInfo.squash &&
        !fromCommit->commitInfo.robSquashing) {
        ldstQueue.commitStores(fromCommit->commitInfo.doneSeqNum);
        ldstQueue.commitLoads(fromCommit->commitInfo.doneSeqNum);
    }

    if (fromCommit->commitInfo.nonSpecSeqNum != 0) {
        instQueue.scheduleNonSpec(fromCommit->commitInfo.nonSpecSeqNum);
    }

    DPRINTF(IEW, "IEW: IQ has %i free entries.\n",
            instQueue.numFreeEntries());
}

template<class Impl>
void
SimpleIEW<Impl>::iew()
{
    // Might want to put all state checks in the tick() function.
    // Check if being told to stall from commit.
    if (fromCommit->commitInfo.stall) {
        block();
        return;
    } else if (fromCommit->commitInfo.squash ||
               fromCommit->commitInfo.robSquashing) {
        // Also check if commit is telling this stage to squash.
        squash();
        return;
    }

    dispatchInsts();

    // Have the instruction queue try to schedule any ready instructions.
    instQueue.scheduleReadyInsts();

    executeInsts();

    // Loop through the head of the time buffer and wake any dependents.
    // These instructions are about to write back.  In the simple model
    // this loop can really happen within the previous loop, but when
    // instructions have actual latencies, this loop must be separate.
    // Also mark scoreboard that this instruction is finally complete.
    // Either have IEW have direct access to rename map, or have this as
    // part of backwards communication.
    for (int inst_num = 0; inst_num < issueWidth &&
             toCommit->insts[inst_num]; inst_num++)
    {
        DynInstPtr inst = toCommit->insts[inst_num];

        DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n",
                inst->readPC());

        if(!inst->isSquashed()) {
            instQueue.wakeDependents(inst);

            for (int i = 0; i < inst->numDestRegs(); i++)
            {
                renameMap->markAsReady(inst->renamedDestRegIdx(i));
            }
        }
    }

    // Also should advance its own time buffers if the stage ran.
    // Not the best place for it, but this works (hopefully).
    issueToExecQueue.advance();
}

#if !FULL_SYSTEM
template<class Impl>
void
SimpleIEW<Impl>::lsqWriteback()
{
    ldstQueue.writebackAllInsts();
}
#endif