MEM: Split SimpleTimingPort into PacketQueue and ports
authorAndreas Hansson <andreas.hansson@arm.com>
Thu, 22 Mar 2012 10:36:27 +0000 (06:36 -0400)
committerAndreas Hansson <andreas.hansson@arm.com>
Thu, 22 Mar 2012 10:36:27 +0000 (06:36 -0400)
This patch decouples the queueing and the port interactions to
simplify the introduction of the master and slave ports. By separating
the queueing functionality from the port itself, it becomes much
easier to distinguish between master and slave ports, and still retain
the queueing ability for both (without code duplication).

As part of the split into a PacketQueue and a port, there is now also
a hierarchy of two port classes, QueuedPort and SimpleTimingPort. The
QueuedPort is useful for ports that want to leave the packet
transmission of outgoing packets to the queue and is used by both
master and slave ports. The SimpleTimingPort inherits from the
QueuedPort and adds the implemention of recvTiming and recvFunctional
through recvAtomic.

The PioPort and MessagePort are cleaned up as part of the changes.

--HG--
rename : src/mem/tport.cc => src/mem/packet_queue.cc
rename : src/mem/tport.hh => src/mem/packet_queue.hh

17 files changed:
src/dev/io_device.cc
src/dev/io_device.hh
src/dev/x86/intdev.cc
src/mem/SConscript
src/mem/cache/base.cc
src/mem/cache/base.hh
src/mem/cache/cache.hh
src/mem/cache/cache_impl.hh
src/mem/mport.hh
src/mem/packet_queue.cc [new file with mode: 0644]
src/mem/packet_queue.hh [new file with mode: 0644]
src/mem/physical.cc
src/mem/qport.hh [new file with mode: 0644]
src/mem/ruby/system/RubyPort.cc
src/mem/ruby/system/RubyPort.hh
src/mem/tport.cc
src/mem/tport.hh

index 5d6255f5cd8eb64c2ada27ff3ebefbf2ee3a4dea..f7b8db09d3db751bc1ad2d544daaa0576bfa054f 100644 (file)
 #include "dev/io_device.hh"
 #include "sim/system.hh"
 
-PioPort::PioPort(PioDevice *dev, System *s, std::string pname)
-    : SimpleTimingPort(dev->name() + pname, dev), device(dev)
-{ }
+PioPort::PioPort(PioDevice *dev)
+    : SimpleTimingPort(dev->name() + "-pioport", dev), device(dev)
+{
+}
 
 
 Tick
@@ -55,7 +56,7 @@ PioPort::getAddrRanges()
 
 
 PioDevice::PioDevice(const Params *p)
-    : MemObject(p), sys(p->system), pioPort(this, sys)
+    : MemObject(p), sys(p->system), pioPort(this)
 {}
 
 PioDevice::~PioDevice()
index d7ed9380524ad71ff4502a8305ba81428b07ed47..400263957114ca64af53002cbf18bcbb7f0712c3 100644 (file)
@@ -65,7 +65,7 @@ class PioPort : public SimpleTimingPort
 
   public:
 
-    PioPort(PioDevice *dev, System *s, std::string pname = "-pioport");
+    PioPort(PioDevice *dev);
 };
 
 
