summaryrefslogtreecommitdiff
path: root/src/mem/qos/mem_ctrl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/qos/mem_ctrl.cc')
-rw-r--r--src/mem/qos/mem_ctrl.cc355
1 files changed, 355 insertions, 0 deletions
diff --git a/src/mem/qos/mem_ctrl.cc b/src/mem/qos/mem_ctrl.cc
new file mode 100644
index 000000000..ce15fd779
--- /dev/null
+++ b/src/mem/qos/mem_ctrl.cc
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2017 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 "mem_ctrl.hh"
+
+#include "turnaround_policy.hh"
+
+namespace QoS {
+
+MemCtrl::MemCtrl(const QoSMemCtrlParams * p)
+ : AbstractMemory(p),
+ policy(p->qos_policy),
+ turnPolicy(p->qos_turnaround_policy),
+ queuePolicy(QueuePolicy::create(p)),
+ _numPriorities(p->qos_priorities),
+ qosPriorityEscalation(p->qos_priority_escalation),
+ qosSyncroScheduler(p->qos_syncro_scheduler),
+ totalReadQueueSize(0), totalWriteQueueSize(0),
+ busState(READ), busStateNext(READ)
+{
+ // Set the priority policy
+ if (policy) {
+ policy->setMemCtrl(this);
+ }
+
+ // Set the queue priority policy
+ if (queuePolicy) {
+ queuePolicy->setMemCtrl(this);
+ }
+
+ // Set the bus turnaround policy
+ if (turnPolicy) {
+ turnPolicy->setMemCtrl(this);
+ }
+
+ readQueueSizes.resize(_numPriorities);
+ writeQueueSizes.resize(_numPriorities);
+ serviceTick.resize(_numPriorities);
+}
+
+MemCtrl::~MemCtrl()
+{}
+
+void
+MemCtrl::init()
+{
+ AbstractMemory::init();
+}
+
+void
+MemCtrl::logRequest(BusState dir, MasterID m_id, uint8_t qos,
+ Addr addr, uint64_t entries)
+{
+ // If needed, initialize all counters and statistics
+ // for this master
+ addMaster(m_id);
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::logRequest MASTER %s [id %d] address %d"
+ " prio %d this master q packets %d"
+ " - queue size %d - requested entries %d\n",
+ masters[m_id], m_id, addr, qos, packetPriorities[m_id][qos],
+ (dir == READ) ? readQueueSizes[qos]: writeQueueSizes[qos],
+ entries);
+
+ if (dir == READ) {
+ readQueueSizes[qos] += entries;
+ totalReadQueueSize += entries;
+ } else if (dir == WRITE) {
+ writeQueueSizes[qos] += entries;
+ totalWriteQueueSize += entries;
+ }
+
+ packetPriorities[m_id][qos] += entries;
+ for (auto j = 0; j < entries; ++j) {
+ requestTimes[m_id][addr].push_back(curTick());
+ }
+
+ // Record statistics
+ avgPriority[m_id].sample(qos);
+
+ // Compute avg priority distance
+
+ for (uint8_t i = 0; i < packetPriorities[m_id].size(); ++i) {
+ uint8_t distance =
+ (abs(int(qos) - int(i))) * packetPriorities[m_id][i];
+
+ if (distance > 0) {
+ avgPriorityDistance[m_id].sample(distance);
+ DPRINTF(QOS,
+ "QoSMemCtrl::logRequest MASTER %s [id %d]"
+ " registering priority distance %d for priority %d"
+ " (packets %d)\n",
+ masters[m_id], m_id, distance, i,
+ packetPriorities[m_id][i]);
+ }
+ }
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::logRequest MASTER %s [id %d] prio %d "
+ "this master q packets %d - new queue size %d\n",
+ masters[m_id], m_id, qos, packetPriorities[m_id][qos],
+ (dir == READ) ? readQueueSizes[qos]: writeQueueSizes[qos]);
+
+}
+
+void
+MemCtrl::logResponse(BusState dir, MasterID m_id, uint8_t qos,
+ Addr addr, uint64_t entries, double delay)
+{
+ panic_if(!hasMaster(m_id),
+ "Logging response with invalid master\n");
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::logResponse MASTER %s [id %d] address %d prio"
+ " %d this master q packets %d"
+ " - queue size %d - requested entries %d\n",
+ masters[m_id], m_id, addr, qos, packetPriorities[m_id][qos],
+ (dir == READ) ? readQueueSizes[qos]: writeQueueSizes[qos],
+ entries);
+
+ if (dir == READ) {
+ readQueueSizes[qos] -= entries;
+ totalReadQueueSize -= entries;
+ } else if (dir == WRITE) {
+ writeQueueSizes[qos] -= entries;
+ totalWriteQueueSize -= entries;
+ }
+
+ panic_if(packetPriorities[m_id][qos] == 0,
+ "QoSMemCtrl::logResponse master %s negative packets for priority"
+ " %d", masters[m_id], qos);
+
+ packetPriorities[m_id][qos] -= entries;
+
+ for (auto j = 0; j < entries; ++j) {
+ auto it = requestTimes[m_id].find(addr);
+ panic_if(it == requestTimes[m_id].end(),
+ "QoSMemCtrl::logResponse master %s unmatched response for"
+ " address %d received", masters[m_id], addr);
+
+ // Load request time
+ uint64_t requestTime = it->second.front();
+
+ // Remove request entry
+ it->second.pop_front();
+
+ // Remove whole address entry if last one
+ if (it->second.empty()) {
+ requestTimes[m_id].erase(it);
+ }
+ // Compute latency
+ double latency = (double) (curTick() + delay - requestTime)
+ / SimClock::Float::s;
+
+ if (latency > 0) {
+ // Record per-priority latency stats
+ if (priorityMaxLatency[qos].value() < latency) {
+ priorityMaxLatency[qos] = latency;
+ }
+
+ if (priorityMinLatency[qos].value() > latency
+ || priorityMinLatency[qos].value() == 0) {
+ priorityMinLatency[qos] = latency;
+ }
+ }
+ }
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::logResponse MASTER %s [id %d] prio %d "
+ "this master q packets %d - new queue size %d\n",
+ masters[m_id], m_id, qos, packetPriorities[m_id][qos],
+ (dir == READ) ? readQueueSizes[qos]: writeQueueSizes[qos]);
+}
+
+uint8_t
+MemCtrl::schedule(MasterID m_id, uint64_t data)
+{
+ if (policy) {
+ return policy->schedule(m_id, data);
+ } else {
+ DPRINTF(QOS,
+ "QoSScheduler::schedule master ID [%d] "
+ "data received [%d], but QoS scheduler not initialized\n",
+ m_id,data);
+ return 0;
+ }
+}
+
+uint8_t
+MemCtrl::schedule(const PacketPtr pkt)
+{
+ assert(pkt->req);
+
+ if (policy) {
+ return schedule(pkt->req->masterId(), pkt->getSize());
+ } else {
+ DPRINTF(QOS, "QoSScheduler::schedule Packet received [Qv %d], "
+ "but QoS scheduler not initialized\n",
+ pkt->qosValue());
+ return pkt->qosValue();
+ }
+}
+
+MemCtrl::BusState
+MemCtrl::selectNextBusState()
+{
+ auto bus_state = getBusState();
+
+ if (turnPolicy) {
+ DPRINTF(QOS,
+ "QoSMemoryTurnaround::selectBusState running policy %s\n",
+ turnPolicy->name());
+
+ bus_state = turnPolicy->selectBusState();
+ } else {
+ DPRINTF(QOS,
+ "QoSMemoryTurnaround::selectBusState running "
+ "default bus direction selection policy\n");
+
+ if ((!getTotalReadQueueSize() && bus_state == MemCtrl::READ) ||
+ (!getTotalWriteQueueSize() && bus_state == MemCtrl::WRITE)) {
+ // READ/WRITE turnaround
+ bus_state = (bus_state == MemCtrl::READ) ? MemCtrl::WRITE :
+ MemCtrl::READ;
+
+ }
+ }
+
+ return bus_state;
+}
+
+void
+MemCtrl::addMaster(MasterID m_id)
+{
+ if (!hasMaster(m_id)) {
+ masters.emplace(m_id, _system->getMasterName(m_id));
+ packetPriorities[m_id].resize(numPriorities(), 0);
+
+ DPRINTF(QOS,
+ "QoSMemCtrl::addMaster registering"
+ " Master %s [id %d]\n",
+ masters[m_id], m_id);
+ }
+}
+
+void
+MemCtrl::regStats()
+{
+ AbstractMemory::regStats();
+
+ using namespace Stats;
+
+ // Initializes per master statistics
+ avgPriority.init(_system->maxMasters()).name(name() + ".avgPriority")
+ .desc("Average QoS priority value for accepted requests")
+ .flags(nozero | nonan).precision(2);
+
+ avgPriorityDistance.init(_system->maxMasters())
+ .name(name() + ".avgPriorityDistance")
+ .desc("Average QoS priority distance between assigned and "
+ "queued values").flags(nozero | nonan);
+
+ priorityMinLatency.init(numPriorities())
+ .name(name() + ".priorityMinLatency")
+ .desc("per QoS priority minimum request to response latency (s)")
+ .precision(12);
+
+ priorityMaxLatency.init(numPriorities())
+ .name(name() + ".priorityMaxLatency")
+ .desc("per QoS priority maximum request to response latency (s)")
+ .precision(12);
+
+ numReadWriteTurnArounds.name(name() + ".numReadWriteTurnArounds")
+ .desc("Number of turnarounds from READ to WRITE");
+
+ numWriteReadTurnArounds.name(name() + ".numWriteReadTurnArounds")
+ .desc("Number of turnarounds from WRITE to READ");
+
+ numStayReadState.name(name() + ".numStayReadState")
+ .desc("Number of times bus staying in READ state");
+
+ numStayWriteState.name(name() + ".numStayWriteState")
+ .desc("Number of times bus staying in WRITE state");
+
+ for (int i = 0; i < _system->maxMasters(); i++) {
+ const std::string master = _system->getMasterName(i);
+ avgPriority.subname(i, master);
+ avgPriorityDistance.subname(i, master);
+ }
+
+ for (int j = 0; j < numPriorities(); ++j) {
+ priorityMinLatency.subname(j, std::to_string(j));
+ priorityMaxLatency.subname(j, std::to_string(j));
+ }
+
+ if (policy)
+ policy->regStats();
+}
+
+void
+MemCtrl::recordTurnaroundStats()
+{
+ if (busStateNext != busState) {
+ if (busState == READ) {
+ numWriteReadTurnArounds++;
+ } else if (busState == WRITE) {
+ numReadWriteTurnArounds++;
+ }
+ } else {
+ if (busState == READ) {
+ numStayReadState++;
+ } else if (busState == WRITE) {
+ numStayWriteState++;
+ }
+ }
+}
+
+} // namespace QoS