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