cpu: Block traffic generator when requests have to retry
authorAndreas Hansson <andreas.hansson@arm.com>
Thu, 30 May 2013 16:54:05 +0000 (12:54 -0400)
committerAndreas Hansson <andreas.hansson@arm.com>
Thu, 30 May 2013 16:54:05 +0000 (12:54 -0400)
This patch changes the queued port for a conventional master port and
stalls the traffic generator when requests are not immediately
accepted. This is a first step to allowing elasticity in the injection
of requests.

The patch also adds stats for the sent packets and retries, and
slightly changes how the nextPacketTick and getNextPacket
interact. The advancing of the trace is now moved to getNextPacket and
nextPacketTick is only responsible for answering the question when the
next packet should be sent.

src/cpu/testers/traffic_gen/generators.cc
src/cpu/testers/traffic_gen/generators.hh
src/cpu/testers/traffic_gen/traffic_gen.cc
src/cpu/testers/traffic_gen/traffic_gen.hh

index 4d1812cd4f958f2e8d3c961f4dbf20e58fd1ff58..f9556b2b34d664e24302886be5ecfb699d92bd20 100644 (file)
@@ -100,20 +100,20 @@ LinearGen::getNextPacket()
     // increment the address
     nextAddr += blocksize;
 
-    return pkt;
-}
-
-Tick
-LinearGen::nextPacketTick()
-{
     // If we have reached the end of the address space, reset the
     // address to the start of the range
-    if (nextAddr + blocksize > endAddr) {
+    if (nextAddr > endAddr) {
         DPRINTF(TrafficGen, "Wrapping address to the start of "
                 "the range\n");
         nextAddr = startAddr;
     }
 
+    return pkt;
+}
+
+Tick
+LinearGen::nextPacketTick() const
+{
     // Check to see if we have reached the data limit. If dataLimit is
     // zero we do not have a data limit and therefore we will keep
     // generating requests for the entire residency in this state.
@@ -162,7 +162,7 @@ RandomGen::getNextPacket()
 }
 
 Tick
-RandomGen::nextPacketTick()
+RandomGen::nextPacketTick() const
 {
     // Check to see if we have reached the data limit. If dataLimit is
     // zero we do not have a data limit and therefore we will keep
@@ -217,38 +217,19 @@ TraceGen::InputStream::read(TraceElement& element)
 }
 
 Tick
-TraceGen::nextPacketTick() {
-    if (traceComplete)
+TraceGen::nextPacketTick() const
+{
+    if (traceComplete) {
+        DPRINTF(TrafficGen, "No next tick as trace is finished\n");
         // We are at the end of the file, thus we have no more data in
         // the trace Return MaxTick to signal that there will be no
         // more transactions in this active period for the state.
         return MaxTick;
-
-
-    //Reset the nextElement to the default values
-    currElement = nextElement;
-    nextElement.clear();
-
-    // We need to look at the next line to calculate the next time an
-    // event occurs, or potentially return MaxTick to signal that
-    // nothing has to be done.
-    if (!trace.read(nextElement)) {
-        traceComplete = true;
-        return MaxTick;
     }
 
-    DPRINTF(TrafficGen, "currElement: %c addr %d size %d tick %d (%d)\n",
-            currElement.cmd.isRead() ? 'r' : 'w',
-            currElement.addr,
-            currElement.blocksize,
-            currElement.tick + tickOffset,
-            currElement.tick);
-
-    DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n",
-            nextElement.cmd.isRead() ? 'r' : 'w',
-            nextElement.addr,
-            nextElement.blocksize,
-            nextElement.tick + tickOffset,
+    assert(nextElement.isValid());
+
+    DPRINTF(TrafficGen, "Next packet tick is %d\n", tickOffset +
             nextElement.tick);
 
     return tickOffset + nextElement.tick;
@@ -261,17 +242,24 @@ TraceGen::enter()
     tickOffset = curTick();
 
     // clear everything
-    nextElement.clear();
     currElement.clear();
 
-    traceComplete = false;
+    // read the first element in the file and set the complete flag
+    traceComplete = !trace.read(nextElement);
 }
 
 PacketPtr
 TraceGen::getNextPacket()
 {
-    // it is the responsibility of nextPacketTick to prevent the
-    // state graph from executing the state if it should not
+    // shift things one step forward
+    currElement = nextElement;
+    nextElement.clear();
+
+    // read the next element and set the complete flag
+    traceComplete = !trace.read(nextElement);
+
+    // it is the responsibility of the traceComplete flag to ensure we
+    // always have a valid element here
     assert(currElement.isValid());
 
     DPRINTF(TrafficGen, "TraceGen::getNextPacket: %c %d %d %d 0x%x\n",
@@ -281,8 +269,19 @@ TraceGen::getNextPacket()
             currElement.tick,
             currElement.flags);
 
-    return getPacket(currElement.addr + addrOffset, currElement.blocksize,
-                     currElement.cmd, currElement.flags);
+    PacketPtr pkt = getPacket(currElement.addr + addrOffset,
+                              currElement.blocksize,
+                              currElement.cmd, currElement.flags);
+
+    if (!traceComplete)
+        DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n",
+                nextElement.cmd.isRead() ? 'r' : 'w',
+                nextElement.addr,
+                nextElement.blocksize,
+                nextElement.tick + tickOffset,
+                nextElement.tick);
+
+    return pkt;
 }
 
 void
index 5bcfc8e808ad160eac2346a30d8b0dcb04635695..2b86afa22dad295bdc6f75a59199232d7b34854c 100644 (file)
@@ -126,7 +126,7 @@ class BaseGen
      *
      * @return next tick when a packet is available
      */
-    virtual Tick nextPacketTick() = 0;
+    virtual Tick nextPacketTick() const = 0;
 
 };
 
@@ -146,7 +146,7 @@ class IdleGen : public BaseGen
 
     PacketPtr getNextPacket() { return NULL; }
 
-    Tick nextPacketTick() { return MaxTick; }
+    Tick nextPacketTick() const { return MaxTick; }
 };
 
 /**
@@ -192,7 +192,7 @@ class LinearGen : public BaseGen
 
     PacketPtr getNextPacket();
 
-    Tick nextPacketTick();
+    Tick nextPacketTick() const;
 
   private:
 
@@ -269,7 +269,7 @@ class RandomGen : public BaseGen
 
     PacketPtr getNextPacket();
 
-    Tick nextPacketTick();
+    Tick nextPacketTick() const;
 
   private:
 
@@ -415,12 +415,11 @@ class TraceGen : public BaseGen
     void exit();
 
     /**
-     * Read a line of the trace file. Returns the raw tick
-     * when the next request should be generated. If the end
-     * of the file has been reached, it returns MaxTick to
+     * Returns the tick when the next request should be generated. If
+     * the end of the file has been reached, it returns MaxTick to
      * indicate that there will be no more requests.
      */
