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