index bcfab5fe4885349f877efeb1b93279da59ee45e3..0d038b93d023b7821a73130914d7e48e26b353a6 100644 (file)
@@ -50,7 +50,7 @@ X86ISA::IntDev::IntPort::sendMessage(ApicList apics, TriggerIntMessage message,
     for (apicIt = apics.begin(); apicIt != apics.end(); apicIt++) {
         PacketPtr pkt = buildIntRequest(*apicIt, message);
         if (timing) {
-            schedSendTiming(pkt, curTick() + latency);
+            queue.schedSendTiming(pkt, curTick() + latency);
             // The target handles cleaning up the packet in timing mode.
         } else {
             // ignore the latency involved in the atomic transaction
index fe43f71bed9769cc4cbb7af09062499d89ba1450..cc5e3a37afab7c9086dda74830dd28f348374942 100644 (file)
@@ -40,6 +40,7 @@ Source('mem_object.cc')
 Source('mport.cc')
 Source('packet.cc')
 Source('port.cc')
+Source('packet_queue.cc')
 Source('tport.cc')
 Source('port_proxy.cc')
 Source('fs_translating_port_proxy.cc')
@@ -57,6 +58,7 @@ DebugFlag('BusBridge')
 DebugFlag('LLSC')
 DebugFlag('MMU')
 DebugFlag('MemoryAccess')
+DebugFlag('PacketQueue')
 
 DebugFlag('ProtocolTrace')
 DebugFlag('RubyCache')
index fb27576163e809e81ec592b088f303b454c977fe..a2cb59a768c8cb2f5cca83aaef0684c25364ccf4 100644 (file)
 
 using namespace std;
 
-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)
+    : QueuedPort(_name, _cache, queue), queue(*_cache, *this, _label),
+      blocked(false), mustSendRetry(false), sendRetryEvent(this)
 {
 }
 
index 2a79fb354556082df5de530a2880824c5c1e6d86..c13d27d42bc0b34acc95d24f3ddaece6bec0d1c6 100644 (file)
@@ -64,8 +64,8 @@
 #include "mem/cache/mshr_queue.hh"
 #include "mem/mem_object.hh"
 #include "mem/packet.hh"
+#include "mem/qport.hh"
 #include "mem/request.hh"
-#include "mem/tport.hh"
 #include "params/BaseCache.hh"
 #include "sim/eventq.hh"
 #include "sim/full_system.hh"
@@ -118,7 +118,7 @@ class BaseCache : public MemObject
      * 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
+    class CacheMasterPort : public QueuedPort
     {
 
       public:
@@ -131,22 +131,31 @@ class BaseCache : public MemObject
         void requestBus(RequestCause cause, Tick time)
         {
             DPRINTF(CachePort, "Asserting bus request for cause %d\n", cause);
-            schedSendEvent(time);
+            queue.schedSendEvent(time);
         }
 
+        /**
+         * Schedule the transmissions of a response packet at a given
+         * point in time.
+         *
+         * @param pkt response packet
+         * @param when time to send the response
+         */
         void respond(PacketPtr pkt, Tick time) {
-            schedSendTiming(pkt, time);
+            queue.schedSendTiming(pkt, time);
         }
 
       protected:
 
         CacheMasterPort(const std::string &_name, BaseCache *_cache,
-                        const std::string &_label);
+                        PacketQueue &_queue) :
+            QueuedPort(_name, _cache, _queue)
+        { }
 
         /**
          * Memory-side port always snoops.
          *
-         * return always true
+         * @return always true
          */
         virtual bool isSnooping() { return true; }
     };
@@ -159,7 +168,7 @@ class BaseCache : public MemObject
      * incoming requests. If blocked, the port will issue a retry once
      * unblocked.
      */
-    class CacheSlavePort : public SimpleTimingPort
+    class CacheSlavePort : public QueuedPort
     {
 
       public:
@@ -170,8 +179,15 @@ class BaseCache : public MemObject
         /** Return to normal operation and accept new requests. */
         void clearBlocked();
 
+        /**
+         * Schedule the transmissions of a response packet at a given
+         * point in time.
+         *
+         * @param pkt response packet
+         * @param when time to send the response
+         */
         void respond(PacketPtr pkt, Tick time) {
-            schedSendTiming(pkt, time);
+            queue.schedSendTiming(pkt, time);
         }
 
       protected:
@@ -179,6 +195,9 @@ class BaseCache : public MemObject
         CacheSlavePort(const std::string &_name, BaseCache *_cache,
                        const std::string &_label);
 
+        /** A normal packet queue used to store responses. */
+        PacketQueue queue;
+
         bool blocked;
 
         bool mustSendRetry;
index 2883955845d121272e45d7d28c5cb2d331b63997..782749aabe719825b1c5dc5183a8239e664ab82a 100644 (file)
@@ -108,6 +108,34 @@ class Cache : public BaseCache
 
     };
 
+    /**
+     * Override the default behaviour of sendDeferredPacket to enable
+     * the memory-side cache port to also send requests based on the
+     * current MSHR status. This queue has a pointer to our specific
+     * cache implementation and is used by the MemSidePort.
+     */
+    class MemSidePacketQueue : public PacketQueue
+    {
+
+      protected:
+
+        Cache<TagStore> &cache;
+
+      public:
+
+        MemSidePacketQueue(Cache<TagStore> &cache, Port &port,
+                           const std::string &label) :
+            PacketQueue(cache, port, label), cache(cache) { }
+
+        /**
+         * Override the normal sendDeferredPacket and do not only
+         * consider the transmit list (used for responses), but also
+         * requests.
+         */
+        virtual void sendDeferredPacket();
+
+    };
+
     /**
      * The memory-side port extends the base cache master port with
      * access functions for functional, atomic and timing snoops.
@@ -116,6 +144,9 @@ class Cache : public BaseCache
     {
       private:
 
+        /** The cache-specific queue. */
+        MemSidePacketQueue _queue;
+
         // a pointer to our specific cache implementation
         Cache<TagStore> *cache;
 
@@ -134,11 +165,6 @@ class Cache : public BaseCache
 
         MemSidePort(const std::string &_name, Cache<TagStore> *_cache,
                     const std::string &_label);
-
-        /**
-         * Overload sendDeferredPacket of SimpleTimingPort.
-         */
-        virtual void sendDeferredPacket();
     };
 
     /** Tag and data Storage */
