changeset bb27575ebb33 in /z/repo/gem5
details: http://repo.gem5.org/gem5?cmd=changeset;node=bb27575ebb33
description:
        Bus: Add a notion of layers to the buses

        This patch moves all flow control, arbitration and state information
        into a bus layer. The layer is thus responsible for all the state
        transitions, and for keeping hold of the retry list. Consequently the
        layer is also responsible for the draining.

        With this change, the non-coherent and coherent bus are given a single
        layer to avoid changing any temporal behaviour, but the patch opens up
        for adding more layers.

diffstat:

 src/mem/bus.cc             |   43 ++++---
 src/mem/bus.hh             |  220 +++++++++++++++++++++++++++++---------------
 src/mem/coherent_bus.cc    |   30 ++++-
 src/mem/coherent_bus.hh    |   11 ++
 src/mem/noncoherent_bus.cc |   26 ++++-
 src/mem/noncoherent_bus.hh |   11 ++
 6 files changed, 232 insertions(+), 109 deletions(-)

diffs (truncated from 657 to 300 lines):

diff -r 9b29b9a4dda6 -r bb27575ebb33 src/mem/bus.cc
--- a/src/mem/bus.cc    Mon Jul 09 12:35:35 2012 -0400
+++ b/src/mem/bus.cc    Mon Jul 09 12:35:36 2012 -0400
@@ -55,9 +55,8 @@
 #include "mem/bus.hh"
 
 BaseBus::BaseBus(const BaseBusParams *p)
-    : MemObject(p), state(IDLE), clock(p->clock),
+    : MemObject(p), clock(p->clock),
       headerCycles(p->header_cycles), width(p->width),
-      drainEvent(NULL), busIdleEvent(this),
       defaultPortID(InvalidPortID),
       useDefaultRange(p->use_default_range),
       defaultBlockSize(p->block_size),
@@ -138,7 +137,13 @@
     return headerTime;
 }
 
-void BaseBus::occupyBus(Tick until)
+BaseBus::Layer::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)
 {
     // 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
@@ -153,14 +158,14 @@
 
     // until should never be 0 as express snoops never occupy the bus
     assert(until != 0);
-    schedule(busIdleEvent, until);
+    bus.schedule(releaseEvent, until);
 
     DPRINTF(BaseBus, "The bus is now busy from tick %d to %d\n",
             curTick(), until);
 }
 
 bool
-BaseBus::tryTiming(Port* port)
+BaseBus::Layer::tryTiming(Port* 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
@@ -180,7 +185,7 @@
 }
 
 void
-BaseBus::succeededTiming(Tick busy_time)
+BaseBus::Layer::succeededTiming(Tick busy_time)
 {
     // if a retrying port succeeded, also take it off the retry list
     if (state == RETRY) {
@@ -195,11 +200,11 @@
     assert(state == BUSY);
 
     // occupy the bus accordingly
-    occupyBus(busy_time);
+    occupyLayer(busy_time);
 }
 
 void
-BaseBus::failedTiming(SlavePort* port, Tick busy_time)
+BaseBus::Layer::failedTiming(SlavePort* 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
@@ -213,15 +218,15 @@
     state = BUSY;
 
     // occupy the bus accordingly
-    occupyBus(busy_time);
+    occupyLayer(busy_time);
 }
 
 void
-BaseBus::releaseBus()
+BaseBus::Layer::releaseLayer()
 {
     // releasing the bus means we should now be idle
     assert(state == BUSY);
-    assert(!busIdleEvent.scheduled());
+    assert(!releaseEvent.scheduled());
 
     // update the state
     state = IDLE;
@@ -242,7 +247,7 @@
 }
 
 void
-BaseBus::retryWaiting()
+BaseBus::Layer::retryWaiting()
 {
     // this should never be called with an empty retry list
     assert(!retryList.empty());
@@ -277,23 +282,23 @@
         // clock edge
         Tick now = divCeil(curTick(), clock) * clock;
 
-        occupyBus(now + clock);
+        occupyLayer(now + clock);
     }
 }
 
 void
-BaseBus::recvRetry()
+BaseBus::Layer::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
     // that source port should be on our retry list, however if the
-    // bus 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
+    // 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 is idle
+    // if the bus layer is idle
     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
@@ -481,7 +486,7 @@
 
 
 unsigned int
