traffic_gen.cc revision 11540
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#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), 65 nextTransitionTick(0), 66 nextPacketTick(0), 67 currState(0), 68 port(name() + ".port", *this), 69 retryPkt(NULL), 70 retryPktTick(0), 71 updateEvent(this), 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 == "LINEAR" || mode == "RANDOM" || 309 mode == "DRAM" || mode == "DRAM_ROTATE") { 310 uint32_t read_percent; 311 Addr start_addr; 312 Addr end_addr; 313 Addr blocksize; 314 Tick min_period; 315 Tick max_period; 316 Addr data_limit; 317 318 is >> read_percent >> start_addr >> end_addr >> 319 blocksize >> min_period >> max_period >> data_limit; 320 321 DPRINTF(TrafficGen, "%s, addr %x to %x, size %d," 322 " period %d to %d, %d%% reads\n", 323 mode, start_addr, end_addr, blocksize, min_period, 324 max_period, read_percent); 325 326 327 if (blocksize > system->cacheLineSize()) 328 fatal("TrafficGen %s block size (%d) is larger than " 329 "cache line size (%d)\n", name(), 330 blocksize, system->cacheLineSize()); 331 332 if (read_percent > 100) 333 fatal("%s cannot have more than 100% reads", name()); 334 335 if (min_period > max_period) 336 fatal("%s cannot have min_period > max_period", name()); 337 338 if (mode == "LINEAR") { 339 states[id] = new LinearGen(name(), masterID, 340 duration, start_addr, 341 end_addr, blocksize, 342 min_period, max_period, 343 read_percent, data_limit); 344 DPRINTF(TrafficGen, "State: %d LinearGen\n", id); 345 } else if (mode == "RANDOM") { 346 states[id] = new RandomGen(name(), masterID, 347 duration, start_addr, 348 end_addr, blocksize, 349 min_period, max_period, 350 read_percent, data_limit); 351 DPRINTF(TrafficGen, "State: %d RandomGen\n", id); 352 } else if (mode == "DRAM" || mode == "DRAM_ROTATE") { 353 // stride size (bytes) of the request for achieving 354 // required hit length 355 unsigned int stride_size; 356 unsigned int page_size; 357 unsigned int nbr_of_banks_DRAM; 358 unsigned int nbr_of_banks_util; 359 unsigned int addr_mapping; 360 unsigned int nbr_of_ranks; 361 362 is >> stride_size >> page_size >> nbr_of_banks_DRAM >> 363 nbr_of_banks_util >> addr_mapping >> 364 nbr_of_ranks; 365 366 if (stride_size > page_size) 367 warn("DRAM generator stride size (%d) is greater " 368 "than page size (%d) of the memory\n", 369 blocksize, page_size); 370 371 if (nbr_of_banks_util > nbr_of_banks_DRAM) 372 fatal("Attempting to use more banks (%d) than " 373 "what is available (%d)\n", 374 nbr_of_banks_util, nbr_of_banks_DRAM); 375 376 // count the number of sequential packets to 377 // generate 378 unsigned int num_seq_pkts = 1; 379 380 if (stride_size > blocksize) { 381 num_seq_pkts = divCeil(stride_size, blocksize); 382 DPRINTF(TrafficGen, "stride size: %d " 383 "block size: %d, num_seq_pkts: %d\n", 384 stride_size, blocksize, num_seq_pkts); 385 } 386 387 if (mode == "DRAM") { 388 states[id] = new DramGen(name(), masterID, 389 duration, start_addr, 390 end_addr, blocksize, 391 min_period, max_period, 392 read_percent, data_limit, 393 num_seq_pkts, page_size, 394 nbr_of_banks_DRAM, 395 nbr_of_banks_util, 396 addr_mapping, 397 nbr_of_ranks); 398 DPRINTF(TrafficGen, "State: %d DramGen\n", id); 399 } else { 400 // Will rotate to the next rank after rotating 401 // through all banks, for each command type. 402 // In the 50% read case, series will be issued 403 // for both RD & WR before the rank in incremented 404 unsigned int max_seq_count_per_rank = 405 (read_percent == 50) ? nbr_of_banks_util * 2 406 : nbr_of_banks_util; 407 408 states[id] = new DramRotGen(name(), masterID, 409 duration, start_addr, 410 end_addr, blocksize, 411 min_period, max_period, 412 read_percent, data_limit, 413 num_seq_pkts, page_size, 414 nbr_of_banks_DRAM, 415 nbr_of_banks_util, 416 addr_mapping, 417 nbr_of_ranks, 418 max_seq_count_per_rank); 419 DPRINTF(TrafficGen, "State: %d DramRotGen\n", id); 420 } 421 } 422 } else { 423 fatal("%s: Unknown traffic generator mode: %s", 424 name(), mode); 425 } 426 } else if (keyword == "TRANSITION") { 427 Transition transition; 428 429 is >> transition.from >> transition.to >> transition.p; 430 431 transitions.push_back(transition); 432 433 DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from, 434 transition.to); 435 } else if (keyword == "INIT") { 436 // set the initial state as the active state 437 is >> currState; 438 439 init_state_set = true; 440 441 DPRINTF(TrafficGen, "Initial state: %d\n", currState); 442 } 443 } 444 } 445 446 if (!init_state_set) 447 fatal("%s: initial state not specified (add 'INIT <id>' line " 448 "to the config file)\n", name()); 449 450 // resize and populate state transition matrix 451 transitionMatrix.resize(states.size()); 452 for (size_t i = 0; i < states.size(); i++) { 453 transitionMatrix[i].resize(states.size()); 454 } 455 456 for (vector<Transition>::iterator t = transitions.begin(); 457 t != transitions.end(); ++t) { 458 transitionMatrix[t->from][t->to] = t->p; 459 } 460 461 // ensure the egress edges do not have a probability larger than 462 // one 463 for (size_t i = 0; i < states.size(); i++) { 464 double sum = 0; 465 for (size_t j = 0; j < states.size(); j++) { 466 sum += transitionMatrix[i][j]; 467 } 468 469 // avoid comparing floating point numbers 470 if (abs(sum - 1.0) > 0.001) 471 fatal("%s has transition probability != 1 for state %d\n", 472 name(), i); 473 } 474 475 // close input file 476 infile.close(); 477} 478 479void 480TrafficGen::transition() 481{ 482 // exit the current state 483 states[currState]->exit(); 484 485 // determine next state 486 double p = random_mt.random<double>(); 487 assert(currState < transitionMatrix.size()); 488 double cumulative = 0.0; 489 size_t i = 0; 490 do { 491 cumulative += transitionMatrix[currState][i]; 492 ++i; 493 } while (cumulative < p && i < transitionMatrix[currState].size()); 494 495 enterState(i - 1); 496} 497 498void 499TrafficGen::enterState(uint32_t newState) 500{ 501 DPRINTF(TrafficGen, "Transition to state %d\n", newState); 502 503 currState = newState; 504 // we could have been delayed and not transitioned on the exact 505 // tick when we were supposed to (due to back pressure when 506 // sending a packet) 507 nextTransitionTick = curTick() + states[currState]->duration; 508 states[currState]->enter(); 509} 510 511void 512TrafficGen::recvReqRetry() 513{ 514 assert(retryPkt != NULL); 515 516 DPRINTF(TrafficGen, "Received retry\n"); 517 numRetries++; 518 // attempt to send the packet, and if we are successful start up 519 // the machinery again 520 if (port.sendTimingReq(retryPkt)) { 521 retryPkt = NULL; 522 // remember how much delay was incurred due to back-pressure 523 // when sending the request, we also use this to derive 524 // the tick for the next packet 525 Tick delay = curTick() - retryPktTick; 526 retryPktTick = 0; 527 retryTicks += delay; 528 529 if (drainState() != DrainState::Draining) { 530 // packet is sent, so find out when the next one is due 531 nextPacketTick = states[currState]->nextPacketTick(elasticReq, 532 delay); 533 Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick); 534 schedule(updateEvent, std::max(curTick(), nextEventTick)); 535 } else { 536 // shut things down 537 nextPacketTick = MaxTick; 538 nextTransitionTick = MaxTick; 539 signalDrainDone(); 540 } 541 } 542} 543 544void 545TrafficGen::noProgress() 546{ 547 fatal("TrafficGen %s spent %llu ticks without making progress", 548 name(), progressCheck); 549} 550 551void 552TrafficGen::regStats() 553{ 554 ClockedObject::regStats(); 555 556 // Initialise all the stats 557 using namespace Stats; 558 559 numPackets 560 .name(name() + ".numPackets") 561 .desc("Number of packets generated"); 562 563 numRetries 564 .name(name() + ".numRetries") 565 .desc("Number of retries"); 566 567 retryTicks 568 .name(name() + ".retryTicks") 569 .desc("Time spent waiting due to back-pressure (ticks)"); 570} 571 572bool 573TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt) 574{ 575 delete pkt->req; 576 delete pkt; 577 578 return true; 579} 580