index f6efc3fb874dfdf6342f31909f00a8174aede808..2463071de689c937e45205581e27706bb272b7be 100644 (file)
@@ -1646,7 +1646,7 @@ Cache<TagStore>::MemSidePort::recvFunctional(PacketPtr pkt)
 
 template<class TagStore>
 void
-Cache<TagStore>::MemSidePort::sendDeferredPacket()
+Cache<TagStore>::MemSidePacketQueue::sendDeferredPacket()
 {
     // if we have a response packet waiting we have to start with that
     if (deferredPacketReady()) {
@@ -1654,7 +1654,7 @@ Cache<TagStore>::MemSidePort::sendDeferredPacket()
         trySendTiming();
     } else {
         // check for request packets (requests & writebacks)
-        PacketPtr pkt = cache->getTimingPacket();
+        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
@@ -1663,7 +1663,7 @@ Cache<TagStore>::MemSidePort::sendDeferredPacket()
         } else {
             MSHR *mshr = dynamic_cast<MSHR*>(pkt->senderState);
 
-            waitingOnRetry = !sendTiming(pkt);
+            waitingOnRetry = !port.sendTiming(pkt);
 
             if (waitingOnRetry) {
                 DPRINTF(CachePort, "now waiting on a retry\n");
@@ -1679,7 +1679,7 @@ Cache<TagStore>::MemSidePort::sendDeferredPacket()
                 // care about this packet and might override it before
                 // it gets retried
             } else {
-                cache->markInService(mshr, pkt);
+                cache.markInService(mshr, pkt);
             }
         }
     }
@@ -1688,7 +1688,7 @@ Cache<TagStore>::MemSidePort::sendDeferredPacket()
     // next send, not only looking at the response transmit list, but
     // also considering when the next MSHR is ready
     if (!waitingOnRetry) {
-        scheduleSend(cache->nextMSHRReadyTime());
+        scheduleSend(cache.nextMSHRReadyTime());
     }
 }
 
@@ -1696,6 +1696,7 @@ template<class TagStore>
 Cache<TagStore>::
 MemSidePort::MemSidePort(const std::string &_name, Cache<TagStore> *_cache,
                          const std::string &_label)
-    : BaseCache::CacheMasterPort(_name, _cache, _label), cache(_cache)
+    : BaseCache::CacheMasterPort(_name, _cache, _queue),
+      _queue(*_cache, *this, _label), cache(_cache)
 {
 }
index 062dcca0b9d88f2f8600925bf476d1e7c8199c2b..7f167c2274483fd333d5f5db2b87c40c91dbdfbc 100644 (file)
@@ -31,6 +31,7 @@
 #ifndef __MEM_MPORT_HH__
 #define __MEM_MPORT_HH__
 
+#include "mem/mem_object.hh"
 #include "mem/tport.hh"
 
 /*
  * the underpinnings of SimpleTimingPort, but it tweaks some of the external
  * functions.
  */
