MEM: Simplify cache ports preparing for master/slave split
authorAndreas Hansson <andreas.hansson@arm.com>
Fri, 24 Feb 2012 16:52:49 +0000 (11:52 -0500)
committerAndreas Hansson <andreas.hansson@arm.com>
Fri, 24 Feb 2012 16:52:49 +0000 (11:52 -0500)
This patch splits the two cache ports into a master (memory-side) and
slave (cpu-side) subclass of port with slightly different
functionality. For example, it is only the CPU-side port that blocks
incoming requests, and only the memory-side port that schedules send
events outside of what the transmit list dictates.

This patch simplifies the two classes by relying further on
SimpleTimingPort and also generalises the latter to better accommodate
the changes (introducing trySendTiming and scheduleSend). The
memory-side cache port overrides sendDeferredPacket to be able to not
only send responses from the transmit list, but also send requests
based on the MSHRs.

A follow on patch further simplifies the SimpleTimingPort and the
cache ports.

src/mem/cache/base.cc
src/mem/cache/base.hh
src/mem/cache/cache.hh
src/mem/cache/cache_impl.hh
src/mem/tport.cc
src/mem/tport.hh

index 27ff6961b43134e469e72f7cc4c4284229477c9d..c7c213cc62ec51cc888cee13b6e09dcc60bae402 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2012 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) 2003-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
 
 using namespace std;
 
-BaseCache::CachePort::CachePort(const std::string &_name, BaseCache *_cache,
-                                const std::string &_label)
-    : SimpleTimingPort(_name, _cache), cache(_cache),
-      label(_label), blocked(false), mustSendRetry(false)
+BaseCache::CacheMasterPort::CacheMasterPort(const std::string &_name,
+                                            BaseCache *_cache,
+                                            const std::string &_label)
+    : SimpleTimingPort(_name, _cache, _label)
 {
 }
 
+BaseCache::CacheSlavePort::CacheSlavePort(const std::string &_name,
+                                          BaseCache *_cache,
+                                          const std::string &_label)
+    : SimpleTimingPort(_name, _cache, _label), blocked(false),
+      mustSendRetry(false), sendRetryEvent(this)
+{
+}
 
 BaseCache::BaseCache(const Params *p)
     : MemObject(p),
@@ -69,56 +88,25 @@ BaseCache::BaseCache(const Params *p)
 {
 }
 
-
-bool
-BaseCache::CachePort::checkFunctional(PacketPtr pkt)
-{
-    pkt->pushLabel(label);
-    bool done = SimpleTimingPort::checkFunctional(pkt);
-    pkt->popLabel();
-    return done;
-}
-
-
-unsigned
-BaseCache::CachePort::deviceBlockSize() const
-{
-    return cache->getBlockSize();
-}
-
-
-bool
-BaseCache::CachePort::recvRetryCommon()
-{
-    assert(waitingOnRetry);
-    waitingOnRetry = false;
-    return false;
-}
-
-
 void
-BaseCache::CachePort::setBlocked()
+BaseCache::CacheSlavePort::setBlocked()
 {
     assert(!blocked);
-    DPRINTF(Cache, "Cache Blocking\n");
+    DPRINTF(CachePort, "Cache port %s blocking new requests\n", name());
     blocked = true;
-    //Clear the retry flag
-    mustSendRetry = false;
 }
 
 void
-BaseCache::CachePort::clearBlocked()
+BaseCache::CacheSlavePort::clearBlocked()
 {
     assert(blocked);
-    DPRINTF(Cache, "Cache Unblocking\n");
+    DPRINTF(CachePort, "Cache port %s accepting new requests\n", name());
     blocked = false;
-    if (mustSendRetry)
-    {
-        DPRINTF(Cache, "Cache Sending Retry\n");
+    if (mustSendRetry) {
+        DPRINTF(CachePort, "Cache port %s sending retry\n", name());
         mustSendRetry = false;
-        SendRetryEvent *ev = new SendRetryEvent(this, true);
         // @TODO: need to find a better time (next bus cycle?)
-        cache->schedule(ev, curTick() + 1);
+        owner->schedule(sendRetryEvent, curTick() + 1);
     }
 }
 
@@ -126,8 +114,8 @@ BaseCache::CachePort::clearBlocked()
 void
 BaseCache::init()
 {
-    if (!cpuSidePort || !memSidePort)
-        panic("Cache not hooked up on both sides\n");
+    if (!cpuSidePort->isConnected() || !memSidePort->isConnected())
+        panic("Cache %s not hooked up on both sides\n", name());
     cpuSidePort->sendRangeChange();
 }
 
index cff8813cdc811c92475a0ca85ecbb03181d159d2..e522bc0c960b4d6ec1f85bfedb86c733c429d96c 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2012 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) 2003-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -97,50 +109,88 @@ class BaseCache : public MemObject
 
   protected:
 
