cpu: Limit TrafficGen outstanding reqs

Parameter to limit the number of requests waiting for a response.

Change-Id: I6cf9e8782a06ae978fb66f7c4278f4c9e9980c79
Signed-off-by: Tiago Muck <tiago.muck@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/18417
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Jason Lowe-Power <jason@lowepower.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/cpu/testers/traffic_gen/BaseTrafficGen.py b/src/cpu/testers/traffic_gen/BaseTrafficGen.py
index 00fe087..5980bfd 100644
--- a/src/cpu/testers/traffic_gen/BaseTrafficGen.py
+++ b/src/cpu/testers/traffic_gen/BaseTrafficGen.py
@@ -72,6 +72,11 @@
     elastic_req = Param.Bool(False,
                              "Slow down requests in case of backpressure")
 
+    # Maximum number of requests waiting for response. Set to 0 for an
+    # unlimited number of outstanding requests.
+    max_outstanding_reqs = Param.Int(0,
+                            "Maximum number of outstanding requests")
+
     # Let the user know if we have waited for a retry and not made any
     # progress for a long period of time. The default value is
     # somewhat arbitrary and may well have to be tuned.
diff --git a/src/cpu/testers/traffic_gen/base.cc b/src/cpu/testers/traffic_gen/base.cc
index 43a1b83..f2385a4 100644
--- a/src/cpu/testers/traffic_gen/base.cc
+++ b/src/cpu/testers/traffic_gen/base.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2016-2018 ARM Limited
+ * Copyright (c) 2012-2013, 2016-2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -75,9 +75,10 @@
       noProgressEvent([this]{ noProgress(); }, name()),
       nextTransitionTick(0),
       nextPacketTick(0),
+      maxOutstandingReqs(p->max_outstanding_reqs),
       port(name() + ".port", *this),
       retryPkt(NULL),
-      retryPktTick(0),
+      retryPktTick(0), blockedWaitingResp(false),
       updateEvent([this]{ update(); }, name()),
       masterID(system->getMasterId(this)),
       streamGenerator(StreamGen::create(p))
@@ -195,7 +196,9 @@
         // device accesses that could be part of a trace
         if (pkt && system->isMemAddr(pkt->getAddr())) {
             numPackets++;
-            if (!port.sendTimingReq(pkt)) {
+            // Only attempts to send if not blocked by pending responses
+            blockedWaitingResp = allocateWaitingRespSlot(pkt);
+            if (blockedWaitingResp || !port.sendTimingReq(pkt)) {
                 retryPkt = pkt;
                 retryPktTick = curTick();
             }
@@ -213,8 +216,8 @@
         }
     }
 
-    // if we are waiting for a retry, do not schedule any further
-    // events, in the case of a transition or a successful send, go
+    // if we are waiting for a retry or for a response, 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) {
         nextPacketTick = activeGenerator->nextPacketTick(elasticReq, 0);
@@ -284,10 +287,18 @@
 void
 BaseTrafficGen::recvReqRetry()
 {
-    assert(retryPkt != NULL);
-
     DPRINTF(TrafficGen, "Received retry\n");
     numRetries++;
+    retryReq();
+}
+
+void
+BaseTrafficGen::retryReq()
+{
+    assert(retryPkt != NULL);
+    assert(retryPktTick != 0);
+    assert(!blockedWaitingResp);
+
     // attempt to send the packet, and if we are successful start up
     // the machinery again
     if (port.sendTimingReq(retryPkt)) {
@@ -449,9 +460,25 @@
 }
 
 bool
-BaseTrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
+BaseTrafficGen::recvTimingResp(PacketPtr pkt)
 {
+    auto iter = waitingResp.find(pkt->req);
+
+    panic_if(iter == waitingResp.end(), "%s: "
+            "Received unexpected response [%s reqPtr=%x]\n",
+               pkt->print(), pkt->req);
+
+    assert(iter->second <= curTick());
+
+    waitingResp.erase(iter);
+
     delete pkt;
 
+    // Sends up the request if we were blocked
+    if (blockedWaitingResp) {
+        blockedWaitingResp = false;
+        retryReq();
+    }
+
     return true;
 }
diff --git a/src/cpu/testers/traffic_gen/base.hh b/src/cpu/testers/traffic_gen/base.hh
index 811770f..5ffe508 100644
--- a/src/cpu/testers/traffic_gen/base.hh
+++ b/src/cpu/testers/traffic_gen/base.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2016-2018 ARM Limited
+ * Copyright (c) 2012-2013, 2016-2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -44,6 +44,7 @@
 
 #include <memory>
 #include <tuple>
+#include <unordered_map>
 
 #include "base/statistics.hh"
 #include "mem/qport.hh"
@@ -93,6 +94,10 @@
      */
     void recvReqRetry();
 
+    void retryReq();
+
+    bool recvTimingResp(PacketPtr pkt);
+
     /** Transition to the next generator */
     void transition();
 
@@ -118,6 +123,8 @@
     /** Time of the next packet. */
     Tick nextPacketTick;
 
+    const int maxOutstandingReqs;
+
 
     /** Master port specialisation for the traffic generator */
     class TrafficGenPort : public MasterPort
@@ -132,7 +139,8 @@
 
         void recvReqRetry() { trafficGen.recvReqRetry(); }
 
-        bool recvTimingResp(PacketPtr pkt);
+        bool recvTimingResp(PacketPtr pkt)
+        { return trafficGen.recvTimingResp(pkt); }
 
         void recvTimingSnoopReq(PacketPtr pkt) { }
 
@@ -161,6 +169,24 @@
     /** Tick when the stalled packet was meant to be sent. */
     Tick retryPktTick;
 
+    /** Set when we blocked waiting for outstanding reqs */
+    bool blockedWaitingResp;
+
+    /**
+     * Puts this packet in the waitingResp list and returns true if
+     * we are above the maximum number of oustanding requests.
+     */
+    bool allocateWaitingRespSlot(PacketPtr pkt)
+    {
+        assert(waitingResp.find(pkt->req) == waitingResp.end());
+        assert(pkt->needsResponse());
+
+        waitingResp[pkt->req] = curTick();
+
+        return (maxOutstandingReqs > 0) &&
+               (waitingResp.size() > maxOutstandingReqs);
+    }
+
     /** Event for scheduling updates */
     EventFunctionWrapper updateEvent;
 
@@ -177,6 +203,9 @@
     /** Count the time incurred from back-pressure. */
     Stats::Scalar retryTicks;
 
+    /** Reqs waiting for response **/
+    std::unordered_map<RequestPtr,Tick> waitingResp;
+
   public:
     BaseTrafficGen(const BaseTrafficGenParams* p);