-
 class MessagePort : public SimpleTimingPort
 {
+
   public:
-    MessagePort(std::string pname, MemObject *_owner = NULL) :
-        SimpleTimingPort(pname, _owner)
+    MessagePort(const std::string &name, MemObject *owner) :
+        SimpleTimingPort(name, owner)
     {}
 
     virtual ~MessagePort()
     {}
 
-    void
-    recvFunctional(PacketPtr pkt)
-    {
-        recvAtomic(pkt);
-    }
+  protected:
 
     Tick recvAtomic(PacketPtr pkt);
 
-  protected:
-
     virtual Tick recvMessage(PacketPtr pkt) = 0;
 
     // Accept and ignore responses.
diff --git a/src/mem/packet_queue.cc b/src/mem/packet_queue.cc
new file mode 100644 (file)
index 0000000..29914be
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ *          Andreas Hansson
+ */
+
+#include "debug/PacketQueue.hh"
+#include "mem/packet_queue.hh"
+
+using namespace std;
+
+PacketQueue::PacketQueue(EventManager& _em, Port& _port,
+                         const std::string _label)
+    : em(_em), label(_label), sendEvent(this), drainEvent(NULL), port(_port),
+      waitingOnRetry(false)
+{
+}
+
+PacketQueue::~PacketQueue()
+{
+}
+
+void
+PacketQueue::retry()
+{
+    DPRINTF(PacketQueue, "Queue %s received retry\n", name());
+    assert(waitingOnRetry);
+    sendDeferredPacket();
+}
+
+bool
+PacketQueue::checkFunctional(PacketPtr pkt)
+{
+    pkt->pushLabel(label);
+
+    DeferredPacketIterator i = transmitList.begin();
+    DeferredPacketIterator end = transmitList.end();
+    bool found = false;
+
+    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;
+    }
+
+    pkt->popLabel();
+
+    return found;
+}
+
+void
+PacketQueue::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());
+        return;
+    }
+
+    if (!sendEvent.scheduled()) {
+        em.schedule(&sendEvent, when);
+    } else if (sendEvent.when() > when) {
+        em.reschedule(&sendEvent, when);
+    }
+}
+
+void
+PacketQueue::schedSendTiming(PacketPtr pkt, Tick when)
+{
+    assert(when > curTick());
+
+    // nothing on the list, or earlier than current front element,
+    // schedule an event
+    if (transmitList.empty() || when < transmitList.front().tick) {
+        // note that currently we ignore a potentially outstanding retry
+        // and could in theory put a new packet at the head of the
+        // transmit list before retrying the existing packet
+        transmitList.push_front(DeferredPacket(when, pkt));
+        schedSendEvent(when);
+        return;
+    }
+
+    // list is non-empty and this belongs at the end
+    if (when >= transmitList.back().tick) {
+        transmitList.push_back(DeferredPacket(when, pkt));
+        return;
+    }
+
+    // this belongs in the middle somewhere, insertion sort
+    DeferredPacketIterator i = transmitList.begin();
+    ++i; // already checked for insertion at front
+    while (i != transmitList.end() && when >= i->tick)
+        ++i;
+    transmitList.insert(i, DeferredPacket(when, pkt));
+}
+
+void PacketQueue::trySendTiming()
+{
+    assert(deferredPacketReady());
+
+    // 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();
+
+    // attempt to send the packet and remember the outcome
+    waitingOnRetry = !port.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);
+    }
+}
+
+void
+PacketQueue::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())
+            em.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;
+        }
+    }
+}
+
+void
+PacketQueue::sendDeferredPacket()
+{
+    // try to send what is on the list, this will set waitingOnRetry
+    // accordingly
+    trySendTiming();
+
+    // if we succeeded and are not waiting for a retry, schedule the
+    // next send
+    if (!waitingOnRetry) {
+        scheduleSend();
+    }
+}
+
+void
+PacketQueue::processSendEvent()
+{
+    assert(!waitingOnRetry);
+    sendDeferredPacket();
+}
+
+unsigned int
+PacketQueue::drain(Event *de)
+{
+    if (transmitList.empty() && !sendEvent.scheduled())
+        return 0;
+    drainEvent = de;
+    return 1;
+}
diff --git a/src/mem/packet_queue.hh b/src/mem/packet_queue.hh
new file mode 100644 (file)
index 0000000..ac86880
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ *          Andreas Hansson
+ */
+
+#ifndef __MEM_PACKET_QUEUE_HH__
+#define __MEM_PACKET_QUEUE_HH__
+
+/**
+ * @file
+ * Declaration of a simple PacketQueue that is associated with
+ * a port on which it attempts to send packets according to the time
+ * stamp given to them at insertion. The packet queue is responsible
+ * for the flow control of the port, but relies on the module
+ * notifying the queue when a transfer ends.
+ */
+
+#include <list>
+
+#include "mem/port.hh"
+#include "sim/eventq.hh"
+
+/**
+ * A packet queue is a class that holds deferred packets and later
+ * sends them using the associated slave port or master port.
+ */
+class PacketQueue
+{
+  private:
+    /** A deferred packet, buffered to transmit later. */
+    class DeferredPacket {
+      public:
+        Tick tick;      ///< The tick when the packet is ready to transmit
+        PacketPtr pkt;  ///< Pointer to the packet to transmit
+        DeferredPacket(Tick t, PacketPtr p)
+            : tick(t), pkt(p)
+        {}
+    };
+
+    typedef std::list<DeferredPacket> DeferredPacketList;
+    typedef std::list<DeferredPacket>::iterator DeferredPacketIterator;
+
+    /** A list of outgoing timing response packets that haven't been
+     * serviced yet. */
+    DeferredPacketList transmitList;
+
+    /** The manager which is used for the event queue */
+    EventManager& em;
+
+    /** 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();
+
+    /**
+     * Event used to call processSendEvent.
+     **/
+    EventWrapper<PacketQueue, &PacketQueue::processSendEvent> sendEvent;
+
+    /** If we need to drain, keep the drain event around until we're done
+     * here.*/
+    Event *drainEvent;
+
+  protected:
+
+    /** The port used to send the packets. */
+    Port& port;
+
+    /** Remember whether we're awaiting a retry from the bus. */
+    bool waitingOnRetry;
+
+    /** Check whether we have a packet ready to go on the transmit list. */
+    bool deferredPacketReady()
+    { return !transmitList.empty() && transmitList.front().tick <= curTick(); }
+
+    Tick deferredPacketReadyTime()
+    { return transmitList.empty() ? MaxTick : transmitList.front().tick; }
+
+    /**
+     * Attempt to send the packet at the head of the transmit
+     * list. Caller must guarantee that the list is non-empty and that
+     * the head packet is scheduled for curTick() (or earlier). Note
+     * that a subclass of the PacketQueue can override this method and
+     * thus change the behaviour (as done by the cache).
+     */
+    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);
+
+    /**
+     * Simple ports are generally used as slave ports (i.e. the
+     * respond to requests) and thus do not expect to receive any
+     * range changes (as the neighbouring port has a master role and
+     * do not have any address ranges. A subclass can override the
+     * default behaviuor if needed.
+     */
+    virtual void recvRangeChange() { }
+
+  public:
+
+    /**
+     * Create a packet queue, linked to an event manager, a port used
+     * to send the packets, and potentially give it a label that will
+     * be used for functional print request packets.
+     *
+     * @param _em Event manager used for scheduling this queue
+     * @param _port Port used to send the packets
+     * @param _label Label to push on the label stack for print request packets
+     */
+    PacketQueue(EventManager& _em, Port& _port,
+                const std::string _label = "PacketQueue");
+
+    /**
+     * Virtual desctructor since the class may be used as a base class.
+     */
+    virtual ~PacketQueue();
+
+    /**
+     * Provide a name to simplify debugging. Base it on the port.
+     *
+     * @return A complete name, appended to module and port
+     */
+    const std::string name() const { return port.name() + "-queue"; }
+
+    /** Check the list of buffered packets against the supplied
+     * functional request. */
+    bool checkFunctional(PacketPtr pkt);
+
+    /**
+     * Schedule a send even if not already waiting for a retry. If the
+     * requested time is before an already scheduled send event it
+     * will be rescheduled.
+     *
+     * @param when
+     */
+    void schedSendEvent(Tick when);
+
+    /**
+     * Add a packet to the transmit list, and ensure that a
+     * processSendEvent is called in the future.
+     *
+     * @param pkt Packet to send
+     * @param when Absolute time (in ticks) to send packet
+     */
+    void schedSendTiming(PacketPtr pkt, Tick when);
+
+    /**
+     * Used by a port to notify the queue that a retry was received
+     * and that the queue can proceed and retry sending the packet
+     * that caused the wait.
+     */
+    void retry();
+
+    /**
+     * Hook for draining the packet queue.
+     *
+     * @param de An event which is used to signal back to the caller
+     * @return A number indicating how many times process will be called
+     */
+    unsigned int drain(Event *de);
+};
+
+#endif // __MEM_TPORT_HH__
index 999ad0cdbde648a088df22bff6b44a316e72f245..f11fbb9478f8cc2eee204ae88b8c59e380bcb9f4 100644 (file)
@@ -496,7 +496,7 @@ PhysicalMemory::MemoryPort::recvFunctional(PacketPtr pkt)
 {
     pkt->pushLabel(memory->name());
 
-    if (!checkFunctional(pkt)) {
+    if (!queue.checkFunctional(pkt)) {
         // Default implementation of SimpleTimingPort::recvFunctional()
         // calls recvAtomic() and throws away the latency; we can save a
         // little here by just not calculating the latency.
diff --git a/src/mem/qport.hh b/src/mem/qport.hh
new file mode 100644 (file)
index 0000000..39612d2
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Andreas Hansson
+ */
+
+#ifndef __MEM_QPORT_HH__
+#define __MEM_QPORT_HH__
+
+/**
+ * @file
+ * Declaration of the queued port.
+ */
+
+#include "mem/packet_queue.hh"
+#include "mem/port.hh"
+
+/**
+ * A queued port is a port that has an infinite queue for outgoing
+ * packets and thus decouples the module that wants to send
+ * request/responses from the flow control (retry mechanism) of the
+ * port. A queued port can be used by both a master and a slave. The
+ * queue is a parameter to allow tailoring of the queue implementation
+ * (used in the cache).
+ */
+class QueuedPort : public Port
+{
+
+  protected:
+
+    /** Packet queue used to store outgoing requests and responses. */
+    PacketQueue &queue;
+
+     /** This function is notification that the device should attempt to send a
+      * packet again. */
+    virtual void recvRetry() { queue.retry(); }
+
+    virtual void recvRangeChange() { }
+
+  public:
+
+    /**
+     * Create a QueuedPort with a given name, owner, and a supplied
+     * implementation of a packet queue. The external definition of
+     * the queue enables e.g. the cache to implement a specific queue
+     * behaviuor in a subclass, and provide the latter to the
+     * QueuePort constructor.
+     */
+    QueuedPort(const std::string& name, MemObject* owner, PacketQueue &queue) :
+        Port(name, owner), queue(queue)
+    { }
+
+    virtual ~QueuedPort() { }
+
+    /** Check the list of buffered packets against the supplied
+     * functional request. */
+    bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); }
+
+    /**
+     * Hook for draining the queued port.
+     *
+     * @param de an event which is used to signal back to the caller
+     * @returns a number indicating how many times process will be called
+     */
+    unsigned int drain(Event *de) { return queue.drain(de); }
+};
+
+#endif // __MEM_QPORT_HH__
index aff129b50464403678ea65765e2c2d0ef64a0d53..aca6604c6fcbb1b71cc419a89d15b07d2c0c6a92 100644 (file)
@@ -98,21 +98,18 @@ RubyPort::getPort(const std::string &if_name, int idx)
 
 RubyPort::PioPort::PioPort(const std::string &_name,
                            RubyPort *_port)
-    : SimpleTimingPort(_name, _port)
+    : QueuedPort(_name, _port, queue), queue(*_port, *this), ruby_port(_port)
 {
     DPRINTF(RubyPort, "creating port to ruby sequencer to cpu %s\n", _name);
-    ruby_port = _port;
 }
 
 RubyPort::M5Port::M5Port(const std::string &_name, RubyPort *_port,
                          RubySystem *_system, bool _access_phys_mem)
-    : SimpleTimingPort(_name, _port)
+    : QueuedPort(_name, _port, queue), queue(*_port, *this),
+      ruby_port(_port), ruby_system(_system),
+      _onRetryList(false), access_phys_mem(_access_phys_mem)
 {
     DPRINTF(RubyPort, "creating port from ruby sequcner to cpu %s\n", _name);
-    ruby_port = _port;
-    ruby_system = _system;
-    _onRetryList = false;
-    access_phys_mem = _access_phys_mem;
 }
 
 Tick
@@ -648,7 +645,7 @@ bool
 RubyPort::M5Port::sendNextCycle(PacketPtr pkt)
 {
     //minimum latency, must be > 0
-    schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
+    queue.schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
     return true;
 }
 
@@ -656,7 +653,7 @@ bool
 RubyPort::PioPort::sendNextCycle(PacketPtr pkt)
 {
     //minimum latency, must be > 0
-    schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
+    queue.schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
     return true;
 }
 
index 4aa132131d0094becb275d16535ff2efdedc4cfe..bef291d6354a8c8fa1f86504259e7a799691055d 100644 (file)
@@ -46,9 +46,11 @@ class AbstractController;
 class RubyPort : public MemObject
 {
   public:
-    class M5Port : public SimpleTimingPort
+    class M5Port : public QueuedPort
     {
       private:
+
+        PacketQueue queue;
         RubyPort *ruby_port;
         RubySystem* ruby_system;
         bool _onRetryList;
@@ -81,9 +83,12 @@ class RubyPort : public MemObject
 
     friend class M5Port;
 
-    class PioPort : public SimpleTimingPort
+    class PioPort : public QueuedPort
     {
       private:
+
+        PacketQueue queue;
+
         RubyPort *ruby_port;
 
       public:
@@ -93,6 +98,7 @@ class RubyPort : public MemObject
       protected:
         virtual bool recvTiming(PacketPtr pkt);
         virtual Tick recvAtomic(PacketPtr pkt);
+        virtual void recvFunctional(PacketPtr pkt) { }
     };
 
     friend class PioPort;
index cbb7ed2ac9ca86ab5ac85ff19f4f1e06bd178a01..bf3d59a8f93905590d452600b5670f71d9b048d5 100644 (file)
  *          Andreas Hansson
  */
 
-#include "debug/Bus.hh"
 #include "mem/mem_object.hh"
 #include "mem/tport.hh"
 
-using namespace std;
-
-SimpleTimingPort::SimpleTimingPort(const string &_name, MemObject *_owner,
-                                   const string _label)
-    : Port(_name, _owner), label(_label), sendEvent(this), drainEvent(NULL),
-      waitingOnRetry(false)
-{
-}
-
-SimpleTimingPort::~SimpleTimingPort()
-{
-}
-
-bool
-SimpleTimingPort::checkFunctional(PacketPtr pkt)
+SimpleTimingPort::SimpleTimingPort(const std::string& _name,
+                                   MemObject* _owner) :
+    QueuedPort(_name, _owner, queue), queue(*_owner, *this)
 {
-    pkt->pushLabel(label);
-
-    DeferredPacketIterator i = transmitList.begin();
-    DeferredPacketIterator end = transmitList.end();
-    bool found = false;
-
-    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;
-    }
-
-    pkt->popLabel();
-
-    return found;
 }
 
 void
 SimpleTimingPort::recvFunctional(PacketPtr pkt)
 {
-    if (!checkFunctional(pkt)) {
-        // Just do an atomic access and throw away the returned latency
+    assert(pkt->isRequest());
+    if (!queue.checkFunctional(pkt)) {
+        // do an atomic access and throw away the returned latency
         recvAtomic(pkt);
     }
 }
@@ -91,12 +63,6 @@ SimpleTimingPort::recvFunctional(PacketPtr pkt)
 bool
 SimpleTimingPort::recvTiming(PacketPtr pkt)
 {
-    // If the device is only a slave, it should only be sending
-    // responses, which should never get nacked.  There used to be
-    // code to hanldle nacks here, but I'm pretty sure it didn't work
-    // correctly with the drain code, so that would need to be fixed
-    // if we ever added it back.
-
     if (pkt->memInhibitAsserted()) {
         // snooper will supply based on copy of packet
         // still target's responsibility to delete packet
@@ -111,142 +77,10 @@ SimpleTimingPort::recvTiming(PacketPtr pkt)
         // recvAtomic() should already have turned packet into
         // atomic response
         assert(pkt->isResponse());
-        schedSendTiming(pkt, curTick() + latency);
+        queue.schedSendTiming(pkt, curTick() + latency);
     } else {
         delete pkt;
     }
 
     return true;
 }
-
-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());
-        return;
-    }
-
-    if (!sendEvent.scheduled()) {
-        owner->schedule(&sendEvent, when);
-    } else if (sendEvent.when() > when) {
-        owner->reschedule(&sendEvent, when);
-    }
-}
-
-void
-SimpleTimingPort::schedSendTiming(PacketPtr pkt, Tick when)
-{
-    assert(when > curTick());
-    assert(when < curTick() + SimClock::Int::ms);
-
-    // Nothing is on the list: add it and schedule an event
-    if (transmitList.empty() || when < transmitList.front().tick) {
-        transmitList.push_front(DeferredPacket(when, pkt));
-        schedSendEvent(when);
-        return;
-    }
-
-    // list is non-empty & this belongs at the end
-    if (when >= transmitList.back().tick) {
-        transmitList.push_back(DeferredPacket(when, pkt));
-        return;
-    }
-
-    // this belongs in the middle somewhere
-    DeferredPacketIterator i = transmitList.begin();
-    i++; // already checked for insertion at front
-    DeferredPacketIterator end = transmitList.end();
-
-    for (; i != end; ++i) {
-        if (when < i->tick) {
-            transmitList.insert(i, DeferredPacket(when, pkt));
-            return;
-        }
-    }
-    assert(false); // should never get here
-}
-
-void SimpleTimingPort::trySendTiming()
-{
-    assert(deferredPacketReady());
-    // 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();
-
-    // 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);
-    }
-}
-
-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;
-        }
-    }
-}
-
-void
-SimpleTimingPort::sendDeferredPacket()
-{
-    // try to send what is on the list
-    trySendTiming();
-
-    // if we succeeded and are not waiting for a retry, schedule the
-    // next send
-    if (!waitingOnRetry) {
-        scheduleSend();
-    }
-}
-
-
-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();
-}
-
-
-void
-SimpleTimingPort::processSendEvent()
-{
-    assert(!waitingOnRetry);
-    sendDeferredPacket();
-}
-
-
-unsigned int
-SimpleTimingPort::drain(Event *de)
-{
-    if (transmitList.empty() && !sendEvent.scheduled())
-        return 0;
-    drainEvent = de;
-    return 1;
-}
index d720f227c5df621ae21597cc17e4f05e7d2a0c94..c77166386ba20aa7aeb733b1226ba7d1223b7392 100644 (file)
  * Declaration of SimpleTimingPort.
  */
 
