2 * Copyright (c) 2020 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include "debug/QOS.hh"
39 #include "mem/qos/policy.hh"
40 #include "mem/qos/q_policy.hh"
41 #include "params/QoSMemCtrl.hh"
42 #include "sim/clocked_object.hh"
43 #include "sim/system.hh"
45 #include <unordered_map>
49 #ifndef __MEM_QOS_MEM_CTRL_HH__
50 #define __MEM_QOS_MEM_CTRL_HH__
55 * The QoS::MemCtrl is a base class for Memory objects
56 * which support QoS - it provides access to a set of QoS
59 class MemCtrl : public ClockedObject
63 enum BusState { READ, WRITE };
66 /** QoS Policy, assigns QoS priority to the incoming packets */
67 const std::unique_ptr<Policy> policy;
69 /** QoS Bus Turnaround Policy: selects the bus direction (READ/WRITE) */
70 const std::unique_ptr<TurnaroundPolicy> turnPolicy;
72 /** QoS Queue Policy: selects packet among same-priority queue */
73 const std::unique_ptr<QueuePolicy> queuePolicy;
75 /** Number of configured QoS priorities */
76 const uint8_t _numPriorities;
78 /** Enables QoS priority escalation */
79 const bool qosPriorityEscalation;
82 * Enables QoS synchronized scheduling invokes the QoS scheduler
83 * on all requestors, at every packet arrival.
85 const bool qosSyncroScheduler;
87 /** Hash of requestor ID - requestor name */
88 std::unordered_map<RequestorID, const std::string> requestors;
90 /** Hash of requestors - number of packets queued per priority */
91 std::unordered_map<RequestorID, std::vector<uint64_t> > packetPriorities;
93 /** Hash of requestors - address of request - queue of times of request */
94 std::unordered_map<RequestorID,
95 std::unordered_map<uint64_t, std::deque<uint64_t>> > requestTimes;
98 * Vector of QoS priorities/last service time. Refreshed at every
101 std::vector<Tick> serviceTick;
103 /** Read request packets queue length in #packets, per QoS priority */
104 std::vector<uint64_t> readQueueSizes;
106 /** Write request packets queue length in #packets, per QoS priority */
107 std::vector<uint64_t> writeQueueSizes;
109 /** Total read request packets queue length in #packets */
110 uint64_t totalReadQueueSize;
112 /** Total write request packets queue length in #packets */
113 uint64_t totalWriteQueueSize;
116 * Bus state used to control the read/write switching and drive
117 * the scheduling of the next request.
121 /** bus state for next request event triggered */
122 BusState busStateNext;
124 struct MemCtrlStats : public Stats::Group
126 MemCtrlStats(MemCtrl &mc);
128 void regStats() override;
130 const MemCtrl &memCtrl;
132 /** per-requestor average QoS priority */
133 Stats::VectorStandardDeviation avgPriority;
135 * per-requestor average QoS distance between assigned and
138 Stats::VectorStandardDeviation avgPriorityDistance;
140 /** per-priority minimum latency */
141 Stats::Vector priorityMinLatency;
142 /** per-priority maximum latency */
143 Stats::Vector priorityMaxLatency;
144 /** Count the number of turnarounds READ to WRITE */
145 Stats::Scalar numReadWriteTurnArounds;
146 /** Count the number of turnarounds WRITE to READ */
147 Stats::Scalar numWriteReadTurnArounds;
148 /** Count the number of times bus staying in READ state */
149 Stats::Scalar numStayReadState;
150 /** Count the number of times bus staying in WRITE state */
151 Stats::Scalar numStayWriteState;
154 /** Pointer to the System object */
158 * Initializes dynamically counters and
159 * statistics for a given Requestor
161 * @param id the requestor's ID
163 void addRequestor(const RequestorID id);
166 * Called upon receiving a request or
167 * updates statistics and updates queues status
169 * @param dir request direction
170 * @param id requestor id
171 * @param qos packet qos value
172 * @param addr packet address
173 * @param entries number of entries to record
175 void logRequest(BusState dir, RequestorID id, uint8_t qos,
176 Addr addr, uint64_t entries);
179 * Called upon receiving a response,
180 * updates statistics and updates queues status
182 * @param dir response direction
183 * @param id requestor id
184 * @param qos packet qos value
185 * @param addr packet address
186 * @param entries number of entries to record
187 * @param delay response delay
189 void logResponse(BusState dir, RequestorID id, uint8_t qos,
190 Addr addr, uint64_t entries, double delay);
193 * Assign priority to a packet by executing
194 * the configured QoS policy.
196 * @param queues_ptr list of pointers to packet queues
197 * @param queue_entry_size size in bytes per each packet in the queue
198 * @param pkt pointer to the Packet
199 * @return a QoS priority value
201 template<typename Queues>
202 uint8_t qosSchedule(std::initializer_list<Queues*> queues_ptr,
203 uint64_t queue_entry_size, const PacketPtr pkt);
205 using SimObject::schedule;
206 uint8_t schedule(RequestorID id, uint64_t data);
207 uint8_t schedule(const PacketPtr pkt);
210 * Returns next bus direction (READ or WRITE)
211 * based on configured policy.
213 BusState selectNextBusState();
216 * Set current bus direction (READ or WRITE)
217 * from next selected one
219 void setCurrentBusState() { busState = busStateNext; }
222 * Record statistics on turnarounds based on
223 * busStateNext and busState values
225 void recordTurnaroundStats();
228 * Escalates/demotes priority of all packets
229 * belonging to the passed requestor to given
232 * @param queues list of pointers to packet queues
233 * @param queue_entry_size size of an entry in the queue
234 * @param id requestor whose packets priority will change
235 * @param tgt_prio target priority value
237 template<typename Queues>
238 void escalate(std::initializer_list<Queues*> queues,
239 uint64_t queue_entry_size,
240 RequestorID id, uint8_t tgt_prio);
243 * Escalates/demotes priority of all packets
244 * belonging to the passed requestor to given
245 * priority value in a specified cluster of queues
246 * (e.g. read queues or write queues) which is passed
247 * as an argument to the function.
248 * The curr_prio/tgt_prio parameters are queue selectors in the
251 * @param queues reference to packet queues
252 * @param queue_entry_size size of an entry in the queue
253 * @param id requestor whose packets priority will change
254 * @param curr_prio source queue priority value
255 * @param tgt_prio target queue priority value
257 template<typename Queues>
258 void escalateQueues(Queues& queues, uint64_t queue_entry_size,
259 RequestorID id, uint8_t curr_prio, uint8_t tgt_prio);
263 * QoS Memory base class
265 * @param p pointer to QoSMemCtrl parameters
267 MemCtrl(const QoSMemCtrlParams*);
272 * Gets the current bus state
274 * @return current bus state
276 BusState getBusState() const { return busState; }
279 * Gets the next bus state
281 * @return next bus state
283 BusState getBusStateNext() const { return busStateNext; }
286 * hasRequestor returns true if the selected requestor(ID) has
287 * been registered in the memory controller, which happens if
288 * the memory controller has received at least a packet from
291 * @param id requestor id to lookup
292 * @return true if the memory controller has received a packet
293 * from the requestor, false otherwise.
295 bool hasRequestor(RequestorID id) const
297 return requestors.find(id) != requestors.end();
301 * Gets a READ queue size
303 * @param prio QoS Priority of the queue
304 * @return queue size in packets
306 uint64_t getReadQueueSize(const uint8_t prio) const
307 { return readQueueSizes[prio]; }
310 * Gets a WRITE queue size
312 * @param prio QoS Priority of the queue
313 * @return queue size in packets
315 uint64_t getWriteQueueSize(const uint8_t prio) const
316 { return writeQueueSizes[prio]; }
319 * Gets the total combined READ queues size
321 * @return total queues size in packets
323 uint64_t getTotalReadQueueSize() const { return totalReadQueueSize; }
326 * Gets the total combined WRITE queues size
328 * @return total queues size in packets
330 uint64_t getTotalWriteQueueSize() const { return totalWriteQueueSize; }
333 * Gets the last service tick related to a QoS Priority
335 * @param prio QoS Priority
338 Tick getServiceTick(const uint8_t prio) const { return serviceTick[prio]; }
341 * Gets the total number of priority levels in the
342 * QoS memory controller.
344 * @return total number of priority levels
346 uint8_t numPriorities() const { return _numPriorities; }
348 /** read the system pointer
349 * @return pointer to the system object */
350 System* system() const { return _system; }
353 template<typename Queues>
355 MemCtrl::escalateQueues(Queues& queues, uint64_t queue_entry_size,
356 RequestorID id, uint8_t curr_prio, uint8_t tgt_prio)
358 auto it = queues[curr_prio].begin();
359 while (it != queues[curr_prio].end()) {
360 // No packets left to move
361 if (packetPriorities[id][curr_prio] == 0)
367 "QoSMemCtrl::escalate checking priority %d packet "
368 "id %d address %d\n", curr_prio,
369 pkt->requestorId(), pkt->getAddr());
371 // Found a packet to move
372 if (pkt->requestorId() == id) {
374 uint64_t moved_entries = divCeil(pkt->getSize(),
378 "QoSMemCtrl::escalate Requestor %s [id %d] moving "
379 "packet addr %d size %d (p size %d) from priority %d "
381 "this requestor packets %d (entries to move %d)\n",
382 requestors[id], id, pkt->getAddr(),
384 queue_entry_size, curr_prio, tgt_prio,
385 packetPriorities[id][curr_prio], moved_entries);
389 panic_if(readQueueSizes[curr_prio] < moved_entries,
390 "QoSMemCtrl::escalate requestor %s negative READ "
391 "packets for priority %d",
392 requestors[id], tgt_prio);
393 readQueueSizes[curr_prio] -= moved_entries;
394 readQueueSizes[tgt_prio] += moved_entries;
395 } else if (pkt->isWrite()) {
396 panic_if(writeQueueSizes[curr_prio] < moved_entries,
397 "QoSMemCtrl::escalate requestor %s negative WRITE "
398 "packets for priority %d",
399 requestors[id], tgt_prio);
400 writeQueueSizes[curr_prio] -= moved_entries;
401 writeQueueSizes[tgt_prio] += moved_entries;
404 // Change QoS priority and move packet
405 pkt->qosValue(tgt_prio);
406 queues[tgt_prio].push_back(pkt);
408 // Erase element from source packet queue, this will
409 // increment the iterator
410 it = queues[curr_prio].erase(it);
411 panic_if(packetPriorities[id][curr_prio] < moved_entries,
412 "QoSMemCtrl::escalate requestor %s negative packets "
414 requestors[id], tgt_prio);
416 packetPriorities[id][curr_prio] -= moved_entries;
417 packetPriorities[id][tgt_prio] += moved_entries;
419 // Increment iterator to next location in the queue
425 template<typename Queues>
427 MemCtrl::escalate(std::initializer_list<Queues*> queues,
428 uint64_t queue_entry_size,
429 RequestorID id, uint8_t tgt_prio)
431 // If needed, initialize all counters and statistics
432 // for this requestor
436 "QoSMemCtrl::escalate Requestor %s [id %d] to priority "
437 "%d (currently %d packets)\n",requestors[id], id, tgt_prio,
438 packetPriorities[id][tgt_prio]);
440 for (uint8_t curr_prio = 0; curr_prio < numPriorities(); ++curr_prio) {
441 // Skip target priority
442 if (curr_prio == tgt_prio)
445 // Process other priority packet
446 while (packetPriorities[id][curr_prio] > 0) {
448 "QoSMemCtrl::escalate MID %d checking priority %d "
449 "(packets %d)- current packets in prio %d: %d\n"
450 "\t(source read %d source write %d target read %d, "
451 "target write %d)\n",
452 id, curr_prio, packetPriorities[id][curr_prio],
453 tgt_prio, packetPriorities[id][tgt_prio],
454 readQueueSizes[curr_prio],
455 writeQueueSizes[curr_prio], readQueueSizes[tgt_prio],
456 writeQueueSizes[tgt_prio]);
458 // Check both read and write queue
459 for (auto q : queues) {
460 escalateQueues(*q, queue_entry_size, id,
461 curr_prio, tgt_prio);
467 "QoSMemCtrl::escalate Completed requestor %s [id %d] to priority "
468 "%d (now %d packets)\n\t(total read %d, total write %d)\n",
469 requestors[id], id, tgt_prio, packetPriorities[id][tgt_prio],
470 readQueueSizes[tgt_prio], writeQueueSizes[tgt_prio]);
473 template<typename Queues>
475 MemCtrl::qosSchedule(std::initializer_list<Queues*> queues,
476 const uint64_t queue_entry_size,
480 uint8_t pkt_priority = schedule(pkt);
482 assert(pkt_priority < numPriorities());
484 pkt->qosValue(pkt_priority);
486 if (qosSyncroScheduler) {
487 // Call the scheduling function on all other requestors.
488 for (const auto& requestor : requestors) {
490 if (requestor.first == pkt->requestorId())
493 uint8_t prio = schedule(requestor.first, 0);
495 if (qosPriorityEscalation) {
497 "QoSMemCtrl::qosSchedule: (syncro) escalating "
498 "REQUESTOR %s to assigned priority %d\n",
499 _system->getRequestorName(requestor.first),
501 escalate(queues, queue_entry_size, requestor.first, prio);
506 if (qosPriorityEscalation) {
508 "QoSMemCtrl::qosSchedule: escalating "
509 "REQUESTOR %s to assigned priority %d\n",
510 _system->getRequestorName(pkt->requestorId()),
512 escalate(queues, queue_entry_size, pkt->requestorId(), pkt_priority);
515 // Update last service tick for selected priority
516 serviceTick[pkt_priority] = curTick();
523 #endif /* __MEM_QOS_MEM_CTRL_HH__ */