traffic_gen.cc revision 9524:d6ffa982a68b
1/*
2 * Copyright (c) 2012 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Thomas Grass
38 *          Andreas Hansson
39 *          Sascha Bischoff
40 */
41
42#include <sstream>
43
44#include "base/random.hh"
45#include "cpu/testers/traffic_gen/traffic_gen.hh"
46#include "debug/Checkpoint.hh"
47#include "debug/TrafficGen.hh"
48#include "proto/packet.pb.h"
49#include "sim/stats.hh"
50#include "sim/system.hh"
51
52using namespace std;
53
54TrafficGen::TrafficGen(const TrafficGenParams* p)
55    : MemObject(p),
56      system(p->system),
57      masterID(system->getMasterId(name())),
58      port(name() + ".port", *this),
59      stateGraph(*this, port, p->config_file, masterID),
60      updateStateGraphEvent(this)
61{
62}
63
64TrafficGen*
65TrafficGenParams::create()
66{
67    return new TrafficGen(this);
68}
69
70BaseMasterPort&
71TrafficGen::getMasterPort(const string& if_name, PortID idx)
72{
73    if (if_name == "port") {
74        return port;
75    } else {
76        return MemObject::getMasterPort(if_name, idx);
77    }
78}
79
80void
81TrafficGen::init()
82{
83    if (!port.isConnected())
84        fatal("The port of %s is not connected!\n", name());
85
86    // if the system is in timing mode active the request generator
87    if (system->isTimingMode()) {
88        DPRINTF(TrafficGen, "Timing mode, activating request generator\n");
89
90        // enter initial state
91        stateGraph.enterState(stateGraph.currState);
92    } else {
93        DPRINTF(TrafficGen,
94                "Traffic generator is only active in timing mode\n");
95    }
96}
97
98void
99TrafficGen::initState()
100{
101    // when not restoring from a checkpoint, make sure we kick things off
102    if (system->isTimingMode()) {
103        Tick nextStateGraphEvent = stateGraph.nextEventTick();
104        schedule(updateStateGraphEvent, nextStateGraphEvent);
105    } else {
106        DPRINTF(TrafficGen,
107                "Traffic generator is only active in timing mode\n");
108    }
109}
110
111unsigned int
112TrafficGen::drain(DrainManager *dm)
113{
114    // @todo we should also stop putting new requests in the queue and
115    // either interrupt the current state or wait for a transition
116    return port.drain(dm);
117}
118
119void
120TrafficGen::serialize(ostream &os)
121{
122    DPRINTF(Checkpoint, "Serializing TrafficGen\n");
123
124    // save ticks of the graph event if it is scheduled
125    Tick nextStateGraphEvent = updateStateGraphEvent.scheduled() ?
126        updateStateGraphEvent.when() : 0;
127
128    DPRINTF(TrafficGen, "Saving nextStateGraphEvent=%llu\n",
129            nextStateGraphEvent);
130
131    SERIALIZE_SCALAR(nextStateGraphEvent);
132
133    Tick nextTransitionTick = stateGraph.nextTransitionTick;
134    SERIALIZE_SCALAR(nextTransitionTick);
135
136    // @todo: also serialise the current state, figure out the best
137    // way to drain and restore
138}
139
140void
141TrafficGen::unserialize(Checkpoint* cp, const string& section)
142{
143    // restore scheduled events
144    Tick nextStateGraphEvent;
145    UNSERIALIZE_SCALAR(nextStateGraphEvent);
146    if (nextStateGraphEvent != 0) {
147        schedule(updateStateGraphEvent, nextStateGraphEvent);
148    }
149
150    Tick nextTransitionTick;
151    UNSERIALIZE_SCALAR(nextTransitionTick);
152    stateGraph.nextTransitionTick = nextTransitionTick;
153}
154
155void
156TrafficGen::updateStateGraph()
157{
158    // schedule next update event based on either the next execute
159    // tick or the next transition, which ever comes first
160    Tick nextStateGraphEvent = stateGraph.nextEventTick();
161    DPRINTF(TrafficGen, "Updating state graph, next event at %lld\n",
162            nextStateGraphEvent);
163    schedule(updateStateGraphEvent, nextStateGraphEvent);
164
165    // perform the update associated with the current update event
166    stateGraph.update();
167}
168
169void
170TrafficGen::StateGraph::parseConfig(const string& file_name,
171                                    MasterID master_id)
172{
173    // keep track of the transitions parsed to create the matrix when
174    // done
175    vector<Transition> transitions;
176
177    // open input file
178    ifstream infile;
179    infile.open(file_name.c_str(), ifstream::in);
180    if (!infile.is_open()) {
181        fatal("Traffic generator %s config file not found at %s\n",
182              owner.name(), file_name);
183    }
184
185    // read line by line and determine the action based on the first
186    // keyword
187    string keyword;
188    string line;
189
190    while (getline(infile, line).good()) {
191        // see if this line is a comment line, and if so skip it
192        if (line.find('#') != 1) {
193            // create an input stream for the tokenization
194            istringstream is(line);
195
196            // determine the keyword
197            is >> keyword;
198
199            if (keyword == "STATE") {
200                // parse the behaviour of this state
201                uint32_t id;
202                Tick duration;
203                string mode;
204
205                is >> id >> duration >> mode;
206
207                if (mode == "TRACE") {
208                    string traceFile;
209                    Addr addrOffset;
210
211                    is >> traceFile >> addrOffset;
212
213                    states[id] = new TraceGen(port, master_id, duration,
214                                              traceFile, addrOffset);
215                    DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
216                } else if (mode == "IDLE") {
217                    states[id] = new IdleGen(port, master_id, duration);
218                    DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
219                } else if (mode == "LINEAR" || mode == "RANDOM") {
220                    uint32_t read_percent;
221                    Addr start_addr;
222                    Addr end_addr;
223                    Addr blocksize;
224                    Tick min_period;
225                    Tick max_period;
226                    Addr data_limit;
227
228                    is >> read_percent >> start_addr >> end_addr >>
229                        blocksize >> min_period >> max_period >> data_limit;
230
231                    DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
232                            " period %d to %d, %d%% reads\n",
233                            mode, start_addr, end_addr, blocksize, min_period,
234                            max_period, read_percent);
235
236                    if (read_percent > 100)
237                        panic("%s cannot have more than 100% reads", name());
238
239                    if (mode == "LINEAR") {
240                        states[id] = new LinearGen(port, master_id,
241                                                   duration, start_addr,
242                                                   end_addr, blocksize,
243                                                   min_period, max_period,
244                                                   read_percent, data_limit);
245                        DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
246                    } else if (mode == "RANDOM") {
247                        states[id] = new RandomGen(port, master_id,
248                                                   duration, start_addr,
249                                                   end_addr, blocksize,
250                                                   min_period, max_period,
251                                                   read_percent, data_limit);
252                        DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
253                    }
254                } else {
255                    fatal("%s: Unknown traffic generator mode: %s",
256                          name(), mode);
257                }
258            } else if (keyword == "TRANSITION") {
259                Transition transition;
260
261                is >> transition.from >> transition.to >> transition.p;
262
263                transitions.push_back(transition);
264
265                DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
266                        transition.to);
267            } else if (keyword == "INIT") {
268                // set the initial state as the active state
269                is >> currState;
270
271                DPRINTF(TrafficGen, "Initial state: %d\n", currState);
272            }
273        }
274    }
275
276    // resize and populate state transition matrix
277    transitionMatrix.resize(transitions.size());
278    for (size_t i = 0; i < transitions.size(); i++) {
279        transitionMatrix[i].resize(transitions.size());
280    }
281
282    for (vector<Transition>::iterator t = transitions.begin();
283         t != transitions.end(); ++t) {
284        transitionMatrix[t->from][t->to] = t->p;
285    }
286
287    // ensure the egress edges do not have a probability larger than
288    // one
289    for (size_t i = 0; i < transitions.size(); i++) {
290        double sum = 0;
291        for (size_t j = 0; j < transitions.size(); j++) {
292            sum += transitionMatrix[i][j];
293        }
294
295        // avoid comparing floating point numbers
296        if (abs(sum - 1.0) > 0.001)
297            fatal("%s has transition probability != 1 for state %d\n",
298                  name(), i);
299    }
300
301    // close input file
302    infile.close();
303}
304
305void
306TrafficGen::StateGraph::update()
307{
308    // if we have reached the time for the next state transition, then
309    // perform the transition
310    if (curTick() >= nextTransitionTick) {
311        transition();
312    } else {
313        // we are still in the current state and should execute it
314        states[currState]->execute();
315    }
316}
317
318void
319TrafficGen::StateGraph::transition()
320{
321    // exit the current state
322    states[currState]->exit();
323
324    // determine next state
325    double p = random_mt.gen_real1();
326    assert(currState < transitionMatrix.size());
327    double cumulative = transitionMatrix[currState][0];
328    size_t i = 1;
329    while (p < cumulative && i != transitionMatrix[currState].size()) {
330        cumulative += transitionMatrix[currState][i];
331        ++i;
332    }
333    enterState(i);
334}
335
336void
337TrafficGen::StateGraph::enterState(uint32_t newState)
338{
339    DPRINTF(TrafficGen, "Transition to state %d\n", newState);
340
341    currState = newState;
342    nextTransitionTick += states[currState]->duration;
343    states[currState]->enter();
344}
345
346TrafficGen::StateGraph::BaseGen::BaseGen(QueuedMasterPort& _port,
347                                         MasterID master_id,
348                                         Tick _duration)
349    : port(_port), masterID(master_id), duration(_duration)
350{
351}
352
353void
354TrafficGen::StateGraph::BaseGen::send(Addr addr, unsigned size,
355                                      const MemCmd& cmd)
356{
357    // Create new request
358    Request::Flags flags;
359    Request *req = new Request(addr, size, flags, masterID);
360
361    // Embed it in a packet
362    PacketPtr pkt = new Packet(req, cmd);
363
364    uint8_t* pkt_data = new uint8_t[req->getSize()];
365    pkt->dataDynamicArray(pkt_data);
366
367    if (cmd.isWrite()) {
368        memset(pkt_data, 0xA, req->getSize());
369    }
370
371    port.schedTimingReq(pkt, curTick());
372}
373
374void
375TrafficGen::StateGraph::LinearGen::enter()
376{
377    // reset the address and the data counter
378    nextAddr = startAddr;
379    dataManipulated = 0;
380
381    // this test only needs to happen once, but cannot be performed
382    // before init() is called and the ports are connected
383    if (port.deviceBlockSize() && blocksize > port.deviceBlockSize())
384        fatal("TrafficGen %s block size (%d) is larger than port"
385              " block size (%d)\n", blocksize, port.deviceBlockSize());
386
387}
388
389void
390TrafficGen::StateGraph::LinearGen::execute()
391{
392    // choose if we generate a read or a write here
393    bool isRead = readPercent != 0 &&
394        (readPercent == 100 || random_mt.random<uint8_t>(0, 100) < readPercent);
395
396    assert((readPercent == 0 && !isRead) || (readPercent == 100 && isRead) ||
397           readPercent != 100);
398
399    DPRINTF(TrafficGen, "LinearGen::execute: %c to addr %x, size %d\n",
400            isRead ? 'r' : 'w', nextAddr, blocksize);
401
402    send(nextAddr, blocksize, isRead ? MemCmd::ReadReq : MemCmd::WriteReq);
403
404    // increment the address
405    nextAddr += blocksize;
406
407    // Add the amount of data manipulated to the total
408    dataManipulated += blocksize;
409}
410
411Tick
412TrafficGen::StateGraph::LinearGen::nextExecuteTick()
413{
414    // If we have reached the end of the address space, reset the
415    // address to the start of the range
416    if (nextAddr + blocksize > endAddr) {
417        DPRINTF(TrafficGen, "Wrapping address to the start of "
418                "the range\n");
419        nextAddr = startAddr;
420    }
421
422    // Check to see if we have reached the data limit. If dataLimit is
423    // zero we do not have a data limit and therefore we will keep
424    // generating requests for the entire residency in this state.
425    if (dataLimit && dataManipulated >= dataLimit) {
426        DPRINTF(TrafficGen, "Data limit for LinearGen reached.\n");
427        // there are no more requests, therefore return MaxTick
428        return MaxTick;
429    } else {
430        // return the time when the next request should take place
431        return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod);
432    }
433}
434
435void
436TrafficGen::StateGraph::RandomGen::enter()
437{
438    // reset the counter to zero
439    dataManipulated = 0;
440
441    // this test only needs to happen once, but cannot be performed
442    // before init() is called and the ports are connected
443    if (port.deviceBlockSize() && blocksize > port.deviceBlockSize())
444        fatal("TrafficGen %s block size (%d) is larger than port"
445              " block size (%d)\n", name(), blocksize, port.deviceBlockSize());
446}
447
448void
449TrafficGen::StateGraph::RandomGen::execute()
450{
451    // choose if we generate a read or a write here
452    bool isRead = readPercent != 0 &&
453        (readPercent == 100 || random_mt.random<uint8_t>(0, 100) < readPercent);
454
455    assert((readPercent == 0 && !isRead) || (readPercent == 100 && isRead) ||
456           readPercent != 100);
457
458    // address of the request
459    Addr addr = random_mt.random<Addr>(startAddr, endAddr - 1);
460
461    // round down to start address of block
462    addr -= addr % blocksize;
463
464    DPRINTF(TrafficGen, "RandomGen::execute: %c to addr %x, size %d\n",
465            isRead ? 'r' : 'w', addr, blocksize);
466
467    // send a new request packet
468    send(addr, blocksize, isRead ? MemCmd::ReadReq : MemCmd::WriteReq);
469
470    // Add the amount of data manipulated to the total
471    dataManipulated += blocksize;
472}
473
474Tick
475TrafficGen::StateGraph::RandomGen::nextExecuteTick()
476{
477    // Check to see if we have reached the data limit. If dataLimit is
478    // zero we do not have a data limit and therefore we will keep
479    // generating requests for the entire residency in this state.
480    if (dataLimit && dataManipulated >= dataLimit)
481    {
482        DPRINTF(TrafficGen, "Data limit for RandomGen reached.\n");
483        // No more requests. Return MaxTick.
484        return MaxTick;
485    } else {
486        // Return the time when the next request should take place.
487        return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod);
488    }
489}
490
491TrafficGen::StateGraph::TraceGen::InputStream::InputStream(const string&
492                                                           filename)
493    : trace(filename)
494{
495    // Create a protobuf message for the header and read it from the stream
496    Message::PacketHeader header_msg;
497    if (!trace.read(header_msg)) {
498        panic("Failed to read packet header from %s\n", filename);
499
500        if (header_msg.tick_freq() != SimClock::Frequency) {
501            panic("Trace %s was recorded with a different tick frequency %d\n",
502                  header_msg.tick_freq());
503        }
504    }
505}
506
507void
508TrafficGen::StateGraph::TraceGen::InputStream::reset()
509{
510    trace.reset();
511}
512
513bool
514TrafficGen::StateGraph::TraceGen::InputStream::read(TraceElement& element)
515{
516    Message::Packet pkt_msg;
517    if (trace.read(pkt_msg)) {
518        element.cmd = pkt_msg.cmd();
519        element.addr = pkt_msg.addr();
520        element.blocksize = pkt_msg.size();
521        element.tick = pkt_msg.tick();
522        return true;
523    }
524
525    // We have reached the end of the file
526    return false;
527}
528
529Tick
530TrafficGen::StateGraph::TraceGen::nextExecuteTick() {
531    if (traceComplete)
532        // We are at the end of the file, thus we have no more data in
533        // the trace Return MaxTick to signal that there will be no
534        // more transactions in this active period for the state.
535        return MaxTick;
536
537
538    //Reset the nextElement to the default values
539    currElement = nextElement;
540    nextElement.clear();
541
542    // We need to look at the next line to calculate the next time an
543    // event occurs, or potentially return MaxTick to signal that
544    // nothing has to be done.
545    if (!trace.read(nextElement)) {
546        traceComplete = true;
547        return MaxTick;
548    }
549
550    DPRINTF(TrafficGen, "currElement: %c addr %d size %d tick %d (%d)\n",
551            currElement.cmd.isRead() ? 'r' : 'w',
552            currElement.addr,
553            currElement.blocksize,
554            currElement.tick + tickOffset,
555            currElement.tick);
556
557    DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n",
558            nextElement.cmd.isRead() ? 'r' : 'w',
559            nextElement.addr,
560            nextElement.blocksize,
561            nextElement.tick + tickOffset,
562            nextElement.tick);
563
564    return tickOffset + nextElement.tick;
565}
566
567void
568TrafficGen::StateGraph::TraceGen::enter() {
569    // update the trace offset to the time where the state was entered.
570    tickOffset = curTick();
571
572    // clear everything
573    nextElement.clear();
574    currElement.clear();
575
576    traceComplete = false;
577}
578
579void
580TrafficGen::StateGraph::TraceGen::execute() {
581    // it is the responsibility of nextExecuteTick to prevent the
582    // state graph from executing the state if it should not
583    assert(currElement.isValid());
584
585    DPRINTF(TrafficGen, "TraceGen::execute: %c %d %d %d\n",
586            currElement.cmd.isRead() ? 'r' : 'w',
587            currElement.addr,
588            currElement.blocksize,
589            currElement.tick);
590
591    send(currElement.addr + addrOffset, currElement.blocksize,
592         currElement.cmd);
593}
594
595void
596TrafficGen::StateGraph::TraceGen::exit() {
597    // Check if we reached the end of the trace file. If we did not
598    // then we want to generate a warning stating that not the entire
599    // trace was played.
600    if (!traceComplete) {
601        warn("Trace player %s was unable to replay the entire trace!\n",
602             name());
603    }
604
605    // Clear any flags and start over again from the beginning of the
606    // file
607    trace.reset();
608}
609
610bool
611TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
612{
613    delete pkt->req;
614    delete pkt;
615
616    return true;
617}
618