mem: Clean up Request initialisation
[gem5.git] / src / mem / dram_ctrl.hh
index 8f2e4825ed6d5699e9ff5410e30a5fe312001814..3aa06feac5608b0364f6cd82cb4f0a73023ab9fd 100644 (file)
@@ -40,6 +40,7 @@
  * Authors: Andreas Hansson
  *          Ani Udipi
  *          Neha Agarwal
+ *          Omar Naji
  */
 
 /**
@@ -51,6 +52,7 @@
 #define __MEM_DRAM_CTRL_HH__
 
 #include <deque>
+#include <string>
 
 #include "base/statistics.hh"
 #include "enums/AddrMap.hh"
 #include "mem/qport.hh"
 #include "params/DRAMCtrl.hh"
 #include "sim/eventq.hh"
+#include "mem/drampower.hh"
 
 /**
- * The DRAM controller is a basic single-channel memory controller
- * aiming to mimic a high-level DRAM controller and the most important
- * timing constraints associated with the DRAM. The focus is really on
- * modelling the impact on the system rather than the DRAM itself,
- * hence the focus is on the controller model and not on the
- * memory. By adhering to the correct timing constraints, ultimately
- * there is no need for a memory model in addition to the controller
- * model.
+ * The DRAM controller is a single-channel memory controller capturing
+ * the most important timing constraints associated with a
+ * contemporary DRAM. For multi-channel memory systems, the controller
+ * is combined with a crossbar model, with the channel address
+ * interleaving taking part in the crossbar.
  *
- * As a basic design principle, this controller is not cycle callable,
- * but instead uses events to decide when new decisions can be made,
- * when resources become available, when things are to be considered
- * done, and when to send things back. Through these simple
- * principles, we achieve a performant model that is not
- * cycle-accurate, but enables us to evaluate the system impact of a
- * wide range of memory technologies, and also collect statistics
- * about the use of the memory.
+ * As a basic design principle, this controller
+ * model is not cycle callable, but instead uses events to: 1) decide
+ * when new decisions can be made, 2) when resources become available,
+ * 3) when things are to be considered done, and 4) when to send
+ * things back. Through these simple principles, the model delivers
+ * high performance, and lots of flexibility, allowing users to
+ * evaluate the system impact of a wide range of memory technologies,
+ * such as DDR3/4, LPDDR2/3/4, WideIO1/2, HBM and HMC.
+ *
+ * For more details, please see Hansson et al, "Simulating DRAM
+ * controllers for future system architecture exploration",
+ * Proc. ISPASS, 2014. If you use this model as part of your research
+ * please cite the paper.
  */
 class DRAMCtrl : public AbstractMemory
 {
@@ -116,15 +121,15 @@ class DRAMCtrl : public AbstractMemory
     MemoryPort port;
 
     /**
-     * Remember if we have to retry a request when available.
+     * Remeber if the memory system is in timing mode
      */
-    bool retryRdReq;
-    bool retryWrReq;
+    bool isTimingMode;
 
     /**
-     * Remember that a row buffer hit occured
+     * Remember if we have to retry a request when available.
      */
-    bool rowHitFlag;
+    bool retryRdReq;
+    bool retryWrReq;
 
     /**
      * Bus state used to control the read/write switching and drive
@@ -139,37 +144,247 @@ class DRAMCtrl : public AbstractMemory
 
     BusState busState;
 
-    /** List to keep track of activate ticks */
-    std::vector<std::deque<Tick>> actTicks;
-
     /**
-     * A basic class to track the bank state indirectly via times
-     * "freeAt" and "tRASDoneAt" and what page is currently open. The
-     * bank also keeps track of how many bytes have been accessed in
-     * the open row since it was opened.
+     * A basic class to track the bank state, i.e. what row is
+     * currently open (if any), when is the bank free to accept a new
+     * column (read/write) command, when can it be precharged, and
+     * when can it be activated.
+     *
+     * The bank also keeps track of how many bytes have been accessed
+     * in the open row since it was opened.
      */
     class Bank
     {
 
       public:
 
-        static const uint32_t INVALID_ROW = -1;
+        static const uint32_t NO_ROW = -1;
 
         uint32_t openRow;
+        uint8_t bank;
+        uint8_t bankgr;
 
-        Tick freeAt;
-        Tick tRASDoneAt;
+        Tick colAllowedAt;
+        Tick preAllowedAt;
         Tick actAllowedAt;
 
         uint32_t rowAccesses;
         uint32_t bytesAccessed;
 
         Bank() :
-            openRow(INVALID_ROW), freeAt(0), tRASDoneAt(0), actAllowedAt(0),
+            openRow(NO_ROW), bank(0), bankgr(0),
+            colAllowedAt(0), preAllowedAt(0), actAllowedAt(0),
             rowAccesses(0), bytesAccessed(0)
         { }
     };
 
+
+    /**
+     * Rank class includes a vector of banks. Refresh and Power state
+     * machines are defined per rank. Events required to change the
+     * state of the refresh and power state machine are scheduled per
+     * rank. This class allows the implementation of rank-wise refresh
+     * and rank-wise power-down.
+     */
+    class Rank : public EventManager
+    {
+
+      private:
+
+        /**
+         * The power state captures the different operational states of
+         * the DRAM and interacts with the bus read/write state machine,
+         * and the refresh state machine. In the idle state all banks are
+         * precharged. From there we either go to an auto refresh (as
+         * determined by the refresh state machine), or to a precharge
+         * power down mode. From idle the memory can also go to the active
+         * state (with one or more banks active), and in turn from there
+         * to active power down. At the moment we do not capture the deep
+         * power down and self-refresh state.
+         */
+        enum PowerState {
+            PWR_IDLE = 0,
+            PWR_REF,
+            PWR_PRE_PDN,
+            PWR_ACT,
+            PWR_ACT_PDN
+        };
+
+        /**
+         * The refresh state is used to control the progress of the
+         * refresh scheduling. When normal operation is in progress the
+         * refresh state is idle. From there, it progresses to the refresh
+         * drain state once tREFI has passed. The refresh drain state
+         * captures the DRAM row active state, as it will stay there until
+         * all ongoing accesses complete. Thereafter all banks are
+         * precharged, and lastly, the DRAM is refreshed.
+         */
+        enum RefreshState {
+            REF_IDLE = 0,
+            REF_DRAIN,
+            REF_PRE,
+            REF_RUN
+        };
+
+        /**
+         * A reference to the parent DRAMCtrl instance
+         */
+        DRAMCtrl& memory;
+
+        /**
+         * Since we are taking decisions out of order, we need to keep
+         * track of what power transition is happening at what time, such
+         * that we can go back in time and change history. For example, if
+         * we precharge all banks and schedule going to the idle state, we
+         * might at a later point decide to activate a bank before the
+         * transition to idle would have taken place.
+         */
+        PowerState pwrStateTrans;
+
+        /**
+         * Current power state.
+         */
+        PowerState pwrState;
+
+        /**
+         * Track when we transitioned to the current power state
+         */
+        Tick pwrStateTick;
+
+        /**
+         * current refresh state
+         */
+        RefreshState refreshState;
+
+        /**
+         * Keep track of when a refresh is due.
+         */
+        Tick refreshDueAt;
+
+        /*
+         * Command energies
+         */
+        Stats::Scalar actEnergy;
+        Stats::Scalar preEnergy;
+        Stats::Scalar readEnergy;
+        Stats::Scalar writeEnergy;
+        Stats::Scalar refreshEnergy;
+
+        /*
+         * Active Background Energy
+         */
+        Stats::Scalar actBackEnergy;
+
+        /*
+         * Precharge Background Energy
+         */
+        Stats::Scalar preBackEnergy;
+
+        Stats::Scalar totalEnergy;
+        Stats::Scalar averagePower;
+
+        /**
+         * Track time spent in each power state.
+         */
+        Stats::Vector pwrStateTime;
+
+        /**
+         * Function to update Power Stats
+         */
+        void updatePowerStats();
+
+        /**
+         * Schedule a power state transition in the future, and
+         * potentially override an already scheduled transition.
+         *
+         * @param pwr_state Power state to transition to
+         * @param tick Tick when transition should take place
+         */
+        void schedulePowerEvent(PowerState pwr_state, Tick tick);
+
+      public:
+
+        /**
+         * Current Rank index
+         */
+        uint8_t rank;
+
+        /**
+         * One DRAMPower instance per rank
+         */
+        DRAMPower power;
+
+        /**
+         * Vector of Banks. Each rank is made of several devices which in
+         * term are made from several banks.
+         */
+        std::vector<Bank> banks;
+
+        /**
+         *  To track number of banks which are currently active for
+         *  this rank.
+         */
+        unsigned int numBanksActive;
+
+        /** List to keep track of activate ticks */
+        std::deque<Tick> actTicks;
+
+        Rank(DRAMCtrl& _memory, const DRAMCtrlParams* _p);
+
+        const std::string name() const
+        {
+            return csprintf("%s_%d", memory.name(), rank);
+        }
+
+        /**
+         * Kick off accounting for power and refresh states and
+         * schedule initial refresh.
+         *
+         * @param ref_tick Tick for first refresh
+         */
+        void startup(Tick ref_tick);
+
+        /**
+         * Stop the refresh events.
+         */
+        void suspend();
+
+        /**
+         * Check if the current rank is available for scheduling.
+         *
+         * @param Return true if the rank is idle from a refresh point of view
+         */
+        bool isAvailable() const { return refreshState == REF_IDLE; }
+
+        /**
+         * Let the rank check if it was waiting for requests to drain
+         * to allow it to transition states.
+         */
+        void checkDrainDone();
+
+        /*
+         * Function to register Stats
+         */
+        void regStats();
+
+        void processActivateEvent();
+        EventWrapper<Rank, &Rank::processActivateEvent>
+        activateEvent;
+
+        void processPrechargeEvent();
+        EventWrapper<Rank, &Rank::processPrechargeEvent>
+        prechargeEvent;
+
+        void processRefreshEvent();
+        EventWrapper<Rank, &Rank::processRefreshEvent>
+        refreshEvent;
+
+        void processPowerEvent();
+        EventWrapper<Rank, &Rank::processPowerEvent>
+        powerEvent;
+
+    };
+
     /**
      * A burst helper helps organize and manage a packet that is larger than
      * the DRAM burst size. A system packet that is larger than the burst size
@@ -188,7 +403,7 @@ class DRAMCtrl : public AbstractMemory
 
         BurstHelper(unsigned int _burstCount)
             : burstCount(_burstCount), burstsServiced(0)
-            { }
+        { }
     };
 
     /**
@@ -213,7 +428,7 @@ class DRAMCtrl : public AbstractMemory
         /** Will be populated by address decoder */
         const uint8_t rank;
         const uint8_t bank;
-        const uint16_t row;
+        const uint32_t row;
 
         /**
          * Bank id is calculated considering banks in all the ranks
@@ -242,14 +457,15 @@ class DRAMCtrl : public AbstractMemory
          */
         BurstHelper* burstHelper;
         Bank& bankRef;
+        Rank& rankRef;
 
         DRAMPacket(PacketPtr _pkt, bool is_read, uint8_t _rank, uint8_t _bank,
-                   uint16_t _row, uint16_t bank_id, Addr _addr,
-                   unsigned int _size, Bank& bank_ref)
+                   uint32_t _row, uint16_t bank_id, Addr _addr,
+                   unsigned int _size, Bank& bank_ref, Rank& rank_ref)
             : entryTime(curTick()), readyTime(curTick()),
               pkt(_pkt), isRead(is_read), rank(_rank), bank(_bank), row(_row),
               bankId(bank_id), addr(_addr), size(_size), burstHelper(NULL),
