Bus: Split the bus into separate request/response layers

This patch splits the existing buses into multiple layers. The
non-coherent bus is split into a request and a response layer, and the
coherent bus adds an additional layer for the snoop responses. The
layer is modified to be templatised on the port type, such that the
different layers can have retryLists with either master or slave
ports. This patch also removes the dynamic cast from the retry, as
previously promised when moving the recvRetry from the port base class
to the master/slave port respectively.

Overall, the split bus more closely reflects any modern on-chip bus
and should be at step in the right direction. From this point, it
would be reasonable straight forward to add separate layers (and thus
contention points and arbitration) for each port and thus create a
true crossbar.

The regressions all produce the correct output, but have varying
degrees of changes to their statistics. A separate patch will be
pushed with the updates to the reference statistics.
This commit is contained in:
Andreas Hansson 2012-07-09 12:35:37 -04:00
parent 995e6e4670
commit 8caaac048a
6 changed files with 82 additions and 48 deletions

View file

@ -137,13 +137,16 @@ BaseBus::calcPacketTiming(PacketPtr pkt)
return headerTime; return headerTime;
} }
BaseBus::Layer::Layer(BaseBus& _bus, const std::string& _name, Tick _clock) : template <typename PortClass>
BaseBus::Layer<PortClass>::Layer(BaseBus& _bus, const std::string& _name,
Tick _clock) :
bus(_bus), _name(_name), state(IDLE), clock(_clock), drainEvent(NULL), bus(_bus), _name(_name), state(IDLE), clock(_clock), drainEvent(NULL),
releaseEvent(this) releaseEvent(this)
{ {
} }
void BaseBus::Layer::occupyLayer(Tick until) template <typename PortClass>
void BaseBus::Layer<PortClass>::occupyLayer(Tick until)
{ {
// ensure the state is busy or in retry and never idle at this // 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 // point, as the bus should transition from idle as soon as it has
@ -164,8 +167,9 @@ void BaseBus::Layer::occupyLayer(Tick until)
curTick(), until); curTick(), until);
} }
template <typename PortClass>
bool bool
BaseBus::Layer::tryTiming(Port* port) BaseBus::Layer<PortClass>::tryTiming(PortClass* port)
{ {
// first we see if the bus is busy, next we check if we are in a // 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 // retry with a port other than the current one
@ -184,8 +188,9 @@ BaseBus::Layer::tryTiming(Port* port)
return true; return true;
} }
template <typename PortClass>
void void
BaseBus::Layer::succeededTiming(Tick busy_time) BaseBus::Layer<PortClass>::succeededTiming(Tick busy_time)
{ {
// if a retrying port succeeded, also take it off the retry list // if a retrying port succeeded, also take it off the retry list
if (state == RETRY) { if (state == RETRY) {
@ -203,8 +208,9 @@ BaseBus::Layer::succeededTiming(Tick busy_time)
occupyLayer(busy_time); occupyLayer(busy_time);
} }
template <typename PortClass>
void void
BaseBus::Layer::failedTiming(SlavePort* port, Tick busy_time) BaseBus::Layer<PortClass>::failedTiming(PortClass* port, Tick busy_time)
{ {
// if we are not in a retry, i.e. busy (but never idle), or we are // if we are not in a retry, i.e. busy (but never idle), or we are
// in a retry but not for the current port, then add the port at // in a retry but not for the current port, then add the port at
@ -221,8 +227,9 @@ BaseBus::Layer::failedTiming(SlavePort* port, Tick busy_time)
occupyLayer(busy_time); occupyLayer(busy_time);
} }
template <typename PortClass>
void void
BaseBus::Layer::releaseLayer() BaseBus::Layer<PortClass>::releaseLayer()
{ {
// releasing the bus means we should now be idle // releasing the bus means we should now be idle
assert(state == BUSY); assert(state == BUSY);
@ -246,8 +253,9 @@ BaseBus::Layer::releaseLayer()
} }
} }
template <typename PortClass>
void void
BaseBus::Layer::retryWaiting() BaseBus::Layer<PortClass>::retryWaiting()
{ {
// this should never be called with an empty retry list // this should never be called with an empty retry list
assert(!retryList.empty()); assert(!retryList.empty());
@ -262,10 +270,7 @@ BaseBus::Layer::retryWaiting()
// note that we might have blocked on the receiving port being // note that we might have blocked on the receiving port being
// busy (rather than the bus itself) and now call retry before the // busy (rather than the bus itself) and now call retry before the
// destination called retry on the bus // destination called retry on the bus
if (dynamic_cast<SlavePort*>(retryList.front()) != NULL) retryList.front()->sendRetry();
(dynamic_cast<SlavePort*>(retryList.front()))->sendRetry();
else
(dynamic_cast<MasterPort*>(retryList.front()))->sendRetry();
// If the bus is still in the retry state, sendTiming wasn't // 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)
@ -286,8 +291,9 @@ BaseBus::Layer::retryWaiting()
} }
} }
template <typename PortClass>
void void
BaseBus::Layer::recvRetry() BaseBus::Layer<PortClass>::recvRetry()
{ {
// we got a retry from a peer that we tried to send something to // 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 // and failed, but we sent it on the account of someone else, and
@ -484,9 +490,9 @@ BaseBus::findBlockSize()
return max_bs; return max_bs;
} }
template <typename PortClass>
unsigned int unsigned int
BaseBus::Layer::drain(Event * de) BaseBus::Layer<PortClass>::drain(Event * de)
{ {
//We should check that we're not "doing" anything, and that noone is //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 //waiting. We might be idle but have someone waiting if the device we
@ -497,3 +503,11 @@ BaseBus::Layer::drain(Event * de)
} }
return 0; return 0;
} }
/**
* Bus layer template instantiations. Could be removed with _impl.hh
* file, but since there are only two given options (MasterPort and
* SlavePort) it seems a bit excessive at this point.
*/
template class BaseBus::Layer<SlavePort>;
template class BaseBus::Layer<MasterPort>;