-    Tick nextPacketTick();
+    Tick nextPacketTick() const;
 
   private:
 
index ea1a74e968ba477247f569cf1c12466e8b088f33..8916dcb8d6c10016b9c7480cccff48112d3ac10a 100644 (file)
@@ -56,8 +56,12 @@ TrafficGen::TrafficGen(const TrafficGenParams* p)
       masterID(system->getMasterId(name())),
       configFile(p->config_file),
       nextTransitionTick(0),
+      nextPacketTick(0),
       port(name() + ".port", *this),
-      updateEvent(this)
+      retryPkt(NULL),
+      retryPktTick(0),
+      updateEvent(this),
+      drainManager(NULL)
 {
 }
 
@@ -102,7 +106,9 @@ TrafficGen::initState()
 {
     // when not restoring from a checkpoint, make sure we kick things off
     if (system->isTimingMode()) {
-        schedule(updateEvent, nextEventTick());
+        // call nextPacketTick on the state to advance it
+        nextPacketTick = states[currState]->nextPacketTick();
+        schedule(updateEvent, std::min(nextPacketTick, nextTransitionTick));
     } else {
         DPRINTF(TrafficGen,
                 "Traffic generator is only active in timing mode\n");
@@ -112,9 +118,16 @@ TrafficGen::initState()
 unsigned int
 TrafficGen::drain(DrainManager *dm)
 {
-    // @todo we should also stop putting new requests in the queue and
-    // either interrupt the current state or wait for a transition
-    return port.drain(dm);
+    if (retryPkt == NULL) {
+        // shut things down
+        nextPacketTick = MaxTick;
+        nextTransitionTick = MaxTick;
+        deschedule(updateEvent);
+        return 0;
+    } else {
+        drainManager = dm;
+        return 1;
+    }
 }
 
 void
@@ -123,18 +136,17 @@ TrafficGen::serialize(ostream &os)
     DPRINTF(Checkpoint, "Serializing TrafficGen\n");
 
     // save ticks of the graph event if it is scheduled
-    Tick nextEvent = updateEvent.scheduled() ?
-        updateEvent.when() : 0;
+    Tick nextEvent = updateEvent.scheduled() ? updateEvent.when() : 0;
 
-    DPRINTF(TrafficGen, "Saving nextEvent=%llu\n",
-            nextEvent);
+    DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", nextEvent);
 
     SERIALIZE_SCALAR(nextEvent);
 
     SERIALIZE_SCALAR(nextTransitionTick);
 
-    // @todo: also serialise the current state, figure out the best
-    // way to drain and restore
+    SERIALIZE_SCALAR(nextPacketTick);
+
+    SERIALIZE_SCALAR(currState);
 }
 
 void
@@ -148,28 +160,43 @@ TrafficGen::unserialize(Checkpoint* cp, const string& section)
     }
 
     UNSERIALIZE_SCALAR(nextTransitionTick);
