traffic_gen.cc (9241:6cfb9a7acb1b) traffic_gen.cc (9294:8fb03b13de02)
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
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)
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(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}
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}