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),
58 // Resize read and write queue to allocate space
59 // for configured QoS priorities
60 readQueue
.resize(numPriorities());
61 writeQueue
.resize(numPriorities());
63 interface
->setMemCtrl(this);
66 MemSinkCtrl::~MemSinkCtrl()
74 // Allow unconnected memories as this is used in several ruby
75 // systems at the moment
76 if (port
.isConnected()) {
77 port
.sendRangeChange();
82 MemSinkCtrl::readQueueFull(const uint64_t packets
) const
84 return (totalReadQueueSize
+ packets
> readBufferSize
);
88 MemSinkCtrl::writeQueueFull(const uint64_t packets
) const
90 return (totalWriteQueueSize
+ packets
> writeBufferSize
);
94 MemSinkCtrl::recvAtomic(PacketPtr pkt
)
96 panic_if(pkt
->cacheResponding(),
97 "%s Should not see packets where cache is responding\n",
100 interface
->access(pkt
);
101 return responseLatency
;
105 MemSinkCtrl::recvFunctional(PacketPtr pkt
)
107 pkt
->pushLabel(name());
109 interface
->functionalAccess(pkt
);
115 MemSinkCtrl::getPort(const std::string
&interface
, PortID idx
)
117 if (interface
!= "port") {
118 return MemCtrl::getPort(interface
, idx
);
125 MemSinkCtrl::recvTimingReq(PacketPtr pkt
)
128 bool req_accepted
= true;
130 panic_if(!(pkt
->isRead() || pkt
->isWrite()),
131 "%s. Should only see "
132 "read and writes at memory controller\n",
135 panic_if(pkt
->cacheResponding(),
136 "%s. Should not see packets where cache is responding\n",
140 "%s: REQUESTOR %s request %s addr %lld size %d\n",
142 _system
->getRequestorName(pkt
->req
->requestorId()),
143 pkt
->cmdString(), pkt
->getAddr(), pkt
->getSize());
145 uint64_t required_entries
= divCeil(pkt
->getSize(), memoryPacketSize
);
147 assert(required_entries
);
150 uint8_t pkt_priority
= qosSchedule({&readQueue
, &writeQueue
},
151 memoryPacketSize
, pkt
);
154 if (readQueueFull(required_entries
)) {
156 "%s Read queue full, not accepting\n", __func__
);
157 // Remember that we have to retry this port
159 stats
.numReadRetries
++;
160 req_accepted
= false;
162 // Enqueue the incoming packet into corresponding
163 // QoS priority queue
164 readQueue
.at(pkt_priority
).push_back(pkt
);
165 queuePolicy
->enqueuePacket(pkt
);
168 if (writeQueueFull(required_entries
)) {
170 "%s Write queue full, not accepting\n", __func__
);
171 // Remember that we have to retry this port
173 stats
.numWriteRetries
++;
174 req_accepted
= false;
176 // Enqueue the incoming packet into corresponding QoS
178 writeQueue
.at(pkt_priority
).push_back(pkt
);
179 queuePolicy
->enqueuePacket(pkt
);
184 // The packet is accepted - log it
185 logRequest(pkt
->isRead()? READ
: WRITE
,
186 pkt
->req
->requestorId(),
192 // Check if we have to process next request event
193 if (!nextReqEvent
.scheduled()) {
195 "%s scheduling next request at "
196 "time %d (next is %d)\n", __func__
,
197 std::max(curTick(), nextRequest
), nextRequest
);
198 schedule(nextReqEvent
, std::max(curTick(), nextRequest
));
204 MemSinkCtrl::processNextReqEvent()
206 PacketPtr pkt
= nullptr;
208 // Evaluate bus direction
209 busStateNext
= selectNextBusState();
211 // Record turnaround stats and update current state direction
212 recordTurnaroundStats();
214 // Set current bus state
215 setCurrentBusState();
217 // Access current direction buffer
218 std::vector
<PacketQueue
>* queue_ptr
= (busState
== READ
? &readQueue
:
222 "%s DUMPING %s queues status\n", __func__
,
223 (busState
== WRITE
? "WRITE" : "READ"));
226 for (uint8_t i
= 0; i
< numPriorities(); ++i
) {
227 std::string plist
= "";
228 for (auto& e
: (busState
== WRITE
? writeQueue
[i
]: readQueue
[i
])) {
229 plist
+= (std::to_string(e
->req
->requestorId())) + " ";
232 "%s priority Queue [%i] contains %i elements, "
233 "packets are: [%s]\n", __func__
, i
,
234 busState
== WRITE
? writeQueueSizes
[i
] :
240 uint8_t curr_prio
= numPriorities();
242 for (auto queue
= (*queue_ptr
).rbegin();
243 queue
!= (*queue_ptr
).rend(); ++queue
) {
248 "%s checking %s queue [%d] priority [%d packets]\n",
249 __func__
, (busState
== READ
? "READ" : "WRITE"),
250 curr_prio
, queue
->size());
252 if (!queue
->empty()) {
253 // Call the queue policy to select packet from priority queue
254 auto p_it
= queuePolicy
->selectPacket(&(*queue
));
259 "%s scheduling packet address %d for requestor %s from "
260 "priority queue %d\n", __func__
, pkt
->getAddr(),
261 _system
->getRequestorName(pkt
->req
->requestorId()),
269 // Setup next request service time - do it here as retry request
270 // hands over control to the port
271 nextRequest
= curTick() + requestLatency
;
273 uint64_t removed_entries
= divCeil(pkt
->getSize(), memoryPacketSize
);
276 "%s scheduled packet address %d for requestor %s size is %d, "
277 "corresponds to %d memory packets\n", __func__
, pkt
->getAddr(),
278 _system
->getRequestorName(pkt
->req
->requestorId()),
279 pkt
->getSize(), removed_entries
);
282 panic_if(!pkt
->needsResponse(),
283 "%s response not required\n", __func__
);
285 // Do the actual memory access which also turns the packet
287 interface
->access(pkt
);
290 logResponse(pkt
->isRead()? READ
: WRITE
,
291 pkt
->req
->requestorId(),
294 removed_entries
, responseLatency
);
296 // Schedule the response
297 port
.schedTimingResp(pkt
, curTick() + responseLatency
);
299 "%s response scheduled at time %d\n",
300 __func__
, curTick() + responseLatency
);
302 // Finally - handle retry requests - this handles control
303 // to the port, so do it last
304 if (busState
== READ
&& retryRdReq
) {
307 } else if (busState
== WRITE
&& retryWrReq
) {
312 // Check if we have to schedule another request event
313 if ((totalReadQueueSize
|| totalWriteQueueSize
) &&
314 !nextReqEvent
.scheduled()) {
316 schedule(nextReqEvent
, curTick() + requestLatency
);
318 "%s scheduling next request event at tick %d\n",
319 __func__
, curTick() + requestLatency
);
326 if (totalReadQueueSize
|| totalWriteQueueSize
) {
328 "%s queues have requests, waiting to drain\n",
330 return DrainState::Draining
;
332 return DrainState::Drained
;
336 MemSinkCtrl::MemSinkCtrlStats::MemSinkCtrlStats(Stats::Group
*parent
)
337 : Stats::Group(parent
),
338 ADD_STAT(numReadRetries
, "Number of read retries"),
339 ADD_STAT(numWriteRetries
, "Number of write retries")
343 MemSinkCtrl::MemoryPort::MemoryPort(const std::string
& n
,
345 : QueuedResponsePort(n
, &m
, queue
, true),
346 memory(m
), queue(memory
, *this, true)
350 MemSinkCtrl::MemoryPort::getAddrRanges() const
352 AddrRangeList ranges
;
353 ranges
.push_back(memory
.interface
->getAddrRange());
358 MemSinkCtrl::MemoryPort::recvAtomic(PacketPtr pkt
)
360 return memory
.recvAtomic(pkt
);
364 MemSinkCtrl::MemoryPort::recvFunctional(PacketPtr pkt
)
366 pkt
->pushLabel(memory
.name());
368 if (!queue
.trySatisfyFunctional(pkt
)) {
369 // Default implementation of SimpleTimingPort::recvFunctional()
370 // calls recvAtomic() and throws away the latency; we can save a
371 // little here by just not calculating the latency.
372 memory
.recvFunctional(pkt
);
379 MemSinkCtrl::MemoryPort::recvTimingReq(PacketPtr pkt
)
381 return memory
.recvTimingReq(pkt
);
386 QoSMemSinkInterface::QoSMemSinkInterface(const QoSMemSinkInterfaceParams
&_p
)