-              bankRef(bank_ref)
+              bankRef(bank_ref), rankRef(rank_ref)
         { }
 
     };
@@ -260,15 +476,11 @@ class DRAMCtrl : public AbstractMemory
      * processRespondEvent is called; no parameters are allowed
      * in these methods
      */
-    void processRespondEvent();
-    EventWrapper<DRAMCtrl, &DRAMCtrl::processRespondEvent> respondEvent;
-
-    void processRefreshEvent();
-    EventWrapper<DRAMCtrl, &DRAMCtrl::processRefreshEvent> refreshEvent;
-
     void processNextReqEvent();
     EventWrapper<DRAMCtrl,&DRAMCtrl::processNextReqEvent> nextReqEvent;
 
+    void processRespondEvent();
+    EventWrapper<DRAMCtrl, &DRAMCtrl::processRespondEvent> respondEvent;
 
     /**
      * Check if the read queue has room for more entries
@@ -357,63 +569,67 @@ class DRAMCtrl : public AbstractMemory
      * The memory schduler/arbiter - picks which request needs to
      * go next, based on the specified policy such as FCFS or FR-FCFS
      * and moves it to the head of the queue.
-     */
-    void chooseNext(std::deque<DRAMPacket*>& queue);
-
-    /**
-     *Looks at the state of the banks, channels, row buffer hits etc
-     * to estimate how long a request will take to complete.
-     *
-     * @param dram_pkt The request for which we want to estimate latency
-     * @param inTime The tick at which you want to probe the memory
+     * Prioritizes accesses to the same rank as previous burst unless
+     * controller is switching command type.
      *
-     * @return A pair of ticks, one indicating how many ticks *after*
-     *         inTime the request require, and the other indicating how
-     *         much of that was just the bank access time, ignoring the
-     *         ticks spent simply waiting for resources to become free
-     */
-    std::pair<Tick, Tick> estimateLatency(DRAMPacket* dram_pkt, Tick inTime);
-
-    /**
-     * Move the request at the head of the read queue to the response
-     * queue, sorting by readyTime.\ If it is the only packet in the
-     * response queue, schedule a respond event to send it back to the
-     * outside world
+     * @param queue Queued requests to consider
+     * @param switched_cmd_type Command type is changing
+     * @return true if a packet is scheduled to a rank which is available else
+     * false
      */
