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