diff options
Diffstat (limited to 'src/mem/serial_link.cc')
-rw-r--r-- | src/mem/serial_link.cc | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/mem/serial_link.cc b/src/mem/serial_link.cc new file mode 100644 index 000000000..577339bb8 --- /dev/null +++ b/src/mem/serial_link.cc @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2011-2013 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Copyright (c) 2006 The Regents of The University of Michigan + * Copyright (c) 2015 The University of Bologna + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Steve Reinhardt + * Andreas Hansson + * Erfan Azarkhish + */ + +/** + * @file + * Implementation of the SerialLink Class, modeling Hybrid-Memory-Cube's + * serial interface. + */ + +#include "mem/serial_link.hh" + +#include "base/trace.hh" +#include "debug/SerialLink.hh" +#include "params/SerialLink.hh" + + +SerialLink::SerialLinkSlavePort::SerialLinkSlavePort(const std::string& _name, + SerialLink& _serial_link, + SerialLinkMasterPort& _masterPort, + Cycles _delay, int _resp_limit, + const std::vector<AddrRange>& + _ranges) + : SlavePort(_name, &_serial_link), serial_link(_serial_link), + masterPort(_masterPort), delay(_delay), + ranges(_ranges.begin(), _ranges.end()), + outstandingResponses(0), retryReq(false), + respQueueLimit(_resp_limit), sendEvent(*this) +{ +} + +SerialLink::SerialLinkMasterPort::SerialLinkMasterPort(const std::string& + _name, SerialLink& _serial_link, + SerialLinkSlavePort& _slavePort, + Cycles _delay, int _req_limit) + : MasterPort(_name, &_serial_link), serial_link(_serial_link), + slavePort(_slavePort), delay(_delay), reqQueueLimit(_req_limit), + sendEvent(*this) +{ +} + +SerialLink::SerialLink(SerialLinkParams *p) + : MemObject(p), + slavePort(p->name + ".slave", *this, masterPort, + ticksToCycles(p->delay), p->resp_size, p->ranges), + masterPort(p->name + ".master", *this, slavePort, + ticksToCycles(p->delay), p->req_size), + num_lanes(p->num_lanes) +{ +} + +BaseMasterPort& +SerialLink::getMasterPort(const std::string &if_name, PortID idx) +{ + if (if_name == "master") + return masterPort; + else + // pass it along to our super class + return MemObject::getMasterPort(if_name, idx); +} + +BaseSlavePort& +SerialLink::getSlavePort(const std::string &if_name, PortID idx) +{ + if (if_name == "slave") + return slavePort; + else + // pass it along to our super class + return MemObject::getSlavePort(if_name, idx); +} + +void +SerialLink::init() +{ + // make sure both sides are connected and have the same block size + if (!slavePort.isConnected() || !masterPort.isConnected()) + fatal("Both ports of a serial_link must be connected.\n"); + + // notify the master side of our address ranges + slavePort.sendRangeChange(); +} + +bool +SerialLink::SerialLinkSlavePort::respQueueFull() const +{ + return outstandingResponses == respQueueLimit; +} + +bool +SerialLink::SerialLinkMasterPort::reqQueueFull() const +{ + return transmitList.size() == reqQueueLimit; +} + +bool +SerialLink::SerialLinkMasterPort::recvTimingResp(PacketPtr pkt) +{ + // all checks are done when the request is accepted on the slave + // side, so we are guaranteed to have space for the response + DPRINTF(SerialLink, "recvTimingResp: %s addr 0x%x\n", + pkt->cmdString(), pkt->getAddr()); + + DPRINTF(SerialLink, "Request queue size: %d\n", transmitList.size()); + + // @todo: We need to pay for this and not just zero it out + pkt->headerDelay = pkt->payloadDelay = 0; + + // This is similar to what happens for the request packets: + // The serializer will start serialization as soon as it receives the + // first flit, but the deserializer (at the host side in this case), will + // have to wait to receive the whole packet. So we only account for the + // deserialization latency. + Cycles cycles = delay; + cycles += Cycles(divCeil(pkt->getSize() * 8, serial_link.num_lanes)); + Tick t = serial_link.clockEdge(cycles); + + //@todo: If the processor sends two uncached requests towards HMC and the + // second one is smaller than the first one. It may happen that the second + // one crosses this link faster than the first one (because the packet + // waits in the link based on its size). This can reorder the received + // response. + slavePort.schedTimingResp(pkt, t); + + return true; +} + +bool +SerialLink::SerialLinkSlavePort::recvTimingReq(PacketPtr pkt) +{ + DPRINTF(SerialLink, "recvTimingReq: %s addr 0x%x\n", + pkt->cmdString(), pkt->getAddr()); + + // we should not see a timing request if we are already in a retry + assert(!retryReq); + + DPRINTF(SerialLink, "Response queue size: %d outresp: %d\n", + transmitList.size(), outstandingResponses); + + // if the request queue is full then there is no hope + if (masterPort.reqQueueFull()) { + DPRINTF(SerialLink, "Request queue full\n"); + retryReq = true; + } else if ( !retryReq ) { + // look at the response queue if we expect to see a response + bool expects_response = pkt->needsResponse() && + !pkt->memInhibitAsserted(); + if (expects_response) { + if (respQueueFull()) { + DPRINTF(SerialLink, "Response queue full\n"); + retryReq = true; + } else { + // ok to send the request with space for the response + DPRINTF(SerialLink, "Reserving space for response\n"); + assert(outstandingResponses != respQueueLimit); + ++outstandingResponses; + + // no need to set retryReq to false as this is already the + // case + } + } + + if (!retryReq) { + // @todo: We need to pay for this and not just zero it out + pkt->headerDelay = pkt->payloadDelay = 0; + + // We assume that the serializer component at the transmitter side + // does not need to receive the whole packet to start the + // serialization (this assumption is consistent with the HMC + // standard). But the deserializer waits for the complete packet + // to check its integrity first. So everytime a packet crosses a + // serial link, we should account for its deserialization latency + // only. + Cycles cycles = delay; + cycles += Cycles(divCeil(pkt->getSize() * 8, + serial_link.num_lanes)); + Tick t = serial_link.clockEdge(cycles); + + //@todo: If the processor sends two uncached requests towards HMC + // and the second one is smaller than the first one. It may happen + // that the second one crosses this link faster than the first one + // (because the packet waits in the link based on its size). + // This can reorder the received response. + masterPort.schedTimingReq(pkt, t); + } + } + + // remember that we are now stalling a packet and that we have to + // tell the sending master to retry once space becomes available, + // we make no distinction whether the stalling is due to the + // request queue or response queue being full + return !retryReq; +} + +void +SerialLink::SerialLinkSlavePort::retryStalledReq() +{ + if (retryReq) { + DPRINTF(SerialLink, "Request waiting for retry, now retrying\n"); + retryReq = false; + sendRetryReq(); + } +} + +void +SerialLink::SerialLinkMasterPort::schedTimingReq(PacketPtr pkt, Tick when) +{ + // If we're about to put this packet at the head of the queue, we + // need to schedule an event to do the transmit. Otherwise there + // should already be an event scheduled for sending the head + // packet. + if (transmitList.empty()) { + serial_link.schedule(sendEvent, when); + } + + assert(transmitList.size() != reqQueueLimit); + + transmitList.emplace_back(DeferredPacket(pkt, when)); +} + + +void +SerialLink::SerialLinkSlavePort::schedTimingResp(PacketPtr pkt, Tick when) +{ + // If we're about to put this packet at the head of the queue, we + // need to schedule an event to do the transmit. Otherwise there + // should already be an event scheduled for sending the head + // packet. + if (transmitList.empty()) { + serial_link.schedule(sendEvent, when); + } + + transmitList.emplace_back(DeferredPacket(pkt, when)); +} + +void +SerialLink::SerialLinkMasterPort::trySendTiming() +{ + assert(!transmitList.empty()); + + DeferredPacket req = transmitList.front(); + + assert(req.tick <= curTick()); + + PacketPtr pkt = req.pkt; + + DPRINTF(SerialLink, "trySend request addr 0x%x, queue size %d\n", + pkt->getAddr(), transmitList.size()); + + if (sendTimingReq(pkt)) { + // send successful + transmitList.pop_front(); + + DPRINTF(SerialLink, "trySend request successful\n"); + + // If there are more packets to send, schedule event to try again. + if (!transmitList.empty()) { + DeferredPacket next_req = transmitList.front(); + DPRINTF(SerialLink, "Scheduling next send\n"); + + // Make sure bandwidth limitation is met + Cycles cycles = Cycles(divCeil(pkt->getSize() * 8, + serial_link.num_lanes)); + Tick t = serial_link.clockEdge(cycles); + serial_link.schedule(sendEvent, std::max(next_req.tick, t)); + } + + // if we have stalled a request due to a full request queue, + // then send a retry at this point, also note that if the + // request we stalled was waiting for the response queue + // rather than the request queue we might stall it again + slavePort.retryStalledReq(); + } + + // if the send failed, then we try again once we receive a retry, + // and therefore there is no need to take any action +} + +void +SerialLink::SerialLinkSlavePort::trySendTiming() +{ + assert(!transmitList.empty()); + + DeferredPacket resp = transmitList.front(); + + assert(resp.tick <= curTick()); + + PacketPtr pkt = resp.pkt; + + DPRINTF(SerialLink, "trySend response addr 0x%x, outstanding %d\n", + pkt->getAddr(), outstandingResponses); + + if (sendTimingResp(pkt)) { + // send successful + transmitList.pop_front(); + DPRINTF(SerialLink, "trySend response successful\n"); + + assert(outstandingResponses != 0); + --outstandingResponses; + + // If there are more packets to send, schedule event to try again. + if (!transmitList.empty()) { + DeferredPacket next_resp = transmitList.front(); + DPRINTF(SerialLink, "Scheduling next send\n"); + + // Make sure bandwidth limitation is met + Cycles cycles = Cycles(divCeil(pkt->getSize() * 8, + serial_link.num_lanes)); + Tick t = serial_link.clockEdge(cycles); + serial_link.schedule(sendEvent, std::max(next_resp.tick, t)); + } + + // if there is space in the request queue and we were stalling + // a request, it will definitely be possible to accept it now + // since there is guaranteed space in the response queue + if (!masterPort.reqQueueFull() && retryReq) { + DPRINTF(SerialLink, "Request waiting for retry, now retrying\n"); + retryReq = false; + sendRetryReq(); + } + } + + // if the send failed, then we try again once we receive a retry, + // and therefore there is no need to take any action +} + +void +SerialLink::SerialLinkMasterPort::recvReqRetry() +{ + trySendTiming(); +} + +void +SerialLink::SerialLinkSlavePort::recvRespRetry() +{ + trySendTiming(); +} + +Tick +SerialLink::SerialLinkSlavePort::recvAtomic(PacketPtr pkt) +{ + return delay * serial_link.clockPeriod() + masterPort.sendAtomic(pkt); +} + +void +SerialLink::SerialLinkSlavePort::recvFunctional(PacketPtr pkt) +{ + pkt->pushLabel(name()); + + // check the response queue + for (auto i = transmitList.begin(); i != transmitList.end(); ++i) { + if (pkt->checkFunctional((*i).pkt)) { + pkt->makeResponse(); + return; + } + } + + // also check the master port's request queue + if (masterPort.checkFunctional(pkt)) { + return; + } + + pkt->popLabel(); + + // fall through if pkt still not satisfied + masterPort.sendFunctional(pkt); +} + +bool +SerialLink::SerialLinkMasterPort::checkFunctional(PacketPtr pkt) +{ + bool found = false; + auto i = transmitList.begin(); + + while(i != transmitList.end() && !found) { + if (pkt->checkFunctional((*i).pkt)) { + pkt->makeResponse(); + found = true; + } + ++i; + } + + return found; +} + +AddrRangeList +SerialLink::SerialLinkSlavePort::getAddrRanges() const +{ + return ranges; +} + +SerialLink * +SerialLinkParams::create() +{ + return new SerialLink(this); +} |