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 69BaseMasterPort& 70TrafficGen::getMasterPort(const string& if_name, PortID 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(DrainManager *dm) 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(dm); 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
|
378 379 DPRINTF(TrafficGen, "LinearGen::execute: %c to addr %x, size %d\n", 380 isRead ? 'r' : 'w', nextAddr, blocksize); 381 382 // Create new request 383 Request::Flags flags; 384 Request *req = new Request(nextAddr, blocksize, flags, masterID); 385 386 PacketPtr pkt = new Packet(req, isRead ? MemCmd::ReadReq : 387 MemCmd::WriteReq); 388 389 uint8_t* pkt_data = new uint8_t[req->getSize()]; 390 pkt->dataDynamicArray(pkt_data); 391 392 if (!isRead) { 393 memset(pkt_data, 0xA, req->getSize()); 394 } 395 396 port.schedTimingReq(pkt, curTick()); 397 398 // increment the address 399 nextAddr += blocksize; 400 401 // Add the amount of data manipulated to the total 402 dataManipulated += blocksize; 403} 404 405Tick 406TrafficGen::StateGraph::LinearGen::nextExecuteTick() 407{ 408 // If we have reached the end of the address space, reset the 409 // address to the start of the range 410 if (nextAddr + blocksize > endAddr) { 411 DPRINTF(TrafficGen, "Wrapping address to the start of " 412 "the range\n"); 413 nextAddr = startAddr; 414 } 415 416 // Check to see if we have reached the data limit. If dataLimit is 417 // zero we do not have a data limit and therefore we will keep 418 // generating requests for the entire residency in this state. 419 if (dataLimit && dataManipulated >= dataLimit) { 420 DPRINTF(TrafficGen, "Data limit for LinearGen reached.\n"); 421 // there are no more requests, therefore return MaxTick 422 return MaxTick; 423 } else { 424 // return the time when the next request should take place 425 return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod); 426 } 427} 428 429void 430TrafficGen::StateGraph::RandomGen::enter() 431{ 432 // reset the counter to zero 433 dataManipulated = 0; 434 435 // this test only needs to happen once, but cannot be performed 436 // before init() is called and the ports are connected 437 if (port.deviceBlockSize() && blocksize > port.deviceBlockSize()) 438 fatal("TrafficGen %s block size (%d) is larger than port" 439 " block size (%d)\n", name(), blocksize, port.deviceBlockSize()); 440} 441 442void 443TrafficGen::StateGraph::RandomGen::execute() 444{ 445 // choose if we generate a read or a write here
|
451 452 // address of the request 453 Addr addr = random_mt.random<Addr>(startAddr, endAddr - 1); 454 455 // round down to start address of block 456 addr -= addr % blocksize; 457 458 DPRINTF(TrafficGen, "RandomGen::execute: %c to addr %x, size %d\n", 459 isRead ? 'r' : 'w', addr, blocksize); 460 461 // create new request packet 462 Request::Flags flags; 463 Request *req = new Request(addr, blocksize, flags, masterID); 464 465 PacketPtr pkt = new Packet(req, isRead ? MemCmd::ReadReq : 466 MemCmd::WriteReq); 467 468 uint8_t* pkt_data = new uint8_t[req->getSize()]; 469 pkt->dataDynamicArray(pkt_data); 470 471 if (!isRead) { 472 memset(pkt_data, 0xA, req->getSize()); 473 } 474 475 port.schedTimingReq(pkt, curTick()); 476 477 // Add the amount of data manipulated to the total 478 dataManipulated += blocksize; 479} 480 481Tick 482TrafficGen::StateGraph::RandomGen::nextExecuteTick() 483{ 484 // Check to see if we have reached the data limit. If dataLimit is 485 // zero we do not have a data limit and therefore we will keep 486 // generating requests for the entire residency in this state. 487 if (dataLimit && dataManipulated >= dataLimit) 488 { 489 DPRINTF(TrafficGen, "Data limit for RandomGen reached.\n"); 490 // No more requests. Return MaxTick. 491 return MaxTick; 492 } else { 493 // Return the time when the next request should take place. 494 return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod); 495 } 496} 497 498Tick 499TrafficGen::StateGraph::TraceGen::nextExecuteTick() { 500 // We need to look at the next line to calculate the next time an 501 // event occurs, or potentially return MaxTick to signal that 502 // nothing has to be done. 503 string buffer; 504 if (!traceComplete && trace.good()){ 505 getline(trace, buffer); 506 DPRINTF(TrafficGen, "Input trace: %s\n", buffer); 507 } else { 508 // We are at the end of the file, thus we have no more data in 509 // the trace Return MaxTick to signal that there will be no 510 // more transactions in this active period for the state. 511 return MaxTick; 512 } 513 514 //Reset the nextElement to the default values 515 currElement = nextElement; 516 nextElement.clear(); 517 518 // Check that we have something to process. This assume no EOF at 519 // the end of the line. 520 if (buffer.size() > 0 && !trace.eof()) { 521 istringstream iss(buffer); 522 523 char rOrW, ch; 524 iss >> rOrW; 525 iss >> ch; assert(ch == ','); 526 iss >> nextElement.addr; 527 iss >> ch; assert(ch == ','); 528 iss >> nextElement.blocksize; 529 iss >> ch; assert(ch == ','); 530 iss >> nextElement.tick; 531 532 if (rOrW == 'r') { 533 nextElement.cmd = MemCmd::ReadReq; 534 } else if (rOrW == 'w') { 535 nextElement.cmd = MemCmd::WriteReq; 536 } else { 537 fatal("Incorrect trace file format!\n"); 538 } 539 } 540 541 // Check that we have a valid request 542 if (!nextElement.isValid()) { 543 // If it is not valid, assume that we have reached the end of 544 // the trace. Even if this is not the case, we do not know 545 // what to do with the request as it makes no sense. 546 if (trace.good()) { 547 // Trace is good, therefore we are not at the end of the 548 // file. This means that the input trace cannot be read 549 // correctly or it contains data that makes no sense. 550 warn("Unable to read the trace file format\n"); 551 warn("%s", buffer); 552 } 553 554 traceComplete = true; 555 return MaxTick; 556 } 557 558 DPRINTF(TrafficGen, "currElement: %c addr %d size %d tick %d (%d)\n", 559 currElement.cmd.isRead() ? 'r' : 'w', 560 currElement.addr, 561 currElement.blocksize, 562 currElement.tick + tickOffset, 563 currElement.tick); 564 565 DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n", 566 nextElement.cmd.isRead() ? 'r' : 'w', 567 nextElement.addr, 568 nextElement.blocksize, 569 nextElement.tick + tickOffset, 570 nextElement.tick); 571 572 return tickOffset + nextElement.tick; 573} 574 575void 576TrafficGen::StateGraph::TraceGen::enter() { 577 // update the trace offset to the time where the state was entered. 578 tickOffset = curTick(); 579 580 // seek to the start of the input trace file 581 trace.seekg(0, ifstream::beg); 582 trace.clear(); 583 584 // clear everything 585 nextElement.clear(); 586 currElement.clear(); 587 588 traceComplete = false; 589} 590 591void 592TrafficGen::StateGraph::TraceGen::execute() { 593 // it is the responsibility of nextExecuteTick to prevent the 594 // state graph from executing the state if it should not 595 assert(currElement.isValid()); 596 597 DPRINTF(TrafficGen, "TraceGen::execute: %c %d %d %d\n", 598 currElement.cmd.isRead() ? 'r' : 'w', 599 currElement.addr, 600 currElement.blocksize, 601 currElement.tick); 602 603 Request::Flags flags; 604 Request *req = new Request(currElement.addr + addrOffset, 605 currElement.blocksize, flags, masterID); 606 607 PacketPtr pkt = new Packet(req, currElement.cmd); 608 609 uint8_t* pkt_data = new uint8_t[req->getSize()]; 610 pkt->dataDynamicArray(pkt_data); 611 612 if (currElement.cmd.isWrite()) { 613 memset(pkt_data, 0xA, req->getSize()); 614 } 615 616 port.schedTimingReq(pkt, curTick()); 617} 618 619void 620TrafficGen::StateGraph::TraceGen::exit() { 621 // Check if we reached the end of the trace file. If we did not 622 // then we want to generate a warning stating that not the entire 623 // trace was played. 624 if (!trace.eof()) { 625 warn("Trace player %s was unable to replay the entire trace!\n", 626 name()); 627 } 628 629 // clear any previous error flags for the input trace file 630 trace.clear(); 631} 632 633bool 634TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt) 635{ 636 delete pkt->req; 637 delete pkt; 638 639 return true; 640}
|