--- /dev/null
+# -*- mode:python -*-
+
+# 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
+
+Import('*')
+
+SimObject('TrafficGen.py')
+
+Source('traffic_gen.cc')
+
+DebugFlag('TrafficGen')
--- /dev/null
+# 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: Thomas Grass
+# Andreas Hansson
+# Sascha Bischoff
+
+from m5.params import *
+from m5.proxy import *
+from MemObject import MemObject
+
+# 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
+# alone for creating test cases for interconnect and memory
+# controllers, or function as a black-box replacement for system
+# components that are not yet modelled in detail, e.g. a video engine
+# or baseband subsystem in an SoC.
+#
+# The traffic generator has a single master port that is used to send
+# requests, independent of the specific behaviour. The behaviour of
+# the traffic generator is specified in a configuration file, and this
+# file describes a state transition graph where each state is a
+# specific generator behaviour. Examples include idling, generating
+# linear address sequences, random sequences and replay of captured
+# traces. By describing these behaviours as states, it is straight
+# forward to create very complex behaviours, simply by arranging them
+# in graphs. The graph transitions can also be annotated with
+# probabilities, effectively making it a Markov Chain.
+class TrafficGen(MemObject):
+ type = 'TrafficGen'
+
+ # Port used for sending requests and receiving responses
+ port = MasterPort("Master port")
+
+ # Config file to parse for the state descriptions
+ config_file = Param.String("Configuration file describing the behaviour")
+
+ # System used to determine the mode of the memory system
+ system = Param.System(Parent.any, "System this generator is part of")
--- /dev/null
+/*
+ * 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: Thomas Grass
+ * Andreas Hansson
+ * Sascha Bischoff
+ */
+
+#include <sstream>
+
+#include "base/random.hh"
+#include "cpu/testers/traffic_gen/traffic_gen.hh"
+#include "debug/Checkpoint.hh"
+#include "debug/TrafficGen.hh"
+#include "sim/stats.hh"
+#include "sim/system.hh"
+
+using namespace std;
+
+TrafficGen::TrafficGen(const TrafficGenParams* p)
+ : MemObject(p),
+ system(p->system),
+ masterID(system->getMasterId(name())),
+ port(name() + ".port", *this),
+ stateGraph(*this, port, p->config_file, masterID),
+ updateStateGraphEvent(this)
+{
+}
+
+TrafficGen*
+TrafficGenParams::create()
+{
+ return new TrafficGen(this);
+}
+
+MasterPort&
+TrafficGen::getMasterPort(const string& if_name, int idx)
+{
+ if (if_name == "port") {
+ return port;
+ } else {
+ return MemObject::getMasterPort(if_name, idx);
+ }
+}
+
+void
+TrafficGen::init()
+{
+ if (!port.isConnected())
+ fatal("The port of %s is not connected!\n", name());
+
+ Enums::MemoryMode mode = system->getMemoryMode();
+
+ // if the system is in timing mode active the request generator
+ if (mode == Enums::timing) {
+ DPRINTF(TrafficGen, "Timing mode, activating request generator\n");
+
+ // enter initial state
+ stateGraph.enterState(stateGraph.currState);
+ } else {
+ DPRINTF(TrafficGen,
+ "Traffic generator is only active in timing mode\n");
+ }
+}
+
+void
+TrafficGen::initState()
+{
+ // when not restoring from a checkpoint, make sure we kick things off
+ if (system->getMemoryMode() == Enums::timing) {
+ Tick nextStateGraphEvent = stateGraph.nextEventTick();
+ schedule(updateStateGraphEvent, nextStateGraphEvent);
+ } else {
+ DPRINTF(TrafficGen,
+ "Traffic generator is only active in timing mode\n");
+ }
+}
+
+unsigned int
+TrafficGen::drain(Event* drain_event)
+{
+ // @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(drain_event);
+}
+
+void
+TrafficGen::serialize(ostream &os)
+{
+ DPRINTF(Checkpoint, "Serializing TrafficGen\n");
+
+ // save ticks of the graph event if it is scheduled
+ Tick nextStateGraphEvent = updateStateGraphEvent.scheduled() ?
+ updateStateGraphEvent.when() : 0;
+
+ DPRINTF(TrafficGen, "Saving nextStateGraphEvent=%llu\n",
+ nextStateGraphEvent);
+
+ SERIALIZE_SCALAR(nextStateGraphEvent);
+
+ Tick nextTransitionTick = stateGraph.nextTransitionTick;
+ SERIALIZE_SCALAR(nextTransitionTick);
+
+ // @todo: also serialise the current state, figure out the best
+ // way to drain and restore
+}
+
+void
+TrafficGen::unserialize(Checkpoint* cp, const string& section)
+{
+ // restore scheduled events
+ Tick nextStateGraphEvent;
+ UNSERIALIZE_SCALAR(nextStateGraphEvent);
+ if (nextStateGraphEvent != 0) {
+ schedule(updateStateGraphEvent, nextStateGraphEvent);
+ }
+
+ Tick nextTransitionTick;
+ UNSERIALIZE_SCALAR(nextTransitionTick);
+ stateGraph.nextTransitionTick = nextTransitionTick;
+}
+
+void
+TrafficGen::updateStateGraph()
+{
+ // schedule next update event based on either the next execute
+ // tick or the next transition, which ever comes first
+ Tick nextStateGraphEvent = stateGraph.nextEventTick();
+ DPRINTF(TrafficGen, "Updating state graph, next event at %lld\n",
+ nextStateGraphEvent);
+ schedule(updateStateGraphEvent, nextStateGraphEvent);
+
+ // perform the update associated with the current update event
+ stateGraph.update();
+}
+
+void
+TrafficGen::StateGraph::parseConfig(const string& file_name,
+ MasterID master_id)
+{
+ // keep track of the transitions parsed to create the matrix when
+ // done
+ vector<Transition> transitions;
+
+ // open input file
+ ifstream infile;
+ infile.open(file_name.c_str(), ifstream::in);
+ if (!infile.is_open()) {
+ fatal("Traffic generator %s config file not found at %s\n",
+ owner.name(), file_name);
+ }
+
+ // read line by line and determine the action based on the first
+ // keyword
+ string keyword;
+ string line;
+
+ while (getline(infile, line).good()) {
+ // see if this line is a comment line, and if so skip it
+ if (line.find('#') != 1) {
+ // create an input stream for the tokenization
+ istringstream is(line);
+
+ // determine the keyword
+ is >> keyword;
+
+ if (keyword == "STATE") {
+ // parse the behaviour of this state
+ uint32_t id;
+ Tick duration;
+ string mode;
+
+ is >> id >> duration >> mode;
+
+ if (mode == "TRACE") {
+ string traceFile;
+ Addr addrOffset;
+
+ is >> traceFile >> addrOffset;
+
+ states[id] = new TraceGen(port, master_id, duration,
+ traceFile, addrOffset);
+ DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
+ } else if (mode == "IDLE") {
+ states[id] = new IdleGen(port, master_id, duration);
+ DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
+ } else if (mode == "LINEAR" || mode == "RANDOM") {
+ uint32_t read_percent;
+ Addr start_addr;
+ Addr end_addr;
+ Addr blocksize;
+ Tick min_period;
+ Tick max_period;
+ Addr data_limit;
+
+ is >> read_percent >> start_addr >> end_addr >>
+ blocksize >> min_period >> max_period >> data_limit;
+
+ DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
+ " period %d to %d, %d%% reads\n",
+ mode, start_addr, end_addr, blocksize, min_period,
+ max_period, read_percent);
+
+ if (read_percent > 100)
+ panic("%s cannot have more than 100% reads", name());
+
+ if (mode == "LINEAR") {
+ states[id] = new LinearGen(port, master_id,
+ duration, start_addr,
+ end_addr, blocksize,
+ min_period, max_period,
+ read_percent, data_limit);
+ DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
+ } else if (mode == "RANDOM") {
+ states[id] = new RandomGen(port, master_id,
+ duration, start_addr,
+ end_addr, blocksize,
+ min_period, max_period,
+ read_percent, data_limit);
+ DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
+ }
+ } else {
+ fatal("%s: Unknown traffic generator mode: %s",
+ name(), mode);
+ }
+ } else if (keyword == "TRANSITION") {
+ Transition transition;
+
+ is >> transition.from >> transition.to >> transition.p;
+
+ transitions.push_back(transition);
+
+ DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
+ transition.to);
+ } else if (keyword == "INIT") {
+ // set the initial state as the active state
+ is >> currState;
+
+ DPRINTF(TrafficGen, "Initial state: %d\n", currState);
+ }
+ }
+ }
+
+ // resize and populate state transition matrix
+ transitionMatrix.resize(transitions.size());
+ for (size_t i = 0; i < transitions.size(); i++) {
+ transitionMatrix[i].resize(transitions.size());
+ }
+
+ for (vector<Transition>::iterator t = transitions.begin();
+ t != transitions.end(); ++t) {
+ transitionMatrix[t->from][t->to] = t->p;
+ }
+
+ // ensure the egress edges do not have a probability larger than
+ // one
+ for (size_t i = 0; i < transitions.size(); i++) {
+ double sum = 0;
+ for (size_t j = 0; j < transitions.size(); j++) {
+ sum += transitionMatrix[i][j];
+ }
+
+ // avoid comparing floating point numbers
+ if (abs(sum - 1.0) > 0.001)
+ fatal("%s has transition probability != 1 for state %d\n",
+ name(), i);
+ }
+
+ // close input file
+ infile.close();
+}
+
+void
+TrafficGen::StateGraph::update()
+{
+ // 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
+ states[currState]->execute();
+ }
+}
+
+void
+TrafficGen::StateGraph::transition()
+{
+ // exit the current state
+ states[currState]->exit();
+
+ // determine next state
+ double p = random_mt.gen_real1();
+ assert(currState < transitionMatrix.size());
+ double cumulative = transitionMatrix[currState][0];
+ size_t i = 1;
+ while (p < cumulative && i != transitionMatrix[currState].size()) {
+ cumulative += transitionMatrix[currState][i];
+ ++i;
+ }
+ enterState(i);
+}
+
+void
+TrafficGen::StateGraph::enterState(uint32_t newState)
+{
+ DPRINTF(TrafficGen, "Transition to state %d\n", newState);
+
+ currState = newState;
+ nextTransitionTick += states[currState]->duration;
+ states[currState]->enter();
+}
+
+TrafficGen::StateGraph::BaseGen::BaseGen(QueuedMasterPort& _port,
+ MasterID master_id,
+ Tick _duration)
+ : port(_port), masterID(master_id), duration(_duration)
+{
+}
+
+void
+TrafficGen::StateGraph::LinearGen::enter()
+{
+ // reset the address and the data counter
+ nextAddr = startAddr;
+ dataManipulated = 0;
+
+ // this test only needs to happen once, but cannot be performed
+ // before init() is called and the ports are connected
+ if (port.deviceBlockSize() && blocksize > port.deviceBlockSize())
+ fatal("TrafficGen %s block size (%d) is larger than port"
+ " block size (%d)\n", blocksize, port.deviceBlockSize());
+
+}
+
+void
+TrafficGen::StateGraph::LinearGen::execute()
+{
+ // choose if we generate a read or a write here
+ bool isRead = random_mt.random<uint8_t>(0, 100) < readPercent;
+
+ if (readPercent == 0)
+ assert(!isRead);
+
+ DPRINTF(TrafficGen, "LinearGen::execute: %c to addr %x, size %d\n",
+ isRead ? 'r' : 'w', nextAddr, blocksize);
+
+ // Create new request
+ Request::Flags flags;
+ Request *req = new Request(nextAddr, blocksize, flags, masterID);
+
+ PacketPtr pkt = new Packet(req, isRead ? MemCmd::ReadReq :
+ MemCmd::WriteReq);
+
+ uint8_t* pkt_data = new uint8_t[req->getSize()];
+ pkt->dataDynamicArray(pkt_data);
+
+ if (!isRead) {
+ memset(pkt_data, 0xA, req->getSize());
+ }
+
+ port.schedTimingReq(pkt, curTick());
+
+ // increment the address
+ nextAddr += blocksize;
+
+ // Add the amount of data manipulated to the total
+ dataManipulated += blocksize;
+}
+
+Tick
+TrafficGen::StateGraph::LinearGen::nextExecuteTick()
+{
+ // If we have reached the end of the address space, reset the
+ // address to the start of the range
+ if (nextAddr + blocksize > endAddr) {
+ DPRINTF(TrafficGen, "Wrapping address to the start of "
+ "the range\n");
+ nextAddr = startAddr;
+ }
+
+ // 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.
+ if (dataLimit && dataManipulated >= dataLimit) {
+ DPRINTF(TrafficGen, "Data limit for LinearGen reached.\n");
+ // there are no more requests, therefore return MaxTick
+ return MaxTick;
+ } else {
+ // return the time when the next request should take place
+ return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod);
+ }
+}
+
+void
+TrafficGen::StateGraph::RandomGen::enter()
+{
+ // reset the counter to zero
+ dataManipulated = 0;
+
+ // this test only needs to happen once, but cannot be performed
+ // before init() is called and the ports are connected
+ if (port.deviceBlockSize() && blocksize > port.deviceBlockSize())
+ fatal("TrafficGen %s block size (%d) is larger than port"
+ " block size (%d)\n", name(), blocksize, port.deviceBlockSize());
+}
+
+void
+TrafficGen::StateGraph::RandomGen::execute()
+{
+ // choose if we generate a read or a write here
+ bool isRead = random_mt.random<uint8_t>(0, 100) < readPercent;
+
+ if (readPercent == 0)
+ assert(!isRead);
+
+ // address of the request
+ Addr addr = random_mt.random<Addr>(startAddr, endAddr - 1);
+
+ // round down to start address of block
+ addr -= addr % blocksize;
+
+ DPRINTF(TrafficGen, "RandomGen::execute: %c to addr %x, size %d\n",
+ isRead ? 'r' : 'w', addr, blocksize);
+
+ // create new request packet
+ Request::Flags flags;
+ Request *req = new Request(addr, blocksize, flags, masterID);
+
+ PacketPtr pkt = new Packet(req, isRead ? MemCmd::ReadReq :
+ MemCmd::WriteReq);
+
+ uint8_t* pkt_data = new uint8_t[req->getSize()];
+ pkt->dataDynamicArray(pkt_data);
+
+ if (!isRead) {
+ memset(pkt_data, 0xA, req->getSize());
+ }
+
+ port.schedTimingReq(pkt, curTick());
+
+ // Add the amount of data manipulated to the total
+ dataManipulated += blocksize;
+}
+
+Tick
+TrafficGen::StateGraph::RandomGen::nextExecuteTick()
+{
+ // 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.
+ if (dataLimit && dataManipulated >= dataLimit)
+ {
+ DPRINTF(TrafficGen, "Data limit for RandomGen reached.\n");
+ // No more requests. Return MaxTick.
+ return MaxTick;
+ } else {
+ // Return the time when the next request should take place.
+ return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod);
+ }
+}
+
+Tick
+TrafficGen::StateGraph::TraceGen::nextExecuteTick() {
+ // 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.
+ string buffer;
+ if (!traceComplete && trace.good()){
+ getline(trace, buffer);
+ DPRINTF(TrafficGen, "Input trace: %s\n", buffer);
+ } else {
+ // 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();
+
+ // Check that we have something to process. This assume no EOF at
+ // the end of the line.
+ if (buffer.size() > 0 && !trace.eof()) {
+ istringstream iss(buffer);
+
+ char rOrW, ch;
+ iss >> rOrW;
+ iss >> ch; assert(ch == ',');
+ iss >> nextElement.addr;
+ iss >> ch; assert(ch == ',');
+ iss >> nextElement.blocksize;
+ iss >> ch; assert(ch == ',');
+ iss >> nextElement.tick;
+
+ if (rOrW == 'r') {
+ nextElement.cmd = MemCmd::ReadReq;
+ } else if (rOrW == 'w') {
+ nextElement.cmd = MemCmd::WriteReq;
+ } else {
+ fatal("Incorrect trace file format!\n");
+ }
+ }
+
+ // Check that we have a valid request
+ if (!nextElement.isValid()) {
+ // If it is not valid, assume that we have reached the end of
+ // the trace. Even if this is not the case, we do not know
+ // what to do with the request as it makes no sense.
+ if (trace.good()) {
+ // Trace is good, therefore we are not at the end of the
+ // file. This means that the input trace cannot be read
+ // correctly or it contains data that makes no sense.
+ warn("Unable to read the trace file format\n");
+ warn("%s", buffer);
+ }
+
+ 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,
+ nextElement.tick);
+
+ return tickOffset + nextElement.tick;
+}
+
+void
+TrafficGen::StateGraph::TraceGen::enter() {
+ // update the trace offset to the time where the state was entered.
+ tickOffset = curTick();
+
+ // seek to the start of the input trace file
+ trace.seekg(0, ifstream::beg);
+ trace.clear();
+
+ // clear everything
+ nextElement.clear();
+ currElement.clear();
+
+ traceComplete = false;
+}
+
+void
+TrafficGen::StateGraph::TraceGen::execute() {
+ // it is the responsibility of nextExecuteTick to prevent the
+ // state graph from executing the state if it should not
+ assert(currElement.isValid());
+
+ DPRINTF(TrafficGen, "TraceGen::execute: %c %d %d %d\n",
+ currElement.cmd.isRead() ? 'r' : 'w',
+ currElement.addr,
+ currElement.blocksize,
+ currElement.tick);
+
+ Request::Flags flags;
+ Request *req = new Request(currElement.addr + addrOffset,
+ currElement.blocksize, flags, masterID);
+
+ PacketPtr pkt = new Packet(req, currElement.cmd);
+
+ uint8_t* pkt_data = new uint8_t[req->getSize()];
+ pkt->dataDynamicArray(pkt_data);
+
+ if (currElement.cmd.isWrite()) {
+ memset(pkt_data, 0xA, req->getSize());
+ }
+
+ port.schedTimingReq(pkt, curTick());
+}
+
+void
+TrafficGen::StateGraph::TraceGen::exit() {
+ // Check if we reached the end of the trace file. If we did not
+ // then we want to generate a warning stating that not the entire
+ // trace was played.
+ if (!trace.eof()) {
+ warn("Trace player %s was unable to replay the entire trace!\n",
+ name());
+ }
+
+ // clear any previous error flags for the input trace file
+ trace.clear();
+}
+
+bool
+TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
+{
+ delete pkt->req;
+ delete pkt;
+
+ return true;
+}
--- /dev/null
+/*
+ * 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: Thomas Grass
+ * Andreas Hansson
+ * Sascha Bischoff
+ */
+#ifndef __MEM_TRAFFIC_GEN_HH__
+#define __MEM_TRAFFIC_GEN_HH__
+
+#include <fstream>
+
+#include "base/hashmap.hh"
+#include "mem/mem_object.hh"
+#include "mem/qport.hh"
+#include "params/TrafficGen.hh"
+
+/**
+ * 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
+ * alone for creating test cases for interconnect and memory
+ * controllers, or function as a black box replacement for system
+ * components that are not yet modelled in detail, e.g. a video engine
+ * or baseband subsystem.
+ */
+class TrafficGen : public MemObject
+{
+
+ private:
+
+ /**
+ * The system used to determine which mode we are currently operating
+ * in.
+ */
+ System* system;
+
+ /**
+ * MasterID used in generated requests.
+ */
+ MasterID masterID;
+
+ protected:
+
+ /**
+ * The state graph is responsible for instantiating and keeping
+ * track of the various generator states and also perform the
+ * transitions and call the appropriate functions when entering,
+ * executing and exiting a state.
+ */
+ class StateGraph
+ {
+
+ public:
+
+ /**
+ * Create a state graph from an input file.
+ *
+ * @param _owner used solely for the name
+ * @param _port port used to send requests
+ * @param file_name configuration description to read in
+ * @param master_id the unique id used for all requests
+ */
+ StateGraph(TrafficGen& _owner, QueuedMasterPort& _port,
+ const std::string& file_name, MasterID master_id)
+ : nextTransitionTick(0), owner(_owner), port(_port)
+ {
+ parseConfig(file_name, master_id);
+ }
+
+ /**
+ * Get the name, used for DPRINTFs.
+ *
+ * @return the owner's name
+ */
+ std::string name() const { return owner.name(); }
+
+ /**
+ * Either perform a state transition or execute the current
+ * state, depending on the current time.
+ */
+ void update();
+
+ /**
+ * Determine next state and perform the transition.
+ */
+ void transition();
+
+ /**
+ * Enter a new state.
+ *
+ * @param newState identifier of state to enter
+ */
+ void enterState(uint32_t newState);
+
+ /**
+ * Get the tick of the next event, either an execution or a
+ * transition.
+ *
+ * @return tick of the next state graph event
+ */
+ Tick nextEventTick()
+ {
+ return std::min(states[currState]->nextExecuteTick(),
+ nextTransitionTick);
+
+ }
+
+ /** Time of next transition */
+ Tick nextTransitionTick;
+
+ private:
+
+ /**
+ * Parse the config file and build the state map and
+ * transition matrix.
+ *
+ * @param file_name Config file name to parse
+ * @param master_id MasterID to use for generated requests
+ */
+ void parseConfig(const std::string& file_name, MasterID master_id);
+
+ /** Struct to represent a probabilistic transition during parsing. */
+ struct Transition {
+ uint32_t from;
+ uint32_t to;
+ double p;
+ };
+
+ /** Base class for all generator states */
+ class BaseGen
+ {
+
+ protected:
+
+ /** Port used to send requests */
+ QueuedMasterPort& port;
+
+ /** The MasterID used for generating requests */
+ const MasterID masterID;
+
+ public:
+
+ /** Time to spend in this state */
+ const Tick duration;
+
+ /**
+ * Create a base generator.
+ *
+ * @param _port port used to send requests
+ * @param master_id MasterID set on each request
+ * @param _duration duration of this state before transitioning
+ */
+ BaseGen(QueuedMasterPort& _port, MasterID master_id,
+ Tick _duration);
+
+ virtual ~BaseGen() { }
+
+ /**
+ * Get the name, useful for DPRINTFs.
+ *
+ * @return the port name
+ */
+ std::string name() const { return port.name(); }
+
+ /**
+ * Enter this generator state.
+ */
+ virtual void enter() = 0;
+
+ /**
+ * Execute this generator state.
+ */
+ virtual void execute() = 0;
+
+ /**
+ * Exit this generator state. By default do nothing.
+ */
+ virtual void exit() { };
+
+ /**
+ * Determine the next execute tick. MaxTick means that
+ * there will not be any further event in the current
+ * activation cycle of the state.
+ *
+ * @return next tick when the state should be executed
+ */
+ virtual Tick nextExecuteTick() = 0;
+
+ };
+
+ /**
+ * The idle generator does nothing.
+ */
+ class IdleGen : public BaseGen
+ {
+
+ public:
+
+ IdleGen(QueuedMasterPort& _port, MasterID master_id,
+ Tick _duration)
+ : BaseGen(_port, master_id, _duration)
+ { }
+
+ void enter() { }
+
+ void execute() { }
+
+ Tick nextExecuteTick() { return MaxTick; }
+ };
+
+ /**
+ * The linear generator generates sequential requests from a
+ * start to an end address, with a fixed block size. A
+ * fraction of the requests are reads, as determined by the
+ * read percent. There is an optional data limit for when to
+ * stop generating new requests.
+ */
+ class LinearGen : public BaseGen
+ {
+
+ public:
+
+ /**
+ * Create a linear address sequence generator. Set
+ * min_period == max_period for a fixed inter-transaction
+ * time.
+ *
+ * @param _port port used to send requests
+ * @param master_id MasterID set on each request
+ * @param _duration duration of this state before transitioning
+ * @param start_addr Start address
+ * @param end_addr End address
+ * @param _blocksize Size used for transactions injected
+ * @param min_period Lower limit of random inter-transaction time
+ * @param max_period Upper limit of random inter-transaction time
+ * @param read_percent Percent of transactions that are reads
+ * @param data_limit Upper limit on how much data to read/write
+ */
+ LinearGen(QueuedMasterPort& _port, MasterID master_id,
+ Tick _duration, Addr start_addr, Addr end_addr,
+ Addr _blocksize, Tick min_period, Tick max_period,
+ uint8_t read_percent, Addr data_limit)
+ : BaseGen(_port, master_id, _duration),
+ startAddr(start_addr), endAddr(end_addr),
+ blocksize(_blocksize), minPeriod(min_period),
+ maxPeriod(max_period), readPercent(read_percent),
+ dataLimit(data_limit)
+ { }
+
+ void enter();
+
+ void execute();
+
+ Tick nextExecuteTick();
+
+ private:
+
+ /** Start of address range */
+ const Addr startAddr;
+
+ /** End of address range */
+ const Addr endAddr;
+
+ /** Blocksize and address increment */
+ const Addr blocksize;
+
+ /** Request generation period */
+ const Tick minPeriod;
+ const Tick maxPeriod;
+
+ /**
+ * Percent of generated transactions that should be reads
+ */
+ const uint8_t readPercent;
+
+ /** Maximum amount of data to manipulate */
+ const Addr dataLimit;
+
+ /** Address of next request */
+ Addr nextAddr;
+
+ /**
+ * Counter to determine the amount of data
+ * manipulated. Used to determine if we should continue
+ * generating requests.
+ */
+ Addr dataManipulated;
+ };
+
+ /**
+ * The random generator is similar to the linear one, but does
+ * not generate sequential addresses. Instead it randomly
+ * picks an address in the range, aligned to the block size.
+ */
+ class RandomGen : public BaseGen
+ {
+
+ public:
+
+ /**
+ * Create a random address sequence generator. Set
+ * min_period == max_period for a fixed inter-transaction
+ * time.
+ *
+ * @param _port port used to send requests
+ * @param master_id MasterID set on each request
+ * @param _duration duration of this state before transitioning
+ * @param start_addr Start address
+ * @param end_addr End address
+ * @param _blocksize Size used for transactions injected
+ * @param min_period Lower limit of random inter-transaction time
+ * @param max_period Upper limit of random inter-transaction time
+ * @param read_percent Percent of transactions that are reads
+ * @param data_limit Upper limit on how much data to read/write
+ */
+ RandomGen(QueuedMasterPort& _port, MasterID master_id,
+ Tick _duration, Addr start_addr, Addr end_addr,
+ Addr _blocksize, Tick min_period, Tick max_period,
+ uint8_t read_percent, Addr data_limit)
+ : BaseGen(_port, master_id, _duration),
+ startAddr(start_addr), endAddr(end_addr),
+ blocksize(_blocksize), minPeriod(min_period),
+ maxPeriod(max_period), readPercent(read_percent),
+ dataLimit(data_limit)
+ { }
+
+ void enter();
+
+ void execute();
+
+ Tick nextExecuteTick();
+
+ private:
+
+ /** Start of address range */
+ const Addr startAddr;
+
+ /** End of address range */
+ const Addr endAddr;
+
+ /** Block size */
+ const Addr blocksize;
+
+ /** Request generation period */
+ const Tick minPeriod;
+ const Tick maxPeriod;
+
+ /**
+ * Percent of generated transactions that should be reads
+ */
+ const uint8_t readPercent;
+
+ /** Maximum amount of data to manipulate */
+ const Addr dataLimit;
+
+ /**
+ * Counter to determine the amount of data
+ * manipulated. Used to determine if we should continue
+ * generating requests.
+ */
+ Addr dataManipulated;
+ };
+
+ /**
+ * The trace replay generator reads a trace file and plays
+ * back the transactions. The trace is offset with respect to
+ * the time when the state was entered.
+ */
+ class TraceGen : public BaseGen
+ {
+
+ private:
+
+ /**
+ * This struct stores a line in the trace file.
+ */
+ struct TraceElement {
+
+ /** Specifies if the request is to be a read or a write */
+ MemCmd cmd;
+
+ /** The address for the request */
+ Addr addr;
+
+ /** The size of the access for the request */
+ Addr blocksize;
+
+ /** The time at which the request should be sent */
+ Tick tick;
+
+ /**
+ * Check validity of this element.
+ *
+ * @return if this element is valid
+ */
+ bool isValid() const {
+ return cmd != MemCmd::InvalidCmd;
+ }
+
+ /**
+ * Make this element invalid.
+ */
+ void clear() {
+ cmd = MemCmd::InvalidCmd;
+ }
+ };
+
+ public:
+
+ /**
+ * Create a trace generator.
+ *
+ * @param _port port used to send requests
+ * @param master_id MasterID set on each request
+ * @param _duration duration of this state before transitioning
+ * @param trace_file File to read the transactions from
+ * @param addr_offset Positive offset to add to trace address
+ */
+ TraceGen(QueuedMasterPort& _port, MasterID master_id,
+ Tick _duration, const std::string& trace_file,
+ Addr addr_offset)
+ : BaseGen(_port, master_id, _duration),
+ traceFile(trace_file),
+ addrOffset(addr_offset),
+ traceComplete(false)
+ {
+ /**
+ * Create a 4MB read buffer for the input trace
+ * file. This is to reduce the number of disk accesses
+ * and thereby speed up the execution of the code.
+ */
+ readBuffer = new char[4 * 1024 * 1024];
+ trace.rdbuf()->pubsetbuf(readBuffer, 4 * 1024 * 1024);
+ trace.open(traceFile.c_str(), std::ifstream::in);
+
+ if (!trace.is_open()) {
+ fatal("Traffic generator %s trace file could not be"
+ " opened: %s\n", name(), traceFile);
+ }
+ }
+
+ ~TraceGen() {
+ // free the memory used by the readBuffer
+ delete[] readBuffer;
+ }
+
+ void enter();
+
+ void execute();
+
+ 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
+ * indicate that there will be no more requests.
+ */
+ Tick nextExecuteTick();
+
+ private:
+
+ /** Path to the trace file */
+ std::string traceFile;
+
+ /** Input stream used for reading the input trace file */
+ std::ifstream trace;
+
+ /** Larger buffer used for reading from the stream */
+ char* readBuffer;
+
+ /** Store the current and next element in the trace */
+ TraceElement currElement;
+ TraceElement nextElement;
+
+ /**
+ * Stores the time when the state was entered. This is to add an
+ * offset to the times stored in the trace file.
+ */
+ Tick tickOffset;
+
+ /**
+ * Offset for memory requests. Used to shift the trace
+ * away from the CPU address space.
+ */
+ Addr addrOffset;
+
+ /**
+ * Set to true when the trace replay for one instance of
+ * state is complete.
+ */
+ bool traceComplete;
+
+ /**
+ * Used to store the Tick when the next generate should
+ * occur. It is to remove a transaction as soon as we
+ * enter the state.
+ */
+ Tick oldEmitTime;
+ };
+
+ /** Pointer to owner of request handler */
+ TrafficGen& owner;
+
+ /** Pointer to request handler */
+ QueuedMasterPort& port;
+
+ /** State transition matrix */
+ std::vector<std::vector<double> > transitionMatrix;
+
+ public:
+
+ /** Index of the current state */
+ uint32_t currState;
+
+ /** Map of states */
+ m5::hash_map<uint32_t, BaseGen*> states;
+ };
+
+
+ /** Queued handler */
+ class TrafficGenPort : public QueuedMasterPort
+ {
+ public:
+
+ TrafficGenPort(const std::string& name, TrafficGen& _owner)
+ : QueuedMasterPort(name, &_owner, queue), queue(_owner, *this),
+ owner(_owner)
+ { }
+
+ protected:
+
+ bool recvTimingResp(PacketPtr pkt);
+
+ private:
+
+ MasterPacketQueue queue;
+
+ // Owner of the port
+ TrafficGen& owner;
+
+ };
+
+ TrafficGenPort port;
+
+ /** Request generator state graph */
+ StateGraph stateGraph;
+
+ /**
+ * Schedules event for next update and executes an update on the
+ * state graph.
+ */
+ void updateStateGraph();
+
+ /** Event for updating the state graph */
+ EventWrapper<TrafficGen,
+ &TrafficGen::updateStateGraph> updateStateGraphEvent;
+
+
+ public:
+
+ TrafficGen(const TrafficGenParams* p);
+
+ ~TrafficGen() {}
+
+ virtual MasterPort& getMasterPort(const std::string &if_name,
+ int idx = InvalidPortID);
+
+ void init();
+
+ void initState();
+
+ unsigned int drain(Event *drain_event);
+
+ void serialize(std::ostream &os);
+
+ void unserialize(Checkpoint* cp, const std::string& section);
+
+};
+
+#endif //__MEM_TRAFFIC_GEN_HH__