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