}
template <typename PortClass>
-BaseBus::Layer<PortClass>::Layer(BaseBus& _bus, const std::string& _name) :
+BaseBus::Layer<PortClass>::Layer(BaseBus& _bus, const std::string& _name,
+ uint16_t num_dest_ports) :
Drainable(),
bus(_bus), _name(_name), state(IDLE), drainManager(NULL),
- retryingPort(NULL), releaseEvent(this)
+ retryingPort(NULL), waitingForPeer(num_dest_ports, NULL),
+ releaseEvent(this)
{
}
template <typename PortClass>
void BaseBus::Layer<PortClass>::occupyLayer(Tick until)
{
- // ensure the state is busy or in retry and never idle at this
- // point, as the bus should transition from idle as soon as it has
- // decided to forward the packet to prevent any follow-on calls to
- // sendTiming seeing an unoccupied bus
- assert(state != IDLE);
-
- // note that we do not change the bus state here, if we are going
- // from idle to busy it is handled by tryTiming, and if we
- // are in retry we should remain in retry such that
- // succeededTiming still sees the accurate state
+ // ensure the state is busy at this point, as the bus should
+ // transition from idle as soon as it has decided to forward the
+ // packet to prevent any follow-on calls to sendTiming seeing an
+ // unoccupied bus
+ assert(state == BUSY);
// until should never be 0 as express snoops never occupy the bus
assert(until != 0);
template <typename PortClass>
bool
-BaseBus::Layer<PortClass>::tryTiming(PortClass* port)
+BaseBus::Layer<PortClass>::tryTiming(PortClass* port, PortID dest_port_id)
{
// first we see if the bus is busy, next we check if we are in a
- // retry with a port other than the current one
- if (state == BUSY || (state == RETRY && port != retryingPort)) {
- // put the port at the end of the retry list
- retryList.push_back(port);
+ // retry with a port other than the current one, lastly we check
+ // if the destination port is already engaged in a transaction
+ // waiting for a retry from the peer
+ if (state == BUSY || (state == RETRY && port != retryingPort) ||
+ (dest_port_id != InvalidPortID &&
+ waitingForPeer[dest_port_id] != NULL)) {
+ // put the port at the end of the retry list waiting for the
+ // layer to be freed up (and in the case of a busy peer, for
+ // that transaction to go through, and then the bus to free
+ // up)
+ waitingForLayer.push_back(port);
return false;
}
- // @todo: here we should no longer consider this port retrying
- // once we can differentiate retries due to a busy bus and a
- // failed forwarding, for now keep it so we can stick it back at
- // the front of the retry list if needed
+ // update the state to busy
+ state = BUSY;
- // update the state which is shared for request, response and
- // snoop responses, if we were idle we are now busy, if we are in
- // a retry, then do not change
- if (state == IDLE)
- state = BUSY;
+ // reset the retrying port
+ retryingPort = NULL;
return true;
}
void
BaseBus::Layer<PortClass>::succeededTiming(Tick busy_time)
{
- // if a retrying port succeeded, update the state and reset the
- // retrying port
- if (state == RETRY) {
- DPRINTF(BaseBus, "Succeeded retry from %s\n",
- retryingPort->name());
- state = BUSY;
- retryingPort = NULL;
- }
-
- // we should either have gone from idle to busy in the
- // tryTiming test, or just gone from a retry to busy
+ // we should have gone from idle or retry to busy in the tryTiming
+ // test
assert(state == BUSY);
// occupy the bus accordingly
template <typename PortClass>
void
-BaseBus::Layer<PortClass>::failedTiming(PortClass* port, Tick busy_time)
+BaseBus::Layer<PortClass>::failedTiming(PortClass* src_port,
+ PortID dest_port_id, Tick busy_time)
{
- // if the current failing port is the retrying one, then for now stick it
- // back at the front of the retry list to not change any regressions
- if (state == RETRY) {
- // we should never see a retry from any port but the current
- // retry port at this point
- assert(port == retryingPort);
- retryList.push_front(port);
- retryingPort = NULL;
- } else {
- // if we are not in a retry, i.e. busy (but never idle), then
- // add the port at the end of the retry list
- retryList.push_back(port);
- }
+ // ensure no one got in between and tried to send something to
+ // this port
+ assert(waitingForPeer[dest_port_id] == NULL);
- // even if we retried the current one and did not succeed,
- // we are no longer retrying but instead busy
- state = BUSY;
+ // if the source port is the current retrying one or not, we have
+ // failed in forwarding and should track that we are now waiting
+ // for the peer to send a retry
+ waitingForPeer[dest_port_id] = src_port;
+
+ // we should have gone from idle or retry to busy in the tryTiming
+ // test
+ assert(state == BUSY);
// occupy the bus accordingly
occupyLayer(busy_time);
// update the state
state = IDLE;
- // bus is now idle, so if someone is waiting we can retry
- if (!retryList.empty()) {
- // note that we block (return false on recvTiming) both
- // because the bus is busy and because the destination is
- // busy, and in the latter case the bus may be released before
- // we see a retry from the destination
+ // bus layer is now idle, so if someone is waiting we can retry
+ if (!waitingForLayer.empty()) {
retryWaiting();
} else if (drainManager) {
DPRINTF(Drain, "Bus done draining, signaling drain manager\n");
void
BaseBus::Layer<PortClass>::retryWaiting()
{
- // this should never be called with an empty retry list
- assert(!retryList.empty());
+ // this should never be called with no one waiting
+ assert(!waitingForLayer.empty());
// we always go to retrying from idle
assert(state == IDLE);
// set the retrying port to the front of the retry list and pop it
// off the list
assert(retryingPort == NULL);
- retryingPort = retryList.front();
- retryList.pop_front();
+ retryingPort = waitingForLayer.front();
+ waitingForLayer.pop_front();
- // note that we might have blocked on the receiving port being
- // busy (rather than the bus itself) and now call retry before the
- // destination called retry on the bus
+ // tell the port to retry, which in some cases ends up calling the
+ // bus
retryingPort->sendRetry();
// If the bus is still in the retry state, sendTiming wasn't
- // called in zero time (e.g. the cache does this)
+ // called in zero time (e.g. the cache does this), burn a cycle
if (state == RETRY) {
- //Burn a cycle for the missed grant.
-
- // update the state to busy and reset the retrying port
+ // update the state to busy and reset the retrying port, we
+ // have done our bit and sent the retry
state = BUSY;
retryingPort = NULL;
template <typename PortClass>
void
-BaseBus::Layer<PortClass>::recvRetry()
+BaseBus::Layer<PortClass>::recvRetry(PortID port_id)
{
- // we got a retry from a peer that we tried to send something to
- // and failed, but we sent it on the account of someone else, and
- // that source port should be on our retry list, however if the
- // bus layer is released before this happens and the retry (from
- // the bus point of view) is successful then this no longer holds
- // and we could in fact have an empty retry list
- if (retryList.empty())
- return;
-
- // if the bus layer is idle
+ // we should never get a retry without having failed to forward
+ // something to this port
+ assert(waitingForPeer[port_id] != NULL);
+
+ // find the port where the failed packet originated and remove the
+ // item from the waiting list
+ PortClass* retry_port = waitingForPeer[port_id];
+ waitingForPeer[port_id] = NULL;
+
+ // add this port at the front of the waiting ports for the layer,
+ // this allows us to call retry on the port immediately if the bus
+ // layer is idle
+ waitingForLayer.push_front(retry_port);
+
+ // if the bus layer is idle, retry this port straight away, if we
+ // are busy, then simply let the port wait for its turn
if (state == IDLE) {
- // note that we do not care who told us to retry at the moment, we
- // merely let the first one on the retry list go
retryWaiting();
+ } else {
+ assert(state == BUSY);
}
}
//We should check that we're not "doing" anything, and that noone is
//waiting. We might be idle but have someone waiting if the device we
//contacted for a retry didn't actually retry.
- if (!retryList.empty() || state != IDLE) {
+ if (state != IDLE) {
DPRINTF(Drain, "Bus not drained\n");
drainManager = dm;
return 1;
*
* @param _bus the bus this layer belongs to
* @param _name the layer's name
+ * @param num_dest_ports number of destination ports
*/
- Layer(BaseBus& _bus, const std::string& _name);
+ Layer(BaseBus& _bus, const std::string& _name, uint16_t num_dest_ports);
/**
* Drain according to the normal semantics, so that the bus
/**
* Determine if the bus layer accepts a packet from a specific
* port. If not, the port in question is also added to the
- * retry list. In either case the state of the layer is updated
- * accordingly.
+ * retry list. In either case the state of the layer is
+ * updated accordingly. To ignore checking the destination
+ * port (used by snoops), pass InvalidPortID.
*
- * @param port Source port resenting the packet
+ * @param port Source port presenting the packet
+ * @param dest_port_id Destination port id
*
* @return True if the bus layer accepts the packet
*/
- bool tryTiming(PortClass* port);
+ bool tryTiming(PortClass* port, PortID dest_port_id);
/**
* Deal with a destination port accepting a packet by potentially
* not already at the front) and occupying the bus layer
* accordingly.
*
+ * @param src_port Source port
+ * @param dest_port_id Destination port id
* @param busy_time Time to spend as a result of a failed send
*/
- void failedTiming(PortClass* port, Tick busy_time);
+ void failedTiming(PortClass* src_port, PortID dest_port_id,
+ Tick busy_time);
/** Occupy the bus layer until until */
void occupyLayer(Tick until);
/**
- * Send a retry to the port at the head of the retryList. The
+ * Send a retry to the port at the head of waitingForLayer. The
* caller must ensure that the list is not empty.
*/
void retryWaiting();
/**
- * Handler a retry from a neighbouring module. Eventually this
- * should be all encapsulated in the bus. This wraps
+ * Handle a retry from a neighbouring module. This wraps
* retryWaiting by verifying that there are ports waiting
* before calling retryWaiting.
+ *
+ * @param port_id Id of the port that received the retry
*/
- void recvRetry();
+ void recvRetry(PortID port_id);
private:
* spent. Once the bus layer leaves the busy state, it can
* either go back to idle, if no packets have arrived while it
* was busy, or the bus layer goes on to retry the first port
- * on the retryList. A similar transition takes place from
+ * in waitingForLayer. A similar transition takes place from
* idle to retry if the bus layer receives a retry from one of
* its connected ports. The retry state lasts until the port
* in questions calls sendTiming and returns control to the
DrainManager *drainManager;
/**
- * An array of ports that retry should be called
- * on because the original send failed for whatever reason.
+ * A deque of ports that retry should be called on because
+ * the original send was delayed due to a busy layer.
*/
- std::deque<PortClass*> retryList;
+ std::deque<PortClass*> waitingForLayer;
/**
* Port that we are currently in the process of telling to
*/
PortClass* retryingPort;
+ /**
+ * A vector that tracks who is waiting for the retry when
+ * receiving it from a peer. The vector indices are port ids
+ * of the outgoing ports for the specific layer. The values
+ * are the incoming ports that tried to forward something to
+ * the outgoing port, but was told to wait and is now waiting
+ * for a retry. If no port is waiting NULL is stored on the
+ * location in question.
+ */
+ std::vector<PortClass*> waitingForPeer;
+
/**
* Release the bus layer after being occupied and return to an
* idle state where we proceed to send a retry to any
#include "sim/system.hh"
CoherentBus::CoherentBus(const CoherentBusParams *p)
- : BaseBus(p), reqLayer(*this, ".reqLayer"),
- respLayer(*this, ".respLayer"),
- snoopRespLayer(*this, ".snoopRespLayer"),
+ : BaseBus(p),
+ reqLayer(*this, ".reqLayer", p->port_master_connection_count +
+ p->port_default_connection_count),
+ respLayer(*this, ".respLayer", p->port_slave_connection_count),
+ snoopRespLayer(*this, ".snoopRespLayer",
+ p->port_master_connection_count +
+ p->port_default_connection_count),
system(p->system)
{
// create the ports based on the size of the master and slave
// remember if the packet is an express snoop
bool is_express_snoop = pkt->isExpressSnoop();
+ // determine the destination based on the address
+ PortID dest_port_id = findPort(pkt->getAddr());
+
// test if the bus should be considered occupied for the current
// port, and exclude express snoops from the check
- if (!is_express_snoop && !reqLayer.tryTiming(src_port)) {
- DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
+ if (!is_express_snoop && !reqLayer.tryTiming(src_port, dest_port_id)) {
+ DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUS BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
}
outstandingReq.insert(pkt->req);
}
- // since it is a normal request, determine the destination
- // based on the address and attempt to send the packet
- bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
+ // since it is a normal request, attempt to send the packet
+ bool success = masterPorts[dest_port_id]->sendTimingReq(pkt);
// if this is an express snoop, we are done at this point
if (is_express_snoop) {
src_port->name(), pkt->cmdString(), pkt->getAddr());
// update the bus state and schedule an idle event
- reqLayer.failedTiming(src_port, clockEdge(Cycles(headerCycles)));
+ reqLayer.failedTiming(src_port, dest_port_id,
+ clockEdge(Cycles(headerCycles)));
} else {
// update the bus state and schedule an idle event
reqLayer.succeededTiming(packetFinishTime);
// test if the bus should be considered occupied for the current
// port
- if (!respLayer.tryTiming(src_port)) {
+ if (!respLayer.tryTiming(src_port, pkt->getDest())) {
DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
SlavePort* src_port = slavePorts[slave_port_id];
// test if the bus should be considered occupied for the current
- // port
- if (!snoopRespLayer.tryTiming(src_port)) {
+ // port, do not use the destination port in the check as we do not
+ // know yet if it is to be passed on as a snoop response or normal
+ // response and we never block on either
+ if (!snoopRespLayer.tryTiming(src_port, InvalidPortID)) {
DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
}
void
-CoherentBus::recvRetry()
+CoherentBus::recvRetry(PortID master_port_id)
{
// responses and snoop responses never block on forwarding them,
// so the retry will always be coming from a port to which we
// tried to forward a request
- reqLayer.recvRetry();
+ reqLayer.recvRetry(master_port_id);
}
Tick
/** When reciving a retry from the peer port (at id),
pass it to the bus. */
virtual void recvRetry()
- { bus.recvRetry(); }
+ { bus.recvRetry(id); }
// Ask the bus to ask everyone on the bus what their block size is and
// take the max of it. This might need to be changed a bit if we ever
/** Timing function called by port when it is once again able to process
* requests. */
- void recvRetry();
+ void recvRetry(PortID master_port_id);
/**
* Forward a timing packet to our snoopers, potentially excluding
#include "mem/noncoherent_bus.hh"
NoncoherentBus::NoncoherentBus(const NoncoherentBusParams *p)
- : BaseBus(p), reqLayer(*this, ".reqLayer"),
- respLayer(*this, ".respLayer")
+ : BaseBus(p),
+ reqLayer(*this, ".reqLayer", p->port_master_connection_count +
+ p->port_default_connection_count),
+ respLayer(*this, ".respLayer", p->port_slave_connection_count)
{
// create the ports based on the size of the master and slave
// vector ports, and the presence of the default port, the ports
// we should never see express snoops on a non-coherent bus
assert(!pkt->isExpressSnoop());
+ // determine the destination based on the address
+ PortID dest_port_id = findPort(pkt->getAddr());
+
// test if the bus should be considered occupied for the current
// port
- if (!reqLayer.tryTiming(src_port)) {
+ if (!reqLayer.tryTiming(src_port, dest_port_id)) {
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
calcPacketTiming(pkt);
Tick packetFinishTime = pkt->busLastWordDelay + curTick();
- // since it is a normal request, determine the destination
- // based on the address and attempt to send the packet
- bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
+ // since it is a normal request, attempt to send the packet
+ bool success = masterPorts[dest_port_id]->sendTimingReq(pkt);
if (!success) {
// inhibited packets should never be forced to retry
pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
// occupy until the header is sent
- reqLayer.failedTiming(src_port, clockEdge(Cycles(headerCycles)));
+ reqLayer.failedTiming(src_port, dest_port_id,
+ clockEdge(Cycles(headerCycles)));
return false;
}
// test if the bus should be considered occupied for the current
// port
- if (!respLayer.tryTiming(src_port)) {
+ if (!respLayer.tryTiming(src_port, pkt->getDest())) {
DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
}
void
-NoncoherentBus::recvRetry()
+NoncoherentBus::recvRetry(PortID master_port_id)
{
// responses never block on forwarding them, so the retry will
// always be coming from a port to which we tried to forward a
// request
- reqLayer.recvRetry();
+ reqLayer.recvRetry(master_port_id);
}
Tick
/** When reciving a retry from the peer port (at id),
pass it to the bus. */
virtual void recvRetry()
- { bus.recvRetry(); }
+ { bus.recvRetry(id); }
/**
* Get the maximum block size as seen by the bus.
/** Timing function called by port when it is once again able to process
* requests. */
- void recvRetry();
+ void recvRetry(PortID master_port_id);
/** Function called by the port when the bus is recieving a Atomic
transaction.*/