cpu: Stream/SubstreamID support in TrafficGen

This patch is adding support for generating memory requests which set
the StreamID/SubstreamID field, so that is possible to emulate devices
attached to an external IOMMU/SMMU with a Traffic generator.

Change-Id: Iea068de581ae7125a9d49314124a08c045c75b49
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/12188
diff --git a/src/cpu/testers/traffic_gen/BaseTrafficGen.py b/src/cpu/testers/traffic_gen/BaseTrafficGen.py
index 2569c1e..dbe0c84 100644
--- a/src/cpu/testers/traffic_gen/BaseTrafficGen.py
+++ b/src/cpu/testers/traffic_gen/BaseTrafficGen.py
@@ -41,6 +41,13 @@
 from m5.proxy import *
 from MemObject import MemObject
 
+# Types of Stream Generators.
+# Those are orthogonal to the other generators in the TrafficGen
+# and are meant to initialize the stream and substream IDs for
+# every memory request, regardless of how the packet has been
+# generated (Random, Linear, Trace etc)
+class StreamGenType(Enum): vals = [ 'none', 'fixed', 'random' ]
+
 # The traffic generator is a master module that generates stimuli for
 # the memory system, based on a collection of simple behaviours that
 # are either probabilistic or based on traces. It can be used stand
@@ -70,3 +77,11 @@
     # somewhat arbitrary and may well have to be tuned.
     progress_check = Param.Latency('1ms', "Time before exiting " \
                                    "due to lack of progress")
+
+    # Generator type used for applying Stream and/or Substream IDs to requests
+    stream_gen = Param.StreamGenType('none',
+        "Generator for adding Stream and/or Substream ID's to requests")
+
+    # Sources for Stream/Substream IDs to apply to requests
+    sids = VectorParam.Unsigned([], "StreamIDs to use")
+    ssids = VectorParam.Unsigned([], "SubstreamIDs to use")
diff --git a/src/cpu/testers/traffic_gen/SConscript b/src/cpu/testers/traffic_gen/SConscript
index fc99d6e..700676a 100644
--- a/src/cpu/testers/traffic_gen/SConscript
+++ b/src/cpu/testers/traffic_gen/SConscript
@@ -48,6 +48,7 @@
 Source('idle_gen.cc')
 Source('linear_gen.cc')
 Source('random_gen.cc')
+Source('stream_gen.cc')
 
 DebugFlag('TrafficGen')
 SimObject('BaseTrafficGen.py')
diff --git a/src/cpu/testers/traffic_gen/base.cc b/src/cpu/testers/traffic_gen/base.cc
index 6778e41..ad4f67d 100644
--- a/src/cpu/testers/traffic_gen/base.cc
+++ b/src/cpu/testers/traffic_gen/base.cc
@@ -52,6 +52,7 @@
 #include "cpu/testers/traffic_gen/idle_gen.hh"
 #include "cpu/testers/traffic_gen/linear_gen.hh"
 #include "cpu/testers/traffic_gen/random_gen.hh"
+#include "cpu/testers/traffic_gen/stream_gen.hh"
 #include "debug/Checkpoint.hh"
 #include "debug/TrafficGen.hh"
 #include "params/BaseTrafficGen.hh"
@@ -78,7 +79,12 @@
       retryPkt(NULL),
       retryPktTick(0),
       updateEvent([this]{ update(); }, name()),
-      masterID(system->getMasterId(this))
+      masterID(system->getMasterId(this)),
+      streamGenerator(StreamGen::create(p))
+{
+}
+
+BaseTrafficGen::~BaseTrafficGen()
 {
 }
 
@@ -172,6 +178,19 @@
         // get the next packet and try to send it
         PacketPtr pkt = activeGenerator->getNextPacket();
 
+        // If generating stream/substream IDs are enabled,
+        // try to pick and assign them to the new packet
+        if (streamGenerator) {
+            auto sid = streamGenerator->pickStreamID();
+            auto ssid = streamGenerator->pickSubStreamID();
+
+            pkt->req->setStreamId(sid);
+
+            if (streamGenerator->ssidValid()) {
+                pkt->req->setSubStreamId(ssid);
+            }
+        }
+
         // suppress packets that are not destined for a memory, such as
         // device accesses that could be part of a trace
         if (pkt && system->isMemAddr(pkt->getAddr())) {
diff --git a/src/cpu/testers/traffic_gen/base.hh b/src/cpu/testers/traffic_gen/base.hh
index fe4229f..272dcb5 100644
--- a/src/cpu/testers/traffic_gen/base.hh
+++ b/src/cpu/testers/traffic_gen/base.hh
@@ -50,6 +50,7 @@
 #include "mem/qport.hh"
 
 class BaseGen;
+class StreamGen;
 class System;
 struct BaseTrafficGenParams;
 
@@ -179,7 +180,7 @@
   public:
     BaseTrafficGen(const BaseTrafficGenParams* p);
 
-    ~BaseTrafficGen() {}
+    ~BaseTrafficGen();
 
     BaseMasterPort& getMasterPort(const std::string &if_name,
                                   PortID idx = InvalidPortID) override;
@@ -247,6 +248,9 @@
 
     /** Currently active generator */
     std::shared_ptr<BaseGen> activeGenerator;
+
+    /** Stream/SubStreamID Generator */
+    std::unique_ptr<StreamGen> streamGenerator;
 };
 
 #endif //__CPU_TRAFFIC_GEN_BASE_HH__
