From 7e3f670457d2705c97078d6a20ee263fabd21ef4 Mon Sep 17 00:00:00 2001 From: Erfan Azarkhish Date: Tue, 3 Nov 2015 12:17:57 -0600 Subject: [PATCH] mem: hmc: serial link model This changeset adds a serial link model for the Hybrid Memory Cube (HMC). SerialLink is a simple variation of the Bridge class, with the ability to account for the latency of packet serialization. Also trySendTiming has been modified to correctly model bandwidth. Committed by: Nilay Vaish --- src/mem/SConscript | 3 + src/mem/SerialLink.py | 63 ++++++ src/mem/serial_link.cc | 437 +++++++++++++++++++++++++++++++++++++++++ src/mem/serial_link.hh | 329 +++++++++++++++++++++++++++++++ 4 files changed, 832 insertions(+) create mode 100644 src/mem/SerialLink.py create mode 100644 src/mem/serial_link.cc create mode 100644 src/mem/serial_link.hh diff --git a/src/mem/SConscript b/src/mem/SConscript index 3b65131a2..1d3249918 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -43,6 +43,7 @@ SimObject('MemObject.py') SimObject('SimpleMemory.py') SimObject('XBar.py') SimObject('HMCController.py') +SimObject('SerialLink.py') Source('abstract_mem.cc') Source('addr_mapper.cc') @@ -66,6 +67,7 @@ Source('stack_dist_calc.cc') Source('tport.cc') Source('xbar.cc') Source('hmc_controller.cc') +Source('serial_link.cc') if env['TARGET_ISA'] != 'null': Source('fs_translating_port_proxy.cc') @@ -104,6 +106,7 @@ DebugFlag('PacketQueue') DebugFlag('StackDist') DebugFlag("DRAMSim2") DebugFlag('HMCController') +DebugFlag('SerialLink') DebugFlag("MemChecker") DebugFlag("MemCheckerMonitor") diff --git a/src/mem/SerialLink.py b/src/mem/SerialLink.py new file mode 100644 index 000000000..f05f2872d --- /dev/null +++ b/src/mem/SerialLink.py @@ -0,0 +1,63 @@ +# Copyright (c) 2012-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-2007 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 +# Andreas Hansson +# Erfan Azarkhish + +from m5.params import * +from MemObject import MemObject + +# SerialLink is a simple variation of the Bridge class, with the ability to +# account for the latency of packet serialization. + +class SerialLink(MemObject): + type = 'SerialLink' + cxx_header = "mem/serial_link.hh" + slave = SlavePort('Slave port') + master = MasterPort('Master port') + req_size = Param.Unsigned(16, "The number of requests to buffer") + resp_size = Param.Unsigned(16, "The number of responses to buffer") + delay = Param.Latency('0ns', "The latency of this serial_link") + ranges = VectorParam.AddrRange([AllMemory], + "Address ranges to pass through the serial_link") + # Bandwidth of the serial link is determined by the clock domain which the + # link belongs to and the number of lanes: + num_lanes = Param.Unsigned(1, "Number of parallel lanes inside the serial" + "link. (aka. lane width)") 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& + _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); +} diff --git a/src/mem/serial_link.hh b/src/mem/serial_link.hh new file mode 100644 index 000000000..d4f6ca488 --- /dev/null +++ b/src/mem/serial_link.hh @@ -0,0 +1,329 @@ +/* + * 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 + * Declaration of the SerialLink Class, modeling Hybrid-Memory-Cube's serial + * interface. + */ + +#ifndef __MEM_SERIAL_LINK_HH__ +#define __MEM_SERIAL_LINK_HH__ + +#include + +#include "base/types.hh" +#include "mem/mem_object.hh" +#include "params/SerialLink.hh" + +/** + * SerialLink is a simple variation of the Bridge class, with the ability to + * account for the latency of packet serialization. We assume that the + * serializer component at the transmitter side does not need to receive the + * whole packet to start the serialization. But the deserializer waits for the + * complete packet to check its integrity first. + */ +class SerialLink : public MemObject +{ + protected: + + /** + * A deferred packet stores a packet along with its scheduled + * transmission time + */ + class DeferredPacket + { + + public: + + const Tick tick; + const PacketPtr pkt; + + DeferredPacket(PacketPtr _pkt, Tick _tick) : tick(_tick), pkt(_pkt) + { } + }; + + // Forward declaration to allow the slave port to have a pointer + class SerialLinkMasterPort; + + /** + * The port on the side that receives requests and sends + * responses. The slave port has a set of address ranges that it + * is responsible for. The slave port also has a buffer for the + * responses not yet sent. + */ + class SerialLinkSlavePort : public SlavePort + { + + private: + + /** The serial_link to which this port belongs. */ + SerialLink& serial_link; + + /** + * Master port on the other side of the serial_link. + */ + SerialLinkMasterPort& masterPort; + + /** Minimum request delay though this serial_link. */ + const Cycles delay; + + /** Address ranges to pass through the serial_link */ + const AddrRangeList ranges; + + /** + * Response packet queue. Response packets are held in this + * queue for a specified delay to model the processing delay + * of the serial_link. We use a deque as we need to iterate over + * the items for functional accesses. + */ + std::deque transmitList; + + /** Counter to track the outstanding responses. */ + unsigned int outstandingResponses; + + /** If we should send a retry when space becomes available. */ + bool retryReq; + + /** Max queue size for reserved responses. */ + unsigned int respQueueLimit; + + /** + * Is this side blocked from accepting new response packets. + * + * @return true if the reserved space has reached the set limit + */ + bool respQueueFull() const; + + /** + * Handle send event, scheduled when the packet at the head of + * the response queue is ready to transmit (for timing + * accesses only). + */ + void trySendTiming(); + + /** Send event for the response queue. */ + EventWrapper sendEvent; + + public: + + /** + * Constructor for the SerialLinkSlavePort. + * + * @param _name the port name including the owner + * @param _serial_link the structural owner + * @param _masterPort the master port on the other side of the + * serial_link + * @param _delay the delay in cycles from receiving to sending + * @param _resp_limit the size of the response queue + * @param _ranges a number of address ranges to forward + */ + SerialLinkSlavePort(const std::string& _name, SerialLink& + _serial_link, SerialLinkMasterPort& _masterPort, + Cycles _delay, int _resp_limit, const + std::vector& _ranges); + + /** + * Queue a response packet to be sent out later and also schedule + * a send if necessary. + * + * @param pkt a response to send out after a delay + * @param when tick when response packet should be sent + */ + void schedTimingResp(PacketPtr pkt, Tick when); + + /** + * Retry any stalled request that we have failed to accept at + * an earlier point in time. This call will do nothing if no + * request is waiting. + */ + void retryStalledReq(); + + protected: + + /** When receiving a timing request from the peer port, + pass it to the serial_link. */ + bool recvTimingReq(PacketPtr pkt); + + /** When receiving a retry request from the peer port, + pass it to the serial_link. */ + void recvRespRetry(); + + /** When receiving a Atomic requestfrom the peer port, + pass it to the serial_link. */ + Tick recvAtomic(PacketPtr pkt); + + /** When receiving a Functional request from the peer port, + pass it to the serial_link. */ + void recvFunctional(PacketPtr pkt); + + /** When receiving a address range request the peer port, + pass it to the serial_link. */ + AddrRangeList getAddrRanges() const; + }; + + + /** + * Port on the side that forwards requests and receives + * responses. The master port has a buffer for the requests not + * yet sent. + */ + class SerialLinkMasterPort : public MasterPort + { + + private: + + /** The serial_link to which this port belongs. */ + SerialLink& serial_link; + + /** + * The slave port on the other side of the serial_link. + */ + SerialLinkSlavePort& slavePort; + + /** Minimum delay though this serial_link. */ + const Cycles delay; + + /** + * Request packet queue. Request packets are held in this + * queue for a specified delay to model the processing delay + * of the serial_link. We use a deque as we need to iterate over + * the items for functional accesses. + */ + std::deque transmitList; + + /** Max queue size for request packets */ + const unsigned int reqQueueLimit; + + /** + * Handle send event, scheduled when the packet at the head of + * the outbound queue is ready to transmit (for timing + * accesses only). + */ + void trySendTiming(); + + /** Send event for the request queue. */ + EventWrapper sendEvent; + + public: + + /** + * Constructor for the SerialLinkMasterPort. + * + * @param _name the port name including the owner + * @param _serial_link the structural owner + * @param _slavePort the slave port on the other side of the + * serial_link + * @param _delay the delay in cycles from receiving to sending + * @param _req_limit the size of the request queue + */ + SerialLinkMasterPort(const std::string& _name, SerialLink& + _serial_link, SerialLinkSlavePort& _slavePort, Cycles + _delay, int _req_limit); + + /** + * Is this side blocked from accepting new request packets. + * + * @return true if the occupied space has reached the set limit + */ + bool reqQueueFull() const; + + /** + * Queue a request packet to be sent out later and also schedule + * a send if necessary. + * + * @param pkt a request to send out after a delay + * @param when tick when response packet should be sent + */ + void schedTimingReq(PacketPtr pkt, Tick when); + + /** + * Check a functional request against the packets in our + * request queue. + * + * @param pkt packet to check against + * + * @return true if we find a match + */ + bool checkFunctional(PacketPtr pkt); + + protected: + + /** When receiving a timing request from the peer port, + pass it to the serial_link. */ + bool recvTimingResp(PacketPtr pkt); + + /** When receiving a retry request from the peer port, + pass it to the serial_link. */ + void recvReqRetry(); + }; + + /** Slave port of the serial_link. */ + SerialLinkSlavePort slavePort; + + /** Master port of the serial_link. */ + SerialLinkMasterPort masterPort; + + /** Number of parallel lanes in this serial link */ + unsigned num_lanes; + + public: + + virtual BaseMasterPort& getMasterPort(const std::string& if_name, + PortID idx = InvalidPortID); + virtual BaseSlavePort& getSlavePort(const std::string& if_name, + PortID idx = InvalidPortID); + + virtual void init(); + + typedef SerialLinkParams Params; + + SerialLink(SerialLinkParams *p); +}; + +#endif //__MEM_SERIAL_LINK_HH__ -- 2.30.2