-BaseBus::drain(Event * de)
+BaseBus::Layer::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
diff -r 9b29b9a4dda6 -r bb27575ebb33 src/mem/bus.hh
--- a/src/mem/bus.hh    Mon Jul 09 12:35:35 2012 -0400
+++ b/src/mem/bus.hh    Mon Jul 09 12:35:36 2012 -0400
@@ -75,34 +75,158 @@
   protected:
 
     /**
-     * We declare an enum to track the state of the bus. The starting
-     * point is an idle state where the bus is waiting for a packet to
-     * arrive. Upon arrival, the bus transitions to the busy state,
-     * where it remains either until the packet transfer is done, or
-     * the header time is spent. Once the bus leaves the busy state,
-     * it can either go back to idle, if no packets have arrived while
-     * it was busy, or the bus goes on to retry the first port on the
-     * retryList. A similar transition takes place from idle to retry
-     * if the bus 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 bus, or goes to a busy
-     * state if the port does not immediately react to the retry by
-     * calling sendTiming.
+     * A bus layer is an internal bus structure with its own flow
+     * control and arbitration. Hence, a single-layer bus mimics a
+     * traditional off-chip tri-state bus (like PCI), where only one
+     * set of wires are shared. For on-chip buses, a good starting
+     * 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.
      */
-    enum State { IDLE, BUSY, RETRY };
+    class Layer
+    {
 
-    /** track the state of the bus */
-    State state;
+      public:
+
+        /**
+         * Create a bus layer and give it a name. The bus layer uses
+         * the bus an event manager.
+         *
+         * @param _bus the bus this layer belongs to
+         * @param _name the layer's name
+         * @param _clock clock period in ticks
+         */
+        Layer(BaseBus& _bus, const std::string& _name, Tick _clock);
+
+        /**
+         * Drain according to the normal semantics, so that the bus
+         * can tell the layer to drain, and pass an event to signal
+         * back when drained.
+         *
+         * @param de drain event to call once drained
+         *
+         * @return 1 if busy or waiting to retry, or 0 if idle
+         */
+        unsigned int drain(Event *de);
+
+        /**
+         * Get the bus layer's name
+         */
+        const std::string name() const { return bus.name() + _name; }
+
+
+        /**
+         * 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.
+         *
+         * @param port Source port resenting the packet
+         *
+         * @return True if the bus layer accepts the packet
+         */
+        bool tryTiming(Port* port);
+
+        /**
+         * Deal with a destination port accepting a packet by potentially
+         * removing the source port from the retry list (if retrying) and
+         * occupying the bus layer accordingly.
+         *
+         * @param busy_time Time to spend as a result of a successful send
+         */
+        void succeededTiming(Tick busy_time);
+
+        /**
+         * Deal with a destination port not accepting a packet by
+         * potentially adding the source port to the retry list (if
+         * not already at the front) and occupying the bus layer
+         * accordingly.
+         *
+         * @param busy_time Time to spend as a result of a failed send
+         */
+        void failedTiming(SlavePort* port, 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
+         * 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
+         * retryWaiting by verifying that there are ports waiting
+         * before calling retryWaiting.
+         */
+        void recvRetry();
+
+      private:
+
+        /** The bus this layer is a part of. */
+        BaseBus& bus;
+
+        /** A name for this layer. */
+        std::string _name;
+
+        /**
+         * We declare an enum to track the state of the bus layer. The
+         * starting point is an idle state where the bus layer is
+         * waiting for a packet to arrive. Upon arrival, the bus layer
+         * transitions to the busy state, where it remains either
+         * until the packet transfer is done, or the header time is
+         * 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
+         * 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
+         * bus layer, or goes to a busy state if the port does not
+         * immediately react to the retry by calling sendTiming.
+         */
+        enum State { IDLE, BUSY, RETRY };
+
+        /** track the state of the bus layer */
+        State state;
+
+        /** the clock speed for the bus layer */
+        Tick clock;
+
+        /** event for signalling when drained */
+        Event * drainEvent;
+
+        /**
+         * An array of pointers to ports that retry should be called
+         * on because the original send failed for whatever reason.
+         */
+        std::list<Port*> retryList;
+
+        /**
+         * Release the bus layer after being occupied and return to an
+         * idle state where we proceed to send a retry to any
+         * potential waiting port, or drain if asked to do so.
+         */
+        void releaseLayer();
+
+        /** event used to schedule a release of the layer */
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to