/*
 * Copyright (c) 2007 MIPS Technologies, Inc.
 * 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.
 *
 * Authors: Korey Sewell
 *
 */

#ifndef __CPU_INORDER_PIPELINE_STAGE_HH__
#define __CPU_INORDER_PIPELINE_STAGE_HH__

#include <queue>
#include <vector>

#include "base/statistics.hh"
#include "cpu/inorder/comm.hh"
#include "cpu/inorder/inorder_dyn_inst.hh"
#include "cpu/inorder/pipeline_traits.hh"
#include "cpu/timebuf.hh"
#include "params/InOrderCPU.hh"

class InOrderCPU;

class PipelineStage
{
  protected:
    typedef ThePipeline::Params Params;
    typedef ThePipeline::DynInstPtr DynInstPtr;

  public:
    /** Overall stage status. Used to determine if the CPU can
     * deschedule itself due to a lack of activity.
     */
    enum StageStatus {
        Active,
        Inactive
    };

    /** Individual thread status. */
    enum ThreadStatus {
        Running,
        Idle,
        StartSquash,
        Squashing,
        Blocked,
        Unblocking,
        MemWaitResponse,
        MemWaitRetry,
        MemAccessComplete
    };

  protected:
    /** The Number of This Pipeline Stage */
    unsigned stageNum;

    /** The width of stage, in instructions. */
    unsigned stageWidth;

    /** Number of Threads*/
    ThreadID numThreads;

    /** Stage status. */
    StageStatus _status;

    /** Per-thread status. */
    ThreadStatus stageStatus[ThePipeline::MaxThreads];

  public:
    PipelineStage(Params *params, unsigned stage_num);

    virtual ~PipelineStage();

    /** PipelineStage initialization. */
    void init(Params *params);

    /** Returns the name of stage. */
    std::string name() const;

    /** Registers statistics. */
    void regStats();

    /** Sets CPU pointer. */
    void setCPU(InOrderCPU *cpu_ptr);