-    class CachePort : public SimpleTimingPort
+    /**
+     * A cache master port is used for the memory-side port of the
+     * cache, and in addition to the basic timing port that only sends
+     * response packets through a transmit list, it also offers the
+     * ability to schedule and send request packets (requests &
+     * writebacks). The send event is scheduled through requestBus,
+     * and the sendDeferredPacket of the timing port is modified to
+     * consider both the transmit list and the requests from the MSHR.
+     */
+    class CacheMasterPort : public SimpleTimingPort
     {
+
       public:
-        BaseCache *cache;
 
-      protected:
-        CachePort(const std::string &_name, BaseCache *_cache,
-                  const std::string &_label);
+        /**
+         * Schedule a send of a request packet (from the MSHR). Note
+         * that we could already have a retry or a transmit list of
+         * responses outstanding.
+         */
+        void requestBus(RequestCause cause, Tick time)
+        {
+            DPRINTF(CachePort, "Asserting bus request for cause %d\n", cause);
+            schedSendEvent(time);
+        }
 
-        virtual unsigned deviceBlockSize() const;
+        void respond(PacketPtr pkt, Tick time) {
+            schedSendTiming(pkt, time);
+        }
 
-        bool recvRetryCommon();
+      protected:
 
-        typedef EventWrapper<Port, &Port::sendRetry>
-            SendRetryEvent;
+        CacheMasterPort(const std::string &_name, BaseCache *_cache,
+                        const std::string &_label);
 
-        const std::string label;
+        /**
+         * Memory-side port always snoops.
+         *
+         * return always true
+         */
+        virtual bool isSnooping() { return true; }
+    };
+
+    /**
+     * A cache slave port is used for the CPU-side port of the cache,
+     * and it is basically a simple timing port that uses a transmit
+     * list for responses to the CPU (or connected master). In
+     * addition, it has the functionality to block the port for
+     * incoming requests. If blocked, the port will issue a retry once
+     * unblocked.
+     */
+    class CacheSlavePort : public SimpleTimingPort
+    {
 
       public:
+
+        /** Do not accept any new requests. */
         void setBlocked();
 
+        /** Return to normal operation and accept new requests. */
         void clearBlocked();
 
-        bool checkFunctional(PacketPtr pkt);
+        void respond(PacketPtr pkt, Tick time) {
+            schedSendTiming(pkt, time);
+        }
+
+      protected:
+
+        CacheSlavePort(const std::string &_name, BaseCache *_cache,
+                       const std::string &_label);
 
         bool blocked;
 
         bool mustSendRetry;
 
-        void requestBus(RequestCause cause, Tick time)
-        {
-            DPRINTF(CachePort, "Asserting bus request for cause %d\n", cause);
-            if (!waitingOnRetry) {
-                schedSendEvent(time);
-            }
-        }
+      private:
+
+        EventWrapper<Port, &Port::sendRetry> sendRetryEvent;
 
-        void respond(PacketPtr pkt, Tick time) {
-            schedSendTiming(pkt, time);
-        }
     };
 
-    CachePort *cpuSidePort;
-    CachePort *memSidePort;
+    CacheSlavePort *cpuSidePort;
+    CacheMasterPort *memSidePort;
 
   protected:
 
index b2569648e2f1d81e191bdecd09ea28a82aab9d59..2883955845d121272e45d7d28c5cb2d331b63997 100644 (file)
@@ -41,6 +41,7 @@
  *          Dave Greene
  *          Steve Reinhardt
  *          Ron Dreslinski
