mem: Add a simple QoS-aware Memory Controller
authorMatteo Andreozzi <matteo.andreozzi@arm.com>
Thu, 10 May 2018 20:59:08 +0000 (21:59 +0100)
committerGiacomo Travaglini <giacomo.travaglini@arm.com>
Fri, 7 Sep 2018 13:16:20 +0000 (13:16 +0000)
This patch implements QoSMemorySink: a simple generic QoS-aware memory
controller which inherits from QoS::MemCtrl.

Change-Id: I537a4e2d4cb8f54fa0002eb088b2c6957afb9973
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/11971
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-by: Matthew Poremba <porembam@gmail.com>
src/mem/qos/QoSMemSinkCtrl.py [new file with mode: 0644]
src/mem/qos/SConscript
src/mem/qos/mem_sink.cc [new file with mode: 0644]
src/mem/qos/mem_sink.hh [new file with mode: 0644]

diff --git a/src/mem/qos/QoSMemSinkCtrl.py b/src/mem/qos/QoSMemSinkCtrl.py
new file mode 100644 (file)
index 0000000..00f19ef
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (c) 2018 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.
+#
+# 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.
+#
+# Author: Matteo Andreozzi
+
+from m5.params import *
+from QoSMemCtrl import *
+
+class QoSMemSinkCtrl(QoSMemCtrl):
+    type = 'QoSMemSinkCtrl'
+    cxx_header = "mem/qos/mem_sink.hh"
+    cxx_class = "QoS::MemSinkCtrl"
+    port = SlavePort("Slave ports")
+
+    # the basic configuration of the controller architecture, note
+    # that each entry corresponds to a burst for the specific DRAM
+    # configuration (e.g. x32 with burst length 8 is 32 bytes) and not
+    # the cacheline size or request/packet size
+    write_buffer_size = Param.Unsigned(64, "Number of write queue entries")
+    read_buffer_size = Param.Unsigned(32, "Number of read queue entries")
+
+    # memory packet size
+    memory_packet_size = Param.MemorySize("32B", "Memory packet size")
+
+    # request latency - minimum timing between requests
+    request_latency = Param.Latency("20ns", "Memory latency between requests")
+
+    # response latency - time to issue a response once a request is serviced
+    response_latency = Param.Latency("20ns", "Memory response latency")
+
+
index 9d44337ab7b5711129adfb586ea8310052129b77..9a9343fc09f4e97781dfd0c83f89969f0a3b38fb 100644 (file)
 Import('*')
 
 SimObject('QoSMemCtrl.py')
+SimObject('QoSMemSinkCtrl.py')
 SimObject('QoSPolicy.py')
 SimObject('QoSTurnaround.py')
 
 Source('policy.cc')
 Source('q_policy.cc')
 Source('mem_ctrl.cc')
