traffic_gen.cc revision 9241
1/* 2 * Copyright (c) 2012 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/random.hh" 45#include "cpu/testers/traffic_gen/traffic_gen.hh" 46#include "debug/Checkpoint.hh" 47#include "debug/TrafficGen.hh" 48#include "sim/stats.hh" 49#include "sim/system.hh" 50 51using namespace std; 52 53TrafficGen::TrafficGen(const TrafficGenParams* p) 54 : MemObject(p), 55 system(p->system), 56 masterID(system->getMasterId(name())), 57 port(name() + ".port", *this), 58 stateGraph(*this, port, p->config_file, masterID), 59 updateStateGraphEvent(this) 60{ 61} 62 63TrafficGen* 64TrafficGenParams::create() 65{ 66 return new TrafficGen(this); 67} 68 69MasterPort& 70TrafficGen::getMasterPort(const string& if_name, int idx) 71{ 72 if (if_name == "port") { 73 return port; 74 } else { 75 return MemObject::getMasterPort(if_name, idx); 76 } 77} 78 79void 80TrafficGen::init() 81{ 82 if (!port.isConnected()) 83 fatal("The port of %s is not connected!\n", name()); 84 85 Enums::MemoryMode mode = system->getMemoryMode(); 86 87 // if the system is in timing mode active the request generator 88 if (mode == Enums::timing) { 89 DPRINTF(TrafficGen, "Timing mode, activating request generator\n"); 90 91 // enter initial state 92 stateGraph.enterState(stateGraph.currState); 93 } else { 94 DPRINTF(TrafficGen, 95 "Traffic generator is only active in timing mode\n"); 96 } 97} 98 99void 100TrafficGen::initState() 101{ 102 // when not restoring from a checkpoint, make sure we kick things off 103 if (system->getMemoryMode() == Enums::timing) { 104 Tick nextStateGraphEvent = stateGraph.nextEventTick(); 105 schedule(updateStateGraphEvent, nextStateGraphEvent); 106 } else { 107 DPRINTF(TrafficGen, 108 "Traffic generator is only active in timing mode\n"); 109 } 110} 111 112unsigned int 113TrafficGen::drain(Event* drain_event) 114{ 115 // @todo we should also stop putting new requests in the queue and 116 // either interrupt the current state or wait for a transition 117 return port.drain(drain_event); 118} 119 120void 121TrafficGen::serialize(ostream &os) 122{ 123 DPRINTF(Checkpoint, "Serializing TrafficGen\n"); 124 125 // save ticks of the graph event if it is scheduled 126 Tick nextStateGraphEvent = updateStateGraphEvent.scheduled() ? 127 updateStateGraphEvent.when() : 0; 128 129 DPRINTF(TrafficGen, "Saving nextStateGraphEvent=%llu\n", 130 nextStateGraphEvent); 131 132 SERIALIZE_SCALAR(nextStateGraphEvent); 133 134 Tick nextTransitionTick = stateGraph.nextTransitionTick; 135 SERIALIZE_SCALAR(nextTransitionTick); 136 137 // @todo: also serialise the current state, figure out the best 138 // way to drain and restore 139} 140 141void 142TrafficGen::unserialize(Checkpoint* cp, const string& section) 143{ 144 // restore scheduled events 145 Tick nextStateGraphEvent; 146 UNSERIALIZE_SCALAR(nextStateGraphEvent); 147 if (nextStateGraphEvent != 0) { 148 schedule(updateStateGraphEvent, nextStateGraphEvent); 149 } 150 151 Tick nextTransitionTick; 152 UNSERIALIZE_SCALAR(nextTransitionTick); 153 stateGraph.nextTransitionTick = nextTransitionTick; 154} 155 156void 157TrafficGen::updateStateGraph() 158{ 159 // schedule next update event based on either the next execute 160 // tick or the next transition, which ever comes first 161 Tick nextStateGraphEvent = stateGraph.nextEventTick(); 162 DPRINTF(TrafficGen, "Updating state graph, next event at %lld\n", 163 nextStateGraphEvent); 164 schedule(updateStateGraphEvent, nextStateGraphEvent); 165 166 // perform the update associated with the current update event 167 stateGraph.update(); 168} 169 170void 171TrafficGen::StateGraph::parseConfig(const string& file_name, 172 MasterID master_id) 173{ 174 // keep track of the transitions parsed to create the matrix when 175 // done 176 vector<Transition> transitions; 177 178 // open input file 179 ifstream infile; 180 infile.open(file_name.c_str(), ifstream::in); 181 if (!infile.is_open()) { 182 fatal("Traffic generator %s config file not found at %s\n", 183 owner.name(), file_name); 184 } 185 186 // read line by line and determine the action based on the first 187 // keyword 188 string keyword; 189 string line; 190 191 while (getline(infile, line).good()) { 192 // see if this line is a comment line, and if so skip it 193 if (line.find('#') != 1) { 194 // create an input stream for the tokenization 195 istringstream is(line); 196 197 // determine the keyword 198 is >> keyword; 199 200 if (keyword == "STATE") { 201 // parse the behaviour of this state 202 uint32_t id; 203 Tick duration; 204 string mode; 205 206 is >> id >> duration >> mode; 207 208 if (mode == "TRACE") { 209 string traceFile; 210 Addr addrOffset; 211 212 is >> traceFile >> addrOffset; 213 214 states[id] = new TraceGen(port, master_id, duration, 215 traceFile, addrOffset); 216 DPRINTF(TrafficGen, "State: %d TraceGen\n", id); 217 } else if (mode == "IDLE") { 218 states[id] = new IdleGen(port, master_id, duration); 219 DPRINTF(TrafficGen, "State: %d IdleGen\n", id); 220 } else if (mode == "LINEAR" || mode == "RANDOM") { 221 uint32_t read_percent; 222 Addr start_addr; 223 Addr end_addr; 224 Addr blocksize; 225 Tick min_period; 226 Tick max_period; 227 Addr data_limit; 228 229 is >> read_percent >> start_addr >> end_addr >> 230 blocksize >> min_period >> max_period >> data_limit; 231 232 DPRINTF(TrafficGen, "%s, addr %x to %x, size %d," 233 " period %d to %d, %d%% reads\n", 234 mode, start_addr, end_addr, blocksize, min_period, 235 max_period, read_percent); 236 237 if (read_percent > 100) 238 panic("%s cannot have more than 100% reads", name()); 239 240 if (mode == "LINEAR") { 241 states[id] = new LinearGen(port, master_id, 242 duration, start_addr, 243 end_addr, blocksize, 244 min_period, max_period, 245 read_percent, data_limit); 246 DPRINTF(TrafficGen, "State: %d LinearGen\n", id); 247 } else if (mode == "RANDOM") { 248 states[id] = new RandomGen(port, master_id, 249 duration, start_addr, 250 end_addr, blocksize, 251 min_period, max_period, 252 read_percent, data_limit); 253 DPRINTF(TrafficGen, "State: %d RandomGen\n", id); 254 } 255 } else { 256 fatal("%s: Unknown traffic generator mode: %s", 257 name(), mode); 258 } 259 } else if (keyword == "TRANSITION") { 260 Transition transition; 261 262 is >> transition.from >> transition.to >> transition.p; 263 264 transitions.push_back(transition); 265 266 DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from, 267 transition.to); 268 } else if (keyword == "INIT") { 269 // set the initial state as the active state 270 is >> currState; 271 272 DPRINTF(TrafficGen, "Initial state: %d\n", currState); 273 } 274 } 275 } 276 277 // resize and populate state transition matrix 278 transitionMatrix.resize(transitions.size()); 279 for (size_t i = 0; i < transitions.size(); i++) { 280 transitionMatrix[i].resize(transitions.size()); 281 } 282 283 for (vector<Transition>::iterator t = transitions.begin(); 284 t != transitions.end(); ++t) { 285 transitionMatrix[t->from][t->to] = t->p; 286 } 287 288 // ensure the egress edges do not have a probability larger than 289 // one 290 for (size_t i = 0; i < transitions.size(); i++) { 291 double sum = 0; 292 for (size_t j = 0; j < transitions.size(); j++) { 293 sum += transitionMatrix[i][j]; 294 } 295 296 // avoid comparing floating point numbers 297 if (abs(sum - 1.0) > 0.001) 298 fatal("%s has transition probability != 1 for state %d\n", 299 name(), i); 300 } 301 302 // close input file 303 infile.close(); 304} 305 306void 307TrafficGen::StateGraph::update() 308{ 309 // if we have reached the time for the next state transition, then 310 // perform the transition 311 if (curTick() >= nextTransitionTick) { 312 transition(); 313 } else { 314 // we are still in the current state and should execute it 315 states[currState]->execute(); 316 } 317} 318 319void 320TrafficGen::StateGraph::transition() 321{ 322 // exit the current state 323 states[currState]->exit(); 324 325 // determine next state 326 double p = random_mt.gen_real1(); 327 assert(currState < transitionMatrix.size()); 328 double cumulative = transitionMatrix[currState][0]; 329 size_t i = 1; 330 while (p < cumulative && i != transitionMatrix[currState].size()) { 331 cumulative += transitionMatrix[currState][i]; 332 ++i; 333 } 334 enterState(i); 335} 336 337void 338TrafficGen::StateGraph::enterState(uint32_t newState) 339{ 340 DPRINTF(TrafficGen, "Transition to state %d\n", newState); 341 342 currState = newState; 343 nextTransitionTick += states[currState]->duration; 344 states[currState]->enter(); 345} 346 347TrafficGen::StateGraph::BaseGen::BaseGen(QueuedMasterPort& _port, 348 MasterID master_id, 349 Tick _duration) 350 : port(_port), masterID(master_id), duration(_duration) 351{ 352} 353 354void 355TrafficGen::StateGraph::LinearGen::enter() 356{ 357 // reset the address and the data counter 358 nextAddr = startAddr; 359 dataManipulated = 0; 360 361 // this test only needs to happen once, but cannot be performed 362 // before init() is called and the ports are connected 363 if (port.deviceBlockSize() && blocksize > port.deviceBlockSize()) 364 fatal("TrafficGen %s block size (%d) is larger than port" 365 " block size (%d)\n", blocksize, port.deviceBlockSize()); 366 367} 368 369void 370TrafficGen::StateGraph::LinearGen::execute() 371{ 372 // choose if we generate a read or a write here 373 bool isRead = random_mt.random<uint8_t>(0, 100) < readPercent; 374 375 if (readPercent == 0) 376 assert(!isRead); 377 378 DPRINTF(TrafficGen, "LinearGen::execute: %c to addr %x, size %d\n", 379 isRead ? 'r' : 'w', nextAddr, blocksize); 380 381 // Create new request 382 Request::Flags flags; 383 Request *req = new Request(nextAddr, blocksize, flags, masterID); 384 385 PacketPtr pkt = new Packet(req, isRead ? MemCmd::ReadReq : 386 MemCmd::WriteReq); 387 388 uint8_t* pkt_data = new uint8_t[req->getSize()]; 389 pkt->dataDynamicArray(pkt_data); 390 391 if (!isRead) { 392 memset(pkt_data, 0xA, req->getSize()); 393 } 394 395 port.schedTimingReq(pkt, curTick()); 396 397 // increment the address 398 nextAddr += blocksize; 399 400 // Add the amount of data manipulated to the total 401 dataManipulated += blocksize; 402} 403 404Tick 405TrafficGen::StateGraph::LinearGen::nextExecuteTick() 406{ 407 // If we have reached the end of the address space, reset the 408 // address to the start of the range 409 if (nextAddr + blocksize > endAddr) { 410 DPRINTF(TrafficGen, "Wrapping address to the start of " 411 "the range\n"); 412 nextAddr = startAddr; 413 } 414 415 // Check to see if we have reached the data limit. If dataLimit is 416 // zero we do not have a data limit and therefore we will keep 417 // generating requests for the entire residency in this state. 418 if (dataLimit && dataManipulated >= dataLimit) { 419 DPRINTF(TrafficGen, "Data limit for LinearGen reached.\n"); 420 // there are no more requests, therefore return MaxTick 421 return MaxTick; 422 } else { 423 // return the time when the next request should take place 424 return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod); 425 } 426} 427 428void 429TrafficGen::StateGraph::RandomGen::enter() 430{ 431 // reset the counter to zero 432 dataManipulated = 0; 433 434 // this test only needs to happen once, but cannot be performed 435 // before init() is called and the ports are connected 436 if (port.deviceBlockSize() && blocksize > port.deviceBlockSize()) 437 fatal("TrafficGen %s block size (%d) is larger than port" 438 " block size (%d)\n", name(), blocksize, port.deviceBlockSize()); 439} 440 441void 442TrafficGen::StateGraph::RandomGen::execute() 443{ 444 // choose if we generate a read or a write here 445 bool isRead = random_mt.random<uint8_t>(0, 100) < readPercent; 446 447 if (readPercent == 0) 448 assert(!isRead); 449 450 // address of the request 451 Addr addr = random_mt.random<Addr>(startAddr, endAddr - 1); 452 453 // round down to start address of block 454 addr -= addr % blocksize; 455 456 DPRINTF(TrafficGen, "RandomGen::execute: %c to addr %x, size %d\n", 457 isRead ? 'r' : 'w', addr, blocksize); 458 459 // create new request packet 460 Request::Flags flags; 461 Request *req = new Request(addr, blocksize, flags, masterID); 462 463 PacketPtr pkt = new Packet(req, isRead ? MemCmd::ReadReq : 464 MemCmd::WriteReq); 465 466 uint8_t* pkt_data = new uint8_t[req->getSize()]; 467 pkt->dataDynamicArray(pkt_data); 468 469 if (!isRead) { 470 memset(pkt_data, 0xA, req->getSize()); 471 } 472 473 port.schedTimingReq(pkt, curTick()); 474 475 // Add the amount of data manipulated to the total 476 dataManipulated += blocksize; 477} 478 479Tick 480TrafficGen::StateGraph::RandomGen::nextExecuteTick() 481{ 482 // Check to see if we have reached the data limit. If dataLimit is 483 // zero we do not have a data limit and therefore we will keep 484 // generating requests for the entire residency in this state. 485 if (dataLimit && dataManipulated >= dataLimit) 486 { 487 DPRINTF(TrafficGen, "Data limit for RandomGen reached.\n"); 488 // No more requests. Return MaxTick. 489 return MaxTick; 490 } else { 491 // Return the time when the next request should take place. 492 return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod); 493 } 494} 495 496Tick 497TrafficGen::StateGraph::TraceGen::nextExecuteTick() { 498 // We need to look at the next line to calculate the next time an 499 // event occurs, or potentially return MaxTick to signal that 500 // nothing has to be done. 501 string buffer; 502 if (!traceComplete && trace.good()){ 503 getline(trace, buffer); 504 DPRINTF(TrafficGen, "Input trace: %s\n", buffer); 505 } else { 506 // We are at the end of the file, thus we have no more data in 507 // the trace Return MaxTick to signal that there will be no 508 // more transactions in this active period for the state. 509 return MaxTick; 510 } 511 512 //Reset the nextElement to the default values 513 currElement = nextElement; 514 nextElement.clear(); 515 516 // Check that we have something to process. This assume no EOF at 517 // the end of the line. 518 if (buffer.size() > 0 && !trace.eof()) { 519 istringstream iss(buffer); 520 521 char rOrW, ch; 522 iss >> rOrW; 523 iss >> ch; assert(ch == ','); 524 iss >> nextElement.addr; 525 iss >> ch; assert(ch == ','); 526 iss >> nextElement.blocksize; 527 iss >> ch; assert(ch == ','); 528 iss >> nextElement.tick; 529 530 if (rOrW == 'r') { 531 nextElement.cmd = MemCmd::ReadReq; 532 } else if (rOrW == 'w') { 533 nextElement.cmd = MemCmd::WriteReq; 534 } else { 535 fatal("Incorrect trace file format!\n"); 536 } 537 } 538 539 // Check that we have a valid request 540 if (!nextElement.isValid()) { 541 // If it is not valid, assume that we have reached the end of 542 // the trace. Even if this is not the case, we do not know 543 // what to do with the request as it makes no sense. 544 if (trace.good()) { 545 // Trace is good, therefore we are not at the end of the 546 // file. This means that the input trace cannot be read 547 // correctly or it contains data that makes no sense. 548 warn("Unable to read the trace file format\n"); 549 warn("%s", buffer); 550 } 551 552 traceComplete = true; 553 return MaxTick; 554 } 555 556 DPRINTF(TrafficGen, "currElement: %c addr %d size %d tick %d (%d)\n", 557 currElement.cmd.isRead() ? 'r' : 'w', 558 currElement.addr, 559 currElement.blocksize, 560 currElement.tick + tickOffset, 561 currElement.tick); 562 563 DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n", 564 nextElement.cmd.isRead() ? 'r' : 'w', 565 nextElement.addr, 566 nextElement.blocksize, 567 nextElement.tick + tickOffset, 568 nextElement.tick); 569 570 return tickOffset + nextElement.tick; 571} 572 573void 574TrafficGen::StateGraph::TraceGen::enter() { 575 // update the trace offset to the time where the state was entered. 576 tickOffset = curTick(); 577 578 // seek to the start of the input trace file 579 trace.seekg(0, ifstream::beg); 580 trace.clear(); 581 582 // clear everything 583 nextElement.clear(); 584 currElement.clear(); 585 586 traceComplete = false; 587} 588 589void 590TrafficGen::StateGraph::TraceGen::execute() { 591 // it is the responsibility of nextExecuteTick to prevent the 592 // state graph from executing the state if it should not 593 assert(currElement.isValid()); 594 595 DPRINTF(TrafficGen, "TraceGen::execute: %c %d %d %d\n", 596 currElement.cmd.isRead() ? 'r' : 'w', 597 currElement.addr, 598 currElement.blocksize, 599 currElement.tick); 600 601 Request::Flags flags; 602 Request *req = new Request(currElement.addr + addrOffset, 603 currElement.blocksize, flags, masterID); 604 605 PacketPtr pkt = new Packet(req, currElement.cmd); 606 607 uint8_t* pkt_data = new uint8_t[req->getSize()]; 608 pkt->dataDynamicArray(pkt_data); 609 610 if (currElement.cmd.isWrite()) { 611 memset(pkt_data, 0xA, req->getSize()); 612 } 613 614 port.schedTimingReq(pkt, curTick()); 615} 616 617void 618TrafficGen::StateGraph::TraceGen::exit() { 619 // Check if we reached the end of the trace file. If we did not 620 // then we want to generate a warning stating that not the entire 621 // trace was played. 622 if (!trace.eof()) { 623 warn("Trace player %s was unable to replay the entire trace!\n", 624 name()); 625 } 626 627 // clear any previous error flags for the input trace file 628 trace.clear(); 629} 630 631bool 632TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt) 633{ 634 delete pkt->req; 635 delete pkt; 636 637 return true; 638} 639