1/*
2 * Copyright (c) 2012-2013, 2016-2018 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#include "cpu/testers/traffic_gen/traffic_gen.hh"
42
43#include <libgen.h>
44#include <unistd.h>
45
46#include <fstream>
47#include <sstream>
48
49#include "base/intmath.hh"
50#include "base/random.hh"
51#include "debug/TrafficGen.hh"
52#include "params/TrafficGen.hh"
53#include "sim/stats.hh"
54#include "sim/system.hh"
55
56using namespace std;
57
58TrafficGen::TrafficGen(const TrafficGenParams* p)
59    : BaseTrafficGen(p),
60      configFile(p->config_file),
61      currState(0)
62{
63}
64
65TrafficGen*
66TrafficGenParams::create()
67{
68    return new TrafficGen(this);
69}
70
71void
72TrafficGen::init()
73{
74    BaseTrafficGen::init();
75
76    parseConfig();
77}
78
79void
80TrafficGen::initState()
81{
82    BaseTrafficGen::initState();
83
84    // when not restoring from a checkpoint, make sure we kick things off
85    if (system->isTimingMode()) {
86        DPRINTF(TrafficGen, "Timing mode, activating request generator\n");
87        start();
88    } else {
89        DPRINTF(TrafficGen,
90                "Traffic generator is only active in timing mode\n");
91    }
92}
93
94void
95TrafficGen::serialize(CheckpointOut &cp) const
96{
97    SERIALIZE_SCALAR(currState);
98
99    BaseTrafficGen::serialize(cp);
100}
101
102void
103TrafficGen::unserialize(CheckpointIn &cp)
104{
105    // @todo In the case of a stateful generator state such as the
106    // trace player we would also have to restore the position in the
107    // trace playback and the tick offset
108    UNSERIALIZE_SCALAR(currState);
109
110    BaseTrafficGen::unserialize(cp);
111}
112
113std::string
114TrafficGen::resolveFile(const std::string &name)
115{
116    // Do nothing for empty and absolute file names
117    if (name.empty() || name[0] == '/')
118        return name;
119
120    char *config_path = strdup(configFile.c_str());
121    char *config_dir = dirname(config_path);
122    const std::string config_rel = csprintf("%s/%s", config_dir, name);
123    free(config_path);
124
125    // Check the path relative to the config file first
126    if (access(config_rel.c_str(), R_OK) == 0)
127        return config_rel;
128
129    // Fall back to the old behavior and search relative to the
130    // current working directory.
131    return name;
132}
133
134void
135TrafficGen::parseConfig()
136{
137    // keep track of the transitions parsed to create the matrix when
138    // done
139    vector<Transition> transitions;
140
141    // open input file
142    ifstream infile;
143    infile.open(configFile.c_str(), ifstream::in);
144    if (!infile.is_open()) {
145        fatal("Traffic generator %s config file not found at %s\n",
146              name(), configFile);
147    }
148
149    bool init_state_set = false;
150
151    // read line by line and determine the action based on the first
152    // keyword
153    string keyword;
154    string line;
155
156    while (getline(infile, line).good()) {
157        // see if this line is a comment line, and if so skip it
158        if (line.find('#') != 1) {
159            // create an input stream for the tokenization
160            istringstream is(line);
161
162            // determine the keyword
163            is >> keyword;
164
165            if (keyword == "STATE") {
166                // parse the behaviour of this state
167                uint32_t id;
168                Tick duration;
169                string mode;
170
171                is >> id >> duration >> mode;
172
173                if (mode == "TRACE") {
174                    string traceFile;
175                    Addr addrOffset;
176
177                    is >> traceFile >> addrOffset;
178                    traceFile = resolveFile(traceFile);
179
180                    states[id] = createTrace(duration, traceFile, addrOffset);
181                    DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
182                } else if (mode == "IDLE") {
183                    states[id] = createIdle(duration);
184                    DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
185                } else if (mode == "EXIT") {
186                    states[id] = createExit(duration);
187                    DPRINTF(TrafficGen, "State: %d ExitGen\n", id);
188                } else if (mode == "LINEAR" || mode == "RANDOM" ||
189                           mode == "DRAM"   || mode == "DRAM_ROTATE") {
190                    uint32_t read_percent;
191                    Addr start_addr;
192                    Addr end_addr;
193                    Addr blocksize;
194                    Tick min_period;
195                    Tick max_period;
196                    Addr data_limit;
197
198                    is >> read_percent >> start_addr >> end_addr >>
199                        blocksize >> min_period >> max_period >> data_limit;
200
201                    DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
202                            " period %d to %d, %d%% reads\n",
203                            mode, start_addr, end_addr, blocksize, min_period,
204                            max_period, read_percent);
205
206
207                    if (mode == "LINEAR") {
208                        states[id] = createLinear(duration, start_addr,
209                                                  end_addr, blocksize,
210                                                  min_period, max_period,
211                                                  read_percent, data_limit);
212                        DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
213                    } else if (mode == "RANDOM") {
214                        states[id] = createRandom(duration, start_addr,
215                                                  end_addr, blocksize,
216                                                  min_period, max_period,
217                                                  read_percent, data_limit);
218                        DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
219                    } else if (mode == "DRAM" || mode == "DRAM_ROTATE") {
220                        // stride size (bytes) of the request for achieving
221                        // required hit length
222                        unsigned int stride_size;
223                        unsigned int page_size;
224                        unsigned int nbr_of_banks_DRAM;
225                        unsigned int nbr_of_banks_util;
226                        unsigned int addr_mapping;
227                        unsigned int nbr_of_ranks;
228
229                        is >> stride_size >> page_size >> nbr_of_banks_DRAM >>
230                            nbr_of_banks_util >> addr_mapping >>
231                            nbr_of_ranks;
232
233                        if (stride_size > page_size)
234                            warn("DRAM generator stride size (%d) is greater "
235                                 "than page size (%d)  of the memory\n",
236                                 blocksize, page_size);
237
238                        // count the number of sequential packets to
239                        // generate
240                        unsigned int num_seq_pkts = 1;
241
242                        if (stride_size > blocksize) {
243                            num_seq_pkts = divCeil(stride_size, blocksize);
244                            DPRINTF(TrafficGen, "stride size: %d "
245                                    "block size: %d, num_seq_pkts: %d\n",
246                                    stride_size, blocksize, num_seq_pkts);
247                        }
248
249                        if (mode == "DRAM") {
250                            states[id] = createDram(duration, start_addr,
251                                                    end_addr, blocksize,
252                                                    min_period, max_period,
253                                                    read_percent, data_limit,
254                                                    num_seq_pkts, page_size,
255                                                    nbr_of_banks_DRAM,
256                                                    nbr_of_banks_util,
257                                                    addr_mapping,
258                                                    nbr_of_ranks);
259                            DPRINTF(TrafficGen, "State: %d DramGen\n", id);
260                        } else {
261                            // Will rotate to the next rank after rotating
262                            // through all banks, for each command type.
263                            // In the 50% read case, series will be issued
264                            // for both RD & WR before the rank in incremented
265                            unsigned int max_seq_count_per_rank =
266                                (read_percent == 50) ? nbr_of_banks_util * 2
267                                                     : nbr_of_banks_util;
268
269                            states[id] = createDramRot(duration, start_addr,
270                                                       end_addr, blocksize,
271                                                       min_period, max_period,
272                                                       read_percent,
273                                                       data_limit,
274                                                       num_seq_pkts, page_size,
275                                                       nbr_of_banks_DRAM,
276                                                       nbr_of_banks_util,
277                                                       addr_mapping,
278                                                       nbr_of_ranks,
279                                                       max_seq_count_per_rank);
280                            DPRINTF(TrafficGen, "State: %d DramRotGen\n", id);
281                        }
282                    }
283                } else {
284                    fatal("%s: Unknown traffic generator mode: %s",
285                          name(), mode);
286                }
287            } else if (keyword == "TRANSITION") {
288                Transition transition;
289
290                is >> transition.from >> transition.to >> transition.p;
291
292                transitions.push_back(transition);
293
294                DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
295                        transition.to);
296            } else if (keyword == "INIT") {
297                // set the initial state as the active state
298                is >> currState;
299
300                init_state_set = true;
301
302                DPRINTF(TrafficGen, "Initial state: %d\n", currState);
303            }
304        }
305    }
306
307    if (!init_state_set)
308        fatal("%s: initial state not specified (add 'INIT <id>' line "
309              "to the config file)\n", name());
310
311    // resize and populate state transition matrix
312    transitionMatrix.resize(states.size());
313    for (size_t i = 0; i < states.size(); i++) {
314        transitionMatrix[i].resize(states.size());
315    }
316
317    for (vector<Transition>::iterator t = transitions.begin();
318         t != transitions.end(); ++t) {
319        transitionMatrix[t->from][t->to] = t->p;
320    }
321
322    // ensure the egress edges do not have a probability larger than
323    // one
324    for (size_t i = 0; i < states.size(); i++) {
325        double sum = 0;
326        for (size_t j = 0; j < states.size(); j++) {
327            sum += transitionMatrix[i][j];
328        }
329
330        // avoid comparing floating point numbers
331        if (abs(sum - 1.0) > 0.001)
332            fatal("%s has transition probability != 1 for state %d\n",
333                  name(), i);
334    }
335
336    // close input file
337    infile.close();
338}
339
340size_t
341TrafficGen::nextState()
342{
343    double p = random_mt.random<double>();
344    assert(currState < transitionMatrix.size());
345    double cumulative = 0.0;
346    size_t i = 0;
347    do {
348        cumulative += transitionMatrix[currState][i];
349        ++i;
350    } while (cumulative < p && i < transitionMatrix[currState].size());
351
352    return i - 1;
353}
354
355std::shared_ptr<BaseGen>
356TrafficGen::nextGenerator()
357{
358    // Return the initial state if there isn't an active generator,
359    // otherwise perform a state transition.
360    if (activeGenerator)
361        currState = nextState();
362
363    DPRINTF(TrafficGen, "Transition to state %d\n", currState);
364    return states[currState];
365}
366