View file

@ -82,11 +82,19 @@ class BaseBus : public MemObject
* point is to have three layers, for requests, responses, and * point is to have three layers, for requests, responses, and
* snoop responses respectively (snoop requests are instantaneous * snoop responses respectively (snoop requests are instantaneous
* and do not need any flow control or arbitration). This case is * and do not need any flow control or arbitration). This case is
* similar to AHB and some OCP configurations. As a further * similar to AHB and some OCP configurations.
* extensions beyond the three-layer bus, a future multi-layer bus *
* has with one layer per connected slave port provides a full or * As a further extensions beyond the three-layer bus, a future
* partial crossbar, like AXI, OCP, PCIe etc. * multi-layer bus has with one layer per connected slave port
* provides a full or partial crossbar, like AXI, OCP, PCIe etc.
*
* The template parameter, PortClass, indicates the destination
* port type for the bus. The retry list holds either master ports
* or slave ports, depending on the direction of the layer. Thus,
* a request layer has a retry list containing slave ports,
* whereas a response layer holds master ports.
*/ */
template <typename PortClass>
class Layer class Layer
{ {
@ -129,7 +137,7 @@ class BaseBus : public MemObject
* *
* @return True if the bus layer accepts the packet * @return True if the bus layer accepts the packet
*/ */
bool tryTiming(Port* port); bool tryTiming(PortClass* port);
/** /**
* Deal with a destination port accepting a packet by potentially * Deal with a destination port accepting a packet by potentially
@ -148,7 +156,7 @@ class BaseBus : public MemObject
* *
* @param busy_time Time to spend as a result of a failed send * @param busy_time Time to spend as a result of a failed send
*/ */
void failedTiming(SlavePort* port, Tick busy_time); void failedTiming(PortClass* port, Tick busy_time);
/** Occupy the bus layer until until */ /** Occupy the bus layer until until */
void occupyLayer(Tick until); void occupyLayer(Tick until);
@ -203,10 +211,10 @@ class BaseBus : public MemObject
Event * drainEvent; Event * drainEvent;
/** /**
* An array of pointers to ports that retry should be called * An array of ports that retry should be called
* on because the original send failed for whatever reason. * on because the original send failed for whatever reason.
*/ */
std::list<Port*> retryList; std::list<PortClass*> retryList;
/** /**
* Release the bus layer after being occupied and return to an * Release the bus layer after being occupied and return to an

View file

@ -54,7 +54,9 @@
#include "mem/coherent_bus.hh" #include "mem/coherent_bus.hh"
CoherentBus::CoherentBus(const CoherentBusParams *p) CoherentBus::CoherentBus(const CoherentBusParams *p)
: BaseBus(p), layer(*this, ".layer", p->clock) : BaseBus(p), reqLayer(*this, ".reqLayer", p->clock),
respLayer(*this, ".respLayer", p->clock),
snoopRespLayer(*this, ".snoopRespLayer", p->clock)
{ {
// create the ports based on the size of the master and slave // create the ports based on the size of the master and slave
// vector ports, and the presence of the default port, the ports // vector ports, and the presence of the default port, the ports
@ -115,7 +117,7 @@ CoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
// test if the bus should be considered occupied for the current // test if the bus should be considered occupied for the current
// port, and exclude express snoops from the check // port, and exclude express snoops from the check
if (!is_express_snoop && !layer.tryTiming(src_port)) { if (!is_express_snoop && !reqLayer.tryTiming(src_port)) {
DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n", DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr()); src_port->name(), pkt->cmdString(), pkt->getAddr());
return false; return false;
@ -176,10 +178,10 @@ CoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
src_port->name(), pkt->cmdString(), pkt->getAddr()); src_port->name(), pkt->cmdString(), pkt->getAddr());
// update the bus state and schedule an idle event // update the bus state and schedule an idle event
layer.failedTiming(src_port, headerFinishTime); reqLayer.failedTiming(src_port, headerFinishTime);
} else { } else {
// update the bus state and schedule an idle event // update the bus state and schedule an idle event
layer.succeededTiming(packetFinishTime); reqLayer.succeededTiming(packetFinishTime);
} }
} }
@ -194,7 +196,7 @@ CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
// test if the bus should be considered occupied for the current // test if the bus should be considered occupied for the current
// port // port
if (!layer.tryTiming(src_port)) { if (!respLayer.tryTiming(src_port)) {
DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n", DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr()); src_port->name(), pkt->cmdString(), pkt->getAddr());
return false; return false;
@ -221,7 +223,7 @@ CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
// deadlock // deadlock
assert(success); assert(success);
layer.succeededTiming(packetFinishTime); respLayer.succeededTiming(packetFinishTime);
return true; return true;
} }
@ -258,7 +260,7 @@ CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
// test if the bus should be considered occupied for the current // test if the bus should be considered occupied for the current
// port // port
if (!layer.tryTiming(src_port)) { if (!snoopRespLayer.tryTiming(src_port)) {
DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n", DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr()); src_port->name(), pkt->cmdString(), pkt->getAddr());
return false; return false;
@ -309,7 +311,7 @@ CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
assert(success); assert(success);
} }
layer.succeededTiming(packetFinishTime); snoopRespLayer.succeededTiming(packetFinishTime);
return true; return true;
} }
@ -335,8 +337,10 @@ CoherentBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
void void
CoherentBus::recvRetry() CoherentBus::recvRetry()
{ {
// only one layer that can deal with it // responses and snoop responses never block on forwarding them,
layer.recvRetry(); // so the retry will always be coming from a port to which we
// tried to forward a request
reqLayer.recvRetry();
} }
Tick Tick
@ -503,8 +507,8 @@ CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
unsigned int unsigned int
CoherentBus::drain(Event *de) CoherentBus::drain(Event *de)
{ {
// only one layer to worry about at the moment // sum up the individual layers
return layer.drain(de); return reqLayer.drain(de) + respLayer.drain(de) + snoopRespLayer.drain(de);
} }
CoherentBus * CoherentBus *

View file

@ -70,9 +70,12 @@ class CoherentBus : public BaseBus
protected: protected:
/** /**
* Declare the single layer of this bus. * Declare the three layers of this bus, one for requests, one
* for responses, and one for snoop responses
*/ */
Layer layer; Layer<SlavePort> reqLayer;
Layer<MasterPort> respLayer;
Layer<SlavePort> snoopRespLayer;
/** /**
* Declaration of the coherent bus slave port type, one will be * Declaration of the coherent bus slave port type, one will be

View file

@ -55,7 +55,8 @@
#include "mem/noncoherent_bus.hh" #include "mem/noncoherent_bus.hh"
NoncoherentBus::NoncoherentBus(const NoncoherentBusParams *p) NoncoherentBus::NoncoherentBus(const NoncoherentBusParams *p)
: BaseBus(p), layer(*this, ".layer", p->clock) : BaseBus(p), reqLayer(*this, ".reqLayer", p->clock),
respLayer(*this, ".respLayer", p->clock)
{ {
// create the ports based on the size of the master and slave // create the ports based on the size of the master and slave
// vector ports, and the presence of the default port, the ports // vector ports, and the presence of the default port, the ports
@ -97,7 +98,7 @@ NoncoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
// test if the bus should be considered occupied for the current // test if the bus should be considered occupied for the current
// port // port
if (!layer.tryTiming(src_port)) { if (!reqLayer.tryTiming(src_port)) {
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n", DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr()); src_port->name(), pkt->cmdString(), pkt->getAddr());
return false; return false;
@ -123,12 +124,12 @@ NoncoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n", DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr()); src_port->name(), pkt->cmdString(), pkt->getAddr());
layer.failedTiming(src_port, headerFinishTime); reqLayer.failedTiming(src_port, headerFinishTime);
return false; return false;
} }
layer.succeededTiming(packetFinishTime); reqLayer.succeededTiming(packetFinishTime);
return true; return true;
} }
@ -141,7 +142,7 @@ NoncoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
// test if the bus should be considered occupied for the current // test if the bus should be considered occupied for the current
// port // port
if (!layer.tryTiming(src_port)) { if (!respLayer.tryTiming(src_port)) {
DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n", DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr()); src_port->name(), pkt->cmdString(), pkt->getAddr());
return false; return false;
@ -161,7 +162,7 @@ NoncoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
// deadlock // deadlock
assert(success); assert(success);
layer.succeededTiming(packetFinishTime); respLayer.succeededTiming(packetFinishTime);
return true; return true;
} }
@ -169,8 +170,10 @@ NoncoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
void void
NoncoherentBus::recvRetry() NoncoherentBus::recvRetry()
{ {
// only one layer that can deal with it // responses never block on forwarding them, so the retry will
layer.recvRetry(); // always be coming from a port to which we tried to forward a
// request
reqLayer.recvRetry();
} }
Tick Tick
@ -211,8 +214,8 @@ NoncoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
unsigned int unsigned int
NoncoherentBus::drain(Event *de) NoncoherentBus::drain(Event *de)
{ {
// only one layer to worry about at the moment // sum up the individual layers
return layer.drain(de); return reqLayer.drain(de) + respLayer.drain(de);
} }
NoncoherentBus* NoncoherentBus*

View file

@ -73,9 +73,11 @@ class NoncoherentBus : public BaseBus
protected: protected:
/** /**
* Declare the single layer of this bus. * Declare the two layers of this bus, one for requests and one
* for responses.
*/ */
Layer layer; Layer<SlavePort> reqLayer;
Layer<MasterPort> respLayer;
/** /**
* Declaration of the non-coherent bus slave port type, one will * Declaration of the non-coherent bus slave port type, one will