misc: merge branch 'release-staging-v19.0.0.0' into develop
[gem5.git] / src / mem / serial_link.cc
1 /*
2 * Copyright (c) 2011-2013 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 * Copyright (c) 2006 The Regents of The University of Michigan
15 * Copyright (c) 2015 The University of Bologna
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42 /**
43 * @file
44 * Implementation of the SerialLink Class, modeling Hybrid-Memory-Cube's
45 * serial interface.
46 */
47
48 #include "mem/serial_link.hh"
49
50 #include "base/trace.hh"
51 #include "debug/SerialLink.hh"
52 #include "params/SerialLink.hh"
53
54 SerialLink::SerialLinkSlavePort::SerialLinkSlavePort(const std::string& _name,
55 SerialLink& _serial_link,
56 SerialLinkMasterPort& _masterPort,
57 Cycles _delay, int _resp_limit,
58 const std::vector<AddrRange>&
59 _ranges)
60 : SlavePort(_name, &_serial_link), serial_link(_serial_link),
61 masterPort(_masterPort), delay(_delay),
62 ranges(_ranges.begin(), _ranges.end()),
63 outstandingResponses(0), retryReq(false),
64 respQueueLimit(_resp_limit),
65 sendEvent([this]{ trySendTiming(); }, _name)
66 {
67 }
68
69 SerialLink::SerialLinkMasterPort::SerialLinkMasterPort(const std::string&
70 _name, SerialLink& _serial_link,
71 SerialLinkSlavePort& _slavePort,
72 Cycles _delay, int _req_limit)
73 : MasterPort(_name, &_serial_link), serial_link(_serial_link),
74 slavePort(_slavePort), delay(_delay), reqQueueLimit(_req_limit),
75 sendEvent([this]{ trySendTiming(); }, _name)
76 {
77 }
78
79 SerialLink::SerialLink(SerialLinkParams *p)
80 : ClockedObject(p),
81 slavePort(p->name + ".slave", *this, masterPort,
82 ticksToCycles(p->delay), p->resp_size, p->ranges),
83 masterPort(p->name + ".master", *this, slavePort,
84 ticksToCycles(p->delay), p->req_size),
85 num_lanes(p->num_lanes),
86 link_speed(p->link_speed)
87
88 {
89 }
90
91 Port&
92 SerialLink::getPort(const std::string &if_name, PortID idx)
93 {
94 if (if_name == "master")
95 return masterPort;
96 else if (if_name == "slave")
97 return slavePort;
98 else
99 // pass it along to our super class
100 return ClockedObject::getPort(if_name, idx);
101 }
102
103 void
104 SerialLink::init()
105 {
106 // make sure both sides are connected and have the same block size
107 if (!slavePort.isConnected() || !masterPort.isConnected())
108 fatal("Both ports of a serial_link must be connected.\n");
109
110 // notify the master side of our address ranges
111 slavePort.sendRangeChange();
112 }
113
114 bool
115 SerialLink::SerialLinkSlavePort::respQueueFull() const
116 {
117 return outstandingResponses == respQueueLimit;
118 }
119
120 bool
121 SerialLink::SerialLinkMasterPort::reqQueueFull() const
122 {
123 return transmitList.size() == reqQueueLimit;
124 }
125
126 bool
127 SerialLink::SerialLinkMasterPort::recvTimingResp(PacketPtr pkt)
128 {
129 // all checks are done when the request is accepted on the slave
130 // side, so we are guaranteed to have space for the response
131 DPRINTF(SerialLink, "recvTimingResp: %s addr 0x%x\n",
132 pkt->cmdString(), pkt->getAddr());
133
134 DPRINTF(SerialLink, "Request queue size: %d\n", transmitList.size());
135
136 // @todo: We need to pay for this and not just zero it out
137 pkt->headerDelay = pkt->payloadDelay = 0;
138
139 // This is similar to what happens for the request packets:
140 // The serializer will start serialization as soon as it receives the
141 // first flit, but the deserializer (at the host side in this case), will
142 // have to wait to receive the whole packet. So we only account for the
143 // deserialization latency.
144 Cycles cycles = delay;
145 cycles += Cycles(divCeil(pkt->getSize() * 8, serial_link.num_lanes
146 * serial_link.link_speed));
147 Tick t = serial_link.clockEdge(cycles);
148
149 //@todo: If the processor sends two uncached requests towards HMC and the
150 // second one is smaller than the first one. It may happen that the second
151 // one crosses this link faster than the first one (because the packet
152 // waits in the link based on its size). This can reorder the received
153 // response.
154 slavePort.schedTimingResp(pkt, t);
155
156 return true;
157 }
158
159 bool
160 SerialLink::SerialLinkSlavePort::recvTimingReq(PacketPtr pkt)
161 {
162 DPRINTF(SerialLink, "recvTimingReq: %s addr 0x%x\n",
163 pkt->cmdString(), pkt->getAddr());
164
165 // we should not see a timing request if we are already in a retry
166 assert(!retryReq);
167
168 DPRINTF(SerialLink, "Response queue size: %d outresp: %d\n",
169 transmitList.size(), outstandingResponses);
170
171 // if the request queue is full then there is no hope
172 if (masterPort.reqQueueFull()) {
173 DPRINTF(SerialLink, "Request queue full\n");
174 retryReq = true;
175 } else if ( !retryReq ) {
176 // look at the response queue if we expect to see a response
177 bool expects_response = pkt->needsResponse() &&
178 !pkt->cacheResponding();
179 if (expects_response) {
180 if (respQueueFull()) {
181 DPRINTF(SerialLink, "Response queue full\n");
182 retryReq = true;
183 } else {
184 // ok to send the request with space for the response
185 DPRINTF(SerialLink, "Reserving space for response\n");
186 assert(outstandingResponses != respQueueLimit);
187 ++outstandingResponses;
188
189 // no need to set retryReq to false as this is already the
190 // case
191 }
192 }
193
194 if (!retryReq) {
195 // @todo: We need to pay for this and not just zero it out
196 pkt->headerDelay = pkt->payloadDelay = 0;
197
198 // We assume that the serializer component at the transmitter side
199 // does not need to receive the whole packet to start the
200 // serialization (this assumption is consistent with the HMC
201 // standard). But the deserializer waits for the complete packet
202 // to check its integrity first. So everytime a packet crosses a
203 // serial link, we should account for its deserialization latency
204 // only.
205 Cycles cycles = delay;
206 cycles += Cycles(divCeil(pkt->getSize() * 8,
207 serial_link.num_lanes * serial_link.link_speed));
208 Tick t = serial_link.clockEdge(cycles);
209
210 //@todo: If the processor sends two uncached requests towards HMC
211 // and the second one is smaller than the first one. It may happen
212 // that the second one crosses this link faster than the first one
213 // (because the packet waits in the link based on its size).
214 // This can reorder the received response.
215 masterPort.schedTimingReq(pkt, t);
216 }
217 }
218
219 // remember that we are now stalling a packet and that we have to
220 // tell the sending master to retry once space becomes available,
221 // we make no distinction whether the stalling is due to the
222 // request queue or response queue being full
223 return !retryReq;
224 }
225
226 void
227 SerialLink::SerialLinkSlavePort::retryStalledReq()
228 {
229 if (retryReq) {
230 DPRINTF(SerialLink, "Request waiting for retry, now retrying\n");
231 retryReq = false;
232 sendRetryReq();
233 }
234 }
235
236 void
237 SerialLink::SerialLinkMasterPort::schedTimingReq(PacketPtr pkt, Tick when)
238 {
239 // If we're about to put this packet at the head of the queue, we
240 // need to schedule an event to do the transmit. Otherwise there
241 // should already be an event scheduled for sending the head
242 // packet.
243 if (transmitList.empty()) {
244 serial_link.schedule(sendEvent, when);
245 }
246
247 assert(transmitList.size() != reqQueueLimit);
248
249 transmitList.emplace_back(DeferredPacket(pkt, when));
250 }
251
252
253 void
254 SerialLink::SerialLinkSlavePort::schedTimingResp(PacketPtr pkt, Tick when)
255 {
256 // If we're about to put this packet at the head of the queue, we
257 // need to schedule an event to do the transmit. Otherwise there
258 // should already be an event scheduled for sending the head
259 // packet.
260 if (transmitList.empty()) {
261 serial_link.schedule(sendEvent, when);
262 }
263
264 transmitList.emplace_back(DeferredPacket(pkt, when));
265 }
266
267 void
268 SerialLink::SerialLinkMasterPort::trySendTiming()
269 {
270 assert(!transmitList.empty());
271
272 DeferredPacket req = transmitList.front();
273
274 assert(req.tick <= curTick());
275
276 PacketPtr pkt = req.pkt;
277
278 DPRINTF(SerialLink, "trySend request addr 0x%x, queue size %d\n",
279 pkt->getAddr(), transmitList.size());
280
281 if (sendTimingReq(pkt)) {
282 // send successful
283 transmitList.pop_front();
284
285 DPRINTF(SerialLink, "trySend request successful\n");
286
287 // If there are more packets to send, schedule event to try again.
288 if (!transmitList.empty()) {
289 DeferredPacket next_req = transmitList.front();
290 DPRINTF(SerialLink, "Scheduling next send\n");
291
292 // Make sure bandwidth limitation is met
293 Cycles cycles = Cycles(divCeil(pkt->getSize() * 8,
294 serial_link.num_lanes * serial_link.link_speed));
295 Tick t = serial_link.clockEdge(cycles);
296 serial_link.schedule(sendEvent, std::max(next_req.tick, t));
297 }
298
299 // if we have stalled a request due to a full request queue,
300 // then send a retry at this point, also note that if the
301 // request we stalled was waiting for the response queue
302 // rather than the request queue we might stall it again
303 slavePort.retryStalledReq();
304 }
305
306 // if the send failed, then we try again once we receive a retry,
307 // and therefore there is no need to take any action
308 }
309
310 void
311 SerialLink::SerialLinkSlavePort::trySendTiming()
312 {
313 assert(!transmitList.empty());
314
315 DeferredPacket resp = transmitList.front();
316
317 assert(resp.tick <= curTick());
318
319 PacketPtr pkt = resp.pkt;
320
321 DPRINTF(SerialLink, "trySend response addr 0x%x, outstanding %d\n",
322 pkt->getAddr(), outstandingResponses);
323
324 if (sendTimingResp(pkt)) {
325 // send successful
326 transmitList.pop_front();
327 DPRINTF(SerialLink, "trySend response successful\n");
328
329 assert(outstandingResponses != 0);
330 --outstandingResponses;
331
332 // If there are more packets to send, schedule event to try again.
333 if (!transmitList.empty()) {
334 DeferredPacket next_resp = transmitList.front();
335 DPRINTF(SerialLink, "Scheduling next send\n");
336
337 // Make sure bandwidth limitation is met
338 Cycles cycles = Cycles(divCeil(pkt->getSize() * 8,
339 serial_link.num_lanes * serial_link.link_speed));
340 Tick t = serial_link.clockEdge(cycles);
341 serial_link.schedule(sendEvent, std::max(next_resp.tick, t));
342 }
343
344 // if there is space in the request queue and we were stalling
345 // a request, it will definitely be possible to accept it now
346 // since there is guaranteed space in the response queue
347 if (!masterPort.reqQueueFull() && retryReq) {
348 DPRINTF(SerialLink, "Request waiting for retry, now retrying\n");
349 retryReq = false;
350 sendRetryReq();
351 }
352 }
353
354 // if the send failed, then we try again once we receive a retry,
355 // and therefore there is no need to take any action
356 }
357
358 void
359 SerialLink::SerialLinkMasterPort::recvReqRetry()
360 {
361 trySendTiming();
362 }
363
364 void
365 SerialLink::SerialLinkSlavePort::recvRespRetry()
366 {
367 trySendTiming();
368 }
369
370 Tick
371 SerialLink::SerialLinkSlavePort::recvAtomic(PacketPtr pkt)
372 {
373 return delay * serial_link.clockPeriod() + masterPort.sendAtomic(pkt);
374 }
375
376 void
377 SerialLink::SerialLinkSlavePort::recvFunctional(PacketPtr pkt)
378 {
379 pkt->pushLabel(name());
380
381 // check the response queue
382 for (auto i = transmitList.begin(); i != transmitList.end(); ++i) {
383 if (pkt->trySatisfyFunctional((*i).pkt)) {
384 pkt->makeResponse();
385 return;
386 }
387 }
388
389 // also check the master port's request queue
390 if (masterPort.trySatisfyFunctional(pkt)) {
391 return;
392 }
393
394 pkt->popLabel();
395
396 // fall through if pkt still not satisfied
397 masterPort.sendFunctional(pkt);
398 }
399
400 bool
401 SerialLink::SerialLinkMasterPort::trySatisfyFunctional(PacketPtr pkt)
402 {
403 bool found = false;
404 auto i = transmitList.begin();
405
406 while (i != transmitList.end() && !found) {
407 if (pkt->trySatisfyFunctional((*i).pkt)) {
408 pkt->makeResponse();
409 found = true;
410 }
411 ++i;
412 }
413
414 return found;
415 }
416
417 AddrRangeList
418 SerialLink::SerialLinkSlavePort::getAddrRanges() const
419 {
420 return ranges;
421 }
422
423 SerialLink *
424 SerialLinkParams::create()
425 {
426 return new SerialLink(this);
427 }