-    void moveToRespQ();
+    bool chooseNext(std::deque<DRAMPacket*>& queue, bool switched_cmd_type);
 
     /**
      * For FR-FCFS policy reorder the read/write queue depending on row buffer
      * hits and earliest banks available in DRAM
-     */
-    void reorderQueue(std::deque<DRAMPacket*>& queue);
-
-    /**
-     * Looking at all banks, determine the moment in time when they
-     * are all free.
+     * Prioritizes accesses to the same rank as previous burst unless
+     * controller is switching command type.
      *
-     * @return The tick when all banks are free
+     * @param queue Queued requests to consider
+     * @param switched_cmd_type Command type is changing
+     * @return true if a packet is scheduled to a rank which is available else
+     * false
      */
-    Tick maxBankFreeAt() const;
+    bool reorderQueue(std::deque<DRAMPacket*>& queue, bool switched_cmd_type);
 
     /**
-     * Find which are the earliest available banks for the enqueued
-     * requests. Assumes maximum of 64 banks per DIMM
+     * Find which are the earliest banks ready to issue an activate
+     * for the enqueued requests. Assumes maximum of 64 banks per DIMM
+     * Also checks if the bank is already prepped.
      *
-     * @param Queued requests to consider
+     * @param queue Queued requests to consider
+     * @param switched_cmd_type Command type is changing
      * @return One-hot encoded mask of bank indices
      */
-    uint64_t minBankFreeAt(const std::deque<DRAMPacket*>& queue) const;
+    uint64_t minBankPrep(const std::deque<DRAMPacket*>& queue,
+                         bool switched_cmd_type) const;
 
     /**
      * Keep track of when row activations happen, in order to enforce
      * the maximum number of activations in the activation window. The
      * method updates the time that the banks become available based
      * on the current limits.
+     *
+     * @param rank_ref Reference to the rank
+     * @param bank_ref Reference to the bank
+     * @param act_tick Time when the activation takes place
+     * @param row Index of the row
      */
-    void recordActivate(Tick act_tick, uint8_t rank, uint8_t bank);
+    void activateBank(Rank& rank_ref, Bank& bank_ref, Tick act_tick,
+                      uint32_t row);
 
-    void printParams() const;
+    /**
+     * Precharge a given bank and also update when the precharge is
+     * done. This will also deal with any stats related to the
+     * accesses to the open page.
+     *
+     * @param rank_ref The rank to precharge
+     * @param bank_ref The bank to precharge
+     * @param pre_at Time when the precharge takes place
+     * @param trace Is this an auto precharge then do not add to trace
+     */
+    void prechargeBank(Rank& rank_ref, Bank& bank_ref,
+                       Tick pre_at, bool trace = true);
 
     /**
      * Used for debugging to observe the contents of the queues.
@@ -443,10 +659,9 @@ class DRAMCtrl : public AbstractMemory
     DrainManager *drainManager;
 
     /**
-     * Multi-dimensional vector of banks, first dimension is ranks,
-     * second is bank
+     * Vector of ranks
      */
