--- /dev/null
+# 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")
+
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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__ */