    /** Sets the main backwards communication time buffer pointer. */
    void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr);

    /** Sets pointer to time buffer coming from fetch. */
    void setPrevStageQueue(TimeBuffer<InterStageStruct> *prev_stage_ptr);

    /** Sets pointer to time buffer used to communicate to the next stage. */
    void setNextStageQueue(TimeBuffer<InterStageStruct> *next_stage_ptr);

    /** Sets pointer to list of active threads. */
    void setActiveThreads(std::list<ThreadID> *at_ptr);

    bool nextStageQueueValid(int stage_num);

    bool isBlocked(ThreadID tid);

    /** Changes the status of this stage to active, and indicates this
     * to the CPU.
     */
    //inline void switchToActive();

    /** Changes the status of this stage to inactive, and indicates
     * this to the CPU.
     */
    //inline void switchToInactive();

    /** Switches out the stage stage. */
    void switchOut();

    /** Takes over from another CPU's thread. */
    void takeOverFrom();

    /** Ticks stage, processing all input signals and executing as many
     *  instructions as possible.
     */
    void tick();

    /** Set a resource stall in the pipeline-stage */
    void setResStall(ResReqPtr res_req, ThreadID tid);

    /** Unset a resource stall in the pipeline-stage */
    void unsetResStall(ResReqPtr res_req, ThreadID tid);

    /** Remove all stall signals for a particular thread; */
    void removeStalls(ThreadID tid);

    /** Is there room in the stage buffer? */
    int stageBufferAvail();

  protected:
    /** Evaluate Stage Conditions and then process stage */
    virtual void processStage(bool &status_change);

    /** Determines what to do based on stage's current status.
     * @param status_change stage() sets this variable if there was a status
     * change (ie switching from from blocking to unblocking).
     * @param tid Thread id to stage instructions from.
     */
    void processThread(bool &status_change, ThreadID tid);

    /** Processes instructions from fetch and passes them on to rename.
     * Decoding of instructions actually happens when they are created in
     * fetch, so this function mostly checks if PC-relative branches are
     * correct.
     */
    virtual void processInsts(ThreadID tid);

    /** Process all resources on an instruction's resource schedule */
    bool processInstSchedule(DynInstPtr inst, int &reqs_processed);

    /** Is there room in the next stage buffer for this instruction? */
    bool canSendInstToStage(unsigned stage_num);

    /** Send an instruction to the next stage buffer */
    bool sendInstToNextStage(DynInstPtr inst);

    /** Total size of all skid buffers */
    int skidSize();

    /** Returns if all of the skid buffers are empty. */
    bool skidsEmpty();

    /** Updates overall stage status based on all of the threads' statuses. */
    void updateStatus();

    /** Separates instructions from fetch into individual lists of instructions
     * sorted by thread.
     */
    void sortInsts();

    /** Reads all stall signals from the backwards communication timebuffer. */
    void readStallSignals(ThreadID tid);

    /** Checks all input signals and updates stage's status appropriately. */
    bool checkSignalsAndUpdate(ThreadID tid);

    /** Checks all stall signals, and returns if any are true. */
    bool checkStall(ThreadID tid) const;

    /** Returns if there any instructions from the previous stage
     * on this cycle.
     */
    inline bool prevStageInstsValid();

    /** Switches stage to blocking, and signals back that stage has
     * become blocked.
     * @return Returns true if there is a status change.
     */
    bool block(ThreadID tid);

    void blockDueToBuffer(ThreadID tid);

    /** Switches stage to unblocking if the skid buffer is empty, and
     * signals back that stage has unblocked.
     * @return Returns true if there is a status change.
     */
    bool unblock(ThreadID tid);


  public:
    void activateThread(ThreadID tid);
    
    /** Squashes if there is a PC-relative branch that was predicted
     * incorrectly. Sends squash information back to fetch.
     */
    void squashDueToBranch(DynInstPtr &inst, ThreadID tid);

    virtual void squashDueToMemStall(InstSeqNum seq_num, ThreadID tid);

    /** Squash instructions from stage buffer  */
    void squashPrevStageInsts(InstSeqNum squash_seq_num, ThreadID tid);

    /** Squashes due to commit signalling a squash. Changes status to
     *  squashing and clears block/unblock signals as needed.
     */
    virtual void squash(InstSeqNum squash_num, ThreadID tid);

    void dumpInsts();

  protected:
    /** CPU interface. */
    InOrderCPU *cpu;

    Trace::InOrderTrace *tracer;

    /** List of active thread ids */
    std::list<ThreadID> *activeThreads;

    /** Buffer of instructions switched out to mem-stall. 
     *  Only used when using SwitchOnCacheMiss threading model
     *  Used as 1-to-1 mapping between ThreadID and Entry. 
     */
    std::vector<DynInstPtr> switchedOutBuffer;
    std::vector<bool> switchedOutValid;

    /** Instructions that we've processed this tick
     *  NOTE: "Processed" means completed at least 1 instruction request 
     */
    unsigned instsProcessed;    

    /** Skid buffer between previous stage and this one. */
    std::list<DynInstPtr> skidBuffer[ThePipeline::MaxThreads];

    /** Instruction used to signify that there is no *real* instruction in
     *  buffer slot */
    DynInstPtr dummyBufferInst;

    /** SeqNum of Squashing Branch Delay Instruction (used for MIPS) */
    Addr bdelayDoneSeqNum[ThePipeline::MaxThreads];

    /** Tells when their is a pending delay slot inst. to send
     *  to rename. If there is, then wait squash after the next
     *  instruction (used for MIPS).
     */
    bool squashAfterDelaySlot[ThePipeline::MaxThreads];

    /** Instruction used for squashing branch (used for MIPS) */
    DynInstPtr squashInst[ThePipeline::MaxThreads];

    /** Maximum size of the inter-stage buffer connecting the previous stage to
     *  this stage (which we call a skid buffer) */
    unsigned stageBufferMax;

    /** Variable that tracks if stage has written to the time buffer this
     * cycle. Used to tell CPU if there is activity this cycle.
     */
    bool wroteToTimeBuffer;

    /** Index of instructions being sent to the next stage. */
    unsigned toNextStageIndex;

    /** The last stage that this particular stage should look for stalls */
    int lastStallingStage[ThePipeline::MaxThreads];

    /** Time buffer interface. */
    TimeBuffer<TimeStruct> *timeBuffer;

  public:
    /** Wire to get rename's output from backwards time buffer. */
    TimeBuffer<TimeStruct>::wire fromNextStages;

    /** Wire to get iew's information from backwards time buffer. */
    TimeBuffer<TimeStruct>::wire toPrevStages;

    /** Instruction queue linking previous stage */
    TimeBuffer<InterStageStruct> *prevStageQueue;

    /** Wire to get the previous stage's. */
    TimeBuffer<InterStageStruct>::wire prevStage;

    /** Instruction queue linking next stage */
    TimeBuffer<InterStageStruct> *nextStageQueue;

    /** Wire to write to the next stage */
    TimeBuffer<InterStageStruct>::wire nextStage;

    /** Is Previous Stage Valid? */
    bool prevStageValid;

    /** Is Next Stage Valid? */
    bool nextStageValid;

    bool idle;
    
    /** Source of possible stalls. */
    struct Stalls {
        bool stage[ThePipeline::NumStages];
        std::vector<ResReqPtr> resources;
    };

    /** Tracks stage/resource stalls */
    Stalls stalls[ThePipeline::MaxThreads];

    /** Number of cycles 0 instruction(s) are processed. */
    Stats::Scalar idleCycles;

    /** Number of cycles 1+ instructions are processed. */
    Stats::Scalar runCycles;

    /** Percentage of cycles 1+ instructions are processed. */
    Stats::Formula utilization;


};

#endif
