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),
     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
     // point, as the bus should transition from idle as soon as it has
             curTick(), until);
 }
 
+template <typename PortClass>
 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
     // retry with a port other than the current one
     return true;
 }
 
+template <typename PortClass>
 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 (state == RETRY) {
     occupyLayer(busy_time);
 }
 
+template <typename PortClass>
 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
     // in a retry but not for the current port, then add the port at
     occupyLayer(busy_time);
 }
 
+template <typename PortClass>
 void
-BaseBus::Layer::releaseLayer()
+BaseBus::Layer<PortClass>::releaseLayer()
 {
     // releasing the bus means we should now be idle
     assert(state == BUSY);
     }
 }
 
+template <typename PortClass>
 void
-BaseBus::Layer::retryWaiting()
+BaseBus::Layer<PortClass>::retryWaiting()
 {
     // this should never be called with an empty retry list
     assert(!retryList.empty());
     // 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
-    if (dynamic_cast<SlavePort*>(retryList.front()) != NULL)
-        (dynamic_cast<SlavePort*>(retryList.front()))->sendRetry();
-    else
-        (dynamic_cast<MasterPort*>(retryList.front()))->sendRetry();
+    retryList.front()->sendRetry();
 
     // If the bus is still in the retry state, sendTiming wasn't
     // called in zero time (e.g. the cache does this)
     }
 }
 
+template <typename PortClass>
 void
-BaseBus::Layer::recvRetry()
+BaseBus::Layer<PortClass>::recvRetry()
 {
     // 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
     return max_bs;
 }
 
-
+template <typename PortClass>
 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
     //waiting. We might be idle but have someone waiting if the device we
     }
     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>;
 
      * point is to have three layers, for requests, responses, and
      * snoop responses respectively (snoop requests are instantaneous
      * and do not need any flow control or arbitration). This case is
-     * similar to AHB and some OCP configurations. As a further
-     * extensions beyond the three-layer bus, a future multi-layer bus
-     * has with one layer per connected slave port provides a full or
-     * partial crossbar, like AXI, OCP, PCIe etc.
+     * similar to AHB and some OCP configurations.
+     *
+     * As a further extensions beyond the three-layer bus, a future
+     * 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
     {
 
          *
          * @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
          *
          * @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 */
         void occupyLayer(Tick until);
         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.
          */
-        std::list<Port*> retryList;
+        std::list<PortClass*> retryList;
 
         /**
          * Release the bus layer after being occupied and return to an
 
 #include "mem/coherent_bus.hh"
 
 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
     // vector ports, and the presence of the default port, the ports
 
     // test if the bus should be considered occupied for the current
     // 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",
                 src_port->name(), pkt->cmdString(), pkt->getAddr());
         return false;
                     src_port->name(), pkt->cmdString(), pkt->getAddr());
 
             // update the bus state and schedule an idle event
-            layer.failedTiming(src_port, headerFinishTime);
+            reqLayer.failedTiming(src_port, headerFinishTime);
         } else {
             // update the bus state and schedule an idle event
-            layer.succeededTiming(packetFinishTime);
+            reqLayer.succeededTiming(packetFinishTime);
         }
     }
 
 
     // test if the bus should be considered occupied for the current
     // port
-    if (!layer.tryTiming(src_port)) {
+    if (!respLayer.tryTiming(src_port)) {
         DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
                 src_port->name(), pkt->cmdString(), pkt->getAddr());
         return false;
     // deadlock
     assert(success);
 
-    layer.succeededTiming(packetFinishTime);
+    respLayer.succeededTiming(packetFinishTime);
 
     return true;
 }
 
     // test if the bus should be considered occupied for the current
     // port
-    if (!layer.tryTiming(src_port)) {
+    if (!snoopRespLayer.tryTiming(src_port)) {
         DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
                 src_port->name(), pkt->cmdString(), pkt->getAddr());
         return false;
         assert(success);
     }
 
-    layer.succeededTiming(packetFinishTime);
+    snoopRespLayer.succeededTiming(packetFinishTime);
 
     return true;
 }
 void
 CoherentBus::recvRetry()
 {
-    // only one layer that can deal with it
-    layer.recvRetry();
+    // 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();
 }
 
 Tick
 unsigned int
 CoherentBus::drain(Event *de)
 {
-    // only one layer to worry about at the moment
-    return layer.drain(de);
+    // sum up the individual layers
+    return reqLayer.drain(de) + respLayer.drain(de) + snoopRespLayer.drain(de);
 }
 
 CoherentBus *
 
   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
 
 #include "mem/noncoherent_bus.hh"
 
 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
     // vector ports, and the presence of the default port, the ports
 
     // test if the bus should be considered occupied for the current
     // port
-    if (!layer.tryTiming(src_port)) {
+    if (!reqLayer.tryTiming(src_port)) {
         DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
                 src_port->name(), pkt->cmdString(), pkt->getAddr());
         return false;
         DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
                 src_port->name(), pkt->cmdString(), pkt->getAddr());
 
-        layer.failedTiming(src_port, headerFinishTime);
+        reqLayer.failedTiming(src_port, headerFinishTime);
 
         return false;
     }
 
-    layer.succeededTiming(packetFinishTime);
+    reqLayer.succeededTiming(packetFinishTime);
 
     return true;
 }
 
     // test if the bus should be considered occupied for the current
     // port
-    if (!layer.tryTiming(src_port)) {
+    if (!respLayer.tryTiming(src_port)) {
         DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
                 src_port->name(), pkt->cmdString(), pkt->getAddr());
         return false;
     // deadlock
     assert(success);
 
-    layer.succeededTiming(packetFinishTime);
+    respLayer.succeededTiming(packetFinishTime);
 
     return true;
 }
 void
 NoncoherentBus::recvRetry()
 {
-    // only one layer that can deal with it
-    layer.recvRetry();
+    // 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();
 }
 
 Tick
 unsigned int
 NoncoherentBus::drain(Event *de)
 {
-    // only one layer to worry about at the moment
-    return layer.drain(de);
+    // sum up the individual layers
+    return reqLayer.drain(de) + respLayer.drain(de);
 }
 
 NoncoherentBus*
 
   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