/*
 * Copyright (c) 2005
 * The Regents of Michigan Technological University
 * All Rights Reserved
 *
 * This code is part of the M5 simulator, developed by Jun Shao.
 * This work is supported by NSF CAREER Award CCR - 0133777.
 *
 * Permission is granted to use, copy, create derivative works and
 * redistribute this software and such derivative works for any
 * purpose, so long as the copyright notice above, this grant of
 * permission, and the disclaimer below appear in all copies made; and
 * so long as the name of The University of Michigan is not used in
 * any advertising or publicity pertaining to the use or distribution
 * of this software without specific, written prior authorization.
 *
 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE
 * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND
 * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
 * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT,
 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGES.
 */

/**
 * @file
 * Declarations of an SDRAM controller object.
 */

#ifndef __SDRAM_CTRL_HH__
#define __SDRAM_CTRL_HH__
 
#include "sim/faults.hh"
#include "mem/physical.hh"
#include "mem/request.hh"
#include "mem/packet.hh"
#include "sim/eventq.hh"
#include "mem/sdram/mem_access.hh"
#include "mem/sdram/am/base_am.hh"
#include "mem/sdram/addr_convert/addr_translation.hh" 
#include "mem/sdram/addr_convert/timing_TLB.hh" 

#define DELT_FREQUENCY			200000000
#define DFLT_ACCESS_POOL_SIZE		256
#define DFLT_WRITE_QUEUE_SIZE		64

// forward declarations
class TimingTLB;
class TimingITB;
class TimingDTB;
class AddrTranslate;
class BaseAM;
class BaseMAQ;
class SdramCtrl;
class SdramDev;
class WriteBuf;

/**
* Defines a memory tick event
*/
class MemTickEvent : public Event
{
  private:
    SdramCtrl *sdramCtrl;	// pointer to the SDRAM controller

  public:
    /**
     * Construct and initialize a memory tick event.
     * @param sdram_ctrl Pointer to the SDRAM controller that
     *                   this event is belong to.
     */
    MemTickEvent(SdramCtrl *sdram_ctrl);

    /**
     * This member function is invoked when the event occurs.
     */
    void process();

    /**
     * Return the description of this event
     * @return The decription of this event
     */
    virtual const char *description();
};

/**
 * An SDRAM controller
 */
class SdramCtrl : public SimObject 
{
  public:
    /**
     * collection of parameters for the SDRAM controller
     */
    class Params {
      public:
				bool verbose;
				Tick frequency;
				TimingITB *timing_itb;
				TimingDTB *timing_dtb;
				AddrTranslate *addr_convert;
				BaseAM *addr_map;
				SdramDev *sdram_dev;
				WriteBuf *wb;
				std::vector<BaseMAQ *> maq;
				std::vector<Range<Addr> > addr_ranges;
				int access_pool_size;
				int write_queue_size;
				/** for collecting statistics */
				int stat_read_lat_cap;
				int stat_read_lat_bkt;
				int stat_write_lat_cap;
				int stat_write_lat_bkt;
    };

  protected:
    /** Enable or disable verbose mode */
    bool verbose;
    /** Memory clock frequency */
    Tick frequency;
    /** Multiplier between CPU clock and memory clock */
    int multiplier;
    /** Pointer to the timing TLB **/
    TimingITB *timingITB;
    TimingDTB *timingDTB;
    /** Pointer to the address converter */
    AddrTranslate *addrConvert;
    /** Pointer to the SDRAM address mapping */
    BaseAM *addrMap;
    /** Pointer to the SDRAM device */
    SdramDev *sdramDev;
    /** Pointer to the write buffer */
    WriteBuf *wb;
    /** List of memory access queues */
    std::vector<BaseMAQ *> maqList;
    
    /** Address ranges */
    const std::vector<Range<Addr> > addrRanges;

    /** Memory access pool size */
    const int poolSize;
    /** Write queue size */
    const int writeQueueSize;
    /** Read latency statistics cap */
    const int statReadLatCap;
    /** Read latency statistics basket size */
    const int statReadLatBkt;
    /** Write latency statistics cap */
    const int statWriteLatCap;
    /** Write latency statistics basket size */
    const int statWriteLatBkt;
    int statAccessLatCap;
    int statAccessLatBkt;

    /** Memory access pool */
    MemAccess *pool;
    /** List of free memory access pool entries */
    AccessList freeList;
    /** Complete memory accesses list */
    AccessList completeList;

    int outstandingReads;
    int outstandingWrites;

    /** Unique memory access ID */
    int accessId;
    /** For counting out-of-order access*/
    int lastAccessId;
    /** For counting address bit changes */
    Addr lastAddr;

    /** Memory tick event */
    MemTickEvent memTickEvent;

  protected:
    //! Static global pointer to SDRAM controller
    static SdramCtrl *gSdramCtrlPtr;

    // statistics

    /** Frequency of simulated memory tick */
    Stats::Value statMemFreq;
    /** Clock multiplier between cpu and memory tick */
    Stats::Value statMemMultiplier;

    /** Received memory accesses breakout */
    Stats::Vector<> statAccessReceived;
    /** Completed memory accesses breakout */
    Stats::Vector<> statAccessCompleted;
    /** Total received memory accesses */
    Stats::Formula statAccessTotalReceived;
    /** Total completed memory accesses */
    Stats::Formula statAccessTotalCompleted;
    /** Average row hit/conflict/empty */
    Stats::Vector<> statRowStatus;
    /** Total row hit rate */
    Stats::Formula statRowHitRate;
    /** Average row conflict rate */
    Stats::Formula statRowConflictRate;
    /** Average row miss rate */
    Stats::Formula statRowEmptyRate;
    /** The number of out of order memory accesses */
    Stats::Scalar<> statAccessOutOrder;
    /** Out of order access rate */
    Stats::Formula statAccessOutOrderRate;

    /** The overall latency of each type of memory accesses */
    Stats::Vector<> statLatency;
    /** The average latency of each type of memory accesses */
    Stats::Formula statAverageLatency[MEM_ACCESS_NUM];
    /** The average latency of all memory accesses */
    Stats::Formula statOverallLatency;

    /** The distribution of memory access latency */
    Stats::Distribution<> statLatencyDist;
    /** The distribution of read latency */
    Stats::Distribution<> statReadLatencyDist;
    /** The distribution of write latency */
    Stats::Distribution<> statWriteLatencyDist;
    
    /** Memory access latency breakout */
    Stats::Vector<> statLatencyBreakout;

    /** The probability of change of each physical address bits */
    Stats::Distribution<> statBitChangeDist;

    // power consumption statistics

    /** Prechared standby power consumption */
    Stats::Value statPowerPrechargedStandby;
    /** Activate standby power consumption */
    Stats::Value statPowerActiveStandby;
    /** Refresh power consumption */
    Stats::Value statPowerRefresh;
    /** Activate power consumption */
    Stats::Value statPowerActivate;
    /** Write power consumption */
    Stats::Value statPowerWrite;
    /** Read power consumption */
    Stats::Value statPowerRead;
    /** DQ power consumption */
    Stats::Value statPowerDQ;
    /** Termination power consumption */
    Stats::Value statPowerTermination;

    /** Average power consumed by the memory */
    Stats::Formula statAvePower;
    /** Total energy consumed by the memory */
    Stats::Formula statEnergy;

    /** Access pool occupancy */
    Stats::Distribution<> statAccessPoolOccuDist;
    /** Read */
    Stats::Distribution<> statOutstandingReadDist;
    /** Write */
    Stats::Distribution<> statOutstandingWriteDist;
    
  public:
    /**
     * Construct and initialize an SDRAM controller
     * @param name The name of this SDRAM controller
     * @param params Parameters of this SDRAM controler
     */
    SdramCtrl(const std::string &name, Params &params);

    /**
     * Deconstruct an SDRAM controller
     */
    ~SdramCtrl();

    /**
     * Register statistics.
     */
    virtual void regStats();

    /**
     * Calculate averaged prechared standby power consumption
     * @return Prechared standby power consumption
     */
    static double getPowerPrechargedStandby();

    /**
     * Calculate averaged activate standby power consumption
     * @return Activate standby power consumption
     */
    static double getPowerActiveStandby();

    /**
     * Calculate averaged refresh power consumption
     * @return Refresh power consumption
     */
    static double getPowerRefresh();

    /**
     * Calculate averaged activate power consumption
     * @return Activate power consumption
     */
    static double getPowerActivate();

    /**
     * Calcuate averaged write power consumption
     * @return Write power consumption
     */
    static double getPowerWrite();

    /**
     * Calculate averaged read power consumption
     * @return Read power consumption
     */
    static double getPowerRead();

    /**
     * Calculate averaged DQ power consumption
     * @return DQ power consumption
     */
    static double getPowerDQ();

    /**
     * Calculate Termination power consumption
     * @return Termination power consumption
     */
    static double getPowerTermination();

    
    /**
     * Mark the memory interface as blocked.
     */
    void setBlocked();

    /**
     * Mark the memory interface as unblocked.
     */
    void clearBlocked();

    /**
     * Called on each memory tick
     */
    void tick();

    /**
     * Perform a memory access on the SDRAM channel
     * @param req Pointer to the memory request
     * @param now The current memory tick
     */
    MemAccessResult access(PacketPtr pkt);

    /**
     * Send a memory access to the memory access queue
     * @param access The memory access
     * @param now The current memory tick
     */
    void enterMAQ(MemAccess *access, Tick now);

    /**
     * Check if the target bank of the memory access is idle.
     * A bank is considred idle when there is no access to
     * this bank waiting in the memory access queue.
     * @param access The memory access
     * @return True when the target bank is idle
     */
    bool isTargetBankIdle(MemAccess *access);

    /**
     * Send responce to a memory access. If the memory access
     * has been scheduled, move it to the complete list.
     * @param access Pointer to the memory access
     * @param scheduled If the memory access has been scheduled
     */
    void respond(MemAccess *access, bool scheduled);

    inline int getAccessPoolSize()
    {
    	return poolSize;
    }
    
    /**
     * Dump the contains of complete list.
     * @param out The output stream.
     */
    void dump(std::ostream &out);

  private:
    /**
     * Allocate a memory access in memmory access pool.
     * @param req The pointer to the memory request.
     * @return The pointer to the allocated memory access.
     */
    MemAccess* allocateMemAccess(PacketPtr pkt);

    /**
     * Deallocate a memory access. Return the freed memory access
     * to the memory access pool
     * @param access The pointer to the memory access to be freed.
     */
    void deallocateMemAccess(MemAccess* access);

  public:
    // dummy implementations
    // TODO not supported yet

    Tick probe(PacketPtr &pkt, bool update)
    {
			fatal("probe not implemented yet");
			return 0;
    }

    int getBlockSize() const
    {
			fatal("getBlockSize not implemented yet");
			return 0;
    }

    void squash(int thread_num)
    {
	fatal("squash not implemented yet");
    }

    int outstandingMisses() const
    {
			fatal("outstandingMisses not implemented yet");
			return 0;
    }

    PacketPtr getCoherenceReq()
    {
			fatal("getCoherenceReq not implemented yet");
			return NULL;
    }
    

    int getHitLatency()
    {
			fatal("getHitLatency not implemented yet");
			return 0;
    }
};

#endif // __SDRAM_CTRL_HH__
