1/*
2 * Copyright (c) 2018 ARM Limited
3 * All rights reserved
4 *
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.
13 *
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.
24 *
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.
36 *
37 * Authors: Matteo Andreozzi
38 */
39
40#include "debug/QOS.hh"
41#include "mem/abstract_mem.hh"
42#include "mem/qos/q_policy.hh"
43#include "mem/qos/policy.hh"
44#include "params/QoSMemCtrl.hh"
45#include "sim/system.hh"
46
47#include <unordered_map>
48#include <vector>
49#include <deque>
50
51#ifndef __MEM_QOS_MEM_CTRL_HH__
52#define __MEM_QOS_MEM_CTRL_HH__
53
54namespace QoS {
55
56/**
57 * The QoS::MemCtrl is a base class for Memory objects
58 * which support QoS - it provides access to a set of QoS
59 * scheduling policies
60 */
61class MemCtrl: public AbstractMemory
62{
63  public:
64    /** Bus Direction */
65    enum BusState { READ, WRITE };
66
67  protected:
68    /** QoS Policy, assigns QoS priority to the incoming packets */
69    const std::unique_ptr<Policy> policy;
70
71    /** QoS Bus Turnaround Policy: selects the bus direction (READ/WRITE) */
72    const std::unique_ptr<TurnaroundPolicy> turnPolicy;
73
74    /** QoS Queue Policy: selects packet among same-priority queue */
75    const std::unique_ptr<QueuePolicy> queuePolicy;
76
77    /** Number of configured QoS priorities */
78    const uint8_t _numPriorities;
79
80    /** Enables QoS priority escalation */
81    const bool qosPriorityEscalation;
82
83    /**
84     * Enables QoS synchronized scheduling invokes the QoS scheduler
85     * on all masters, at every packet arrival.
86     */
87    const bool qosSyncroScheduler;
88
89    /** Hash of master ID - master name */
90    std::unordered_map<MasterID, const std::string> masters;
91
92    /** Hash of masters - number of packets queued per priority */
93    std::unordered_map<MasterID, std::vector<uint64_t> > packetPriorities;
94
95    /** Hash of masters - address of request - queue of times of request */
96    std::unordered_map<MasterID,
97            std::unordered_map<uint64_t, std::deque<uint64_t>> > requestTimes;
98
99    /**
100     * Vector of QoS priorities/last service time. Refreshed at every
101     * qosSchedule call.
102     */
103    std::vector<Tick> serviceTick;
104
105    /** Read request packets queue length in #packets, per QoS priority */
106    std::vector<uint64_t> readQueueSizes;
107
108    /** Write request packets queue length in #packets, per QoS priority */
109    std::vector<uint64_t> writeQueueSizes;
110
111    /** Total read request packets queue length in #packets */
112    uint64_t totalReadQueueSize;
113
114    /** Total write request packets queue length in #packets */
115    uint64_t totalWriteQueueSize;
116
117    /**
118     * Bus state used to control the read/write switching and drive
119     * the scheduling of the next request.
120     */
121    BusState busState;
122
123    /** bus state for next request event triggered */
124    BusState busStateNext;
125
126    /** per-master average QoS priority */
127    Stats::VectorStandardDeviation avgPriority;
128    /** per-master average QoS distance between assigned and queued values */
129    Stats::VectorStandardDeviation avgPriorityDistance;
130
131    /** per-priority minimum latency */
132    Stats::Vector priorityMinLatency;
133    /** per-priority maximum latency */
134    Stats::Vector priorityMaxLatency;
135    /** Count the number of turnarounds READ to WRITE */
136    Stats::Scalar numReadWriteTurnArounds;
137    /** Count the number of turnarounds WRITE to READ */
138    Stats::Scalar numWriteReadTurnArounds;
139    /** Count the number of times bus staying in READ state */
140    Stats::Scalar numStayReadState;
141    /** Count the number of times bus staying in WRITE state */
142    Stats::Scalar numStayWriteState;
143
144    /** registers statistics */
145    void regStats() override;
146
147    /**
148     * Initializes dynamically counters and
149     * statistics for a given Master
150     *
151     * @param m_id the master ID
152     */
153    void addMaster(const MasterID m_id);
154
155    /**
156     * Called upon receiving a request or
157     * updates statistics and updates queues status
158     *
159     * @param dir request direction
160     * @param m_id master id
161     * @param qos packet qos value
162     * @param addr packet address
163     * @param entries number of entries to record
164     */
165    void logRequest(BusState dir, MasterID m_id, uint8_t qos,
166                    Addr addr, uint64_t entries);
167
168    /**
169     * Called upon receiving a response,
170     * updates statistics and updates queues status
171     *
172     * @param dir response direction
173     * @param m_id master id
174     * @param qos packet qos value
175     * @param addr packet address
176     * @param entries number of entries to record
177     * @param delay response delay
178     */
179    void logResponse(BusState dir, MasterID m_id, uint8_t qos,
180                     Addr addr, uint64_t entries, double delay);
181
182    /**
183     * Assign priority to a packet by executing
184     * the configured QoS policy.
185     *
186     * @param queues_ptr list of pointers to packet queues
187     * @param queue_entry_size size in bytes per each packet in the queue
188     * @param pkt pointer to the Packet
189     * @return a QoS priority value
190     */
191    template<typename Queues>
192    uint8_t qosSchedule(std::initializer_list<Queues*> queues_ptr,
193                        uint64_t queue_entry_size, const PacketPtr pkt);
194
195    using SimObject::schedule;
196    uint8_t schedule(MasterID m_id, uint64_t data);
197    uint8_t schedule(const PacketPtr pkt);
198
199    /**
200     * Returns next bus direction (READ or WRITE)
201     * based on configured policy.
202     */
203    BusState selectNextBusState();
204
205    /**
206     * Set current bus direction (READ or WRITE)
207     * from next selected one
208     */
209    void setCurrentBusState() { busState = busStateNext; }
210
211    /**
212     * Record statistics on turnarounds based on
213     * busStateNext and busState values
214     */
215    void recordTurnaroundStats();
216
217    /**
218     * Escalates/demotes priority of all packets
219     * belonging to the passed master to given
220     * priority value
221     *
222     * @param queues list of pointers to packet queues
223     * @param queue_entry_size size of an entry in the queue
224     * @param m_id master whose packets priority will change
225     * @param tgt_prio target priority value
226     */
227    template<typename Queues>
228    void escalate(std::initializer_list<Queues*> queues,
229                  uint64_t queue_entry_size,
230                  MasterID m_id, uint8_t tgt_prio);
231
232    /**
233     * Escalates/demotes priority of all packets
234     * belonging to the passed master to given
235     * priority value in a specified cluster of queues
236     * (e.g. read queues or write queues) which is passed
237     * as an argument to the function.
238     * The curr_prio/tgt_prio parameters are queue selectors in the
239     * queue cluster.
240     *
241     * @param queues reference to packet queues
242     * @param queue_entry_size size of an entry in the queue
243     * @param m_id master whose packets priority will change
244     * @param curr_prio source queue priority value
245     * @param tgt_prio target queue priority value
246     */
247    template<typename Queues>
248    void escalateQueues(Queues& queues, uint64_t queue_entry_size,
249                        MasterID m_id, uint8_t curr_prio, uint8_t tgt_prio);
250
251  public:
252    /**
253     * QoS Memory base class
254     *
255     * @param p pointer to QoSMemCtrl parameters
256     */
257    MemCtrl(const QoSMemCtrlParams*);
258
259    virtual ~MemCtrl();
260
261    /**
262     * Initializes this object
263     */
264    void init() override;
265
266    /**
267     * Gets the current bus state
268     *
269     * @return current bus state
270     */
271    BusState getBusState() const { return busState; }
272
273    /**
274     * Gets the next bus state
275     *
276     * @return next bus state
277     */
278    BusState getBusStateNext() const { return busStateNext; }
279
280    /**
281     * hasMaster returns true if the selected master(ID) has
282     * been registered in the memory controller, which happens if
283     * the memory controller has received at least a packet from
284     * that master.
285     *
286     * @param m_id master id to lookup
287     * @return true if the memory controller has received a packet
288     *         from the master, false otherwise.
289     */
290    bool hasMaster(MasterID m_id) const
291    {
292        return masters.find(m_id) != masters.end();
293    }
294
295    /**
296     * Gets a READ queue size
297     *
298     * @param prio QoS Priority of the queue
299     * @return queue size in packets
300     */
301    uint64_t getReadQueueSize(const uint8_t prio) const
302    { return readQueueSizes[prio]; }
303
304    /**
305     * Gets a WRITE queue size
306     *
307     * @param prio QoS Priority of the queue
308     * @return queue size in packets
309     */
310    uint64_t getWriteQueueSize(const uint8_t prio) const
311    { return writeQueueSizes[prio]; }
312
313    /**
314     * Gets the total combined READ queues size
315     *
316     * @return total queues size in packets
317     */
318    uint64_t getTotalReadQueueSize() const { return totalReadQueueSize; }
319
320    /**
321     * Gets the total combined WRITE queues size
322     *
323     * @return total queues size in packets
324     */
325    uint64_t getTotalWriteQueueSize() const { return totalWriteQueueSize; }
326
327    /**
328     * Gets the last service tick related to a QoS Priority
329     *
330     * @param prio QoS Priority
331     * @return tick
332     */
333    Tick getServiceTick(const uint8_t prio) const { return serviceTick[prio]; }
334
335    /**
336     * Gets the total number of priority levels in the
337     * QoS memory controller.
338     *
339     * @return total number of priority levels
340     */
341    uint8_t numPriorities() const { return _numPriorities; }
342};
343
344template<typename Queues>
345void
346MemCtrl::escalateQueues(Queues& queues, uint64_t queue_entry_size,
347                        MasterID m_id, uint8_t curr_prio, uint8_t tgt_prio)
348{
349    auto it = queues[curr_prio].begin();
350    while (it != queues[curr_prio].end()) {
351        // No packets left to move
352        if (packetPriorities[m_id][curr_prio] == 0)
353            break;
354
355        auto pkt = *it;
356
357        DPRINTF(QOS,
358                "QoSMemCtrl::escalate checking priority %d packet "
359                "m_id %d address %d\n", curr_prio,
360                pkt->masterId(), pkt->getAddr());
361
362        // Found a packet to move
363        if (pkt->masterId() == m_id) {
364
365            uint64_t moved_entries = divCeil(pkt->getSize(),
366                                             queue_entry_size);
367
368            DPRINTF(QOS,
369                    "QoSMemCtrl::escalate Master %s [id %d] moving "
370                    "packet addr %d size %d (p size %d) from priority %d "
371                    "to priority %d - "
372                    "this master packets %d (entries to move %d)\n",
373                    masters[m_id], m_id, pkt->getAddr(),
374                    pkt->getSize(),
375                    queue_entry_size, curr_prio, tgt_prio,
376                    packetPriorities[m_id][curr_prio], moved_entries);
377
378
379            if (pkt->isRead()) {
380                panic_if(readQueueSizes[curr_prio] < moved_entries,
381                         "QoSMemCtrl::escalate master %s negative READ "
382                         "packets for priority %d",
383                        masters[m_id], tgt_prio);
384                readQueueSizes[curr_prio] -= moved_entries;
385                readQueueSizes[tgt_prio] += moved_entries;
386            } else if (pkt->isWrite()) {
387                panic_if(writeQueueSizes[curr_prio] < moved_entries,
388                         "QoSMemCtrl::escalate master %s negative WRITE "
389                         "packets for priority %d",
390                        masters[m_id], tgt_prio);
391                writeQueueSizes[curr_prio] -= moved_entries;
392                writeQueueSizes[tgt_prio] += moved_entries;
393            }
394
395            // Change QoS priority and move packet
396            pkt->qosValue(tgt_prio);
397            queues[tgt_prio].push_back(pkt);
398
399            // Erase element from source packet queue, this will
400            // increment the iterator
401            it = queues[curr_prio].erase(it);
402            panic_if(packetPriorities[m_id][curr_prio] < moved_entries,
403                     "QoSMemCtrl::escalate master %s negative packets "
404                     "for priority %d",
405                     masters[m_id], tgt_prio);
406
407            packetPriorities[m_id][curr_prio] -= moved_entries;
408            packetPriorities[m_id][tgt_prio] += moved_entries;
409        } else {
410            // Increment iterator to next location in the queue
411            it++;
412        }
413    }
414}
415
416template<typename Queues>
417void
418MemCtrl::escalate(std::initializer_list<Queues*> queues,
419                  uint64_t queue_entry_size,
420                  MasterID m_id, uint8_t tgt_prio)
421{
422    // If needed, initialize all counters and statistics
423    // for this master
424    addMaster(m_id);
425
426    DPRINTF(QOS,
427            "QoSMemCtrl::escalate Master %s [id %d] to priority "
428            "%d (currently %d packets)\n",masters[m_id], m_id, tgt_prio,
429            packetPriorities[m_id][tgt_prio]);
430
431    for (uint8_t curr_prio = 0; curr_prio < numPriorities(); ++curr_prio) {
432        // Skip target priority
433        if (curr_prio == tgt_prio)
434            continue;
435
436        // Process other priority packet
437        while (packetPriorities[m_id][curr_prio] > 0) {
438            DPRINTF(QOS,
439                    "QoSMemCtrl::escalate MID %d checking priority %d "
440                    "(packets %d)- current packets in prio %d:  %d\n"
441                    "\t(source read %d source write %d target read %d, "
442                    "target write %d)\n",
443                    m_id, curr_prio, packetPriorities[m_id][curr_prio],
444                    tgt_prio, packetPriorities[m_id][tgt_prio],
445                    readQueueSizes[curr_prio],
446                    writeQueueSizes[curr_prio], readQueueSizes[tgt_prio],
447                    writeQueueSizes[tgt_prio]);
448
449            // Check both read and write queue
450            for (auto q : queues) {
451                escalateQueues(*q, queue_entry_size, m_id,
452                               curr_prio, tgt_prio);
453            }
454        }
455    }
456
457    DPRINTF(QOS,
458            "QoSMemCtrl::escalate Completed master %s [id %d] to priority %d "
459            "(now %d packets)\n\t(total read %d, total write %d)\n",
460            masters[m_id], m_id, tgt_prio, packetPriorities[m_id][tgt_prio],
461            readQueueSizes[tgt_prio], writeQueueSizes[tgt_prio]);
462}
463
464template<typename Queues>
465uint8_t
466MemCtrl::qosSchedule(std::initializer_list<Queues*> queues,
467                     const uint64_t queue_entry_size,
468                     const PacketPtr pkt)
469{
470    // Schedule packet.
471    uint8_t pkt_priority = schedule(pkt);
472
473    assert(pkt_priority < numPriorities());
474
475    pkt->qosValue(pkt_priority);
476
477    if (qosSyncroScheduler) {
478        // Call the scheduling function on all other masters.
479        for (const auto& m : masters) {
480
481            if (m.first == pkt->masterId())
482                continue;
483
484            uint8_t prio = schedule(m.first, 0);
485
486            if (qosPriorityEscalation) {
487                DPRINTF(QOS,
488                        "QoSMemCtrl::qosSchedule: (syncro) escalating "
489                        "MASTER %s to assigned priority %d\n",
490                        _system->getMasterName(m.first),
491                        prio);
492                escalate(queues, queue_entry_size, m.first, prio);
493            }
494        }
495    }
496
497    if (qosPriorityEscalation) {
498        DPRINTF(QOS,
499                "QoSMemCtrl::qosSchedule: escalating "
500                "MASTER %s to assigned priority %d\n",
501                _system->getMasterName(pkt->masterId()),
502                pkt_priority);
503        escalate(queues, queue_entry_size, pkt->masterId(), pkt_priority);
504    }
505
506    // Update last service tick for selected priority
507    serviceTick[pkt_priority] = curTick();
508
509    return pkt_priority;
510}
511
512} // namespace QoS
513
514#endif /* __MEM_QOS_MEM_CTRL_HH__ */
515