* Authors: Ali Saidi
*/
+#ifndef __MEM_TPORT_HH__
+#define __MEM_TPORT_HH__
+
/**
* @file
- * Implement a port which adds simple support of a sendTiming() function that
- * takes a delay. In this way the * device can immediatly call
- * sendTiming(pkt, time) after processing a request and the request will be
- * handled by the port even if the port bus the device connects to is blocked.
+ *
+ * Declaration of SimpleTimingPort.
*/
-/** recvTiming and drain should be implemented something like this when this
- * class is used.
-
-bool
-PioPort::recvTiming(Packet *pkt)
-{
- if (pkt->result == Packet::Nacked) {
- resendNacked(pkt);
- } else {
- Tick latency = device->recvAtomic(pkt);
- // turn packet around to go back to requester
- pkt->makeTimingResponse();
- sendTiming(pkt, latency);
- }
- return true;
-}
-
-PioDevice::drain(Event *de)
-{
- unsigned int count;
- count = SimpleTimingPort->drain(de);
- if (count)
- changeState(Draining);
- else
- changeState(Drained);
- return count;
-}
-*/
-
-#ifndef __MEM_TPORT_HH__
-#define __MEM_TPORT_HH__
-
#include "mem/port.hh"
#include "sim/eventq.hh"
#include <list>
#include <string>
+/**
+ * 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.
+ */
class SimpleTimingPort : public Port
{
protected:
- /** A list of outgoing timing response packets that haven't been serviced
- * yet. */
- std::list<Packet*> transmitList;
- /**
- * This class is used to implemented sendTiming() with a delay. When a delay
- * is requested a new event is created. When the event time expires it
- * attempts to send the packet. If it cannot, the packet is pushed onto the
- * transmit list to be sent when recvRetry() is called. */
- class SendEvent : public Event
- {
- SimpleTimingPort *port;
- Packet *packet;
-
- SendEvent(SimpleTimingPort *p, Packet *pkt, Tick t)
- : Event(&mainEventQueue), port(p), packet(pkt)
- { setFlags(AutoDelete); schedule(curTick + t); }
+ /** 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)
+ {}
+ };
- virtual void process();
+ typedef std::list<DeferredPacket> DeferredPacketList;
+ typedef std::list<DeferredPacket>::iterator DeferredPacketIterator;
- virtual const char *description()
- { return "Future scheduled sendTiming event"; }
+ /** A list of outgoing timing response packets that haven't been
+ * serviced yet. */
+ DeferredPacketList transmitList;
- friend class SimpleTimingPort;
- };
+ /** 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.
+ **/
+ typedef EventWrapper<SimpleTimingPort, &SimpleTimingPort::processSendEvent>
+ SendEvent;
- /** Number of timing requests that are emulating the device timing before
- * attempting to end up on the bus.
- */
- int outTiming;
+ Event *sendEvent;
/** If we need to drain, keep the drain event around until we're done
* here.*/
Event *drainEvent;
- /** Schedule a sendTiming() event to be called in the future. */
- void sendTiming(Packet *pkt, Tick time)
- { outTiming++; new SimpleTimingPort::SendEvent(this, pkt, time); }
+ /** 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; }
+
+ Tick deferredPacketReadyTime()
+ { return transmitList.empty() ? MaxTick : transmitList.front().tick; }
+
+ void schedSendEvent(Tick when)
+ {
+ if (waitingOnRetry) {
+ assert(!sendEvent->scheduled());
+ return;
+ }
+
+ if (!sendEvent->scheduled()) {
+ sendEvent->schedule(when);
+ } else if (sendEvent->when() > when) {
+ sendEvent->reschedule(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).
+ */
+ void sendDeferredPacket();
/** This function is notification that the device should attempt to send a
* packet again. */
virtual void recvRetry();
- void resendNacked(Packet *pkt);
+ /** Implemented using recvAtomic(). */
+ void recvFunctional(PacketPtr pkt);
+
+ /** Implemented using recvAtomic(). */
+ bool recvTiming(PacketPtr pkt);
+
+ /**
+ * Simple ports generally don't care about any status
+ * changes... can always override this in cases where that's not
+ * true. */
+ virtual void recvStatusChange(Status status) { }
+
+
public:
- SimpleTimingPort(std::string pname)
- : Port(pname), outTiming(0), drainEvent(NULL)
+ SimpleTimingPort(std::string pname, MemObject *_owner = NULL)
+ : Port(pname, _owner),
+ sendEvent(new SendEvent(this)),
+ drainEvent(NULL),
+ waitingOnRetry(false)
{}
+ ~SimpleTimingPort() { delete sendEvent; }
+
+ /** 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);
-
- friend class SimpleTimingPort::SendEvent;
};
#endif // __MEM_TPORT_HH__