serial_link.cc revision 11321
112837Sgabeblack@google.com/*
212837Sgabeblack@google.com * Copyright (c) 2011-2013 ARM Limited
312837Sgabeblack@google.com * All rights reserved
412837Sgabeblack@google.com *
512837Sgabeblack@google.com * The license below extends only to copyright in the software and shall
612837Sgabeblack@google.com * not be construed as granting a license to any other intellectual
712837Sgabeblack@google.com * property including but not limited to intellectual property relating
812837Sgabeblack@google.com * to a hardware implementation of the functionality of the software
912837Sgabeblack@google.com * licensed hereunder.  You may use the software subject to the license
1012837Sgabeblack@google.com * terms below provided that you ensure that this notice is replicated
1112837Sgabeblack@google.com * unmodified and in its entirety in all distributions of the software,
1212837Sgabeblack@google.com * modified or unmodified, in source code or in binary form.
1312837Sgabeblack@google.com *
1412837Sgabeblack@google.com * Copyright (c) 2006 The Regents of The University of Michigan
1512837Sgabeblack@google.com * Copyright (c) 2015 The University of Bologna
1612837Sgabeblack@google.com * All rights reserved.
1712837Sgabeblack@google.com *
1812837Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
1912837Sgabeblack@google.com * modification, are permitted provided that the following conditions are
2012837Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
2112837Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
2212837Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
2312837Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
2412837Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
2512837Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
2612837Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
2712837Sgabeblack@google.com * this software without specific prior written permission.
2812837Sgabeblack@google.com *
2912837Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3012837Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3112837Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3212837Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3312837Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3412837Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3512837Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3612837Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3712837Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3812837Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3912837Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4012837Sgabeblack@google.com *
4112837Sgabeblack@google.com * Authors: Ali Saidi
4212837Sgabeblack@google.com *          Steve Reinhardt
4312837Sgabeblack@google.com *          Andreas Hansson
4412837Sgabeblack@google.com *          Erfan Azarkhish
4512837Sgabeblack@google.com */
4612837Sgabeblack@google.com
4712837Sgabeblack@google.com/**
4812837Sgabeblack@google.com * @file
4912837Sgabeblack@google.com * Implementation of the SerialLink Class, modeling Hybrid-Memory-Cube's
5012837Sgabeblack@google.com * serial interface.
5112837Sgabeblack@google.com */
5212837Sgabeblack@google.com
5312837Sgabeblack@google.com#include "mem/serial_link.hh"
5412837Sgabeblack@google.com
5512837Sgabeblack@google.com#include "base/trace.hh"
5612837Sgabeblack@google.com#include "debug/SerialLink.hh"
5712837Sgabeblack@google.com#include "params/SerialLink.hh"
5812837Sgabeblack@google.com
5912837Sgabeblack@google.com
6012837Sgabeblack@google.comSerialLink::SerialLinkSlavePort::SerialLinkSlavePort(const std::string& _name,
6112837Sgabeblack@google.com                                         SerialLink& _serial_link,
6212837Sgabeblack@google.com                                         SerialLinkMasterPort& _masterPort,
6312837Sgabeblack@google.com                                         Cycles _delay, int _resp_limit,
6412837Sgabeblack@google.com                                         const std::vector<AddrRange>&
6512837Sgabeblack@google.com                                         _ranges)
6612837Sgabeblack@google.com    : SlavePort(_name, &_serial_link), serial_link(_serial_link),
6712837Sgabeblack@google.com      masterPort(_masterPort), delay(_delay),
6812837Sgabeblack@google.com      ranges(_ranges.begin(), _ranges.end()),
6912837Sgabeblack@google.com      outstandingResponses(0), retryReq(false),
7012837Sgabeblack@google.com      respQueueLimit(_resp_limit), sendEvent(*this)
7112837Sgabeblack@google.com{
7212837Sgabeblack@google.com}
7312837Sgabeblack@google.com
7412837Sgabeblack@google.comSerialLink::SerialLinkMasterPort::SerialLinkMasterPort(const std::string&
7512837Sgabeblack@google.com                                           _name, SerialLink& _serial_link,
7612837Sgabeblack@google.com                                           SerialLinkSlavePort& _slavePort,
7712837Sgabeblack@google.com                                           Cycles _delay, int _req_limit)
7812837Sgabeblack@google.com    : MasterPort(_name, &_serial_link), serial_link(_serial_link),
7912837Sgabeblack@google.com      slavePort(_slavePort), delay(_delay), reqQueueLimit(_req_limit),
8012837Sgabeblack@google.com      sendEvent(*this)
8112837Sgabeblack@google.com{
8212837Sgabeblack@google.com}
8312837Sgabeblack@google.com
8412837Sgabeblack@google.comSerialLink::SerialLink(SerialLinkParams *p)
8512837Sgabeblack@google.com    : MemObject(p),
8612837Sgabeblack@google.com      slavePort(p->name + ".slave", *this, masterPort,
8712837Sgabeblack@google.com                ticksToCycles(p->delay), p->resp_size, p->ranges),
8812837Sgabeblack@google.com      masterPort(p->name + ".master", *this, slavePort,
8912837Sgabeblack@google.com                 ticksToCycles(p->delay), p->req_size),
9012837Sgabeblack@google.com      num_lanes(p->num_lanes)
9112837Sgabeblack@google.com{
9212837Sgabeblack@google.com}
9312837Sgabeblack@google.com
9412837Sgabeblack@google.comBaseMasterPort&
9512837Sgabeblack@google.comSerialLink::getMasterPort(const std::string &if_name, PortID idx)
9612837Sgabeblack@google.com{
9712837Sgabeblack@google.com    if (if_name == "master")
9812837Sgabeblack@google.com        return masterPort;
9912837Sgabeblack@google.com    else
10012837Sgabeblack@google.com        // pass it along to our super class
10112837Sgabeblack@google.com        return MemObject::getMasterPort(if_name, idx);
10212837Sgabeblack@google.com}
10312837Sgabeblack@google.com
10412837Sgabeblack@google.comBaseSlavePort&
10512837Sgabeblack@google.comSerialLink::getSlavePort(const std::string &if_name, PortID idx)
10612837Sgabeblack@google.com{
10712837Sgabeblack@google.com    if (if_name == "slave")
10812837Sgabeblack@google.com        return slavePort;
10912837Sgabeblack@google.com    else
11012837Sgabeblack@google.com        // pass it along to our super class
11112837Sgabeblack@google.com        return MemObject::getSlavePort(if_name, idx);
11212837Sgabeblack@google.com}
11312837Sgabeblack@google.com
11412837Sgabeblack@google.comvoid
11512837Sgabeblack@google.comSerialLink::init()
11612837Sgabeblack@google.com{
11712837Sgabeblack@google.com    // make sure both sides are connected and have the same block size
11812837Sgabeblack@google.com    if (!slavePort.isConnected() || !masterPort.isConnected())
11912837Sgabeblack@google.com        fatal("Both ports of a serial_link must be connected.\n");
12012837Sgabeblack@google.com
12112837Sgabeblack@google.com    // notify the master side  of our address ranges
12212837Sgabeblack@google.com    slavePort.sendRangeChange();
12312837Sgabeblack@google.com}
12412860Sgabeblack@google.com
12512860Sgabeblack@google.combool
12612860Sgabeblack@google.comSerialLink::SerialLinkSlavePort::respQueueFull() const
12712860Sgabeblack@google.com{
12812860Sgabeblack@google.com    return outstandingResponses == respQueueLimit;
12912860Sgabeblack@google.com}
13012860Sgabeblack@google.com
13112860Sgabeblack@google.combool
13212860Sgabeblack@google.comSerialLink::SerialLinkMasterPort::reqQueueFull() const
13312860Sgabeblack@google.com{
13412860Sgabeblack@google.com    return transmitList.size() == reqQueueLimit;
13512860Sgabeblack@google.com}
13612860Sgabeblack@google.com
13712860Sgabeblack@google.combool
13812860Sgabeblack@google.comSerialLink::SerialLinkMasterPort::recvTimingResp(PacketPtr pkt)
13912860Sgabeblack@google.com{
14012860Sgabeblack@google.com    // all checks are done when the request is accepted on the slave
14112860Sgabeblack@google.com    // side, so we are guaranteed to have space for the response
14212860Sgabeblack@google.com    DPRINTF(SerialLink, "recvTimingResp: %s addr 0x%x\n",
14312860Sgabeblack@google.com            pkt->cmdString(), pkt->getAddr());
14412860Sgabeblack@google.com
14512860Sgabeblack@google.com    DPRINTF(SerialLink, "Request queue size: %d\n", transmitList.size());
14612860Sgabeblack@google.com
14712860Sgabeblack@google.com    // @todo: We need to pay for this and not just zero it out
14812860Sgabeblack@google.com    pkt->headerDelay = pkt->payloadDelay = 0;
14912860Sgabeblack@google.com
15012860Sgabeblack@google.com    // This is similar to what happens for the request packets:
15112860Sgabeblack@google.com    // The serializer will start serialization as soon as it receives the
15212860Sgabeblack@google.com    // first flit, but the deserializer (at the host side in this case), will
15312860Sgabeblack@google.com    // have to wait to receive the whole packet. So we only account for the
15412860Sgabeblack@google.com    // deserialization latency.
15512860Sgabeblack@google.com    Cycles cycles = delay;
15612860Sgabeblack@google.com    cycles += Cycles(divCeil(pkt->getSize() * 8, serial_link.num_lanes));
15712860Sgabeblack@google.com    Tick t = serial_link.clockEdge(cycles);
15812860Sgabeblack@google.com
15912860Sgabeblack@google.com    //@todo: If the processor sends two uncached requests towards HMC and the
16012860Sgabeblack@google.com    // second one is smaller than the first one. It may happen that the second
16112860Sgabeblack@google.com    // one crosses this link faster than the first one (because the packet
16212860Sgabeblack@google.com    // waits in the link based on its size). This can reorder the received
16312860Sgabeblack@google.com    // response.
16412860Sgabeblack@google.com    slavePort.schedTimingResp(pkt, t);
16512860Sgabeblack@google.com
16612860Sgabeblack@google.com    return true;
16712860Sgabeblack@google.com}
16812860Sgabeblack@google.com
16912860Sgabeblack@google.combool
17012860Sgabeblack@google.comSerialLink::SerialLinkSlavePort::recvTimingReq(PacketPtr pkt)
17112860Sgabeblack@google.com{
17212860Sgabeblack@google.com    DPRINTF(SerialLink, "recvTimingReq: %s addr 0x%x\n",
17312860Sgabeblack@google.com            pkt->cmdString(), pkt->getAddr());
17412860Sgabeblack@google.com
17512860Sgabeblack@google.com    // we should not see a timing request if we are already in a retry
17612860Sgabeblack@google.com    assert(!retryReq);
17712860Sgabeblack@google.com
17812860Sgabeblack@google.com    DPRINTF(SerialLink, "Response queue size: %d outresp: %d\n",
17912860Sgabeblack@google.com            transmitList.size(), outstandingResponses);
18012860Sgabeblack@google.com
18112860Sgabeblack@google.com    // if the request queue is full then there is no hope
18212860Sgabeblack@google.com    if (masterPort.reqQueueFull()) {
18312860Sgabeblack@google.com        DPRINTF(SerialLink, "Request queue full\n");
18412860Sgabeblack@google.com        retryReq = true;
18512860Sgabeblack@google.com    } else if ( !retryReq ) {
18612860Sgabeblack@google.com        // look at the response queue if we expect to see a response
18712860Sgabeblack@google.com        bool expects_response = pkt->needsResponse() &&
18812860Sgabeblack@google.com            !pkt->cacheResponding();
18912860Sgabeblack@google.com        if (expects_response) {
19012860Sgabeblack@google.com            if (respQueueFull()) {
19112860Sgabeblack@google.com                DPRINTF(SerialLink, "Response queue full\n");
19212860Sgabeblack@google.com                retryReq = true;
19312860Sgabeblack@google.com            } else {
19412860Sgabeblack@google.com                // ok to send the request with space for the response
19512860Sgabeblack@google.com                DPRINTF(SerialLink, "Reserving space for response\n");
19612860Sgabeblack@google.com                assert(outstandingResponses != respQueueLimit);
19712860Sgabeblack@google.com                ++outstandingResponses;
19812860Sgabeblack@google.com
19912860Sgabeblack@google.com                // no need to set retryReq to false as this is already the
20012860Sgabeblack@google.com                // case
20112860Sgabeblack@google.com            }
20212860Sgabeblack@google.com        }
20312860Sgabeblack@google.com
20412860Sgabeblack@google.com        if (!retryReq) {
20512860Sgabeblack@google.com            // @todo: We need to pay for this and not just zero it out
20612860Sgabeblack@google.com            pkt->headerDelay = pkt->payloadDelay = 0;
20712860Sgabeblack@google.com
20812860Sgabeblack@google.com            // We assume that the serializer component at the transmitter side
20912860Sgabeblack@google.com            // does not need to receive the whole packet to start the
21012860Sgabeblack@google.com            // serialization (this assumption is consistent with the HMC
21112860Sgabeblack@google.com            // standard). But the deserializer waits for the complete packet
21212860Sgabeblack@google.com            // to check its integrity first. So everytime a packet crosses a
21312860Sgabeblack@google.com            // serial link, we should account for its deserialization latency
21412860Sgabeblack@google.com            // only.
21512860Sgabeblack@google.com            Cycles cycles = delay;
21612860Sgabeblack@google.com            cycles += Cycles(divCeil(pkt->getSize() * 8,
21712837Sgabeblack@google.com                serial_link.num_lanes));
218            Tick t = serial_link.clockEdge(cycles);
219
220            //@todo: If the processor sends two uncached requests towards HMC
221            // and the second one is smaller than the first one. It may happen
222            // that the second one crosses this link faster than the first one
223            // (because the packet waits in the link based on its size).
224            // This can reorder the received response.
225            masterPort.schedTimingReq(pkt, t);
226        }
227    }
228
229    // remember that we are now stalling a packet and that we have to
230    // tell the sending master to retry once space becomes available,
231    // we make no distinction whether the stalling is due to the
232    // request queue or response queue being full
233    return !retryReq;
234}
235
236void
237SerialLink::SerialLinkSlavePort::retryStalledReq()
238{
239    if (retryReq) {
240        DPRINTF(SerialLink, "Request waiting for retry, now retrying\n");
241        retryReq = false;
242        sendRetryReq();
243    }
244}
245
246void
247SerialLink::SerialLinkMasterPort::schedTimingReq(PacketPtr pkt, Tick when)
248{
249    // If we're about to put this packet at the head of the queue, we
250    // need to schedule an event to do the transmit.  Otherwise there
251    // should already be an event scheduled for sending the head
252    // packet.
253    if (transmitList.empty()) {
254        serial_link.schedule(sendEvent, when);
255    }
256
257    assert(transmitList.size() != reqQueueLimit);
258
259    transmitList.emplace_back(DeferredPacket(pkt, when));
260}
261
262
263void
264SerialLink::SerialLinkSlavePort::schedTimingResp(PacketPtr pkt, Tick when)
265{
266    // If we're about to put this packet at the head of the queue, we
267    // need to schedule an event to do the transmit.  Otherwise there
268    // should already be an event scheduled for sending the head
269    // packet.
270    if (transmitList.empty()) {
271        serial_link.schedule(sendEvent, when);
272    }
273
274    transmitList.emplace_back(DeferredPacket(pkt, when));
275}
276
277void
278SerialLink::SerialLinkMasterPort::trySendTiming()
279{
280    assert(!transmitList.empty());
281
282    DeferredPacket req = transmitList.front();
283
284    assert(req.tick <= curTick());
285
286    PacketPtr pkt = req.pkt;
287
288    DPRINTF(SerialLink, "trySend request addr 0x%x, queue size %d\n",
289            pkt->getAddr(), transmitList.size());
290
291    if (sendTimingReq(pkt)) {
292        // send successful
293        transmitList.pop_front();
294
295        DPRINTF(SerialLink, "trySend request successful\n");
296
297        // If there are more packets to send, schedule event to try again.
298        if (!transmitList.empty()) {
299            DeferredPacket next_req = transmitList.front();
300            DPRINTF(SerialLink, "Scheduling next send\n");
301
302            // Make sure bandwidth limitation is met
303            Cycles cycles = Cycles(divCeil(pkt->getSize() * 8,
304                serial_link.num_lanes));
305            Tick t = serial_link.clockEdge(cycles);
306            serial_link.schedule(sendEvent, std::max(next_req.tick, t));
307        }
308
309        // if we have stalled a request due to a full request queue,
310        // then send a retry at this point, also note that if the
311        // request we stalled was waiting for the response queue
312        // rather than the request queue we might stall it again
313        slavePort.retryStalledReq();
314    }
315
316    // if the send failed, then we try again once we receive a retry,
317    // and therefore there is no need to take any action
318}
319
320void
321SerialLink::SerialLinkSlavePort::trySendTiming()
322{
323    assert(!transmitList.empty());
324
325    DeferredPacket resp = transmitList.front();
326
327    assert(resp.tick <= curTick());
328
329    PacketPtr pkt = resp.pkt;
330
331    DPRINTF(SerialLink, "trySend response addr 0x%x, outstanding %d\n",
332            pkt->getAddr(), outstandingResponses);
333
334    if (sendTimingResp(pkt)) {
335        // send successful
336        transmitList.pop_front();
337        DPRINTF(SerialLink, "trySend response successful\n");
338
339        assert(outstandingResponses != 0);
340        --outstandingResponses;
341
342        // If there are more packets to send, schedule event to try again.
343        if (!transmitList.empty()) {
344            DeferredPacket next_resp = transmitList.front();
345            DPRINTF(SerialLink, "Scheduling next send\n");
346
347            // Make sure bandwidth limitation is met
348            Cycles cycles = Cycles(divCeil(pkt->getSize() * 8,
349                serial_link.num_lanes));
350            Tick t = serial_link.clockEdge(cycles);
351            serial_link.schedule(sendEvent, std::max(next_resp.tick, t));
352        }
353
354        // if there is space in the request queue and we were stalling
355        // a request, it will definitely be possible to accept it now
356        // since there is guaranteed space in the response queue
357        if (!masterPort.reqQueueFull() && retryReq) {
358            DPRINTF(SerialLink, "Request waiting for retry, now retrying\n");
359            retryReq = false;
360            sendRetryReq();
361        }
362    }
363
364    // if the send failed, then we try again once we receive a retry,
365    // and therefore there is no need to take any action
366}
367
368void
369SerialLink::SerialLinkMasterPort::recvReqRetry()
370{
371    trySendTiming();
372}
373
374void
375SerialLink::SerialLinkSlavePort::recvRespRetry()
376{
377    trySendTiming();
378}
379
380Tick
381SerialLink::SerialLinkSlavePort::recvAtomic(PacketPtr pkt)
382{
383    return delay * serial_link.clockPeriod() + masterPort.sendAtomic(pkt);
384}
385
386void
387SerialLink::SerialLinkSlavePort::recvFunctional(PacketPtr pkt)
388{
389    pkt->pushLabel(name());
390
391    // check the response queue
392    for (auto i = transmitList.begin();  i != transmitList.end(); ++i) {
393        if (pkt->checkFunctional((*i).pkt)) {
394            pkt->makeResponse();
395            return;
396        }
397    }
398
399    // also check the master port's request queue
400    if (masterPort.checkFunctional(pkt)) {
401        return;
402    }
403
404    pkt->popLabel();
405
406    // fall through if pkt still not satisfied
407    masterPort.sendFunctional(pkt);
408}
409
410bool
411SerialLink::SerialLinkMasterPort::checkFunctional(PacketPtr pkt)
412{
413    bool found = false;
414    auto i = transmitList.begin();
415
416    while (i != transmitList.end() && !found) {
417        if (pkt->checkFunctional((*i).pkt)) {
418            pkt->makeResponse();
419            found = true;
420        }
421        ++i;
422    }
423
424    return found;
425}
426
427AddrRangeList
428SerialLink::SerialLinkSlavePort::getAddrRanges() const
429{
430    return ranges;
431}
432
433SerialLink *
434SerialLinkParams::create()
435{
436    return new SerialLink(this);
437}
438