traffic_gen.cc revision 10913:38dbdeea7f1f
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/intmath.hh"
45#include "base/random.hh"
46#include "cpu/testers/traffic_gen/traffic_gen.hh"
47#include "debug/Checkpoint.hh"
48#include "debug/TrafficGen.hh"
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      configFile(p->config_file),
59      elasticReq(p->elastic_req),
60      nextTransitionTick(0),
61      nextPacketTick(0),
62      currState(0),
63      port(name() + ".port", *this),
64      retryPkt(NULL),
65      retryPktTick(0),
66      updateEvent(this)
67{
68}
69
70TrafficGen*
71TrafficGenParams::create()
72{
73    return new TrafficGen(this);
74}
75
76BaseMasterPort&
77TrafficGen::getMasterPort(const string& if_name, PortID idx)
78{
79    if (if_name == "port") {
80        return port;
81    } else {
82        return MemObject::getMasterPort(if_name, idx);
83    }
84}
85
86void
87TrafficGen::init()
88{
89    if (!port.isConnected())
90        fatal("The port of %s is not connected!\n", name());
91
92    // if the system is in timing mode active the request generator
93    if (system->isTimingMode()) {
94        DPRINTF(TrafficGen, "Timing mode, activating request generator\n");
95
96        parseConfig();
97
98        // enter initial state
99        enterState(currState);
100    } else {
101        DPRINTF(TrafficGen,
102                "Traffic generator is only active in timing mode\n");
103    }
104}
105
106void
107TrafficGen::initState()
108{
109    // when not restoring from a checkpoint, make sure we kick things off
110    if (system->isTimingMode()) {
111        // call nextPacketTick on the state to advance it
112        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
113        schedule(updateEvent, std::min(nextPacketTick, nextTransitionTick));
114    } else {
115        DPRINTF(TrafficGen,
116                "Traffic generator is only active in timing mode\n");
117    }
118}
119
120DrainState
121TrafficGen::drain()
122{
123    if (!updateEvent.scheduled()) {
124        // no event has been scheduled yet (e.g. switched from atomic mode)
125        return DrainState::Drained;
126    }
127
128    if (retryPkt == NULL) {
129        // shut things down
130        nextPacketTick = MaxTick;
131        nextTransitionTick = MaxTick;
132        deschedule(updateEvent);
133        return DrainState::Drained;
134    } else {
135        return DrainState::Draining;
136    }
137}
138
139void
140TrafficGen::serialize(CheckpointOut &cp) const
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(CheckpointIn &cp)
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
190        // suppress packets that are not destined for a memory, such as
191        // device accesses that could be part of a trace
192        if (system->isMemAddr(pkt->getAddr())) {
193            numPackets++;
194            if (!port.sendTimingReq(pkt)) {
195                retryPkt = pkt;
196                retryPktTick = curTick();
197            }
198        } else {
199            DPRINTF(TrafficGen, "Suppressed packet %s 0x%x\n",
200                    pkt->cmdString(), pkt->getAddr());
201        }
202    }
203
204    // if we are waiting for a retry, do not schedule any further
205    // events, in the case of a transition or a successful send, go
206    // ahead and determine when the next update should take place
207    if (retryPkt == NULL) {
208        // schedule next update event based on either the next execute
209        // tick or the next transition, which ever comes first
210        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
211        Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
212        DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick);
213        schedule(updateEvent, nextEventTick);
214    }
215}
216
217void
218TrafficGen::parseConfig()
219{
220    // keep track of the transitions parsed to create the matrix when
221    // done
222    vector<Transition> transitions;
223
224    // open input file
225    ifstream infile;
226    infile.open(configFile.c_str(), ifstream::in);
227    if (!infile.is_open()) {
228        fatal("Traffic generator %s config file not found at %s\n",
229              name(), configFile);
230    }
231
232    bool init_state_set = false;
233
234    // read line by line and determine the action based on the first
235    // keyword
236    string keyword;
237    string line;
238
239    while (getline(infile, line).good()) {
240        // see if this line is a comment line, and if so skip it
241        if (line.find('#') != 1) {
242            // create an input stream for the tokenization
243            istringstream is(line);
244
245            // determine the keyword
246            is >> keyword;
247
248            if (keyword == "STATE") {
249                // parse the behaviour of this state
250                uint32_t id;
251                Tick duration;
252                string mode;
253
254                is >> id >> duration >> mode;
255
256                if (mode == "TRACE") {
257                    string traceFile;
258                    Addr addrOffset;
259
260                    is >> traceFile >> addrOffset;
261
262                    states[id] = new TraceGen(name(), masterID, duration,
263                                              traceFile, addrOffset);
264                    DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
265                } else if (mode == "IDLE") {
266                    states[id] = new IdleGen(name(), masterID, duration);
267                    DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
268                } else if (mode == "LINEAR" || mode == "RANDOM" ||
269                           mode == "DRAM"   || mode == "DRAM_ROTATE") {
270                    uint32_t read_percent;
271                    Addr start_addr;
272                    Addr end_addr;
273                    Addr blocksize;
274                    Tick min_period;
275                    Tick max_period;
276                    Addr data_limit;
277
278                    is >> read_percent >> start_addr >> end_addr >>
279                        blocksize >> min_period >> max_period >> data_limit;
280
281                    DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
282                            " period %d to %d, %d%% reads\n",
283                            mode, start_addr, end_addr, blocksize, min_period,
284                            max_period, read_percent);
285
286
287                    if (blocksize > system->cacheLineSize())
288                        fatal("TrafficGen %s block size (%d) is larger than "
289                              "cache line size (%d)\n", name(),
290                              blocksize, system->cacheLineSize());
291
292                    if (read_percent > 100)
293                        fatal("%s cannot have more than 100% reads", name());
294
295                    if (min_period > max_period)
296                        fatal("%s cannot have min_period > max_period", name());
297
298                    if (mode == "LINEAR") {
299                        states[id] = new LinearGen(name(), masterID,
300                                                   duration, start_addr,
301                                                   end_addr, blocksize,
302                                                   min_period, max_period,
303                                                   read_percent, data_limit);
304                        DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
305                    } else if (mode == "RANDOM") {
306                        states[id] = new RandomGen(name(), masterID,
307                                                   duration, start_addr,
308                                                   end_addr, blocksize,
309                                                   min_period, max_period,
310                                                   read_percent, data_limit);
311                        DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
312                    } else if (mode == "DRAM" || mode == "DRAM_ROTATE") {
313                        // stride size (bytes) of the request for achieving
314                        // required hit length
315                        unsigned int stride_size;
316                        unsigned int page_size;
317                        unsigned int nbr_of_banks_DRAM;
318                        unsigned int nbr_of_banks_util;
319                        unsigned int addr_mapping;
320                        unsigned int nbr_of_ranks;
321
322                        is >> stride_size >> page_size >> nbr_of_banks_DRAM >>
323                            nbr_of_banks_util >> addr_mapping >>
324                            nbr_of_ranks;
325
326                        if (stride_size > page_size)
327                            warn("DRAM generator stride size (%d) is greater "
328                                 "than page size (%d)  of the memory\n",
329                                 blocksize, page_size);
330
331                        if (nbr_of_banks_util > nbr_of_banks_DRAM)
332                            fatal("Attempting to use more banks (%d) than "
333                                  "what is available (%d)\n",
334                                  nbr_of_banks_util, nbr_of_banks_DRAM);
335
336                        // count the number of sequential packets to
337                        // generate
338                        unsigned int num_seq_pkts = 1;
339
340                        if (stride_size > blocksize) {
341                            num_seq_pkts = divCeil(stride_size, blocksize);
342                            DPRINTF(TrafficGen, "stride size: %d "
343                                    "block size: %d, num_seq_pkts: %d\n",
344                                    stride_size, blocksize, num_seq_pkts);
345                        }
346
347                        if (mode == "DRAM") {
348                            states[id] = new DramGen(name(), masterID,
349                                                     duration, start_addr,
350                                                     end_addr, blocksize,
351                                                     min_period, max_period,
352                                                     read_percent, data_limit,
353                                                     num_seq_pkts, page_size,
354                                                     nbr_of_banks_DRAM,
355                                                     nbr_of_banks_util,
356                                                     addr_mapping,
357                                                     nbr_of_ranks);
358                            DPRINTF(TrafficGen, "State: %d DramGen\n", id);
359                        } else {
360                            // Will rotate to the next rank after rotating
361                            // through all banks, for each command type.
362                            // In the 50% read case, series will be issued
363                            // for both RD & WR before the rank in incremented
364                            unsigned int max_seq_count_per_rank =
365                                (read_percent == 50) ? nbr_of_banks_util * 2
366                                                     : nbr_of_banks_util;
367
368                            states[id] = new DramRotGen(name(), masterID,
369                                                     duration, start_addr,
370                                                     end_addr, blocksize,
371                                                     min_period, max_period,
372                                                     read_percent, data_limit,
373                                                     num_seq_pkts, page_size,
374                                                     nbr_of_banks_DRAM,
375                                                     nbr_of_banks_util,
376                                                     addr_mapping,
377                                                     nbr_of_ranks,
378                                                     max_seq_count_per_rank);
379                            DPRINTF(TrafficGen, "State: %d DramRotGen\n", id);
380                        }
381                    }
382                } else {
383                    fatal("%s: Unknown traffic generator mode: %s",
384                          name(), mode);
385                }
386            } else if (keyword == "TRANSITION") {
387                Transition transition;
388
389                is >> transition.from >> transition.to >> transition.p;
390
391                transitions.push_back(transition);
392
393                DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
394                        transition.to);
395            } else if (keyword == "INIT") {
396                // set the initial state as the active state
397                is >> currState;
398
399                init_state_set = true;
400
401                DPRINTF(TrafficGen, "Initial state: %d\n", currState);
402            }
403        }
404    }
405
406    if (!init_state_set)
407        fatal("%s: initial state not specified (add 'INIT <id>' line "
408              "to the config file)\n", name());
409
410    // resize and populate state transition matrix
411    transitionMatrix.resize(states.size());
412    for (size_t i = 0; i < states.size(); i++) {
413        transitionMatrix[i].resize(states.size());
414    }
415
416    for (vector<Transition>::iterator t = transitions.begin();
417         t != transitions.end(); ++t) {
418        transitionMatrix[t->from][t->to] = t->p;
419    }
420
421    // ensure the egress edges do not have a probability larger than
422    // one
423    for (size_t i = 0; i < states.size(); i++) {
424        double sum = 0;
425        for (size_t j = 0; j < states.size(); j++) {
426            sum += transitionMatrix[i][j];
427        }
428
429        // avoid comparing floating point numbers
430        if (abs(sum - 1.0) > 0.001)
431            fatal("%s has transition probability != 1 for state %d\n",
432                  name(), i);
433    }
434
435    // close input file
436    infile.close();
437}
438
439void
440TrafficGen::transition()
441{
442    // exit the current state
443    states[currState]->exit();
444
445    // determine next state
446    double p = random_mt.random<double>();
447    assert(currState < transitionMatrix.size());
448    double cumulative = 0.0;
449    size_t i = 0;
450    do {
451        cumulative += transitionMatrix[currState][i];
452        ++i;
453    } while (cumulative < p && i < transitionMatrix[currState].size());
454
455    enterState(i - 1);
456}
457
458void
459TrafficGen::enterState(uint32_t newState)
460{
461    DPRINTF(TrafficGen, "Transition to state %d\n", newState);
462
463    currState = newState;
464    // we could have been delayed and not transitioned on the exact
465    // tick when we were supposed to (due to back pressure when
466    // sending a packet)
467    nextTransitionTick = curTick() + states[currState]->duration;
468    states[currState]->enter();
469}
470
471void
472TrafficGen::recvReqRetry()
473{
474    assert(retryPkt != NULL);
475
476    DPRINTF(TrafficGen, "Received retry\n");
477    numRetries++;
478    // attempt to send the packet, and if we are successful start up
479    // the machinery again
480    if (port.sendTimingReq(retryPkt)) {
481        retryPkt = NULL;
482        // remember how much delay was incurred due to back-pressure
483        // when sending the request, we also use this to derive
484        // the tick for the next packet
485        Tick delay = curTick() - retryPktTick;
486        retryPktTick = 0;
487        retryTicks += delay;
488
489        if (drainState() != DrainState::Draining) {
490            // packet is sent, so find out when the next one is due
491            nextPacketTick = states[currState]->nextPacketTick(elasticReq,
492                                                               delay);
493            Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
494            schedule(updateEvent, std::max(curTick(), nextEventTick));
495        } else {
496            // shut things down
497            nextPacketTick = MaxTick;
498            nextTransitionTick = MaxTick;
499            signalDrainDone();
500        }
501    }
502}
503
504void
505TrafficGen::regStats()
506{
507    // Initialise all the stats
508    using namespace Stats;
509
510    numPackets
511        .name(name() + ".numPackets")
512        .desc("Number of packets generated");
513
514    numRetries
515        .name(name() + ".numRetries")
516        .desc("Number of retries");
517
518    retryTicks
519        .name(name() + ".retryTicks")
520        .desc("Time spent waiting due to back-pressure (ticks)");
521}
522
523bool
524TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
525{
526    delete pkt->req;
527    delete pkt;
528
529    return true;
530}
531