| /* | |
| * Copyright (c) 2022 The Regents of the University of California | |
| * 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 | |
| * HBMCtrl declaration | |
| */ | |
| #ifndef __HBM_CTRL_HH__ | |
| #define __HBM_CTRL_HH__ | |
| #include <deque> | |
| #include <string> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include "mem/mem_ctrl.hh" | |
| #include "params/HBMCtrl.hh" | |
| namespace gem5 | |
| { | |
| namespace memory | |
| { | |
| class MemInterface; | |
| class DRAMInterface; | |
| /** | |
| * HBM2 is divided into two pseudo channels which have independent data buses | |
| * but share a command bus (separate row and column command bus). Therefore, | |
| * the HBM memory controller should be able to control both pseudo channels. | |
| * This HBM memory controller inherits from gem5's default | |
| * memory controller (pseudo channel 0) and manages the additional HBM pseudo | |
| * channel (pseudo channel 1). | |
| */ | |
| class HBMCtrl : public MemCtrl | |
| { | |
| protected: | |
| bool respQEmpty() override | |
| { | |
| return (respQueue.empty() && respQueuePC1.empty()); | |
| } | |
| private: | |
| /** | |
| * Remember if we have to retry a request for second pseudo channel. | |
| */ | |
| bool retryRdReqPC1; | |
| bool retryWrReqPC1; | |
| /** | |
| * Remove commands that have already issued from rowBurstTicks | |
| * and colBurstTicks | |
| */ | |
| void pruneBurstTick() override; | |
| AddrRangeList getAddrRanges() override; | |
| public: | |
| HBMCtrl(const HBMCtrlParams &p); | |
| void pruneRowBurstTick(); | |
| void pruneColBurstTick(); | |
| /** | |
| * Check for command bus contention for single cycle command. | |
| * If there is contention, shift command to next burst. | |
| * Check verifies that the commands issued per burst is less | |
| * than a defined max number, maxCommandsPerWindow. | |
| * Therefore, contention per cycle is not verified and instead | |
| * is done based on a burst window. | |
| * | |
| * @param cmd_tick Initial tick of command, to be verified | |
| * @param max_cmds_per_burst Number of commands that can issue | |
| * in a burst window | |
| * @return tick for command issue without contention | |
| */ | |
| Tick verifySingleCmd(Tick cmd_tick, Tick max_cmds_per_burst, | |
| bool row_cmd) override; | |
| /** | |
| * Check for command bus contention for multi-cycle (2 currently) | |
| * command. If there is contention, shift command(s) to next burst. | |
| * Check verifies that the commands issued per burst is less | |
| * than a defined max number, maxCommandsPerWindow. | |
| * Therefore, contention per cycle is not verified and instead | |
| * is done based on a burst window. | |
| * For HBM2, only row cmds (activate) can be multi-cycle | |
| * | |
| * @param cmd_tick Initial tick of command, to be verified | |
| * @param max_multi_cmd_split Maximum delay between commands | |
| * @param max_cmds_per_burst Number of commands that can issue | |
| * in a burst window | |
| * @return tick for command issue without contention | |
| */ | |
| Tick verifyMultiCmd(Tick cmd_tick, Tick max_cmds_per_burst, | |
| Tick max_multi_cmd_split = 0) override; | |
| /** | |
| * NextReq and Respond events for second pseudo channel | |
| * | |
| */ | |
| EventFunctionWrapper nextReqEventPC1; | |
| EventFunctionWrapper respondEventPC1; | |
| /** | |
| * Check if the read queue partition of both pseudo | |
| * channels has room for more entries. This is used when the HBM ctrl | |
| * is run with partitioned queues | |
| * | |
| * @param pkt_count The number of entries needed in the read queue | |
| * @return true if read queue partition is full, false otherwise | |
| */ | |
| bool readQueueFullPC0(unsigned int pkt_count) const; | |
| bool readQueueFullPC1(unsigned int pkt_count) const; | |
| /** | |
| * Check if the write queue partition of both pseudo | |
| * channels has room for more entries. This is used when the HBM ctrl | |
| * is run with partitioned queues | |
| * | |
| * @param pkt_count The number of entries needed in the write queue | |
| * @return true if write queue is full, false otherwise | |
| */ | |
| bool writeQueueFullPC0(unsigned int pkt_count) const; | |
| bool writeQueueFullPC1(unsigned int pkt_count) const; | |
| /** | |
| * Following counters are used to keep track of the entries in read/write | |
| * queue for each pseudo channel (useful when the partitioned queues are | |
| * used) | |
| */ | |
| uint64_t readQueueSizePC0 = 0; | |
| uint64_t readQueueSizePC1 = 0; | |
| uint64_t writeQueueSizePC0 = 0; | |
| uint64_t writeQueueSizePC1 = 0; | |
| /** | |
| * Response queue for pkts sent to second pseudo channel | |
| * The first pseudo channel uses MemCtrl::respQueue | |
| */ | |
| std::deque<MemPacket*> respQueuePC1; | |
| /** | |
| * Holds count of row commands issued in burst window starting at | |
| * defined Tick. This is used to ensure that the row command bandwidth | |
| * does not exceed the allowable media constraints. | |
| */ | |
| std::unordered_multiset<Tick> rowBurstTicks; | |
| /** | |
| * This is used to ensure that the column command bandwidth | |
| * does not exceed the allowable media constraints. HBM2 has separate | |
| * command bus for row and column commands | |
| */ | |
| std::unordered_multiset<Tick> colBurstTicks; | |
| /** | |
| * Pointers to interfaces of the two pseudo channels | |
| * pc0Int is same as MemCtrl::dram (it will be pointing to | |
| * the DRAM interface defined in base MemCtrl) | |
| */ | |
| DRAMInterface* pc0Int; | |
| DRAMInterface* pc1Int; | |
| /** | |
| * This indicates if the R/W queues will be partitioned among | |
| * pseudo channels | |
| */ | |
| bool partitionedQ; | |
| public: | |
| /** | |
| * Is there a respondEvent scheduled? | |
| * | |
| * @return true if event is scheduled | |
| */ | |
| bool respondEventScheduled(uint8_t pseudo_channel) const override | |
| { | |
| if (pseudo_channel == 0) { | |
| return MemCtrl::respondEventScheduled(pseudo_channel); | |
| } else { | |
| assert(pseudo_channel == 1); | |
| return respondEventPC1.scheduled(); | |
| } | |
| } | |
| /** | |
| * Is there a read/write burst Event scheduled? | |
| * | |
| * @return true if event is scheduled | |
| */ | |
| bool requestEventScheduled(uint8_t pseudo_channel) const override | |
| { | |
| if (pseudo_channel == 0) { | |
| return MemCtrl::requestEventScheduled(pseudo_channel); | |
| } else { | |
| assert(pseudo_channel == 1); | |
| return nextReqEventPC1.scheduled(); | |
| } | |
| } | |
| /** | |
| * restart the controller scheduler | |
| * | |
| * @param Tick to schedule next event | |
| * @param pseudo_channel pseudo channel number for which scheduler | |
| * needs to restart | |
| */ | |
| void restartScheduler(Tick tick, uint8_t pseudo_channel) override | |
| { | |
| if (pseudo_channel == 0) { | |
| MemCtrl::restartScheduler(tick); | |
| } else { | |
| schedule(nextReqEventPC1, tick); | |
| } | |
| } | |
| virtual void init() override; | |
| virtual void startup() override; | |
| virtual void drainResume() override; | |
| protected: | |
| Tick recvAtomic(PacketPtr pkt) override; | |
| Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor) override; | |
| void recvFunctional(PacketPtr pkt) override; | |
| void recvMemBackdoorReq(const MemBackdoorReq &req, | |
| MemBackdoorPtr &_backdoor) override; | |
| bool recvTimingReq(PacketPtr pkt) override; | |
| }; | |
| } // namespace memory | |
| } // namespace gem5 | |
| #endif //__HBM_CTRL_HH__ |