diff options
-rw-r--r-- | src/cpu/testers/traffic_gen/SConscript | 46 | ||||
-rw-r--r-- | src/cpu/testers/traffic_gen/TrafficGen.py | 72 | ||||
-rw-r--r-- | src/cpu/testers/traffic_gen/traffic_gen.cc | 638 | ||||
-rw-r--r-- | src/cpu/testers/traffic_gen/traffic_gen.hh | 615 |
4 files changed, 1371 insertions, 0 deletions
diff --git a/src/cpu/testers/traffic_gen/SConscript b/src/cpu/testers/traffic_gen/SConscript new file mode 100644 index 000000000..63ce20984 --- /dev/null +++ b/src/cpu/testers/traffic_gen/SConscript @@ -0,0 +1,46 @@ +# -*- 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') diff --git a/src/cpu/testers/traffic_gen/TrafficGen.py b/src/cpu/testers/traffic_gen/TrafficGen.py new file mode 100644 index 000000000..15e9d7a9b --- /dev/null +++ b/src/cpu/testers/traffic_gen/TrafficGen.py @@ -0,0 +1,72 @@ +# 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") diff --git a/src/cpu/testers/traffic_gen/traffic_gen.cc b/src/cpu/testers/traffic_gen/traffic_gen.cc new file mode 100644 index 000000000..054900b20 --- /dev/null +++ b/src/cpu/testers/traffic_gen/traffic_gen.cc @@ -0,0 +1,638 @@ +/* + * 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; +} diff --git a/src/cpu/testers/traffic_gen/traffic_gen.hh b/src/cpu/testers/traffic_gen/traffic_gen.hh new file mode 100644 index 000000000..19182fa15 --- /dev/null +++ b/src/cpu/testers/traffic_gen/traffic_gen.hh @@ -0,0 +1,615 @@ +/* + * 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__ |