+Source('mem_sink.cc')
diff --git a/src/mem/qos/mem_sink.cc b/src/mem/qos/mem_sink.cc
new file mode 100644 (file)
index 0000000..a951dae
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2018 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.
+ *
+ * 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.
+ *
+ * Author: Matteo Andreozzi
+ */
+
+#include "debug/Drain.hh"
+#include "debug/QOS.hh"
+#include "mem_sink.hh"
+#include "sim/system.hh"
+
+namespace QoS {
+
+MemSinkCtrl::MemSinkCtrl(const QoSMemSinkCtrlParams* p)
+  : MemCtrl(p), requestLatency(p->request_latency),
+    responseLatency(p->response_latency),
+    memoryPacketSize(p->memory_packet_size),
+    readBufferSize(p->read_buffer_size),
+    writeBufferSize(p->write_buffer_size), port(name() + ".port", *this),
+    retryRdReq(false), retryWrReq(false), nextRequest(0), nextReqEvent(this)
+{
+    // Resize read and write queue to allocate space
+    // for configured QoS priorities
+    readQueue.resize(numPriorities());
+    writeQueue.resize(numPriorities());
+}
+
+MemSinkCtrl::~MemSinkCtrl()
+{}
+
+void
+MemSinkCtrl::init()
+{
+    MemCtrl::init();
+
+    // Allow unconnected memories as this is used in several ruby
+    // systems at the moment
+    if (port.isConnected()) {
+        port.sendRangeChange();
+    }
+}
+
+bool
+MemSinkCtrl::readQueueFull(const uint64_t packets) const
+{
+    return (totalReadQueueSize + packets > readBufferSize);
+}
+
+bool
+MemSinkCtrl::writeQueueFull(const uint64_t packets) const
+{
+    return (totalWriteQueueSize + packets > writeBufferSize);
+}
+
+Tick
+MemSinkCtrl::recvAtomic(PacketPtr pkt)
+{
+    panic_if(pkt->cacheResponding(),
+             "%s Should not see packets where cache is responding\n",
+             __func__);
+
+    access(pkt);
+    return responseLatency;
+}
+
+void
+MemSinkCtrl::recvFunctional(PacketPtr pkt)
+{
+    pkt->pushLabel(name());
+
+    functionalAccess(pkt);
+
+    pkt->popLabel();
+}
+
+BaseSlavePort &
+MemSinkCtrl::getSlavePort(const std::string &interface, PortID idx)
+{
+    if (interface != "port") {
+        return MemObject::getSlavePort(interface, idx);
+    } else {
+        return port;
+    }
+}
+
+bool
+MemSinkCtrl::recvTimingReq(PacketPtr pkt)
+{
+    // Request accepted
+    bool req_accepted = true;
+
+    panic_if(!(pkt->isRead() || pkt->isWrite()),
+             "%s. Should only see "
+             "read and writes at memory controller\n",
+             __func__);
+
+    panic_if(pkt->cacheResponding(),
+             "%s. Should not see packets where cache is responding\n",
+             __func__);
+
+    DPRINTF(QOS,
+            "%s: MASTER %s request %s addr %lld size %d\n",
+            __func__,
+            _system->getMasterName(pkt->req->masterId()),
+            pkt->cmdString(), pkt->getAddr(), pkt->getSize());
+
+    uint64_t required_entries = divCeil(pkt->getSize(), memoryPacketSize);
+
+    assert(required_entries);
+
+    // Schedule packet
+    uint8_t pkt_priority = qosSchedule({&readQueue, &writeQueue},
+                                       memoryPacketSize, pkt);
+
+    if (pkt->isRead()) {
+        if (readQueueFull(required_entries)) {
+            DPRINTF(QOS,
+                    "%s Read queue full, not accepting\n", __func__);
+            // Remember that we have to retry this port
+            retryRdReq = true;
+            numReadRetries++;
+            req_accepted = false;
+        } else {
+            // Enqueue the incoming packet into corresponding
+            // QoS priority queue
+            readQueue.at(pkt_priority).push_back(pkt);
+            queuePolicy->enqueuePacket(pkt);
+        }
+    } else {
+        if (writeQueueFull(required_entries)) {
+            DPRINTF(QOS,
+                    "%s Write queue full, not accepting\n", __func__);
+            // Remember that we have to retry this port
+            retryWrReq = true;
+            numWriteRetries++;
+            req_accepted = false;
+        } else {
+            // Enqueue the incoming packet into corresponding QoS
+            // priority queue
+            writeQueue.at(pkt_priority).push_back(pkt);
+            queuePolicy->enqueuePacket(pkt);
+        }
+    }
+
+    if (req_accepted) {
+        // The packet is accepted - log it
+        logRequest(pkt->isRead()? READ : WRITE,
+                   pkt->req->masterId(),
+                   pkt->qosValue(),
+                   pkt->getAddr(),
+                   required_entries);
+    }
+
+    // Check if we have to process next request event
+    if (!nextReqEvent.scheduled()) {
+        DPRINTF(QOS,
+                "%s scheduling next request at "
+                "time %d (next is %d)\n", __func__,
+                std::max(curTick(), nextRequest), nextRequest);
+        schedule(nextReqEvent, std::max(curTick(), nextRequest));
+    }
+    return req_accepted;
+}
+
+void
+MemSinkCtrl::processNextReqEvent()
+{
+    PacketPtr pkt = nullptr;
+
+    // Evaluate bus direction
+    busStateNext = selectNextBusState();
+
+    // Record turnaround stats and update current state direction
+    recordTurnaroundStats();
+
+    // Set current bus state
+    setCurrentBusState();
+
+    // Access current direction buffer
+    std::vector<PacketQueue>* queue_ptr = (busState == READ ? &readQueue :
+                                                              &writeQueue);
+
+    DPRINTF(QOS,
+            "%s DUMPING %s queues status\n", __func__,
+            (busState == WRITE ? "WRITE" : "READ"));
+
+    if (DTRACE(QOS)) {
+        for (uint8_t i = 0; i < numPriorities(); ++i) {
+            std::string plist = "";
+            for (auto& e : (busState == WRITE ? writeQueue[i]: readQueue[i])) {
+                plist += (std::to_string(e->req->masterId())) + " ";
+            }
+            DPRINTF(QOS,
+                    "%s priority Queue [%i] contains %i elements, "
+                    "packets are: [%s]\n", __func__, i,
+                    busState == WRITE ? writeQueueSizes[i] :
+                                        readQueueSizes[i],
+                    plist);
+        }
+    }
+
+    uint8_t curr_prio = numPriorities();
+
+    for (auto queue = (*queue_ptr).rbegin();
+         queue != (*queue_ptr).rend(); ++queue) {
+
+        curr_prio--;
+
+        DPRINTF(QOS,
+                "%s checking %s queue [%d] priority [%d packets]\n",
+                __func__, (busState == READ? "READ" : "WRITE"),
+                curr_prio, queue->size());
+
+        if (!queue->empty()) {
+            // Call the queue policy to select packet from priority queue
+            auto p_it = queuePolicy->selectPacket(&(*queue));
+            pkt = *p_it;
+            queue->erase(p_it);
+
+            DPRINTF(QOS,
+                    "%s scheduling packet address %d for master %s from "
+                    "priority queue %d\n", __func__, pkt->getAddr(),
+                    _system->getMasterName(pkt->req->masterId()),
+                    curr_prio);
+            break;
+        }
+    }
+
+    assert(pkt);
+
+    // Setup next request service time - do it here as retry request
+    // hands over control to the port
+    nextRequest = curTick() + requestLatency;
+
+    uint64_t removed_entries = divCeil(pkt->getSize(), memoryPacketSize);
+
+    DPRINTF(QOS,
+            "%s scheduled packet address %d for master %s size is %d, "
+            "corresponds to %d memory packets\n", __func__, pkt->getAddr(),
+            _system->getMasterName(pkt->req->masterId()),
+            pkt->getSize(), removed_entries);
+
+    // Schedule response
+    panic_if(!pkt->needsResponse(),
+        "%s response not required\n", __func__);
+
+    // Do the actual memory access which also turns the packet
+    // into a response
+    access(pkt);
+
+    // Log the response
+    logResponse(pkt->isRead()? READ : WRITE,
+                pkt->req->masterId(),
+                pkt->qosValue(),
+                pkt->getAddr(),
+                removed_entries, responseLatency);
+
+    // Schedule the response
+    port.schedTimingResp(pkt, curTick() + responseLatency, true);
+    DPRINTF(QOS,
+            "%s response scheduled at time %d\n",
+            __func__, curTick() + responseLatency);
+
+    // Finally - handle retry requests - this handles control
+    // to the port, so do it last
+    if (busState == READ && retryRdReq) {
+        retryRdReq = false;
+        port.sendRetryReq();
+    } else if (busState == WRITE && retryWrReq) {
+        retryWrReq = false;
+        port.sendRetryReq();
+    }
+
+    // Check if we have to schedule another request event
+    if ((totalReadQueueSize || totalWriteQueueSize) &&
+        !nextReqEvent.scheduled()) {
+
+        schedule(nextReqEvent, curTick() + requestLatency);
+        DPRINTF(QOS,
+                "%s scheduling next request event at tick %d\n",
+                __func__, curTick() + requestLatency);
+    }
+}
+
+DrainState
+MemSinkCtrl::drain()
+{
+    if (totalReadQueueSize || totalWriteQueueSize) {
+        DPRINTF(Drain,
+                "%s queues have requests, waiting to drain\n",
+                __func__);
+        return DrainState::Draining;
+    } else {
+        return DrainState::Drained;
+    }
+}
+
+void
+MemSinkCtrl::regStats()
+{
+    MemCtrl::regStats();
+
+    // Initialize all the stats
+    using namespace Stats;
+
+    numReadRetries.name(name() + ".numReadRetries")
+        .desc("Number of read retries");
+    numWriteRetries.name(name() + ".numWriteRetries")
+        .desc("Number of write retries");
+}
+
+MemSinkCtrl::MemoryPort::MemoryPort(const std::string& n,
+                                    MemSinkCtrl& m)
+  : QueuedSlavePort(n, &m, queue), memory(m), queue(memory, *this)
+{}
+
+AddrRangeList
+MemSinkCtrl::MemoryPort::getAddrRanges() const
+{
+    AddrRangeList ranges;
+    ranges.push_back(memory.getAddrRange());
+    return ranges;
+}
+
+Tick
+MemSinkCtrl::MemoryPort::recvAtomic(PacketPtr pkt)
+{
+    return memory.recvAtomic(pkt);
+}
+
+void
+MemSinkCtrl::MemoryPort::recvFunctional(PacketPtr pkt)
+{
+    pkt->pushLabel(memory.name());
+
+    if (!queue.trySatisfyFunctional(pkt)) {
+        // Default implementation of SimpleTimingPort::recvFunctional()
+        // calls recvAtomic() and throws away the latency; we can save a
+        // little here by just not calculating the latency.
+        memory.recvFunctional(pkt);
+    }
+
+    pkt->popLabel();
+}
+
+bool
+MemSinkCtrl::MemoryPort::recvTimingReq(PacketPtr pkt)
+{
+    return memory.recvTimingReq(pkt);
+}
+
+} // namespace QoS
+
+QoS::MemSinkCtrl*
+QoSMemSinkCtrlParams::create()
+{
+    return new QoS::MemSinkCtrl(this);
+}
+
diff --git a/src/mem/qos/mem_sink.hh b/src/mem/qos/mem_sink.hh
new file mode 100644 (file)
index 0000000..84258e0
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2018 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.
+ *
+ * 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.
+ *
+ * Author: Matteo Andreozzi
+ */
+
+
+#ifndef __MEM_QOS_MEM_SINK_HH__
+#define __MEM_QOS_MEM_SINK_HH__
+
+#include "mem/qos/mem_ctrl.hh"
+#include "mem/qport.hh"
+#include "params/QoSMemSinkCtrl.hh"
+
+namespace QoS {
+
+/**
+ * QoS Memory Sink
+ *
+ * The QoS Memory Sink is a lightweight memory controller with QoS
+ * support. It is meant to provide a QoS aware simple memory system
+ * without the need of using a complex DRAM memory controller
+ */
+class MemSinkCtrl : public MemCtrl
+{
+  protected:
+    /**
+     * The Request packets are store in a multiple dequeue structure,
+     * based on their QoS priority
+     */
+    using PacketQueue = std::deque<PacketPtr>;
+
+  private:
+    class MemoryPort : public QueuedSlavePort
+    {
+      private:
+        /** reference to parent memory object */
+        MemSinkCtrl& memory;
+
+        /** Outgoing packet responses queue */
+        RespPacketQueue queue;
+
+      public:
+       /**
+        * Constructor
+        *
+        * @param n port name
+        * @param m reference to ProfileGen parent object
+        */
+        MemoryPort(const std::string&, MemSinkCtrl&);
+
+      protected:
+       /**
+        * Receive a Packet in Atomic mode
+        *
+        * @param pkt pointer to memory packet
+        * @return packet access latency in ticks
+        */
+        Tick recvAtomic(PacketPtr pkt);
+
+        /**
+        * Receive a Packet in Functional mode
+        *
+        * @param pkt pointer to memory packet
+        */
+        void recvFunctional(PacketPtr pkt);
+
+        /**
+        * Receive a Packet in Timing mode
+        *
+        * @param pkt pointer to memory packet
+        * @return true if the request was accepted
+        */
+        bool recvTimingReq(PacketPtr pkt);
+
+        /**
+         * Gets the configured address ranges for this port
+         * @return the configured address ranges for this port
+         */
+        AddrRangeList getAddrRanges() const;
+
+    };
+
+  public:
+    /**
+     * QoS Memory Sink Constructor
+     *
+     * @param p QoS Memory Sink configuration parameters
+     */
+    MemSinkCtrl(const QoSMemSinkCtrlParams*);
+
+    virtual ~MemSinkCtrl();
+
+    /**
+     * Checks and return the Drain state of this SimObject
+     * @return current Drain state
+     */
+    DrainState drain() override;
+
+    /**
+     * Getter method to access this memory's slave port
+     *
+     * @param interface interface name
+     * @param idx port ID number
+     * @return reference to this memory's slave port
+     */
+    BaseSlavePort& getSlavePort(const std::string&,
+            PortID = InvalidPortID) override;
+
+    /**
+     * Initializes this object
+     */
+    void init() override;
+
+  protected:
+    /** Memory between requests latency (ticks) */
+    const Tick requestLatency;
+
+    /** Memory response latency (ticks) */
+    const Tick responseLatency;
+
+    /** Memory packet size in bytes */
+    const uint64_t memoryPacketSize;
+
+    /** Read request packets queue buffer size in #packets */
+    const uint64_t readBufferSize;
+
+    /** Write request packets queue buffer size in #packets */
+    const uint64_t writeBufferSize;
+
+    /** Memory slave port */
+    MemoryPort port;
+
+    /** Read request pending */
+    bool retryRdReq;
+
+    /** Write request pending */
+    bool retryWrReq;
+
+    /** Next request service time */
+    Tick nextRequest;
+
+    /** Count the number of read retries */
+    Stats::Scalar numReadRetries;
+
+    /** Count the number of write retries */
+    Stats::Scalar numWriteRetries;
+
+    /**
+     * QoS-aware (per priority) incoming read requests packets queue
+     */
+    std::vector<PacketQueue> readQueue;
+
+    /**
+     * QoS-aware (per priority) incoming read requests packets queue
+     */
+    std::vector<PacketQueue> writeQueue;
+
+    /**
+     * Processes the next Request event according to configured
+     * request latency
+     */
+    void processNextReqEvent();
+
+    /** Event wrapper to schedule next request handler function */
+    EventWrapper<
+        MemSinkCtrl,
+        &MemSinkCtrl::processNextReqEvent> nextReqEvent;
+
+    /**
+     * Check if the read queue has room for more entries
+     *
+     * @param packets The number of entries needed in the read queue
+     * @return true if read queue is full, false otherwise
+     */
+    inline bool readQueueFull(const uint64_t packets) const;
+
+    /**
+     * Check if the write queue has room for more entries
+     *
+     * @param packets  The number of entries needed in the write queue
+     * @return true if write queue is full, false otherwise
+     */
+    inline bool writeQueueFull(const uint64_t packets) const;
+
+    /**
+     * Receive a Packet in Atomic mode
+     *
+     * @param pkt pointer to memory packet
+     * @return packet access latency in ticks
+     */
+    Tick recvAtomic(PacketPtr pkt);
+
+    /**
+     * Receive a Packet in Functional mode
+     *
+     * @param pkt pointer to memory packet
+     */
+    void recvFunctional(PacketPtr pkt);
+
+   /**
+    * Receive a Packet in Timing mode
+    *
+    * @param pkt pointer to memory packet
+    * @return true if the request was accepted
+    */
+    bool recvTimingReq(PacketPtr pkt);
+
+    /** Registers statistics */
+    void regStats() override;
+};
+
+} // namespace QoS
+
+#endif /* __MEM_QOS_MEM_SINK_HH__ */