2 * Copyright (c) 2018-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.
37 * Author: Matteo Andreozzi
40 #include "debug/Drain.hh"
41 #include "debug/QOS.hh"
42 #include "mem_sink.hh"
43 #include "params/QoSMemSinkInterface.hh"
44 #include "sim/system.hh"
48 MemSinkCtrl::MemSinkCtrl(const QoSMemSinkCtrlParams
* p
)
49 : MemCtrl(p
), requestLatency(p
->request_latency
),
50 responseLatency(p
->response_latency
),
51 memoryPacketSize(p
->memory_packet_size
),
52 readBufferSize(p
->read_buffer_size
),
53 writeBufferSize(p
->write_buffer_size
), port(name() + ".port", *this),
54 interface(p
->interface
),
55 retryRdReq(false), retryWrReq(false), nextRequest(0), nextReqEvent(this)
57 // Resize read and write queue to allocate space
58 // for configured QoS priorities
59 readQueue
.resize(numPriorities());
60 writeQueue
.resize(numPriorities());
62 interface
->setMemCtrl(this);
65 MemSinkCtrl::~MemSinkCtrl()
73 // Allow unconnected memories as this is used in several ruby
74 // systems at the moment
75 if (port
.isConnected()) {
76 port
.sendRangeChange();
81 MemSinkCtrl::readQueueFull(const uint64_t packets
) const
83 return (totalReadQueueSize
+ packets
> readBufferSize
);
87 MemSinkCtrl::writeQueueFull(const uint64_t packets
) const
89 return (totalWriteQueueSize
+ packets
> writeBufferSize
);
93 MemSinkCtrl::recvAtomic(PacketPtr pkt
)
95 panic_if(pkt
->cacheResponding(),
96 "%s Should not see packets where cache is responding\n",
99 interface
->access(pkt
);
100 return responseLatency
;
104 MemSinkCtrl::recvFunctional(PacketPtr pkt
)
106 pkt
->pushLabel(name());
108 interface
->functionalAccess(pkt
);
114 MemSinkCtrl::getPort(const std::string
&interface
, PortID idx
)
116 if (interface
!= "port") {
117 return MemCtrl::getPort(interface
, idx
);
124 MemSinkCtrl::recvTimingReq(PacketPtr pkt
)
127 bool req_accepted
= true;
129 panic_if(!(pkt
->isRead() || pkt
->isWrite()),
130 "%s. Should only see "
131 "read and writes at memory controller\n",
134 panic_if(pkt
->cacheResponding(),
135 "%s. Should not see packets where cache is responding\n",
139 "%s: REQUESTOR %s request %s addr %lld size %d\n",
141 _system
->getRequestorName(pkt
->req
->requestorId()),
142 pkt
->cmdString(), pkt
->getAddr(), pkt
->getSize());
144 uint64_t required_entries
= divCeil(pkt
->getSize(), memoryPacketSize
);
146 assert(required_entries
);
149 uint8_t pkt_priority
= qosSchedule({&readQueue
, &writeQueue
},
150 memoryPacketSize
, pkt
);
153 if (readQueueFull(required_entries
)) {
155 "%s Read queue full, not accepting\n", __func__
);
156 // Remember that we have to retry this port
159 req_accepted
= false;
161 // Enqueue the incoming packet into corresponding
162 // QoS priority queue
163 readQueue
.at(pkt_priority
).push_back(pkt
);
164 queuePolicy
->enqueuePacket(pkt
);
167 if (writeQueueFull(required_entries
)) {
169 "%s Write queue full, not accepting\n", __func__
);
170 // Remember that we have to retry this port
173 req_accepted
= false;
175 // Enqueue the incoming packet into corresponding QoS
177 writeQueue
.at(pkt_priority
).push_back(pkt
);
178 queuePolicy
->enqueuePacket(pkt
);
183 // The packet is accepted - log it
184 logRequest(pkt
->isRead()? READ
: WRITE
,
185 pkt
->req
->requestorId(),
191 // Check if we have to process next request event
192 if (!nextReqEvent
.scheduled()) {
194 "%s scheduling next request at "
195 "time %d (next is %d)\n", __func__
,
196 std::max(curTick(), nextRequest
), nextRequest
);
197 schedule(nextReqEvent
, std::max(curTick(), nextRequest
));
203 MemSinkCtrl::processNextReqEvent()
205 PacketPtr pkt
= nullptr;
207 // Evaluate bus direction
208 busStateNext
= selectNextBusState();
210 // Record turnaround stats and update current state direction
211 recordTurnaroundStats();
213 // Set current bus state
214 setCurrentBusState();
216 // Access current direction buffer
217 std::vector
<PacketQueue
>* queue_ptr
= (busState
== READ
? &readQueue
:
221 "%s DUMPING %s queues status\n", __func__
,
222 (busState
== WRITE
? "WRITE" : "READ"));
225 for (uint8_t i
= 0; i
< numPriorities(); ++i
) {
226 std::string plist
= "";
227 for (auto& e
: (busState
== WRITE
? writeQueue
[i
]: readQueue
[i
])) {
228 plist
+= (std::to_string(e
->req
->requestorId())) + " ";
231 "%s priority Queue [%i] contains %i elements, "
232 "packets are: [%s]\n", __func__
, i
,
233 busState
== WRITE
? writeQueueSizes
[i
] :
239 uint8_t curr_prio
= numPriorities();
241 for (auto queue
= (*queue_ptr
).rbegin();
242 queue
!= (*queue_ptr
).rend(); ++queue
) {
247 "%s checking %s queue [%d] priority [%d packets]\n",
248 __func__
, (busState
== READ
? "READ" : "WRITE"),
249 curr_prio
, queue
->size());
251 if (!queue
->empty()) {
252 // Call the queue policy to select packet from priority queue
253 auto p_it
= queuePolicy
->selectPacket(&(*queue
));
258 "%s scheduling packet address %d for requestor %s from "
259 "priority queue %d\n", __func__
, pkt
->getAddr(),
260 _system
->getRequestorName(pkt
->req
->requestorId()),
268 // Setup next request service time - do it here as retry request
269 // hands over control to the port
270 nextRequest
= curTick() + requestLatency
;
272 uint64_t removed_entries
= divCeil(pkt
->getSize(), memoryPacketSize
);
275 "%s scheduled packet address %d for requestor %s size is %d, "
276 "corresponds to %d memory packets\n", __func__
, pkt
->getAddr(),
277 _system
->getRequestorName(pkt
->req
->requestorId()),
278 pkt
->getSize(), removed_entries
);
281 panic_if(!pkt
->needsResponse(),
282 "%s response not required\n", __func__
);
284 // Do the actual memory access which also turns the packet
286 interface
->access(pkt
);
289 logResponse(pkt
->isRead()? READ
: WRITE
,
290 pkt
->req
->requestorId(),
293 removed_entries
, responseLatency
);
295 // Schedule the response
296 port
.schedTimingResp(pkt
, curTick() + responseLatency
);
298 "%s response scheduled at time %d\n",
299 __func__
, curTick() + responseLatency
);
301 // Finally - handle retry requests - this handles control
302 // to the port, so do it last
303 if (busState
== READ
&& retryRdReq
) {
306 } else if (busState
== WRITE
&& retryWrReq
) {
311 // Check if we have to schedule another request event
312 if ((totalReadQueueSize
|| totalWriteQueueSize
) &&
313 !nextReqEvent
.scheduled()) {
315 schedule(nextReqEvent
, curTick() + requestLatency
);
317 "%s scheduling next request event at tick %d\n",
318 __func__
, curTick() + requestLatency
);
325 if (totalReadQueueSize
|| totalWriteQueueSize
) {
327 "%s queues have requests, waiting to drain\n",
329 return DrainState::Draining
;
331 return DrainState::Drained
;
336 MemSinkCtrl::regStats()
340 // Initialize all the stats
341 using namespace Stats
;
343 numReadRetries
.name(name() + ".numReadRetries")
344 .desc("Number of read retries");
345 numWriteRetries
.name(name() + ".numWriteRetries")
346 .desc("Number of write retries");
349 MemSinkCtrl::MemoryPort::MemoryPort(const std::string
& n
,
351 : QueuedResponsePort(n
, &m
, queue
, true),
352 memory(m
), queue(memory
, *this, true)
356 MemSinkCtrl::MemoryPort::getAddrRanges() const
358 AddrRangeList ranges
;
359 ranges
.push_back(memory
.interface
->getAddrRange());
364 MemSinkCtrl::MemoryPort::recvAtomic(PacketPtr pkt
)
366 return memory
.recvAtomic(pkt
);
370 MemSinkCtrl::MemoryPort::recvFunctional(PacketPtr pkt
)
372 pkt
->pushLabel(memory
.name());
374 if (!queue
.trySatisfyFunctional(pkt
)) {
375 // Default implementation of SimpleTimingPort::recvFunctional()
376 // calls recvAtomic() and throws away the latency; we can save a
377 // little here by just not calculating the latency.
378 memory
.recvFunctional(pkt
);
385 MemSinkCtrl::MemoryPort::recvTimingReq(PacketPtr pkt
)
387 return memory
.recvTimingReq(pkt
);
393 QoSMemSinkCtrlParams::create()
395 return new QoS::MemSinkCtrl(this);
398 QoSMemSinkInterface::QoSMemSinkInterface(const QoSMemSinkInterfaceParams
* _p
)
404 QoSMemSinkInterfaceParams::create()
406 return new QoSMemSinkInterface(this);