/* * Copyright (c) 2012-2020 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Copyright (c) 2013 Amin Farmahini-Farahani * 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. */ /** * @file * MemInterface declaration */ #ifndef __MEM_INTERFACE_HH__ #define __MEM_INTERFACE_HH__ #include #include #include #include #include #include "base/statistics.hh" #include "enums/AddrMap.hh" #include "enums/PageManage.hh" #include "mem/abstract_mem.hh" #include "mem/drampower.hh" #include "mem/mem_ctrl.hh" #include "params/DRAMInterface.hh" #include "params/MemInterface.hh" #include "params/NVMInterface.hh" #include "sim/eventq.hh" /** * General interface to memory device * Includes functions and parameters shared across media types */ class MemInterface : public AbstractMemory { protected: /** * 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 NO_ROW = -1; uint32_t openRow; uint8_t bank; uint8_t bankgr; Tick rdAllowedAt; Tick wrAllowedAt; Tick preAllowedAt; Tick actAllowedAt; uint32_t rowAccesses; uint32_t bytesAccessed; Bank() : openRow(NO_ROW), bank(0), bankgr(0), rdAllowedAt(0), wrAllowedAt(0), preAllowedAt(0), actAllowedAt(0), rowAccesses(0), bytesAccessed(0) { } }; /** * A pointer to the parent MemCtrl instance */ MemCtrl* ctrl; /** * Number of commands that can issue in the defined controller * command window, used to verify command bandwidth */ unsigned int maxCommandsPerWindow; /** * Memory controller configuration initialized based on parameter * values. */ Enums::AddrMap addrMapping; /** * General device and channel characteristics * The rowsPerBank is determined based on the capacity, number of * ranks and banks, the burst size, and the row buffer size. */ const uint32_t burstSize; const uint32_t deviceSize; const uint32_t deviceRowBufferSize; const uint32_t devicesPerRank; const uint32_t rowBufferSize; const uint32_t burstsPerRowBuffer; const uint32_t burstsPerStripe; const uint32_t ranksPerChannel; const uint32_t banksPerRank; uint32_t rowsPerBank; /** * General timing requirements */ M5_CLASS_VAR_USED const Tick tCK; const Tick tCS; const Tick tBURST; const Tick tRTW; const Tick tWTR; /* * @return delay between write and read commands */ virtual Tick writeToReadDelay() const { return tBURST + tWTR; } /* * @return delay between write and read commands */ Tick readToWriteDelay() const { return tBURST + tRTW; } /* * @return delay between accesses to different ranks */ Tick rankToRankDelay() const { return tBURST + tCS; } public: /** * Buffer sizes for read and write queues in the controller * These are passed to the controller on instantiation * Defining them here allows for buffers to be resized based * on memory type / configuration. */ const uint32_t readBufferSize; const uint32_t writeBufferSize; /** Set a pointer to the controller and initialize * interface based on controller parameters * @param _ctrl pointer to the parent controller * @param command_window size of command window used to * check command bandwidth */ void setCtrl(MemCtrl* _ctrl, unsigned int command_window); /** * Get an address in a dense range which starts from 0. The input * address is the physical address of the request in an address * space that contains other SimObjects apart from this * controller. * * @param addr The intput address which should be in the addrRange * @return An address in the continues range [0, max) */ Addr getCtrlAddr(Addr addr) { return range.getOffset(addr); } /** * Setup the rank based on packet received * * @param integer value of rank to be setup. used to index ranks vector * @param are we setting up rank for read or write packet? */ virtual void setupRank(const uint8_t rank, const bool is_read) = 0; /** * Check drain state of interface * * @return true if all ranks are drained and idle * */ virtual bool allRanksDrained() const = 0; /** * For FR-FCFS policy, find first command that can issue * Function will be overriden by interface to select based * on media characteristics, used to determine when read * or write can issue. * * @param queue Queued requests to consider * @param min_col_at Minimum tick for 'seamless' issue * @return an iterator to the selected packet, else queue.end() * @return the tick when the packet selected will issue */ virtual std::pair chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const = 0; /* * Function to calulate unloaded latency */ virtual Tick accessLatency() const = 0; /** * @return number of bytes in a burst for this interface */ uint32_t bytesPerBurst() const { return burstSize; } /* * @return time to offset next command */ virtual Tick commandOffset() const = 0; /** * Check if a burst operation can be issued to the interface * * @param Return true if RD/WR can issue */ virtual bool burstReady(MemPacket* pkt) const = 0; /** * Determine the required delay for an access to a different rank * * @return required rank to rank delay */ Tick rankDelay() const { return tCS; } /** * * @return minimum additional bus turnaround required for read-to-write */ Tick minReadToWriteDataGap() const { return std::min(tRTW, tCS); } /** * * @return minimum additional bus turnaround required for write-to-read */ Tick minWriteToReadDataGap() const { return std::min(tWTR, tCS); } /** * Address decoder to figure out physical mapping onto ranks, * banks, and rows. This function is called multiple times on the same * system packet if the pakcet is larger than burst of the memory. The * pkt_addr is used for the offset within the packet. * * @param pkt The packet from the outside world * @param pkt_addr The starting address of the packet * @param size The size of the packet in bytes * @param is_read Is the request for a read or a write to memory * @param is_dram Is the request to a DRAM interface * @return A MemPacket pointer with the decoded information */ MemPacket* decodePacket(const PacketPtr pkt, Addr pkt_addr, unsigned int size, bool is_read, bool is_dram); /** * Add rank to rank delay to bus timing to all banks in all ranks * when access to an alternate interface is issued * * param cmd_at Time of current command used as starting point for * addition of rank-to-rank delay */ virtual void addRankToRankDelay(Tick cmd_at) = 0; typedef MemInterfaceParams Params; MemInterface(const Params &_p); }; /** * Interface to DRAM devices with media specific parameters, * statistics, and functions. * The DRAMInterface includes a class for individual ranks * and per rank functions. */ class DRAMInterface : public MemInterface { private: /** * Simple structure to hold the values needed to keep track of * commands for DRAMPower */ struct Command { Data::MemCommand::cmds type; uint8_t bank; Tick timeStamp; constexpr Command(Data::MemCommand::cmds _type, uint8_t _bank, Tick time_stamp) : type(_type), bank(_bank), timeStamp(time_stamp) { } }; /** * 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. * * PWR_IDLE : The idle state in which all banks are closed * From here can transition to: PWR_REF, PWR_ACT, * PWR_PRE_PDN * * PWR_REF : Auto-refresh state. Will transition when refresh is * complete based on power state prior to PWR_REF * From here can transition to: PWR_IDLE, PWR_PRE_PDN, * PWR_SREF * * PWR_SREF : Self-refresh state. Entered after refresh if * previous state was PWR_PRE_PDN * From here can transition to: PWR_IDLE * * PWR_PRE_PDN : Precharge power down state * From here can transition to: PWR_REF, PWR_IDLE * * PWR_ACT : Activate state in which one or more banks are open * From here can transition to: PWR_IDLE, PWR_ACT_PDN * * PWR_ACT_PDN : Activate power down state * From here can transition to: PWR_ACT */ enum PowerState { PWR_IDLE = 0, PWR_REF, PWR_SREF, 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. Once tREFI has elasped, a refresh event * is triggered to start the following STM transitions which are * used to issue a refresh and return back to normal operation * * REF_IDLE : IDLE state used during normal operation * From here can transition to: REF_DRAIN * * REF_SREF_EXIT : Exiting a self-refresh; refresh event scheduled * after self-refresh exit completes * From here can transition to: REF_DRAIN * * REF_DRAIN : Drain state in which on going accesses complete. * From here can transition to: REF_PD_EXIT * * REF_PD_EXIT : Evaluate pwrState and issue wakeup if needed * Next state dependent on whether banks are open * From here can transition to: REF_PRE, REF_START * * REF_PRE : Close (precharge) all open banks * From here can transition to: REF_START * * REF_START : Issue refresh command and update DRAMPower stats * From here can transition to: REF_RUN * * REF_RUN : Refresh running, waiting for tRFC to expire * From here can transition to: REF_IDLE, REF_SREF_EXIT */ enum RefreshState { REF_IDLE = 0, REF_DRAIN, REF_PD_EXIT, REF_SREF_EXIT, REF_PRE, REF_START, REF_RUN }; class Rank; struct RankStats : public Stats::Group { RankStats(DRAMInterface &dram, Rank &rank); void regStats() override; void resetStats() override; void preDumpStats() override; Rank &rank; /* * 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; /* * Active Power-Down Energy */ Stats::Scalar actPowerDownEnergy; /* * Precharge Power-Down Energy */ Stats::Scalar prePowerDownEnergy; /* * self Refresh Energy */ Stats::Scalar selfRefreshEnergy; Stats::Scalar totalEnergy; Stats::Scalar averagePower; /** * Stat to track total DRAM idle time * */ Stats::Scalar totalIdleTime; /** * Track time spent in each power state. */ Stats::Vector pwrStateTime; }; /** * 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: /** * A reference to the parent DRAMInterface instance */ DRAMInterface& dram; /** * Since we are taking decisions out of order, we need to keep * track of what power transition is happening at what time */ PowerState pwrStateTrans; /** * Previous low-power state, which will be re-entered after refresh. */ PowerState pwrStatePostRefresh; /** * Track when we transitioned to the current power state */ Tick pwrStateTick; /** * Keep track of when a refresh is due. */ Tick refreshDueAt; /** * 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 power state. */ PowerState pwrState; /** * current refresh state */ RefreshState refreshState; /** * rank is in or transitioning to power-down or self-refresh */ bool inLowPowerState; /** * Current Rank index */ uint8_t rank; /** * Track number of packets in read queue going to this rank */ uint32_t readEntries; /** * Track number of packets in write queue going to this rank */ uint32_t writeEntries; /** * Number of ACT, RD, and WR events currently scheduled * Incremented when a refresh event is started as well * Used to determine when a low-power state can be entered */ uint8_t outstandingEvents; /** * delay low-power exit until this requirement is met */ Tick wakeUpAllowedAt; /** * One DRAMPower instance per rank */ DRAMPower power; /** * List of commands issued, to be sent to DRAMPpower at refresh * and stats dump. Keep commands here since commands to different * banks are added out of order. Will only pass commands up to * curTick() to DRAMPower after sorting. */ std::vector cmdList; /** * Vector of Banks. Each rank is made of several devices which in * term are made from several banks. */ std::vector 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 actTicks; /** * Track when we issued the last read/write burst */ Tick lastBurstTick; Rank(const DRAMInterfaceParams &_p, int _rank, DRAMInterface& _dram); const std::string name() const { return csprintf("%d", 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 there is no refresh and no preparation of refresh ongoing * i.e. the refresh state machine is in idle * * @param Return true if the rank is idle from a refresh point of view */ bool inRefIdleState() const { return refreshState == REF_IDLE; } /** * Check if the current rank has all banks closed and is not * in a low power state * * @param Return true if the rank is idle from a bank * and power point of view */ bool inPwrIdleState() const { return pwrState == PWR_IDLE; } /** * Trigger a self-refresh exit if there are entries enqueued * Exit if there are any read entries regardless of the bus state. * If we are currently issuing write commands, exit if we have any * write commands enqueued as well. * Could expand this in the future to analyze state of entire queue * if needed. * * @return boolean indicating self-refresh exit should be scheduled */ bool forceSelfRefreshExit() const; /** * Check if the command queue of current rank is idle * * @param Return true if the there are no commands in Q. * Bus direction determines queue checked. */ bool isQueueEmpty() const; /** * Let the rank check if it was waiting for requests to drain * to allow it to transition states. */ void checkDrainDone(); /** * Push command out of cmdList queue that are scheduled at * or before curTick() to DRAMPower library * All commands before curTick are guaranteed to be complete * and can safely be flushed. */ void flushCmdList(); /** * Computes stats just prior to dump event */ void computeStats(); /** * Reset stats on a stats event */ void resetStats(); /** * Schedule a transition to power-down (sleep) * * @param pwr_state Power state to transition to * @param tick Absolute tick when transition should take place */ void powerDownSleep(PowerState pwr_state, Tick tick); /** * schedule and event to wake-up from power-down or self-refresh * and update bank timing parameters * * @param exit_delay Relative tick defining the delay required between * low-power exit and the next command */ void scheduleWakeUpEvent(Tick exit_delay); void processWriteDoneEvent(); EventFunctionWrapper writeDoneEvent; void processActivateEvent(); EventFunctionWrapper activateEvent; void processPrechargeEvent(); EventFunctionWrapper prechargeEvent; void processRefreshEvent(); EventFunctionWrapper refreshEvent; void processPowerEvent(); EventFunctionWrapper powerEvent; void processWakeUpEvent(); EventFunctionWrapper wakeUpEvent; protected: RankStats stats; }; /** * Function for sorting Command structures based on timeStamp * * @param a Memory Command * @param next Memory Command * @return true if timeStamp of Command 1 < timeStamp of Command 2 */ static bool sortTime(const Command& cmd, const Command& cmd_next) { return cmd.timeStamp < cmd_next.timeStamp; } /** * DRAM specific device characteristics */ const uint32_t bankGroupsPerRank; const bool bankGroupArch; /** * DRAM specific timing requirements */ const Tick tCL; const Tick tBURST_MIN; const Tick tBURST_MAX; const Tick tCCD_L_WR; const Tick tCCD_L; const Tick tRCD; 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 tPPD; const Tick tAAD; const Tick tXAW; const Tick tXP; const Tick tXS; const Tick clkResyncDelay; const bool dataClockSync; const bool burstInterleave; const uint8_t twoCycleActivate; const uint32_t activationLimit; const Tick wrToRdDlySameBG; const Tick rdToWrDlySameBG; Enums::PageManage pageMgmt; /** * Max column accesses (read and write) per row, before forefully * closing it. */ const uint32_t maxAccessesPerRow; // timestamp offset uint64_t timeStampOffset; // Holds the value of the DRAM rank of burst issued uint8_t activeRank; /** Enable or disable DRAM powerdown states. */ bool enableDRAMPowerdown; /** The time when stats were last reset used to calculate average power */ Tick lastStatsResetTick; /** * 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 activateBank(Rank& rank_ref, Bank& bank_ref, Tick act_tick, uint32_t row); /** * 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_tick Time when the precharge takes place * @param auto_or_preall Is this an auto-precharge or precharge all command * @param trace Is this an auto precharge then do not add to trace */ void prechargeBank(Rank& rank_ref, Bank& bank_ref, Tick pre_tick, bool auto_or_preall = false, bool trace = true); struct DRAMStats : public Stats::Group { DRAMStats(DRAMInterface &dram); void regStats() override; void resetStats() override; DRAMInterface &dram; /** total number of DRAM bursts serviced */ Stats::Scalar readBursts; Stats::Scalar writeBursts; /** DRAM per bank stats */ Stats::Vector perBankRdBursts; Stats::Vector perBankWrBursts; // Latencies summed over all requests Stats::Scalar totQLat; Stats::Scalar totBusLat; Stats::Scalar totMemAccLat; // Average latencies per request Stats::Formula avgQLat; Stats::Formula avgBusLat; Stats::Formula avgMemAccLat; // Row hit count and rate Stats::Scalar readRowHits; Stats::Scalar writeRowHits; Stats::Formula readRowHitRate; Stats::Formula writeRowHitRate; Stats::Histogram bytesPerActivate; // Number of bytes transferred to/from DRAM Stats::Scalar bytesRead; Stats::Scalar bytesWritten; // Average bandwidth Stats::Formula avgRdBW; Stats::Formula avgWrBW; Stats::Formula peakBW; // bus utilization Stats::Formula busUtil; Stats::Formula busUtilRead; Stats::Formula busUtilWrite; Stats::Formula pageHitRate; }; DRAMStats stats; /** * Vector of dram ranks */ std::vector ranks; /* * @return delay between write and read commands */ Tick writeToReadDelay() const override { return tBURST + tWTR + tCL; } /** * Find which are the earliest banks ready to issue an activate * for the enqueued requests. Assumes maximum of 32 banks per rank * Also checks if the bank is already prepped. * * @param queue Queued requests to consider * @param min_col_at time of seamless burst command * @return One-hot encoded mask of bank indices * @return boolean indicating burst can issue seamlessly, with no gaps */ std::pair, bool> minBankPrep(const MemPacketQueue& queue, Tick min_col_at) const; /* * @return time to send a burst of data without gaps */ Tick burstDelay() const { return (burstInterleave ? tBURST_MAX / 2 : tBURST); } public: /** * Initialize the DRAM interface and verify parameters */ void init() override; /** * Iterate through dram ranks and instantiate per rank startup routine */ void startup() override; /** * Setup the rank based on packet received * * @param integer value of rank to be setup. used to index ranks vector * @param are we setting up rank for read or write packet? */ void setupRank(const uint8_t rank, const bool is_read) override; /** * Iterate through dram ranks to exit self-refresh in order to drain */ void drainRanks(); /** * Return true once refresh is complete for all ranks and there are no * additional commands enqueued. (only evaluated when draining) * This will ensure that all banks are closed, power state is IDLE, and * power stats have been updated * * @return true if all ranks have refreshed, with no commands enqueued * */ bool allRanksDrained() const override; /** * Iterate through DRAM ranks and suspend them */ void suspend(); /* * @return time to offset next command */ Tick commandOffset() const override { return (tRP + tRCD); } /* * Function to calulate unloaded, closed bank access latency */ Tick accessLatency() const override { return (tRP + tRCD + tCL); } /** * For FR-FCFS policy, find first DRAM command that can issue * * @param queue Queued requests to consider * @param min_col_at Minimum tick for 'seamless' issue * @return an iterator to the selected packet, else queue.end() * @return the tick when the packet selected will issue */ std::pair chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const override; /** * Actually do the burst - figure out the latency it * will take to service the req based on bank state, channel state etc * and then update those states to account for this request. Based * on this, update the packet's "readyTime" and move it to the * response q from where it will eventually go back to the outside * world. * * @param mem_pkt The packet created from the outside world pkt * @param next_burst_at Minimum bus timing requirement from controller * @param queue Reference to the read or write queue with the packet * @return pair, tick when current burst is issued and * tick when next burst can issue */ std::pair doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at, const std::vector& queue); /** * Check if a burst operation can be issued to the DRAM * * @param Return true if RD/WR can issue * This requires the DRAM to be in the * REF IDLE state */ bool burstReady(MemPacket* pkt) const override { return ranks[pkt->rank]->inRefIdleState(); } /** * This function checks if ranks are actively refreshing and * therefore busy. The function also checks if ranks are in * the self-refresh state, in which case, a self-refresh exit * is initiated. * * return boolean if all ranks are in refresh and therefore busy */ bool isBusy(); /** * Add rank to rank delay to bus timing to all DRAM banks in alli ranks * when access to an alternate interface is issued * * param cmd_at Time of current command used as starting point for * addition of rank-to-rank delay */ void addRankToRankDelay(Tick cmd_at) override; /** * Complete response process for DRAM when read burst is complete * This will update the counters and check if a power down state * can be entered. * * @param rank Specifies rank associated with read burst */ void respondEvent(uint8_t rank); /** * Check the refresh state to determine if refresh needs * to be kicked back into action after a read response * * @param rank Specifies rank associated with read burst */ void checkRefreshState(uint8_t rank); DRAMInterface(const DRAMInterfaceParams &_p); }; /** * Interface to NVM devices with media specific parameters, * statistics, and functions. * The NVMInterface includes a class for individual ranks * and per rank functions. */ class NVMInterface : public MemInterface { private: /** * NVM rank class simply includes a vector of banks. */ class Rank : public EventManager { public: /** * Current Rank index */ uint8_t rank; /** * Vector of NVM banks. Each rank is made of several banks * that can be accessed in parallel. */ std::vector banks; Rank(const NVMInterfaceParams &_p, int _rank, NVMInterface& _nvm); }; /** * NVM specific device and channel characteristics */ const uint32_t maxPendingWrites; const uint32_t maxPendingReads; const bool twoCycleRdWr; /** * NVM specific timing requirements */ const Tick tREAD; const Tick tWRITE; const Tick tSEND; struct NVMStats : public Stats::Group { NVMStats(NVMInterface &nvm); void regStats() override; NVMInterface &nvm; /** NVM stats */ Stats::Scalar readBursts; Stats::Scalar writeBursts; Stats::Vector perBankRdBursts; Stats::Vector perBankWrBursts; // Latencies summed over all requests Stats::Scalar totQLat; Stats::Scalar totBusLat; Stats::Scalar totMemAccLat; // Average latencies per request Stats::Formula avgQLat; Stats::Formula avgBusLat; Stats::Formula avgMemAccLat; Stats::Scalar bytesRead; Stats::Scalar bytesWritten; // Average bandwidth Stats::Formula avgRdBW; Stats::Formula avgWrBW; Stats::Formula peakBW; Stats::Formula busUtil; Stats::Formula busUtilRead; Stats::Formula busUtilWrite; /** NVM stats */ Stats::Histogram pendingReads; Stats::Histogram pendingWrites; Stats::Histogram bytesPerBank; }; NVMStats stats; void processWriteRespondEvent(); EventFunctionWrapper writeRespondEvent; void processReadReadyEvent(); EventFunctionWrapper readReadyEvent; /** * Vector of nvm ranks */ std::vector ranks; /** * Holding queue for non-deterministic write commands, which * maintains writes that have been issued but have not completed * Stored seperately mostly to keep the code clean and help with * events scheduling. * This mimics a buffer on the media controller and therefore is * not added to the main write queue for sizing */ std::list writeRespQueue; std::deque readReadyQueue; /** * Check if the write response queue is empty * * @param Return true if empty */ bool writeRespQueueEmpty() const { return writeRespQueue.empty(); } /** * Till when must we wait before issuing next read command? */ Tick nextReadAt; // keep track of reads that have issued for which data is either // not yet ready or has not yet been transferred to the ctrl uint16_t numPendingReads; uint16_t numReadDataReady; public: // keep track of the number of reads that have yet to be issued uint16_t numReadsToIssue; // number of writes in the writeQueue for the NVM interface uint32_t numWritesQueued; /** * Initialize the NVM interface and verify parameters */ void init() override; /** * Setup the rank based on packet received * * @param integer value of rank to be setup. used to index ranks vector * @param are we setting up rank for read or write packet? */ void setupRank(const uint8_t rank, const bool is_read) override; /** * Check drain state of NVM interface * * @return true if write response queue is empty * */ bool allRanksDrained() const override { return writeRespQueueEmpty(); } /* * @return time to offset next command */ Tick commandOffset() const override { return tBURST; } /** * Check if a burst operation can be issued to the NVM * * @param Return true if RD/WR can issue * for reads, also verfy that ready count * has been updated to a non-zero value to * account for race conditions between events */ bool burstReady(MemPacket* pkt) const override; /** * This function checks if ranks are busy. * This state is true when either: * 1) There is no command with read data ready to transmit or * 2) The NVM inteface has reached the maximum number of outstanding * writes commands. * @param read_queue_empty There are no read queued * @param all_writes_nvm All writes in queue are for NVM interface * @return true of NVM is busy * */ bool isBusy(bool read_queue_empty, bool all_writes_nvm); /** * For FR-FCFS policy, find first NVM command that can issue * default to first command to prepped region * * @param queue Queued requests to consider * @param min_col_at Minimum tick for 'seamless' issue * @return an iterator to the selected packet, else queue.end() * @return the tick when the packet selected will issue */ std::pair chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const override; /** * Add rank to rank delay to bus timing to all NVM banks in alli ranks * when access to an alternate interface is issued * * param cmd_at Time of current command used as starting point for * addition of rank-to-rank delay */ void addRankToRankDelay(Tick cmd_at) override; /** * Select read command to issue asynchronously */ void chooseRead(MemPacketQueue& queue); /* * Function to calulate unloaded access latency */ Tick accessLatency() const override { return (tREAD + tSEND); } /** * Check if the write response queue has reached defined threshold * * @param Return true if full */ bool writeRespQueueFull() const { return writeRespQueue.size() == maxPendingWrites; } bool readsWaitingToIssue() const { return ((numReadsToIssue != 0) && (numPendingReads < maxPendingReads)); } /** * Actually do the burst and update stats. * * @param pkt The packet created from the outside world pkt * @param next_burst_at Minimum bus timing requirement from controller * @return pair, tick when current burst is issued and * tick when next burst can issue */ std::pair doBurstAccess(MemPacket* pkt, Tick next_burst_at); NVMInterface(const NVMInterfaceParams &_p); }; #endif //__MEM_INTERFACE_HH__