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