-#include <list>
-#include <string>
-
-#include "mem/port.hh"
-#include "sim/eventq.hh"
+#include "mem/qport.hh"
 
 /**
- * A simple port for interfacing objects that basically have only
- * functional memory behavior (e.g. I/O devices) to the memory system.
- * Both timing and functional accesses are implemented in terms of
- * atomic accesses.  A derived port class thus only needs to provide
- * recvAtomic() to support all memory access modes.
- *
- * The tricky part is handling recvTiming(), where the response must
- * be scheduled separately via a later call to sendTiming().  This
- * feature is handled by scheduling an internal event that calls
- * sendTiming() after a delay, and optionally rescheduling the
- * response if it is nacked.
+ * The simple timing port uses a queued port to implement
+ * recvFunctional and recvTiming through recvAtomic. It is always a
+ * slave port.
  */
-class SimpleTimingPort : public Port
+class SimpleTimingPort : public QueuedPort
 {
-  protected:
-    /** A deferred packet, buffered to transmit later. */
-    class DeferredPacket {
-      public:
-        Tick tick;      ///< The tick when the packet is ready to transmit
-        PacketPtr pkt;  ///< Pointer to the packet to transmit
-        DeferredPacket(Tick t, PacketPtr p)
-            : tick(t), pkt(p)
-        {}
-    };
-
-    typedef std::list<DeferredPacket> DeferredPacketList;
-    typedef std::list<DeferredPacket>::iterator DeferredPacketIterator;
-
-    /** A list of outgoing timing response packets that haven't been
-     * 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();
-
-    /**
-     * This class is used to implemented sendTiming() with a delay. When
-     * a delay is requested a the event is scheduled if it isn't already.
-     * When the event time expires it attempts to send the packet.
-     * If it cannot, the packet sent when recvRetry() is called.
-     **/
-    EventWrapper<SimpleTimingPort,
-                 &SimpleTimingPort::processSendEvent> sendEvent;
 
-    /** If we need to drain, keep the drain event around until we're done
-     * here.*/
-    Event *drainEvent;
-
-    /** Remember whether we're awaiting a retry from the bus. */
-    bool waitingOnRetry;
-
-    /** Check whether we have a packet ready to go on the transmit list. */
-    bool deferredPacketReady()
-    { return !transmitList.empty() && transmitList.front().tick <= curTick(); }
-
-    Tick deferredPacketReadyTime()
-    { return transmitList.empty() ? MaxTick : transmitList.front().tick; }
-
-    /**
-     * Schedule a send even if not already waiting for a retry. If the
-     * requested time is before an already scheduled send event it
-     * will be rescheduled.
-     *
-     * @param when
-     */
-    void schedSendEvent(Tick when);
-
-    /** Schedule a sendTiming() event to be called in the future.
-     * @param pkt packet to send
-     * @param absolute time (in ticks) to send packet
-     */
-    void schedSendTiming(PacketPtr pkt, Tick when);
-
-    /** Attempt to send the packet at the head of the deferred packet
-     * list.  Caller must guarantee that the deferred packet list is
-     * non-empty and that the head packet is scheduled for curTick() (or
-     * earlier).
-     */
-    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);
+  protected:
 
