summaryrefslogtreecommitdiff
path: root/src/mem/qos/mem_ctrl.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/qos/mem_ctrl.hh')
-rw-r--r--src/mem/qos/mem_ctrl.hh514
1 files changed, 514 insertions, 0 deletions
diff --git a/src/mem/qos/mem_ctrl.hh b/src/mem/qos/mem_ctrl.hh
new file mode 100644
index 000000000..db85f276d
--- /dev/null
+++ b/src/mem/qos/mem_ctrl.hh
@@ -0,0 +1,514 @@
+/*
+ * 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.
+ *
+ * Authors: Matteo Andreozzi
+ */
+
+#include "debug/QOS.hh"
+#include "mem/abstract_mem.hh"
+#include "mem/qos/q_policy.hh"
+#include "mem/qos/policy.hh"
+#include "params/QoSMemCtrl.hh"
+#include "sim/system.hh"
+
+#include <unordered_map>
+#include <vector>
+#include <deque>
+
+#ifndef __MEM_QOS_MEM_CTRL_HH__
+#define __MEM_QOS_MEM_CTRL_HH__
+
+namespace QoS {
+
+/**
+ * The QoS::MemCtrl is a base class for Memory objects
+ * which support QoS - it provides access to a set of QoS
+ * scheduling policies
+ */
+class MemCtrl: public AbstractMemory
+{
+ public:
+ /** Bus Direction */
+ enum BusState { READ, WRITE };
+
+ protected:
+ /** QoS Policy, assigns QoS priority to the incoming packets */
+ const std::unique_ptr<Policy> policy;
+
+ /** QoS Bus Turnaround Policy: selects the bus direction (READ/WRITE) */
+ const std::unique_ptr<TurnaroundPolicy> turnPolicy;
+
+ /** QoS Queue Policy: selects packet among same-priority queue */
+ const std::unique_ptr<QueuePolicy> queuePolicy;
+
+ /** Number of configured QoS priorities */
+ const uint8_t _numPriorities;
+
+ /** Enables QoS priority escalation */
+ const bool qosPriorityEscalation;
+
+ /**
+ * Enables QoS synchronized scheduling invokes the QoS scheduler
+ * on all masters, at every packet arrival.
+ */
+ const bool qosSyncroScheduler;
+
+ /** Hash of master ID - master name */
+ std::unordered_map<MasterID, const std::string> masters;
+
+ /** Hash of masters - number of packets queued per priority */
+ std::unordered_map<MasterID, std::vector<uint64_t> > packetPriorities;
+
+ /** Hash of masters - address of request - queue of times of request */
+ std::unordered_map<MasterID,
+ std::unordered_map<uint64_t, std::deque<uint64_t>> > requestTimes;
+
+ /**
+ * Vector of QoS priorities/last service time. Refreshed at every
+ * qosSchedule call.
+ */
+ std::vector<Tick> serviceTick;
+
+ /** Read request packets queue length in #packets, per QoS priority */
+ std::vector<uint64_t> readQueueSizes;
+
+ /** Write request packets queue length in #packets, per QoS priority */
+ std::vector<uint64_t> writeQueueSizes;
+
+ /** Total read request packets queue length in #packets */
+ uint64_t totalReadQueueSize;
+
+ /** Total write request packets queue length in #packets */
+ uint64_t totalWriteQueueSize;
+
+ /**
+ * Bus state used to control the read/write switching and drive
+ * the scheduling of the next request.
+ */
+ BusState busState;
+
+ /** bus state for next request event triggered */
+ BusState busStateNext;
+
+ /** per-master average QoS priority */
+ Stats::VectorStandardDeviation avgPriority;
+ /** per-master average QoS distance between assigned and queued values */
+ Stats::VectorStandardDeviation avgPriorityDistance;
+
+ /** per-priority minimum latency */
+ Stats::Vector priorityMinLatency;
+ /** per-priority maximum latency */
+ Stats::Vector priorityMaxLatency;
+ /** Count the number of turnarounds READ to WRITE */
+ Stats::Scalar numReadWriteTurnArounds;
+ /** Count the number of turnarounds WRITE to READ */
+ Stats::Scalar numWriteReadTurnArounds;
+ /** Count the number of times bus staying in READ state */
+ Stats::Scalar numStayReadState;
+ /** Count the number of times bus staying in WRITE state */
+ Stats::Scalar numStayWriteState;
+
+ /** registers statistics */
+ void regStats() override;
+
+ /**
+ * Initializes dynamically counters and
+ * statistics for a given Master
+ *
+ * @param m_id the master ID
+ */
+ void addMaster(const MasterID m_id);
+
+ /**
+ * Called upon receiving a request or
+ * updates statistics and updates queues status
+ *
+ * @param dir request direction
+ * @param m_id master id
+ * @param qos packet qos value
+ * @param addr packet address
+ * @param entries number of entries to record
+ */
+ void logRequest(BusState dir, MasterID m_id, uint8_t qos,
+ Addr addr, uint64_t entries);
+
+ /**
+ * Called upon receiving a response,
+ * updates statistics and updates queues status
+ *
+ * @param dir response direction
+ * @param m_id master id
+ * @param qos packet qos value
+ * @param addr packet address
+ * @param entries number of entries to record
+ * @param delay response delay
+ */
+ void logResponse(BusState dir, MasterID m_id, uint8_t qos,
+ Addr addr, uint64_t entries, double delay);
+
+ /**
+ * Assign priority to a packet by executing
+ * the configured QoS policy.
+ *
+ * @param queues_ptr list of pointers to packet queues
+ * @param queue_entry_size size in bytes per each packet in the queue
+ * @param pkt pointer to the Packet
+ * @return a QoS priority value
+ */
+ template<typename Queues>
+ uint8_t qosSchedule(std::initializer_list<Queues*> queues_ptr,
+ uint64_t queue_entry_size, const PacketPtr pkt);
+
+ using SimObject::schedule;
+ uint8_t schedule(MasterID m_id, uint64_t data);
+ uint8_t schedule(const PacketPtr pkt);
+
+ /**
+ * Returns next bus direction (READ or WRITE)
+ * based on configured policy.
+ */
+ BusState selectNextBusState();
+
+ /**
+ * Set current bus direction (READ or WRITE)
+ * from next selected one
+ */
+ void setCurrentBusState() { busState = busStateNext; }
+
+ /**
+ * Record statistics on turnarounds based on
+ * busStateNext and busState values
+ */
+ void recordTurnaroundStats();
+
+ /**
+ * Escalates/demotes priority of all packets
+ * belonging to the passed master to given
+ * priority value
+ *
+ * @param queues list of pointers to packet queues
+ * @param queue_entry_size size of an entry in the queue
+ * @param m_id master whose packets priority will change
+ * @param tgt_prio target priority value
+ */
+ template<typename Queues>
+ void escalate(std::initializer_list<Queues*> queues,
+ uint64_t queue_entry_size,
+ MasterID m_id, uint8_t tgt_prio);
+
+ /**
+ * Escalates/demotes priority of all packets
+ * belonging to the passed master to given
+ * priority value in a specified cluster of queues
+ * (e.g. read queues or write queues) which is passed
+ * as an argument to the function.
+ * The curr_prio/tgt_prio parameters are queue selectors in the
+ * queue cluster.
+ *
+ * @param queues reference to packet queues
+ * @param queue_entry_size size of an entry in the queue
+ * @param m_id master whose packets priority will change
+ * @param curr_prio source queue priority value
+ * @param tgt_prio target queue priority value
+ */
+ template<typename Queues>
+ void escalateQueues(Queues& queues, uint64_t queue_entry_size,
+ MasterID m_id, uint8_t curr_prio, uint8_t tgt_prio);
+
+ public:
+ /**
+ * QoS Memory base class
+ *
+ * @param p pointer to QoSMemCtrl parameters
+ */
+ MemCtrl(const QoSMemCtrlParams*);
+
+ virtual ~MemCtrl();
+
+ /**
+ * Initializes this object
+ */
+ void init() override;
+
+ /**
+ * Gets the current bus state
+ *
+ * @return current bus state
+ */
+ BusState getBusState() const { return busState; }
+
+ /**
+ * Gets the next bus state
+ *
+ * @return next bus state
+ */
+ BusState getBusStateNext() const { return busStateNext; }
+
+ /**
+ * hasMaster returns true if the selected master(ID) has
+ * been registered in the memory controller, which happens if
+ * the memory controller has received at least a packet from
+ * that master.
+ *
+ * @param m_id master id to lookup
+ * @return true if the memory controller has received a packet
+ * from the master, false otherwise.
+ */
+ bool hasMaster(MasterID m_id) const
+ {
+ return masters.find(m_id) != masters.end();
+ }
+
+ /**
+ * Gets a READ queue size
+ *
+ * @param prio QoS Priority of the queue
+ * @return queue size in packets
+ */
+ uint64_t getReadQueueSize(const uint8_t prio) const
+ { return readQueueSizes[prio]; }
+
+ /**
+ * Gets a WRITE queue size
+ *
+ * @param prio QoS Priority of the queue
+ * @return queue size in packets
+ */
+ uint64_t getWriteQueueSize(const uint8_t prio) const
+ { return writeQueueSizes[prio]; }
+
+ /**
+ * Gets the total combined READ queues size
+ *
+ * @return total queues size in packets
+ */
+ uint64_t getTotalReadQueueSize() const { return totalReadQueueSize; }
+
+ /**
+ * Gets the total combined WRITE queues size
+ *
+ * @return total queues size in packets
+ */
+ uint64_t getTotalWriteQueueSize() const { return totalWriteQueueSize; }
+
+ /**
+ * Gets the last service tick related to a QoS Priority
+ *
+ * @param prio QoS Priority
+ * @return tick
+ */
+ Tick getServiceTick(const uint8_t prio) const { return serviceTick[prio]; }
+
+ /**
+ * Gets the total number of priority levels in the
+ * QoS memory controller.
+ *
+ * @return total number of priority levels
+ */
+ uint8_t numPriorities() const { return _numPriorities; }
+};
+
+template<typename Queues>
+void
+MemCtrl::escalateQueues(Queues& queues, uint64_t queue_entry_size,
+ MasterID m_id, uint8_t curr_prio, uint8_t tgt_prio)
+{
+ auto it = queues[curr_prio].begin();
+ while (it != queues[curr_prio].end()) {
+ // No packets left to move
+ if (packetPriorities[m_id][curr_prio] == 0)
+ break;
+
+ auto pkt = *it;
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::escalate checking priority %d packet "
+ "m_id %d address %d\n", curr_prio,
+ pkt->masterId(), pkt->getAddr());
+
+ // Found a packet to move
+ if (pkt->masterId() == m_id) {
+
+ uint64_t moved_entries = divCeil(pkt->getSize(),
+ queue_entry_size);
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::escalate Master %s [id %d] moving "
+ "packet addr %d size %d (p size %d) from priority %d "
+ "to priority %d - "
+ "this master packets %d (entries to move %d)\n",
+ masters[m_id], m_id, pkt->getAddr(),
+ pkt->getSize(),
+ queue_entry_size, curr_prio, tgt_prio,
+ packetPriorities[m_id][curr_prio], moved_entries);
+
+
+ if (pkt->isRead()) {
+ panic_if(readQueueSizes[curr_prio] < moved_entries,
+ "QoSMemCtrl::escalate master %s negative READ "
+ "packets for priority %d",
+ masters[m_id], tgt_prio);
+ readQueueSizes[curr_prio] -= moved_entries;
+ readQueueSizes[tgt_prio] += moved_entries;
+ } else if (pkt->isWrite()) {
+ panic_if(writeQueueSizes[curr_prio] < moved_entries,
+ "QoSMemCtrl::escalate master %s negative WRITE "
+ "packets for priority %d",
+ masters[m_id], tgt_prio);
+ writeQueueSizes[curr_prio] -= moved_entries;
+ writeQueueSizes[tgt_prio] += moved_entries;
+ }
+
+ // Change QoS priority and move packet
+ pkt->qosValue(tgt_prio);
+ queues[tgt_prio].push_back(pkt);
+
+ // Erase element from source packet queue, this will
+ // increment the iterator
+ it = queues[curr_prio].erase(it);
+ panic_if(packetPriorities[m_id][curr_prio] < moved_entries,
+ "QoSMemCtrl::escalate master %s negative packets "
+ "for priority %d",
+ masters[m_id], tgt_prio);
+
+ packetPriorities[m_id][curr_prio] -= moved_entries;
+ packetPriorities[m_id][tgt_prio] += moved_entries;
+ } else {
+ // Increment iterator to next location in the queue
+ it++;
+ }
+ }
+}
+
+template<typename Queues>
+void
+MemCtrl::escalate(std::initializer_list<Queues*> queues,
+ uint64_t queue_entry_size,
+ MasterID m_id, uint8_t tgt_prio)
+{
+ // If needed, initialize all counters and statistics
+ // for this master
+ addMaster(m_id);
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::escalate Master %s [id %d] to priority "
+ "%d (currently %d packets)\n",masters[m_id], m_id, tgt_prio,
+ packetPriorities[m_id][tgt_prio]);
+
+ for (uint8_t curr_prio = 0; curr_prio < numPriorities(); ++curr_prio) {
+ // Skip target priority
+ if (curr_prio == tgt_prio)
+ continue;
+
+ // Process other priority packet
+ while (packetPriorities[m_id][curr_prio] > 0) {
+ DPRINTF(QOS,
+ "QoSMemCtrl::escalate MID %d checking priority %d "
+ "(packets %d)- current packets in prio %d: %d\n"
+ "\t(source read %d source write %d target read %d, "
+ "target write %d)\n",
+ m_id, curr_prio, packetPriorities[m_id][curr_prio],
+ tgt_prio, packetPriorities[m_id][tgt_prio],
+ readQueueSizes[curr_prio],
+ writeQueueSizes[curr_prio], readQueueSizes[tgt_prio],
+ writeQueueSizes[tgt_prio]);
+
+ // Check both read and write queue
+ for (auto q : queues) {
+ escalateQueues(*q, queue_entry_size, m_id,
+ curr_prio, tgt_prio);
+ }
+ }
+ }
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::escalate Completed master %s [id %d] to priority %d "
+ "(now %d packets)\n\t(total read %d, total write %d)\n",
+ masters[m_id], m_id, tgt_prio, packetPriorities[m_id][tgt_prio],
+ readQueueSizes[tgt_prio], writeQueueSizes[tgt_prio]);
+}
+
+template<typename Queues>
+uint8_t
+MemCtrl::qosSchedule(std::initializer_list<Queues*> queues,
+ const uint64_t queue_entry_size,
+ const PacketPtr pkt)
+{
+ // Schedule packet.
+ uint8_t pkt_priority = schedule(pkt);
+
+ assert(pkt_priority < numPriorities());
+
+ pkt->qosValue(pkt_priority);
+
+ if (qosSyncroScheduler) {
+ // Call the scheduling function on all other masters.
+ for (const auto& m : masters) {
+
+ if (m.first == pkt->masterId())
+ continue;
+
+ uint8_t prio = schedule(m.first, 0);
+
+ if (qosPriorityEscalation) {
+ DPRINTF(QOS,
+ "QoSMemCtrl::qosSchedule: (syncro) escalating "
+ "MASTER %s to assigned priority %d\n",
+ _system->getMasterName(m.first),
+ prio);
+ escalate(queues, queue_entry_size, m.first, prio);
+ }
+ }
+ }
+
+ if (qosPriorityEscalation) {
+ DPRINTF(QOS,
+ "QoSMemCtrl::qosSchedule: escalating "
+ "MASTER %s to assigned priority %d\n",
+ _system->getMasterName(pkt->masterId()),
+ pkt_priority);
+ escalate(queues, queue_entry_size, pkt->masterId(), pkt_priority);
+ }
+
+ // Update last service tick for selected priority
+ serviceTick[pkt_priority] = curTick();
+
+ return pkt_priority;
+}
+
+} // namespace QoS
+
+#endif /* __MEM_QOS_MEM_CTRL_HH__ */