+
+    UNSERIALIZE_SCALAR(nextPacketTick);
+
+    // @todo In the case of a stateful generator state such as the
+    // trace player we would also have to restore the position in the
+    // trace playback
+    UNSERIALIZE_SCALAR(currState);
 }
 
 void
 TrafficGen::update()
 {
-    // schedule next update event based on either the next execute
-    // tick or the next transition, which ever comes first
-    Tick nextEvent = nextEventTick();
-    DPRINTF(TrafficGen, "Updating state graph, next event at %lld\n",
-            nextEvent);
-    schedule(updateEvent, nextEvent);
-
-    // perform the update associated with the current update event
-
     // if we have reached the time for the next state transition, then
     // perform the transition
     if (curTick() >= nextTransitionTick) {
         transition();
     } else {
-        // we are still in the current state and should execute it
+        assert(curTick() >= nextPacketTick);
+        // get the next packet and try to send it
         PacketPtr pkt = states[currState]->getNextPacket();
-        port.schedTimingReq(pkt, curTick());
+        numPackets++;
+        if (!port.sendTimingReq(pkt)) {
+            retryPkt = pkt;
+            retryPktTick = curTick();
+        }
+    }
+
+    // if we are waiting for a retry, do not schedule any further
+    // events, in the case of a transition or a successful send, go
+    // ahead and determine when the next update should take place
+    if (retryPkt == NULL) {
+        // schedule next update event based on either the next execute
+        // tick or the next transition, which ever comes first
+        nextPacketTick = states[currState]->nextPacketTick();
+        Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
+        DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick);
+        schedule(updateEvent, nextEventTick);
     }
 }
 
@@ -340,10 +367,65 @@ TrafficGen::enterState(uint32_t newState)
     DPRINTF(TrafficGen, "Transition to state %d\n", newState);
 
     currState = newState;
-    nextTransitionTick += states[currState]->duration;
+    // we could have been delayed and not transitioned on the exact
+    // tick when we were supposed to (due to back pressure when
+    // sending a packet)
+    nextTransitionTick = curTick() + states[currState]->duration;
     states[currState]->enter();
 }
 