-    /** This function is notification that the device should attempt to send a
-     * packet again. */
-    virtual void recvRetry();
+    /** The packet queue used to store outgoing responses. */
+    PacketQueue queue;
 
     /** Implemented using recvAtomic(). */
     void recvFunctional(PacketPtr pkt);
@@ -168,42 +71,25 @@ class SimpleTimingPort : public Port
     /** Implemented using recvAtomic(). */
     bool recvTiming(PacketPtr pkt);
 
-    /**
-     * Simple ports are generally used as slave ports (i.e. the
-     * respond to requests) and thus do not expect to receive any
-     * range changes (as the neighbouring port has a master role and
-     * do not have any address ranges. A subclass can override the
-     * default behaviuor if needed.
-     */
-    virtual void recvRangeChange() { }
-
+    virtual Tick recvAtomic(PacketPtr pkt) = 0;
 
   public:
-    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);
+    /**
+     * Create a new SimpleTimingPort that relies on a packet queue to
+     * hold responses, and implements recvTiming and recvFunctional
+     * through calls to recvAtomic. Once a request arrives, it is
+     * passed to recvAtomic, and in the case of a timing access any
+     * response is scheduled to be sent after the delay of the atomic
+     * operation.
+     *
+     * @param name port name
+     * @param owner structural owner
+     */
+    SimpleTimingPort(const std::string& name, MemObject* owner);
+
+    virtual ~SimpleTimingPort() { }
 
-    /** 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:
-     \code
-          PioDevice::drain(Event *de)
-          {
-              unsigned int count;
-              count = SimpleTimingPort->drain(de);
-              if (count)
-                  changeState(Draining);
-              else
-                  changeState(Drained);
-              return count;
-          }
-     \endcode
-    */
-    unsigned int drain(Event *de);
 };
 
 #endif // __MEM_TPORT_HH__