traffic_gen.cc revision 9721:dd486672c9d0
1/*
2 * Copyright (c) 2012-2013 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 "sim/stats.hh"
49#include "sim/system.hh"
50
51using namespace std;
52
53TrafficGen::TrafficGen(const TrafficGenParams* p)
54    : MemObject(p),
55      system(p->system),
56      masterID(system->getMasterId(name())),
57      configFile(p->config_file),
58      elasticReq(p->elastic_req),
59      nextTransitionTick(0),
60      nextPacketTick(0),
61      port(name() + ".port", *this),
62      retryPkt(NULL),
63      retryPktTick(0),
64      updateEvent(this),
65      drainManager(NULL)
66{
67}
68
69TrafficGen*
70TrafficGenParams::create()
71{
72    return new TrafficGen(this);
73}
74
75BaseMasterPort&
76TrafficGen::getMasterPort(const string& if_name, PortID idx)
77{
78    if (if_name == "port") {
79        return port;
80    } else {
81        return MemObject::getMasterPort(if_name, idx);
82    }
83}
84
85void
86TrafficGen::init()
87{
88    if (!port.isConnected())
89        fatal("The port of %s is not connected!\n", name());
90
91    // if the system is in timing mode active the request generator
92    if (system->isTimingMode()) {
93        DPRINTF(TrafficGen, "Timing mode, activating request generator\n");
94
95        parseConfig();
96
97        // enter initial state
98        enterState(currState);
99    } else {
100        DPRINTF(TrafficGen,
101                "Traffic generator is only active in timing mode\n");
102    }
103}
104
105void
106TrafficGen::initState()
107{
108    // when not restoring from a checkpoint, make sure we kick things off
109    if (system->isTimingMode()) {
110        // call nextPacketTick on the state to advance it
111        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
112        schedule(updateEvent, std::min(nextPacketTick, nextTransitionTick));
113    } else {
114        DPRINTF(TrafficGen,
115                "Traffic generator is only active in timing mode\n");
116    }
117}
118
119unsigned int
120TrafficGen::drain(DrainManager *dm)
121{
122    if (retryPkt == NULL) {
123        // shut things down
124        nextPacketTick = MaxTick;
125        nextTransitionTick = MaxTick;
126        deschedule(updateEvent);
127        return 0;
128    } else {
129        drainManager = dm;
130        return 1;
131    }
132}
133
134void
135TrafficGen::serialize(ostream &os)
136{
137    DPRINTF(Checkpoint, "Serializing TrafficGen\n");
138
139    // save ticks of the graph event if it is scheduled
140    Tick nextEvent = updateEvent.scheduled() ? updateEvent.when() : 0;
141
142    DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", nextEvent);
143
144    SERIALIZE_SCALAR(nextEvent);
145
146    SERIALIZE_SCALAR(nextTransitionTick);
147
148    SERIALIZE_SCALAR(nextPacketTick);
149
150    SERIALIZE_SCALAR(currState);
151}
152
153void
154TrafficGen::unserialize(Checkpoint* cp, const string& section)
155{
156    // restore scheduled events
157    Tick nextEvent;
158    UNSERIALIZE_SCALAR(nextEvent);
159    if (nextEvent != 0) {
160        schedule(updateEvent, nextEvent);
161    }
162
163    UNSERIALIZE_SCALAR(nextTransitionTick);
164
165    UNSERIALIZE_SCALAR(nextPacketTick);
166
167    // @todo In the case of a stateful generator state such as the
168    // trace player we would also have to restore the position in the
169    // trace playback and the tick offset
170    UNSERIALIZE_SCALAR(currState);
171}
172
173void
174TrafficGen::update()
175{
176    // if we have reached the time for the next state transition, then
177    // perform the transition
178    if (curTick() >= nextTransitionTick) {
179        transition();
180    } else {
181        assert(curTick() >= nextPacketTick);
182        // get the next packet and try to send it
183        PacketPtr pkt = states[currState]->getNextPacket();
184        numPackets++;
185        if (!port.sendTimingReq(pkt)) {
186            retryPkt = pkt;
187            retryPktTick = curTick();
188        }
189    }
190
191    // if we are waiting for a retry, do not schedule any further
192    // events, in the case of a transition or a successful send, go
193    // ahead and determine when the next update should take place
194    if (retryPkt == NULL) {
195        // schedule next update event based on either the next execute
196        // tick or the next transition, which ever comes first
197        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
198        Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
199        DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick);
200        schedule(updateEvent, nextEventTick);
201    }
202}
203
204void
205TrafficGen::parseConfig()
206{
207    // keep track of the transitions parsed to create the matrix when
208    // done
209    vector<Transition> transitions;
210
211    // open input file
212    ifstream infile;
213    infile.open(configFile.c_str(), ifstream::in);
214    if (!infile.is_open()) {
215        fatal("Traffic generator %s config file not found at %s\n",
216              name(), configFile);
217    }
218
219    // read line by line and determine the action based on the first
220    // keyword
221    string keyword;
222    string line;
223
224    while (getline(infile, line).good()) {
225        // see if this line is a comment line, and if so skip it
226        if (line.find('#') != 1) {
227            // create an input stream for the tokenization
228            istringstream is(line);
229
230            // determine the keyword
231            is >> keyword;
232
233            if (keyword == "STATE") {
234                // parse the behaviour of this state
235                uint32_t id;
236                Tick duration;
237                string mode;
238
239                is >> id >> duration >> mode;
240
241                if (mode == "TRACE") {
242                    string traceFile;
243                    Addr addrOffset;
244
245                    is >> traceFile >> addrOffset;
246
247                    states[id] = new TraceGen(name(), masterID, duration,
248                                              traceFile, addrOffset);
249                    DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
250                } else if (mode == "IDLE") {
251                    states[id] = new IdleGen(name(), masterID, duration);
252                    DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
253                } else if (mode == "LINEAR" || mode == "RANDOM") {
254                    uint32_t read_percent;
255                    Addr start_addr;
256                    Addr end_addr;
257                    Addr blocksize;
258                    Tick min_period;
259                    Tick max_period;
260                    Addr data_limit;
261
262                    is >> read_percent >> start_addr >> end_addr >>
263                        blocksize >> min_period >> max_period >> data_limit;
264
265                    DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
266                            " period %d to %d, %d%% reads\n",
267                            mode, start_addr, end_addr, blocksize, min_period,
268                            max_period, read_percent);
269
270
271                    if (port.deviceBlockSize() &&
272                        blocksize > port.deviceBlockSize())
273                        fatal("TrafficGen %s block size (%d) is larger than "
274                              "port block size (%d)\n", name(),
275                              blocksize, port.deviceBlockSize());
276
277                    if (read_percent > 100)
278                        fatal("%s cannot have more than 100% reads", name());
279
280                    if (mode == "LINEAR") {
281                        states[id] = new LinearGen(name(), masterID,
282                                                   duration, start_addr,
283                                                   end_addr, blocksize,
284                                                   min_period, max_period,
285                                                   read_percent, data_limit);
286                        DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
287                    } else if (mode == "RANDOM") {
288                        states[id] = new RandomGen(name(), masterID,
289                                                   duration, start_addr,
290                                                   end_addr, blocksize,
291                                                   min_period, max_period,
292                                                   read_percent, data_limit);
293                        DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
294                    }
295                } else {
296                    fatal("%s: Unknown traffic generator mode: %s",
297                          name(), mode);
298                }
299            } else if (keyword == "TRANSITION") {
300                Transition transition;
301
302                is >> transition.from >> transition.to >> transition.p;
303
304                transitions.push_back(transition);
305
306                DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
307                        transition.to);
308            } else if (keyword == "INIT") {
309                // set the initial state as the active state
310                is >> currState;
311
312                DPRINTF(TrafficGen, "Initial state: %d\n", currState);
313            }
314        }
315    }
316
317    // resize and populate state transition matrix
318    transitionMatrix.resize(states.size());
319    for (size_t i = 0; i < states.size(); i++) {
320        transitionMatrix[i].resize(states.size());
321    }
322
323    for (vector<Transition>::iterator t = transitions.begin();
324         t != transitions.end(); ++t) {
325        transitionMatrix[t->from][t->to] = t->p;
326    }
327
328    // ensure the egress edges do not have a probability larger than
329    // one
330    for (size_t i = 0; i < states.size(); i++) {
331        double sum = 0;
332        for (size_t j = 0; j < states.size(); j++) {
333            sum += transitionMatrix[i][j];
334        }
335
336        // avoid comparing floating point numbers
337        if (abs(sum - 1.0) > 0.001)
338            fatal("%s has transition probability != 1 for state %d\n",
339                  name(), i);
340    }
341
342    // close input file
343    infile.close();
344}
345
346void
347TrafficGen::transition()
348{
349    // exit the current state
350    states[currState]->exit();
351
352    // determine next state
353    double p = random_mt.gen_real1();
354    assert(currState < transitionMatrix.size());
355    double cumulative = 0.0;
356    size_t i = 0;
357    do {
358        cumulative += transitionMatrix[currState][i];
359        ++i;
360    } while (cumulative < p && i < transitionMatrix[currState].size());
361
362    enterState(i - 1);
363}
364
365void
366TrafficGen::enterState(uint32_t newState)
367{
368    DPRINTF(TrafficGen, "Transition to state %d\n", newState);
369
370    currState = newState;
371    // we could have been delayed and not transitioned on the exact
372    // tick when we were supposed to (due to back pressure when
373    // sending a packet)
374    nextTransitionTick = curTick() + states[currState]->duration;
375    states[currState]->enter();
376}
377
378void
379TrafficGen::recvRetry()
380{
381    assert(retryPkt != NULL);
382
383    DPRINTF(TrafficGen, "Received retry\n");
384    numRetries++;
385    // attempt to send the packet, and if we are successful start up
386    // the machinery again
387    if (port.sendTimingReq(retryPkt)) {
388        retryPkt = NULL;
389        // remember how much delay was incurred due to back-pressure
390        // when sending the request, we also use this to derive
391        // the tick for the next packet
392        Tick delay = curTick() - retryPktTick;
393        retryPktTick = 0;
394        retryTicks += delay;
395
396        if (drainManager == NULL) {
397            // packet is sent, so find out when the next one is due
398            nextPacketTick = states[currState]->nextPacketTick(elasticReq,
399                                                               delay);
400            Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
401            schedule(updateEvent, std::max(curTick(), nextEventTick));
402        } else {
403            // shut things down
404            nextPacketTick = MaxTick;
405            nextTransitionTick = MaxTick;
406            drainManager->signalDrainDone();
407            // Clear the drain event once we're done with it.
408            drainManager = NULL;
409        }
410    }
411}
412
413void
414TrafficGen::regStats()
415{
416    // Initialise all the stats
417    using namespace Stats;
418
419    numPackets
420        .name(name() + ".numPackets")
421        .desc("Number of packets generated");
422
423    numRetries
424        .name(name() + ".numRetries")
425        .desc("Number of retries");
426
427    retryTicks
428        .name(name() + ".retryTicks")
429        .desc("Time spent waiting due to back-pressure (ticks)");
430}
431
432bool
433TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
434{
435    delete pkt->req;
436    delete pkt;
437
438    return true;
439}
440