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