diff --git a/src/cpu/testers/traffic_gen/stream_gen.cc b/src/cpu/testers/traffic_gen/stream_gen.cc
new file mode 100644
index 0000000..496d402
--- /dev/null
+++ b/src/cpu/testers/traffic_gen/stream_gen.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018 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 here under.  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: Giacomo Travaglini
+ */
+
+#include "stream_gen.hh"
+
+#include "base/random.hh"
+
+StreamGen*
+StreamGen::create(const BaseTrafficGenParams *p)
+{
+    switch (p->stream_gen) {
+      case Enums::fixed:
+        return new FixedStreamGen(p);
+      case Enums::random:
+        return new RandomStreamGen(p);
+      case Enums::none:
+      default:
+        return nullptr;
+    }
+}
+
+uint32_t
+RandomStreamGen::randomPick(const std::vector<uint32_t> &svec)
+{
+    // Pick a random entry in the vector of IDs
+    return svec[random_mt.random<size_t>(0, svec.size()-1)];
+}
diff --git a/src/cpu/testers/traffic_gen/stream_gen.hh b/src/cpu/testers/traffic_gen/stream_gen.hh
new file mode 100644
index 0000000..df9d7b7
--- /dev/null
+++ b/src/cpu/testers/traffic_gen/stream_gen.hh
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018 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 here under.  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: Giacomo Travaglini
+ */
+
+/**
+ * @file
+ * Declaration of the Stream generator for issuing memory requests
+ * with variable/fixed stream and substream IDs.
+ */
+
+#ifndef __CPU_TRAFFIC_GEN_STREAM_GEN_HH__
+#define __CPU_TRAFFIC_GEN_STREAM_GEN_HH__
+
+#include "params/BaseTrafficGen.hh"
+
+class StreamGen
+{
+  protected:
+    StreamGen(const BaseTrafficGenParams *p)
+      : streamIds(p->sids), substreamIds(p->ssids)
+    {
+        // A non empty vector of StreamIDs must be provided.
+        // SubstreamIDs are not mandatory hence having an empty
+        // vector means that they are not used and no configuration
+        // error must be thrown
+        fatal_if(streamIds.empty(),
+            "Must provide a vector of StreamIDs");
+    }
+
+  public:
+    virtual uint32_t pickStreamID() = 0;
+    virtual uint32_t pickSubStreamID() = 0;
+
+    /**
+     * Factory method for constructing a Stream generator.
+     * The Stream generator type is selected by the
+     * StreamGenType enum parameter.
+     *
+     * @params p pointer to BaseTrafficGenParams struct where
+     *           the stream generator type is stored.
+     * @return a pointer to the newly alocated StremGen
+     */
+    static StreamGen* create(const BaseTrafficGenParams *p);
+
+    /**
+     * Returns true if the substreamID generation is valid
+     * and hence should be taken into account.
+     * It is valid if the set of substreamIDs passed as a
+     * parameter to the TrafficGenerator is a non empty list.
+     *
+     * @return true if ssid is valid, false otherwise
+     */
+    bool ssidValid() const { return !substreamIds.empty(); }
+
+  protected:
+    /**
+     * Store preset Stream and Substream IDs to use for requests
+     * This is the set of available streamIDs the generator can
+     * pick. The actual ID being picked for a specific memory
+     * request is selected by the pickStreamID and pickSubStreamID
+     * methods.
+     */
+    std::vector<uint32_t> streamIds;
+    std::vector<uint32_t> substreamIds;
+};
+
+class FixedStreamGen : public StreamGen
+{
+  public:
+    FixedStreamGen(const BaseTrafficGenParams *p)
+      : StreamGen(p)
+    {
+        // For a fixed stream generator only one sid must be provided. The
+        // ssid can have either 0 (not used) or 1 value.
+        fatal_if(streamIds.size() != 1 || substreamIds.size() > 1,
+                 "Invalid sids/ssids configuration");
+    }
+
+    uint32_t pickStreamID() override
+    { return streamIds[0]; }
+
+    uint32_t pickSubStreamID() override
+    { return substreamIds[0]; }
+};
+
+class RandomStreamGen : public StreamGen
+{
+  public:
+    RandomStreamGen(const BaseTrafficGenParams *p)
+      : StreamGen(p)
+    {}
+
+    uint32_t pickStreamID() override
+    { return randomPick(streamIds); }
+
+    uint32_t pickSubStreamID() override
+    { return randomPick(substreamIds); }
+
+  protected:
+    /** Function to pick one of the preset Stream or Substream ID */
+    uint32_t randomPick(const std::vector<uint32_t> &svec);
+};
+
+#endif // __CPU_TRAFFIC_GEN_STREAM_GEN_HH__