+void
+TrafficGen::recvRetry()
+{
+    assert(retryPkt != NULL);
+
+    DPRINTF(TrafficGen, "Received retry\n");
+    numRetries++;
+    // attempt to send the packet, and if we are successful start up
+    // the machinery again
+    if (port.sendTimingReq(retryPkt)) {
+        retryPkt = NULL;
+        // remember how much delay was incurred due to back-pressure
+        // when sending the request
+        Tick delay = curTick() - retryPktTick;
+        retryPktTick = 0;
+        retryTicks += delay;
+
+        if (drainManager == NULL) {
+            // packet is sent, so find out when the next one is due
+            nextPacketTick = states[currState]->nextPacketTick();
+            Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
+            schedule(updateEvent, std::max(curTick(), nextEventTick));
+        } else {
+            // shut things down
+            nextPacketTick = MaxTick;
+            nextTransitionTick = MaxTick;
+            drainManager->signalDrainDone();
+            // Clear the drain event once we're done with it.
+            drainManager = NULL;
+        }
+    }
+}
+
+void
+TrafficGen::regStats()
+{
+    // Initialise all the stats
+    using namespace Stats;
+
+    numPackets
+        .name(name() + ".numPackets")
+        .desc("Number of packets generated");
+
+    numRetries
+        .name(name() + ".numRetries")
+        .desc("Number of retries");
+
+    retryTicks
+        .name(name() + ".retryTicks")
+        .desc("Time spent waiting due to back-pressure (ticks)");
+}
+
 bool
 TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
 {
index c013109b7064f9e0f43a9f2a0bd21104a225eb5e..0adcf781ed3a439249d4d4b128a226c9ae94d5db 100644 (file)
@@ -42,6 +42,7 @@
 #define __CPU_TRAFFIC_GEN_TRAFFIC_GEN_HH__
 
 #include "base/hashmap.hh"
+#include "base/statistics.hh"
 #include "cpu/testers/traffic_gen/generators.hh"
 #include "mem/mem_object.hh"
 #include "mem/qport.hh"
@@ -73,18 +74,6 @@ class TrafficGen : public MemObject
      */
     void enterState(uint32_t newState);
 
-    /**
-     * Get the tick of the next event, either a new packet or a
-     * transition.
-     *
-     * @return tick of the next update event
-     */
-    Tick nextEventTick()
-    {
-        return std::min(states[currState]->nextPacketTick(),
-                        nextTransitionTick);
-    }
-
     /**
      * Parse the config file and build the state map and
      * transition matrix.
@@ -98,6 +87,12 @@ class TrafficGen : public MemObject
      */
     void update();
 
+    /**
+     * Receive a retry from the neighbouring port and attempt to
+     * resend the waiting packet.
+     */
+    void recvRetry();
+
     /** Struct to represent a probabilistic transition during parsing. */
     struct Transition {
         uint32_t from;
@@ -124,6 +119,9 @@ class TrafficGen : public MemObject
     /** Time of next transition */
     Tick nextTransitionTick;
 
+    /** Time of the next packet. */
+    Tick nextPacketTick;
+
     /** State transition matrix */
     std::vector<std::vector<double> > transitionMatrix;
 
@@ -133,31 +131,50 @@ class TrafficGen : public MemObject
     /** Map of generator states */
     m5::hash_map<uint32_t, BaseGen*> states;
 
-    /** Queued master port */
-    class TrafficGenPort : public QueuedMasterPort
+    /** Master port specialisation for the traffic generator */
+    class TrafficGenPort : public MasterPort
     {
       public:
 
-        TrafficGenPort(const std::string& name, TrafficGen& _owner)
-            : QueuedMasterPort(name, &_owner, queue), queue(_owner, *this)
+        TrafficGenPort(const std::string& name, TrafficGen& traffic_gen)
+            : MasterPort(name, &traffic_gen), trafficGen(traffic_gen)
         { }
 
       protected:
 
+        void recvRetry() { trafficGen.recvRetry(); }
+
         bool recvTimingResp(PacketPtr pkt);
 
       private:
 
-        MasterPacketQueue queue;
+        TrafficGen& trafficGen;
 
     };
 
     /** The instance of master port used by the traffic generator. */
     TrafficGenPort port;
 
+    /** Packet waiting to be sent. */
+    PacketPtr retryPkt;
+
+    /** Tick when the stalled packet was meant to be sent. */
+    Tick retryPktTick;
+
     /** Event for scheduling updates */
     EventWrapper<TrafficGen, &TrafficGen::update> updateEvent;
 
+    /** Manager to signal when drained */
+    DrainManager* drainManager;
+
+    /** Count the number of generated packets. */
+    Stats::Scalar numPackets;
+
+    /** Count the number of retries. */
+    Stats::Scalar numRetries;
+
+    /** Count the time incurred from back-pressure. */
+    Stats::Scalar retryTicks;
 
   public:
 
@@ -178,6 +195,9 @@ class TrafficGen : public MemObject
 
     void unserialize(Checkpoint* cp, const std::string& section);
 
+    /** Register statistics */
+    void regStats();
+
 };
 
 #endif //__CPU_TRAFFIC_GEN_TRAFFIC_GEN_HH__