+ *          Andreas Hansson
  */
 
 /**
@@ -76,59 +77,68 @@ class Cache : public BaseCache
 
   protected:
 
-    class CpuSidePort : public CachePort
+    /**
+     * The CPU-side port extends the base cache slave port with access
+     * functions for functional, atomic and timing requests.
+     */
+    class CpuSidePort : public CacheSlavePort
     {
-      public:
-        CpuSidePort(const std::string &_name,
-                    Cache<TagStore> *_cache,
-                    const std::string &_label);
+      private:
 
-        // BaseCache::CachePort just has a BaseCache *; this function
-        // lets us get back the type info we lost when we stored the
-        // cache pointer there.
-        Cache<TagStore> *myCache() {
-            return static_cast<Cache<TagStore> *>(cache);
-        }
+        // a pointer to our specific cache implementation
+        Cache<TagStore> *cache;
 
-        virtual AddrRangeList getAddrRanges();
+      protected:
 
         virtual bool recvTiming(PacketPtr pkt);
 
         virtual Tick recvAtomic(PacketPtr pkt);
 
         virtual void recvFunctional(PacketPtr pkt);
-    };
 
-    class MemSidePort : public CachePort
-    {
+        virtual unsigned deviceBlockSize() const
+        { return cache->getBlockSize(); }
+
+        virtual AddrRangeList getAddrRanges();
+
       public:
-        MemSidePort(const std::string &_name,
-                    Cache<TagStore> *_cache,
+
+        CpuSidePort(const std::string &_name, Cache<TagStore> *_cache,
                     const std::string &_label);
 
-        // BaseCache::CachePort just has a BaseCache *; this function
-        // lets us get back the type info we lost when we stored the
-        // cache pointer there.
-        Cache<TagStore> *myCache() {
-            return static_cast<Cache<TagStore> *>(cache);
-        }
+    };
 
-        void sendPacket();
+    /**
+     * The memory-side port extends the base cache master port with
+     * access functions for functional, atomic and timing snoops.
+     */
+    class MemSidePort : public CacheMasterPort
+    {
+      private:
 
-        void processSendEvent();
+        // a pointer to our specific cache implementation
+        Cache<TagStore> *cache;
 
-        virtual bool isSnooping();
+      protected:
 
         virtual bool recvTiming(PacketPtr pkt);
 
-        virtual void recvRetry();
-
         virtual Tick recvAtomic(PacketPtr pkt);
 
         virtual void recvFunctional(PacketPtr pkt);
 
-        typedef EventWrapper<MemSidePort, &MemSidePort::processSendEvent>
-                SendEvent;
+        virtual unsigned deviceBlockSize() const
+        { return cache->getBlockSize(); }
+
+      public:
+
+        MemSidePort(const std::string &_name, Cache<TagStore> *_cache,
+                    const std::string &_label);
+
+        /**
+         * Overload sendDeferredPacket of SimpleTimingPort.
+         */
+        virtual void sendDeferredPacket();
     };
 
     /** Tag and data Storage */
index 87b6886173ad2c94936dff00e9377ca34b803fbf..40359d31e753df4dbfcc5f7d0221cec68d4a20ce 100644 (file)
@@ -95,7 +95,7 @@ template<class TagStore>
 Port *
 Cache<TagStore>::getPort(const std::string &if_name, int idx)
 {
-    if (if_name == "" || if_name == "cpu_side") {
+    if (if_name == "cpu_side") {
         return cpuSidePort;
     } else if (if_name == "mem_side") {
         return memSidePort;
@@ -1553,17 +1553,13 @@ Cache<TagStore>::nextMSHRReadyTime()
 
 template<class TagStore>
 AddrRangeList
-Cache<TagStore>::CpuSidePort::
-getAddrRanges()
+Cache<TagStore>::CpuSidePort::getAddrRanges()
 {
-    // CPU side port doesn't snoop; it's a target only.  It can
-    // potentially respond to any address.
     AddrRangeList ranges;
-    ranges.push_back(myCache()->getAddrRange());
+    ranges.push_back(cache->getAddrRange());
     return ranges;
 }
 
-
 template<class TagStore>
 bool
 Cache<TagStore>::CpuSidePort::recvTiming(PacketPtr pkt)
@@ -1575,32 +1571,33 @@ Cache<TagStore>::CpuSidePort::recvTiming(PacketPtr pkt)
         return false;
     }
 
-    myCache()->timingAccess(pkt);
+    cache->timingAccess(pkt);
     return true;
 }
 
-
 template<class TagStore>
 Tick
 Cache<TagStore>::CpuSidePort::recvAtomic(PacketPtr pkt)
 {
-    return myCache()->atomicAccess(pkt);
+    assert(pkt->isRequest());
+    // atomic request
+    return cache->atomicAccess(pkt);
 }
 
-
 template<class TagStore>
 void
 Cache<TagStore>::CpuSidePort::recvFunctional(PacketPtr pkt)
 {
-    myCache()->functionalAccess(pkt, true);
+    assert(pkt->isRequest());
+    // functional request
+    cache->functionalAccess(pkt, true);
 }
 
-
 template<class TagStore>
 Cache<TagStore>::
 CpuSidePort::CpuSidePort(const std::string &_name, Cache<TagStore> *_cache,
                          const std::string &_label)
-    : BaseCache::CachePort(_name, _cache, _label)
+    : BaseCache::CacheSlavePort(_name, _cache, _label), cache(_cache)
 {
 }
 
@@ -1610,17 +1607,6 @@ CpuSidePort::CpuSidePort(const std::string &_name, Cache<TagStore> *_cache,
 //
 ///////////////
 
-template<class TagStore>
-bool
-Cache<TagStore>::MemSidePort::isSnooping()
-{
-    // Memory-side port always snoops, but never passes requests
-    // through to targets on the cpu side (so we don't add anything to
-    // the address range list).
-    return true;
-}
-
-
 template<class TagStore>
 bool
 Cache<TagStore>::MemSidePort::recvTiming(PacketPtr pkt)
@@ -1631,60 +1617,45 @@ Cache<TagStore>::MemSidePort::recvTiming(PacketPtr pkt)
     if (pkt->wasNacked())
         panic("Need to implement cache resending nacked packets!\n");
 
-    if (pkt->isRequest() && blocked) {
-        DPRINTF(Cache,"Scheduling a retry while blocked\n");
-        mustSendRetry = true;
-        return false;
-    }
-
     if (pkt->isResponse()) {
-        myCache()->handleResponse(pkt);
+        cache->handleResponse(pkt);
     } else {
-        myCache()->snoopTiming(pkt);
+        cache->snoopTiming(pkt);
     }
     return true;
 }
 
-
 template<class TagStore>
 Tick
 Cache<TagStore>::MemSidePort::recvAtomic(PacketPtr pkt)
 {
-    // in atomic mode, responses go back to the sender via the
-    // function return from sendAtomic(), not via a separate
-    // sendAtomic() from the responder.  Thus we should never see a
-    // response packet in recvAtomic() (anywhere, not just here).
-    assert(!pkt->isResponse());
-    return myCache()->snoopAtomic(pkt);
+    assert(pkt->isRequest());
+    // atomic snoop
+    return cache->snoopAtomic(pkt);
 }
 
-
 template<class TagStore>
 void
 Cache<TagStore>::MemSidePort::recvFunctional(PacketPtr pkt)
 {
-    myCache()->functionalAccess(pkt, false);
+    assert(pkt->isRequest());
+    // functional snoop (note that in contrast to atomic we don't have
+    // a specific functionalSnoop method, as they have the same
+    // behaviour regardless)
+    cache->functionalAccess(pkt, false);
 }
 
-
-
 template<class TagStore>
 void
-Cache<TagStore>::MemSidePort::sendPacket()
+Cache<TagStore>::MemSidePort::sendDeferredPacket()
 {
-    // if we have responses that are ready, they take precedence
+    // if we have a response packet waiting we have to start with that
     if (deferredPacketReady()) {
-        bool success = sendTiming(transmitList.front().pkt);
-
-        if (success) {
-            //send successful, remove packet
-            transmitList.pop_front();
-        }
-
-        waitingOnRetry = !success;
+        // use the normal approach from the timing port
+        trySendTiming();
     } else {
-        // check for non-response packets (requests & writebacks)
-        PacketPtr pkt = myCache()->getTimingPacket();
+        // check for request packets (requests & writebacks)
+        PacketPtr pkt = cache->getTimingPacket();
         if (pkt == NULL) {
             // can happen if e.g. we attempt a writeback and fail, but
             // before the retry, the writeback is eliminated because
@@ -1693,65 +1664,39 @@ Cache<TagStore>::MemSidePort::sendPacket()
         } else {
             MSHR *mshr = dynamic_cast<MSHR*>(pkt->senderState);
 
-            bool success = sendTiming(pkt);
+            waitingOnRetry = !sendTiming(pkt);
 
-            waitingOnRetry = !success;
             if (waitingOnRetry) {
                 DPRINTF(CachePort, "now waiting on a retry\n");
                 if (!mshr->isForwardNoResponse()) {
+                    // we are awaiting a retry, but we
+                    // delete the packet and will be creating a new packet
+                    // when we get the opportunity
                     delete pkt;
                 }
+                // note that we have now masked any requestBus and
+                // schedSendEvent (we will wait for a retry before
+                // doing anything), and this is so even if we do not
+                // care about this packet and might override it before
+                // it gets retried
             } else {
-                myCache()->markInService(mshr, pkt);
+                cache->markInService(mshr, pkt);
             }
         }
     }
 
-
-    // tried to send packet... if it was successful (no retry), see if
-    // we need to rerequest bus or not
+    // if we succeeded and are not waiting for a retry, schedule the
+    // next send, not only looking at the response transmit list, but
+    // also considering when the next MSHR is ready
     if (!waitingOnRetry) {
-        Tick nextReady = std::min(deferredPacketReadyTime(),
-                                  myCache()->nextMSHRReadyTime());
-        // @TODO: need to facotr in prefetch requests here somehow
-        if (nextReady != MaxTick) {
-            DPRINTF(CachePort, "more packets to send @ %d\n", nextReady);
-            cache->schedule(sendEvent, std::max(nextReady, curTick() + 1));
-        } else {
-            // no more to send right now: if we're draining, we may be done
-            if (drainEvent && !sendEvent->scheduled()) {
-                drainEvent->process();
-                drainEvent = NULL;
-            }
-        }
+        scheduleSend(cache->nextMSHRReadyTime());
     }
 }
 
-template<class TagStore>
-void
-Cache<TagStore>::MemSidePort::recvRetry()
-{
-    assert(waitingOnRetry);
-    sendPacket();
-}
-
-
-template<class TagStore>
-void
-Cache<TagStore>::MemSidePort::processSendEvent()
-{
-    assert(!waitingOnRetry);
-    sendPacket();
-}
-
-
 template<class TagStore>
 Cache<TagStore>::
 MemSidePort::MemSidePort(const std::string &_name, Cache<TagStore> *_cache,
                          const std::string &_label)
-    : BaseCache::CachePort(_name, _cache, _label)
+    : BaseCache::CacheMasterPort(_name, _cache, _label), cache(_cache)
 {
-    // override default send event from SimpleTimingPort
-    delete sendEvent;
-    sendEvent = new SendEvent(this);
 }
index 7b1fdb85004889e3de6e9a17cad395ff6b09178a..cbb7ed2ac9ca86ab5ac85ff19f4f1e06bd178a01 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2012 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
  * All rights reserved.
  *
@@ -26,6 +38,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * Authors: Ali Saidi
+ *          Andreas Hansson
  */
 
 #include "debug/Bus.hh"
 
 using namespace std;
 
-SimpleTimingPort::SimpleTimingPort(string pname, MemObject *_owner)
-    : Port(pname, _owner), sendEvent(NULL), drainEvent(NULL),
+SimpleTimingPort::SimpleTimingPort(const string &_name, MemObject *_owner,
+                                   const string _label)
+    : Port(_name, _owner), label(_label), sendEvent(this), drainEvent(NULL),
       waitingOnRetry(false)
 {
-    sendEvent =  new EventWrapper<SimpleTimingPort,
-        &SimpleTimingPort::processSendEvent>(this);
 }
 
 SimpleTimingPort::~SimpleTimingPort()
 {
-    delete sendEvent;
 }
 
 bool
 SimpleTimingPort::checkFunctional(PacketPtr pkt)
 {
+    pkt->pushLabel(label);
+
     DeferredPacketIterator i = transmitList.begin();
     DeferredPacketIterator end = transmitList.end();
+    bool found = false;
 
-    for (; i != end; ++i) {
-        PacketPtr target = i->pkt;
-        // If the target contains data, and it overlaps the
-        // probed request, need to update data
-        if (pkt->checkFunctional(target)) {
-            return true;
-        }
+    while (!found && i != end) {
+        // If the buffered packet contains data, and it overlaps the
+        // current packet, then update data
+        found = pkt->checkFunctional(i->pkt);
+        ++i;
     }
 
-    return false;
+    pkt->popLabel();
+
+    return found;
 }
 
 void
@@ -108,15 +122,17 @@ SimpleTimingPort::recvTiming(PacketPtr pkt)
 void
 SimpleTimingPort::schedSendEvent(Tick when)
 {
+    // if we are waiting on a retry, do not schedule a send event, and
+    // instead rely on retry being called
     if (waitingOnRetry) {
-        assert(!sendEvent->scheduled());
+        assert(!sendEvent.scheduled());
         return;
     }
 
-    if (!sendEvent->scheduled()) {
-        owner->schedule(sendEvent, when);
-    } else if (sendEvent->when() > when) {
-        owner->reschedule(sendEvent, when);
+    if (!sendEvent.scheduled()) {
+        owner->schedule(&sendEvent, when);
+    } else if (sendEvent.when() > when) {
+        owner->reschedule(&sendEvent, when);
     }
 }
 
@@ -153,40 +169,55 @@ SimpleTimingPort::schedSendTiming(PacketPtr pkt, Tick when)
     assert(false); // should never get here
 }
 
-
-void
-SimpleTimingPort::sendDeferredPacket()
+void SimpleTimingPort::trySendTiming()
 {
     assert(deferredPacketReady());
-    // take packet off list here; if recvTiming() on the other side
-    // calls sendTiming() back on us (like SimpleTimingCpu does), then
-    // we get confused by having a non-active packet on transmitList
+    // take the next packet off the list here, as we might return to
+    // ourselves through the sendTiming call below
     DeferredPacket dp = transmitList.front();
     transmitList.pop_front();
-    bool success = sendTiming(dp.pkt);
 
-    if (success) {
-        if (!transmitList.empty() && !sendEvent->scheduled()) {
-            Tick time = transmitList.front().tick;
-            owner->schedule(sendEvent, time <= curTick() ? curTick()+1 : time);
-        }
+    // attempt to send the packet and remember the outcome
+    waitingOnRetry = !sendTiming(dp.pkt);
+
+    if (waitingOnRetry) {
+        // put the packet back at the front of the list (packet should
+        // not have changed since it wasn't accepted)
+        assert(!sendEvent.scheduled());
+        transmitList.push_front(dp);
+    }
+}
 
-        if (transmitList.empty() && drainEvent && !sendEvent->scheduled()) {
+void
+SimpleTimingPort::scheduleSend(Tick time)
+{
+    // the next ready time is either determined by the next deferred packet,
+    // or in the cache through the MSHR ready time
+    Tick nextReady = std::min(deferredPacketReadyTime(), time);
+    if (nextReady != MaxTick) {
+        // if the sendTiming caused someone else to call our
+        // recvTiming we could already have an event scheduled, check
+        if (!sendEvent.scheduled())
+            owner->schedule(&sendEvent, std::max(nextReady, curTick() + 1));
+    } else {
+        // no more to send, so if we're draining, we may be done
+        if (drainEvent && !sendEvent.scheduled()) {
             drainEvent->process();
             drainEvent = NULL;
         }
-    } else {
-        // Unsuccessful, need to put back on transmitList.  Callee
-        // should not have messed with it (since it didn't accept that
-        // packet), so we can just push it back on the front.
-        assert(!sendEvent->scheduled());
-        transmitList.push_front(dp);
     }
+}
 
-    waitingOnRetry = !success;
+void
+SimpleTimingPort::sendDeferredPacket()
+{
+    // try to send what is on the list
+    trySendTiming();
 
-    if (waitingOnRetry) {
-        DPRINTF(Bus, "Send failed, waiting on retry\n");
+    // if we succeeded and are not waiting for a retry, schedule the
+    // next send
+    if (!waitingOnRetry) {
+        scheduleSend();
     }
 }
 
@@ -195,6 +226,9 @@ void
 SimpleTimingPort::recvRetry()
 {
     DPRINTF(Bus, "Received retry\n");
+    // note that in the cache we get a retry even though we may not
+    // have a packet to retry (we could potentially decide on a new
+    // packet every time we retry)
     assert(waitingOnRetry);
     sendDeferredPacket();
 }
@@ -211,7 +245,7 @@ SimpleTimingPort::processSendEvent()
 unsigned int
 SimpleTimingPort::drain(Event *de)
 {
-    if (transmitList.size() == 0 && !sendEvent->scheduled())
+    if (transmitList.empty() && !sendEvent.scheduled())
         return 0;
     drainEvent = de;
     return 1;
index eaf5acd5d6afeefb0a181eac9cb77b90b92ba417..d720f227c5df621ae21597cc17e4f05e7d2a0c94 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2012 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
  * All rights reserved.
  *
@@ -26,6 +38,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * Authors: Ali Saidi
+ *          Andreas Hansson
  */
 
 #ifndef __MEM_TPORT_HH__
@@ -76,6 +89,9 @@ class SimpleTimingPort : public Port
      * serviced yet. */
     DeferredPacketList transmitList;
 
+    /** Label to use for print request packets label stack. */
+    const std::string label;
+
     /** This function attempts to send deferred packets.  Scheduled to
      * be called in the future via SendEvent. */
     void processSendEvent();
@@ -86,7 +102,8 @@ class SimpleTimingPort : public Port
      * When the event time expires it attempts to send the packet.
      * If it cannot, the packet sent when recvRetry() is called.
      **/
-    Event *sendEvent;
+    EventWrapper<SimpleTimingPort,
+                 &SimpleTimingPort::processSendEvent> sendEvent;
 
     /** If we need to drain, keep the drain event around until we're done
      * here.*/
@@ -95,10 +112,6 @@ class SimpleTimingPort : public Port
     /** Remember whether we're awaiting a retry from the bus. */
     bool waitingOnRetry;
 
-    /** Check the list of buffered packets against the supplied
-     * functional request. */
-    bool checkFunctional(PacketPtr funcPkt);
-
     /** Check whether we have a packet ready to go on the transmit list. */
     bool deferredPacketReady()
     { return !transmitList.empty() && transmitList.front().tick <= curTick(); }
@@ -126,7 +139,24 @@ class SimpleTimingPort : public Port
      * non-empty and that the head packet is scheduled for curTick() (or
      * earlier).
      */
-    void sendDeferredPacket();
+    virtual void sendDeferredPacket();
+
+    /**
+     * Attempt to send the packet at the front of the transmit list,
+     * and set waitingOnRetry accordingly. The packet is temporarily
+     * taken off the list, but put back at the front if not
+     * successfully sent.
+     */
+    void trySendTiming();
+
+    /**
+     * Based on the transmit list, or the provided time, schedule a
+     * send event if there are packets to send. If we are idle and
+     * asked to drain then do so.
+     *
+     * @param time an alternative time for the next send event
+     */
+    void scheduleSend(Tick time = MaxTick);
 
     /** This function is notification that the device should attempt to send a
      * packet again. */
@@ -149,9 +179,14 @@ class SimpleTimingPort : public Port
 
 
   public:
-    SimpleTimingPort(std::string pname, MemObject *_owner);
+    SimpleTimingPort(const std::string &_name, MemObject *_owner,
+                     const std::string _label = "SimpleTimingPort");
     ~SimpleTimingPort();
 
+    /** Check the list of buffered packets against the supplied
+     * functional request. */
+    bool checkFunctional(PacketPtr pkt);
+
     /** Hook for draining timing accesses from the system.  The
      * associated SimObject's drain() functions should be implemented
      * something like this when this class is used: