2 * Copyright (c) 2012 ARM Limited
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.
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.
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.
37 * Authors: Thomas Grass
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"
53 TrafficGen::TrafficGen(const TrafficGenParams
* p
)
56 masterID(system
->getMasterId(name())),
57 port(name() + ".port", *this),
58 stateGraph(*this, port
, p
->config_file
, masterID
),
59 updateStateGraphEvent(this)
64 TrafficGenParams::create()
66 return new TrafficGen(this);
70 TrafficGen::getMasterPort(const string
& if_name
, PortID idx
)
72 if (if_name
== "port") {
75 return MemObject::getMasterPort(if_name
, idx
);
82 if (!port
.isConnected())
83 fatal("The port of %s is not connected!\n", name());
85 Enums::MemoryMode mode
= system
->getMemoryMode();
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");
91 // enter initial state
92 stateGraph
.enterState(stateGraph
.currState
);
95 "Traffic generator is only active in timing mode\n");
100 TrafficGen::initState()
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
);
108 "Traffic generator is only active in timing mode\n");
113 TrafficGen::drain(DrainManager
*dm
)
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
);
121 TrafficGen::serialize(ostream
&os
)
123 DPRINTF(Checkpoint
, "Serializing TrafficGen\n");
125 // save ticks of the graph event if it is scheduled
126 Tick nextStateGraphEvent
= updateStateGraphEvent
.scheduled() ?
127 updateStateGraphEvent
.when() : 0;
129 DPRINTF(TrafficGen
, "Saving nextStateGraphEvent=%llu\n",
130 nextStateGraphEvent
);
132 SERIALIZE_SCALAR(nextStateGraphEvent
);
134 Tick nextTransitionTick
= stateGraph
.nextTransitionTick
;
135 SERIALIZE_SCALAR(nextTransitionTick
);
137 // @todo: also serialise the current state, figure out the best
138 // way to drain and restore
142 TrafficGen::unserialize(Checkpoint
* cp
, const string
& section
)
144 // restore scheduled events
145 Tick nextStateGraphEvent
;
146 UNSERIALIZE_SCALAR(nextStateGraphEvent
);
147 if (nextStateGraphEvent
!= 0) {
148 schedule(updateStateGraphEvent
, nextStateGraphEvent
);
151 Tick nextTransitionTick
;
152 UNSERIALIZE_SCALAR(nextTransitionTick
);
153 stateGraph
.nextTransitionTick
= nextTransitionTick
;
157 TrafficGen::updateStateGraph()
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
);
166 // perform the update associated with the current update event
171 TrafficGen::StateGraph::parseConfig(const string
& file_name
,
174 // keep track of the transitions parsed to create the matrix when
176 vector
<Transition
> transitions
;
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
);
186 // read line by line and determine the action based on the first
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
);
197 // determine the keyword
200 if (keyword
== "STATE") {
201 // parse the behaviour of this state
206 is
>> id
>> duration
>> mode
;
208 if (mode
== "TRACE") {
212 is
>> traceFile
>> addrOffset
;
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
;
229 is
>> read_percent
>> start_addr
>> end_addr
>>
230 blocksize
>> min_period
>> max_period
>> data_limit
;
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
);
237 if (read_percent
> 100)
238 panic("%s cannot have more than 100% reads", name());
240 if (mode
== "LINEAR") {
241 states
[id
] = new LinearGen(port
, master_id
,
242 duration
, start_addr
,
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
,
251 min_period
, max_period
,
252 read_percent
, data_limit
);
253 DPRINTF(TrafficGen
, "State: %d RandomGen\n", id
);
256 fatal("%s: Unknown traffic generator mode: %s",
259 } else if (keyword
== "TRANSITION") {
260 Transition transition
;
262 is
>> transition
.from
>> transition
.to
>> transition
.p
;
264 transitions
.push_back(transition
);
266 DPRINTF(TrafficGen
, "Transition: %d -> %d\n", transition
.from
,
268 } else if (keyword
== "INIT") {
269 // set the initial state as the active state
272 DPRINTF(TrafficGen
, "Initial state: %d\n", currState
);
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());
283 for (vector
<Transition
>::iterator t
= transitions
.begin();
284 t
!= transitions
.end(); ++t
) {
285 transitionMatrix
[t
->from
][t
->to
] = t
->p
;
288 // ensure the egress edges do not have a probability larger than
290 for (size_t i
= 0; i
< transitions
.size(); i
++) {
292 for (size_t j
= 0; j
< transitions
.size(); j
++) {
293 sum
+= transitionMatrix
[i
][j
];
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",
307 TrafficGen::StateGraph::update()
309 // if we have reached the time for the next state transition, then
310 // perform the transition
311 if (curTick() >= nextTransitionTick
) {
314 // we are still in the current state and should execute it
315 states
[currState
]->execute();
320 TrafficGen::StateGraph::transition()
322 // exit the current state
323 states
[currState
]->exit();
325 // determine next state
326 double p
= random_mt
.gen_real1();
327 assert(currState
< transitionMatrix
.size());
328 double cumulative
= transitionMatrix
[currState
][0];
330 while (p
< cumulative
&& i
!= transitionMatrix
[currState
].size()) {
331 cumulative
+= transitionMatrix
[currState
][i
];
338 TrafficGen::StateGraph::enterState(uint32_t newState
)
340 DPRINTF(TrafficGen
, "Transition to state %d\n", newState
);
342 currState
= newState
;
343 nextTransitionTick
+= states
[currState
]->duration
;
344 states
[currState
]->enter();
347 TrafficGen::StateGraph::BaseGen::BaseGen(QueuedMasterPort
& _port
,
350 : port(_port
), masterID(master_id
), duration(_duration
)
355 TrafficGen::StateGraph::LinearGen::enter()
357 // reset the address and the data counter
358 nextAddr
= startAddr
;
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());
370 TrafficGen::StateGraph::LinearGen::execute()
372 // choose if we generate a read or a write here
373 bool isRead
= random_mt
.random
<uint8_t>(0, 100) < readPercent
;
375 if (readPercent
== 0)
378 DPRINTF(TrafficGen
, "LinearGen::execute: %c to addr %x, size %d\n",
379 isRead
? 'r' : 'w', nextAddr
, blocksize
);
381 // Create new request
382 Request::Flags flags
;
383 Request
*req
= new Request(nextAddr
, blocksize
, flags
, masterID
);
385 PacketPtr pkt
= new Packet(req
, isRead
? MemCmd::ReadReq
:
388 uint8_t* pkt_data
= new uint8_t[req
->getSize()];
389 pkt
->dataDynamicArray(pkt_data
);
392 memset(pkt_data
, 0xA, req
->getSize());
395 port
.schedTimingReq(pkt
, curTick());
397 // increment the address
398 nextAddr
+= blocksize
;
400 // Add the amount of data manipulated to the total
401 dataManipulated
+= blocksize
;
405 TrafficGen::StateGraph::LinearGen::nextExecuteTick()
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 "
412 nextAddr
= startAddr
;
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
423 // return the time when the next request should take place
424 return curTick() + random_mt
.random
<Tick
>(minPeriod
, maxPeriod
);
429 TrafficGen::StateGraph::RandomGen::enter()
431 // reset the counter to zero
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());
442 TrafficGen::StateGraph::RandomGen::execute()
444 // choose if we generate a read or a write here
445 bool isRead
= random_mt
.random
<uint8_t>(0, 100) < readPercent
;
447 if (readPercent
== 0)
450 // address of the request
451 Addr addr
= random_mt
.random
<Addr
>(startAddr
, endAddr
- 1);
453 // round down to start address of block
454 addr
-= addr
% blocksize
;
456 DPRINTF(TrafficGen
, "RandomGen::execute: %c to addr %x, size %d\n",
457 isRead
? 'r' : 'w', addr
, blocksize
);
459 // create new request packet
460 Request::Flags flags
;
461 Request
*req
= new Request(addr
, blocksize
, flags
, masterID
);
463 PacketPtr pkt
= new Packet(req
, isRead
? MemCmd::ReadReq
:
466 uint8_t* pkt_data
= new uint8_t[req
->getSize()];
467 pkt
->dataDynamicArray(pkt_data
);
470 memset(pkt_data
, 0xA, req
->getSize());
473 port
.schedTimingReq(pkt
, curTick());
475 // Add the amount of data manipulated to the total
476 dataManipulated
+= blocksize
;
480 TrafficGen::StateGraph::RandomGen::nextExecuteTick()
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
)
487 DPRINTF(TrafficGen
, "Data limit for RandomGen reached.\n");
488 // No more requests. Return MaxTick.
491 // Return the time when the next request should take place.
492 return curTick() + random_mt
.random
<Tick
>(minPeriod
, maxPeriod
);
497 TrafficGen::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.
502 if (!traceComplete
&& trace
.good()){
503 getline(trace
, buffer
);
504 DPRINTF(TrafficGen
, "Input trace: %s\n", buffer
);
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.
512 //Reset the nextElement to the default values
513 currElement
= nextElement
;
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
);
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
;
531 nextElement
.cmd
= MemCmd::ReadReq
;
532 } else if (rOrW
== 'w') {
533 nextElement
.cmd
= MemCmd::WriteReq
;
535 fatal("Incorrect trace file format!\n");
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.
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");
552 traceComplete
= true;
556 DPRINTF(TrafficGen
, "currElement: %c addr %d size %d tick %d (%d)\n",
557 currElement
.cmd
.isRead() ? 'r' : 'w',
559 currElement
.blocksize
,
560 currElement
.tick
+ tickOffset
,
563 DPRINTF(TrafficGen
, "nextElement: %c addr %d size %d tick %d (%d)\n",
564 nextElement
.cmd
.isRead() ? 'r' : 'w',
566 nextElement
.blocksize
,
567 nextElement
.tick
+ tickOffset
,
570 return tickOffset
+ nextElement
.tick
;
574 TrafficGen::StateGraph::TraceGen::enter() {
575 // update the trace offset to the time where the state was entered.
576 tickOffset
= curTick();
578 // seek to the start of the input trace file
579 trace
.seekg(0, ifstream::beg
);
586 traceComplete
= false;
590 TrafficGen::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());
595 DPRINTF(TrafficGen
, "TraceGen::execute: %c %d %d %d\n",
596 currElement
.cmd
.isRead() ? 'r' : 'w',
598 currElement
.blocksize
,
601 Request::Flags flags
;
602 Request
*req
= new Request(currElement
.addr
+ addrOffset
,
603 currElement
.blocksize
, flags
, masterID
);
605 PacketPtr pkt
= new Packet(req
, currElement
.cmd
);
607 uint8_t* pkt_data
= new uint8_t[req
->getSize()];
608 pkt
->dataDynamicArray(pkt_data
);
610 if (currElement
.cmd
.isWrite()) {
611 memset(pkt_data
, 0xA, req
->getSize());
614 port
.schedTimingReq(pkt
, curTick());
618 TrafficGen::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
623 warn("Trace player %s was unable to replay the entire trace!\n",
627 // clear any previous error flags for the input trace file
632 TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt
)