-    std::vector<std::vector<Bank> > banks;
+    std::vector<Rank*> ranks;
 
     /**
      * The following are basic design parameters of the memory
@@ -454,6 +669,7 @@ class DRAMCtrl : public AbstractMemory
      * The rowsPerBank is determined based on the capacity, number of
      * ranks and banks, the burst size, and the row buffer size.
      */
+    const uint32_t deviceSize;
     const uint32_t deviceBusWidth;
     const uint32_t burstLength;
     const uint32_t deviceRowBufferSize;
@@ -461,7 +677,10 @@ class DRAMCtrl : public AbstractMemory
     const uint32_t burstSize;
     const uint32_t rowBufferSize;
     const uint32_t columnsPerRowBuffer;
+    const uint32_t columnsPerStripe;
     const uint32_t ranksPerChannel;
+    const uint32_t bankGroupsPerRank;
+    const bool bankGroupArch;
     const uint32_t banksPerRank;
     const uint32_t channels;
     uint32_t rowsPerBank;
@@ -477,16 +696,22 @@ class DRAMCtrl : public AbstractMemory
      * Basic memory timing parameters initialized based on parameter
      * values.
      */
+    const Tick M5_CLASS_VAR_USED tCK;
     const Tick tWTR;
     const Tick tRTW;
+    const Tick tCS;
     const Tick tBURST;
+    const Tick tCCD_L;
     const Tick tRCD;
     const Tick tCL;
     const Tick tRP;
     const Tick tRAS;
+    const Tick tWR;
+    const Tick tRTP;
     const Tick tRFC;
     const Tick tREFI;
     const Tick tRRD;
+    const Tick tRRD_L;
     const Tick tXAW;
     const uint32_t activationLimit;
 
@@ -563,11 +788,9 @@ class DRAMCtrl : public AbstractMemory
     Stats::Scalar totQLat;
     Stats::Scalar totMemAccLat;
     Stats::Scalar totBusLat;
-    Stats::Scalar totBankLat;
 
     // Average latencies per request
     Stats::Formula avgQLat;
-    Stats::Formula avgBankLat;
     Stats::Formula avgBusLat;
     Stats::Formula avgMemAccLat;
 
@@ -594,13 +817,12 @@ class DRAMCtrl : public AbstractMemory
 
     // DRAM Power Calculation
     Stats::Formula pageHitRate;
-    Stats::Formula prechargeAllPercent;
-    Stats::Scalar prechargeAllTime;
 
-    // To track number of cycles all the banks are precharged
-    Tick startTickPrechargeAll;
-    // To track number of banks which are currently active
-    unsigned int numBanksActive;
+    // Holds the value of the rank of burst issued
+    uint8_t activeRank;
+
+    // timestamp offset
+    uint64_t timeStampOffset;
 
     /** @todo this is a temporary workaround until the 4-phase code is
      * committed. upstream caches needs this packet until true is returned, so
@@ -608,6 +830,30 @@ class DRAMCtrl : public AbstractMemory
      */
     std::vector<PacketPtr> pendingDelete;
 
+    /**
+     * This function increments the energy when called. If stats are
+     * dumped periodically, note accumulated energy values will
+     * appear in the stats (even if the stats are reset). This is a
+     * result of the energy values coming from DRAMPower, and there
+     * is currently no support for resetting the state.
+     *
+     * @param rank Currrent rank
+     */
+    void updatePowerStats(Rank& rank_ref);
+
+    /**
+     * Function for sorting commands in the command list of DRAMPower.
+     *
+     * @param a Memory Command in command list of DRAMPower library
+     * @param next Memory Command in command list of DRAMPower
+     * @return true if timestamp of Command 1 < timestamp of Command 2
+     */
+    static bool sortTime(const Data::MemCommand& m1,
+                         const Data::MemCommand& m2) {
+        return m1.getTime() < m2.getTime();
+    };
+
+
   public:
 
     void regStats();
@@ -619,8 +865,9 @@ class DRAMCtrl : public AbstractMemory
     virtual BaseSlavePort& getSlavePort(const std::string& if_name,
                                         PortID idx = InvalidPortID);
 
-    virtual void init();
-    virtual void startup();
+    virtual void init() M5_ATTR_OVERRIDE;
+    virtual void startup() M5_ATTR_OVERRIDE;
+    virtual void drainResume() M5_ATTR_OVERRIDE;
 
   protected: