traffic_gen.cc revision 12749:223c83ed9979
12348SN/A/*
22348SN/A * Copyright (c) 2012-2013, 2016-2017 ARM Limited
32348SN/A * All rights reserved
42348SN/A *
52348SN/A * The license below extends only to copyright in the software and shall
62348SN/A * not be construed as granting a license to any other intellectual
72348SN/A * property including but not limited to intellectual property relating
82348SN/A * to a hardware implementation of the functionality of the software
92348SN/A * licensed hereunder.  You may use the software subject to the license
102348SN/A * terms below provided that you ensure that this notice is replicated
112348SN/A * unmodified and in its entirety in all distributions of the software,
122348SN/A * modified or unmodified, in source code or in binary form.
132348SN/A *
142348SN/A * Redistribution and use in source and binary forms, with or without
152348SN/A * modification, are permitted provided that the following conditions are
162348SN/A * met: redistributions of source code must retain the above copyright
172348SN/A * notice, this list of conditions and the following disclaimer;
182348SN/A * redistributions in binary form must reproduce the above copyright
192348SN/A * notice, this list of conditions and the following disclaimer in the
202348SN/A * documentation and/or other materials provided with the distribution;
212348SN/A * neither the name of the copyright holders nor the names of its
222348SN/A * contributors may be used to endorse or promote products derived from
232348SN/A * this software without specific prior written permission.
242348SN/A *
252348SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
262348SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
272689Sktlim@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
282689Sktlim@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
292348SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
302325SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
315804Snate@binkert.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
323918Ssaidi@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
338229Snate@binkert.org * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
347813Ssteve.reinhardt@amd.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
358232Snate@binkert.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
362325SN/A *
375804Snate@binkert.org * Authors: Thomas Grass
385804Snate@binkert.org *          Andreas Hansson
395804Snate@binkert.org *          Sascha Bischoff
405804Snate@binkert.org */
415804Snate@binkert.org#include "cpu/testers/traffic_gen/traffic_gen.hh"
425804Snate@binkert.org
435804Snate@binkert.org#include <libgen.h>
442325SN/A#include <unistd.h>
452325SN/A
463918Ssaidi@eecs.umich.edu#include <sstream>
472325SN/A
482325SN/A#include "base/intmath.hh"
492325SN/A#include "base/random.hh"
502325SN/A#include "debug/Checkpoint.hh"
512325SN/A#include "debug/TrafficGen.hh"
522348SN/A#include "sim/stats.hh"
532348SN/A#include "sim/system.hh"
542325SN/A
552325SN/Ausing namespace std;
562325SN/A
572325SN/ATrafficGen::TrafficGen(const TrafficGenParams* p)
582325SN/A    : MemObject(p),
592325SN/A      system(p->system),
602325SN/A      masterID(system->getMasterId(this)),
612325SN/A      configFile(p->config_file),
622325SN/A      elasticReq(p->elastic_req),
632325SN/A      progressCheck(p->progress_check),
642325SN/A      noProgressEvent([this]{ noProgress(); }, name()),
652325SN/A      nextTransitionTick(0),
662325SN/A      nextPacketTick(0),
672325SN/A      currState(0),
682348SN/A      port(name() + ".port", *this),
692348SN/A      retryPkt(NULL),
702325SN/A      retryPktTick(0),
712325SN/A      updateEvent([this]{ update(); }, name()),
722325SN/A      numSuppressed(0)
732325SN/A{
742325SN/A}
752325SN/A
762325SN/ATrafficGen*
772325SN/ATrafficGenParams::create()
782325SN/A{
792325SN/A    return new TrafficGen(this);
802325SN/A}
812325SN/A
822325SN/ABaseMasterPort&
832325SN/ATrafficGen::getMasterPort(const string& if_name, PortID idx)
842325SN/A{
852325SN/A    if (if_name == "port") {
862325SN/A        return port;
872325SN/A    } else {
882348SN/A        return MemObject::getMasterPort(if_name, idx);
892325SN/A    }
902325SN/A}
912325SN/A
922325SN/Avoid
932325SN/ATrafficGen::init()
942325SN/A{
952325SN/A    if (!port.isConnected())
962325SN/A        fatal("The port of %s is not connected!\n", name());
972325SN/A
982325SN/A    // if the system is in timing mode active the request generator
992325SN/A    if (system->isTimingMode()) {
1002325SN/A        DPRINTF(TrafficGen, "Timing mode, activating request generator\n");
1012325SN/A
1022325SN/A        parseConfig();
1032325SN/A
1042325SN/A        // enter initial state
1052348SN/A        enterState(currState);
1062325SN/A    } else {
1072325SN/A        DPRINTF(TrafficGen,
1082325SN/A                "Traffic generator is only active in timing mode\n");
1092325SN/A    }
1102325SN/A}
1112325SN/A
1122325SN/Avoid
1132325SN/ATrafficGen::initState()
1142325SN/A{
1152325SN/A    // when not restoring from a checkpoint, make sure we kick things off
1162325SN/A    if (system->isTimingMode()) {
1172325SN/A        // call nextPacketTick on the state to advance it
1182325SN/A        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
1192325SN/A        schedule(updateEvent, std::min(nextPacketTick, nextTransitionTick));
1202325SN/A    } else {
1212325SN/A        DPRINTF(TrafficGen,
1222325SN/A                "Traffic generator is only active in timing mode\n");
1233918Ssaidi@eecs.umich.edu    }
1242325SN/A}
1252325SN/A
1262325SN/ADrainState
1272325SN/ATrafficGen::drain()
1282325SN/A{
1292325SN/A    if (!updateEvent.scheduled()) {
1302325SN/A        // no event has been scheduled yet (e.g. switched from atomic mode)
1312325SN/A        return DrainState::Drained;
1322325SN/A    }
1332325SN/A
1342325SN/A    if (retryPkt == NULL) {
1352325SN/A        // shut things down
1362325SN/A        nextPacketTick = MaxTick;
1372325SN/A        nextTransitionTick = MaxTick;
1382325SN/A        deschedule(updateEvent);
1392325SN/A        return DrainState::Drained;
1402325SN/A    } else {
1412325SN/A        return DrainState::Draining;
1422325SN/A    }
1432325SN/A}
1442325SN/A
1452325SN/Avoid
1462325SN/ATrafficGen::serialize(CheckpointOut &cp) const
1472325SN/A{
1482325SN/A    DPRINTF(Checkpoint, "Serializing TrafficGen\n");
1492325SN/A
1502325SN/A    // save ticks of the graph event if it is scheduled
1512325SN/A    Tick nextEvent = updateEvent.scheduled() ? updateEvent.when() : 0;
1522325SN/A
1532325SN/A    DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", nextEvent);
1542325SN/A
1552325SN/A    SERIALIZE_SCALAR(nextEvent);
1562325SN/A
1572325SN/A    SERIALIZE_SCALAR(nextTransitionTick);
1582325SN/A
1592325SN/A    SERIALIZE_SCALAR(nextPacketTick);
1602325SN/A
1612325SN/A    SERIALIZE_SCALAR(currState);
1622325SN/A}
1632325SN/A
164void
165TrafficGen::unserialize(CheckpointIn &cp)
166{
167    // restore scheduled events
168    Tick nextEvent;
169    UNSERIALIZE_SCALAR(nextEvent);
170    if (nextEvent != 0) {
171        schedule(updateEvent, nextEvent);
172    }
173
174    UNSERIALIZE_SCALAR(nextTransitionTick);
175
176    UNSERIALIZE_SCALAR(nextPacketTick);
177
178    // @todo In the case of a stateful generator state such as the
179    // trace player we would also have to restore the position in the
180    // trace playback and the tick offset
181    UNSERIALIZE_SCALAR(currState);
182}
183
184void
185TrafficGen::update()
186{
187    // shift our progress-tracking event forward
188    reschedule(noProgressEvent, curTick() + progressCheck, true);
189
190    // if we have reached the time for the next state transition, then
191    // perform the transition
192    if (curTick() >= nextTransitionTick) {
193        transition();
194    } else {
195        assert(curTick() >= nextPacketTick);
196        // get the next packet and try to send it
197        PacketPtr pkt = states[currState]->getNextPacket();
198
199        // suppress packets that are not destined for a memory, such as
200        // device accesses that could be part of a trace
201        if (system->isMemAddr(pkt->getAddr())) {
202            numPackets++;
203            if (!port.sendTimingReq(pkt)) {
204                retryPkt = pkt;
205                retryPktTick = curTick();
206            }
207        } else {
208            DPRINTF(TrafficGen, "Suppressed packet %s 0x%x\n",
209                    pkt->cmdString(), pkt->getAddr());
210
211            ++numSuppressed;
212            if (numSuppressed % 10000)
213                warn("%s suppressed %d packets with non-memory addresses\n",
214                     name(), numSuppressed);
215
216            delete pkt;
217            pkt = nullptr;
218        }
219    }
220
221    // if we are waiting for a retry, do not schedule any further
222    // events, in the case of a transition or a successful send, go
223    // ahead and determine when the next update should take place
224    if (retryPkt == NULL) {
225        // schedule next update event based on either the next execute
226        // tick or the next transition, which ever comes first
227        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
228        Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
229        DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick);
230        schedule(updateEvent, nextEventTick);
231    }
232}
233
234std::string
235TrafficGen::resolveFile(const std::string &name)
236{
237    // Do nothing for empty and absolute file names
238    if (name.empty() || name[0] == '/')
239        return name;
240
241    char *config_path = strdup(configFile.c_str());
242    char *config_dir = dirname(config_path);
243    const std::string config_rel = csprintf("%s/%s", config_dir, name);
244    free(config_path);
245
246    // Check the path relative to the config file first
247    if (access(config_rel.c_str(), R_OK) == 0)
248        return config_rel;
249
250    // Fall back to the old behavior and search relative to the
251    // current working directory.
252    return name;
253}
254
255void
256TrafficGen::parseConfig()
257{
258    // keep track of the transitions parsed to create the matrix when
259    // done
260    vector<Transition> transitions;
261
262    // open input file
263    ifstream infile;
264    infile.open(configFile.c_str(), ifstream::in);
265    if (!infile.is_open()) {
266        fatal("Traffic generator %s config file not found at %s\n",
267              name(), configFile);
268    }
269
270    bool init_state_set = false;
271
272    // read line by line and determine the action based on the first
273    // keyword
274    string keyword;
275    string line;
276
277    while (getline(infile, line).good()) {
278        // see if this line is a comment line, and if so skip it
279        if (line.find('#') != 1) {
280            // create an input stream for the tokenization
281            istringstream is(line);
282
283            // determine the keyword
284            is >> keyword;
285
286            if (keyword == "STATE") {
287                // parse the behaviour of this state
288                uint32_t id;
289                Tick duration;
290                string mode;
291
292                is >> id >> duration >> mode;
293
294                if (mode == "TRACE") {
295                    string traceFile;
296                    Addr addrOffset;
297
298                    is >> traceFile >> addrOffset;
299                    traceFile = resolveFile(traceFile);
300
301                    states[id] = new TraceGen(name(), masterID, duration,
302                                              traceFile, addrOffset);
303                    DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
304                } else if (mode == "IDLE") {
305                    states[id] = new IdleGen(name(), masterID, duration);
306                    DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
307                } else if (mode == "EXIT") {
308                    states[id] = new ExitGen(name(), masterID, duration);
309                    DPRINTF(TrafficGen, "State: %d ExitGen\n", id);
310                } else if (mode == "LINEAR" || mode == "RANDOM" ||
311                           mode == "DRAM"   || mode == "DRAM_ROTATE") {
312                    uint32_t read_percent;
313                    Addr start_addr;
314                    Addr end_addr;
315                    Addr blocksize;
316                    Tick min_period;
317                    Tick max_period;
318                    Addr data_limit;
319
320                    is >> read_percent >> start_addr >> end_addr >>
321                        blocksize >> min_period >> max_period >> data_limit;
322
323                    DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
324                            " period %d to %d, %d%% reads\n",
325                            mode, start_addr, end_addr, blocksize, min_period,
326                            max_period, read_percent);
327
328
329                    if (blocksize > system->cacheLineSize())
330                        fatal("TrafficGen %s block size (%d) is larger than "
331                              "cache line size (%d)\n", name(),
332                              blocksize, system->cacheLineSize());
333
334                    if (read_percent > 100)
335                        fatal("%s cannot have more than 100% reads", name());
336
337                    if (min_period > max_period)
338                        fatal("%s cannot have min_period > max_period", name());
339
340                    if (mode == "LINEAR") {
341                        states[id] = new LinearGen(name(), masterID,
342                                                   duration, start_addr,
343                                                   end_addr, blocksize,
344                                                   min_period, max_period,
345                                                   read_percent, data_limit);
346                        DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
347                    } else if (mode == "RANDOM") {
348                        states[id] = new RandomGen(name(), masterID,
349                                                   duration, start_addr,
350                                                   end_addr, blocksize,
351                                                   min_period, max_period,
352                                                   read_percent, data_limit);
353                        DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
354                    } else if (mode == "DRAM" || mode == "DRAM_ROTATE") {
355                        // stride size (bytes) of the request for achieving
356                        // required hit length
357                        unsigned int stride_size;
358                        unsigned int page_size;
359                        unsigned int nbr_of_banks_DRAM;
360                        unsigned int nbr_of_banks_util;
361                        unsigned int addr_mapping;
362                        unsigned int nbr_of_ranks;
363
364                        is >> stride_size >> page_size >> nbr_of_banks_DRAM >>
365                            nbr_of_banks_util >> addr_mapping >>
366                            nbr_of_ranks;
367
368                        if (stride_size > page_size)
369                            warn("DRAM generator stride size (%d) is greater "
370                                 "than page size (%d)  of the memory\n",
371                                 blocksize, page_size);
372
373                        if (nbr_of_banks_util > nbr_of_banks_DRAM)
374                            fatal("Attempting to use more banks (%d) than "
375                                  "what is available (%d)\n",
376                                  nbr_of_banks_util, nbr_of_banks_DRAM);
377
378                        // count the number of sequential packets to
379                        // generate
380                        unsigned int num_seq_pkts = 1;
381
382                        if (stride_size > blocksize) {
383                            num_seq_pkts = divCeil(stride_size, blocksize);
384                            DPRINTF(TrafficGen, "stride size: %d "
385                                    "block size: %d, num_seq_pkts: %d\n",
386                                    stride_size, blocksize, num_seq_pkts);
387                        }
388
389                        if (mode == "DRAM") {
390                            states[id] = new DramGen(name(), masterID,
391                                                     duration, start_addr,
392                                                     end_addr, blocksize,
393                                                     min_period, max_period,
394                                                     read_percent, data_limit,
395                                                     num_seq_pkts, page_size,
396                                                     nbr_of_banks_DRAM,
397                                                     nbr_of_banks_util,
398                                                     addr_mapping,
399                                                     nbr_of_ranks);
400                            DPRINTF(TrafficGen, "State: %d DramGen\n", id);
401                        } else {
402                            // Will rotate to the next rank after rotating
403                            // through all banks, for each command type.
404                            // In the 50% read case, series will be issued
405                            // for both RD & WR before the rank in incremented
406                            unsigned int max_seq_count_per_rank =
407                                (read_percent == 50) ? nbr_of_banks_util * 2
408                                                     : nbr_of_banks_util;
409
410                            states[id] = new DramRotGen(name(), masterID,
411                                                     duration, start_addr,
412                                                     end_addr, blocksize,
413                                                     min_period, max_period,
414                                                     read_percent, data_limit,
415                                                     num_seq_pkts, page_size,
416                                                     nbr_of_banks_DRAM,
417                                                     nbr_of_banks_util,
418                                                     addr_mapping,
419                                                     nbr_of_ranks,
420                                                     max_seq_count_per_rank);
421                            DPRINTF(TrafficGen, "State: %d DramRotGen\n", id);
422                        }
423                    }
424                } else {
425                    fatal("%s: Unknown traffic generator mode: %s",
426                          name(), mode);
427                }
428            } else if (keyword == "TRANSITION") {
429                Transition transition;
430
431                is >> transition.from >> transition.to >> transition.p;
432
433                transitions.push_back(transition);
434
435                DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
436                        transition.to);
437            } else if (keyword == "INIT") {
438                // set the initial state as the active state
439                is >> currState;
440
441                init_state_set = true;
442
443                DPRINTF(TrafficGen, "Initial state: %d\n", currState);
444            }
445        }
446    }
447
448    if (!init_state_set)
449        fatal("%s: initial state not specified (add 'INIT <id>' line "
450              "to the config file)\n", name());
451
452    // resize and populate state transition matrix
453    transitionMatrix.resize(states.size());
454    for (size_t i = 0; i < states.size(); i++) {
455        transitionMatrix[i].resize(states.size());
456    }
457
458    for (vector<Transition>::iterator t = transitions.begin();
459         t != transitions.end(); ++t) {
460        transitionMatrix[t->from][t->to] = t->p;
461    }
462
463    // ensure the egress edges do not have a probability larger than
464    // one
465    for (size_t i = 0; i < states.size(); i++) {
466        double sum = 0;
467        for (size_t j = 0; j < states.size(); j++) {
468            sum += transitionMatrix[i][j];
469        }
470
471        // avoid comparing floating point numbers
472        if (abs(sum - 1.0) > 0.001)
473            fatal("%s has transition probability != 1 for state %d\n",
474                  name(), i);
475    }
476
477    // close input file
478    infile.close();
479}
480
481void
482TrafficGen::transition()
483{
484    // exit the current state
485    states[currState]->exit();
486
487    // determine next state
488    double p = random_mt.random<double>();
489    assert(currState < transitionMatrix.size());
490    double cumulative = 0.0;
491    size_t i = 0;
492    do {
493        cumulative += transitionMatrix[currState][i];
494        ++i;
495    } while (cumulative < p && i < transitionMatrix[currState].size());
496
497    enterState(i - 1);
498}
499
500void
501TrafficGen::enterState(uint32_t newState)
502{
503    DPRINTF(TrafficGen, "Transition to state %d\n", newState);
504
505    currState = newState;
506    // we could have been delayed and not transitioned on the exact
507    // tick when we were supposed to (due to back pressure when
508    // sending a packet)
509    nextTransitionTick = curTick() + states[currState]->duration;
510    states[currState]->enter();
511}
512
513void
514TrafficGen::recvReqRetry()
515{
516    assert(retryPkt != NULL);
517
518    DPRINTF(TrafficGen, "Received retry\n");
519    numRetries++;
520    // attempt to send the packet, and if we are successful start up
521    // the machinery again
522    if (port.sendTimingReq(retryPkt)) {
523        retryPkt = NULL;
524        // remember how much delay was incurred due to back-pressure
525        // when sending the request, we also use this to derive
526        // the tick for the next packet
527        Tick delay = curTick() - retryPktTick;
528        retryPktTick = 0;
529        retryTicks += delay;
530
531        if (drainState() != DrainState::Draining) {
532            // packet is sent, so find out when the next one is due
533            nextPacketTick = states[currState]->nextPacketTick(elasticReq,
534                                                               delay);
535            Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
536            schedule(updateEvent, std::max(curTick(), nextEventTick));
537        } else {
538            // shut things down
539            nextPacketTick = MaxTick;
540            nextTransitionTick = MaxTick;
541            signalDrainDone();
542        }
543    }
544}
545
546void
547TrafficGen::noProgress()
548{
549    fatal("TrafficGen %s spent %llu ticks without making progress",
550          name(), progressCheck);
551}
552
553void
554TrafficGen::regStats()
555{
556    ClockedObject::regStats();
557
558    // Initialise all the stats
559    using namespace Stats;
560
561    numPackets
562        .name(name() + ".numPackets")
563        .desc("Number of packets generated");
564
565    numRetries
566        .name(name() + ".numRetries")
567        .desc("Number of retries");
568
569    retryTicks
570        .name(name() + ".retryTicks")
571        .desc("Time spent waiting due to back-pressure (ticks)");
572}
573
574bool
575TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
576{
577    delete pkt;
578
579    return true;
580}
581