1/*
2 * Copyright (c) 2012-2013, 2016-2019 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/base.hh"
42
43#include <sstream>
44
45#include "base/intmath.hh"
46#include "base/random.hh"
47#include "config/have_protobuf.hh"
48#include "cpu/testers/traffic_gen/base_gen.hh"
49#include "cpu/testers/traffic_gen/dram_gen.hh"
50#include "cpu/testers/traffic_gen/dram_rot_gen.hh"
51#include "cpu/testers/traffic_gen/exit_gen.hh"
52#include "cpu/testers/traffic_gen/idle_gen.hh"
53#include "cpu/testers/traffic_gen/linear_gen.hh"
54#include "cpu/testers/traffic_gen/random_gen.hh"
55#include "cpu/testers/traffic_gen/stream_gen.hh"
56#include "debug/Checkpoint.hh"
57#include "debug/TrafficGen.hh"
58#include "params/BaseTrafficGen.hh"
59#include "sim/sim_exit.hh"
60#include "sim/stats.hh"
61#include "sim/system.hh"
62
63#if HAVE_PROTOBUF
64#include "cpu/testers/traffic_gen/trace_gen.hh"
65#endif
66
67
68using namespace std;
69
70BaseTrafficGen::BaseTrafficGen(const BaseTrafficGenParams* p)
71    : ClockedObject(p),
72      system(p->system),
73      elasticReq(p->elastic_req),
74      progressCheck(p->progress_check),
75      noProgressEvent([this]{ noProgress(); }, name()),
76      nextTransitionTick(0),
77      nextPacketTick(0),
78      maxOutstandingReqs(p->max_outstanding_reqs),
79      port(name() + ".port", *this),
80      retryPkt(NULL),
81      retryPktTick(0), blockedWaitingResp(false),
82      updateEvent([this]{ update(); }, name()),
83      stats(this),
84      masterID(system->getMasterId(this)),
85      streamGenerator(StreamGen::create(p))
86{
87}
88
89BaseTrafficGen::~BaseTrafficGen()
90{
91}
92
93Port &
94BaseTrafficGen::getPort(const string &if_name, PortID idx)
95{
96    if (if_name == "port") {
97        return port;
98    } else {
99        return ClockedObject::getPort(if_name, idx);
100    }
101}
102
103void
104BaseTrafficGen::init()
105{
106    ClockedObject::init();
107
108    if (!port.isConnected())
109        fatal("The port of %s is not connected!\n", name());
110}
111
112DrainState
113BaseTrafficGen::drain()
114{
115    if (!updateEvent.scheduled()) {
116        // no event has been scheduled yet (e.g. switched from atomic mode)
117        return DrainState::Drained;
118    }
119
120    if (retryPkt == NULL) {
121        // shut things down
122        nextPacketTick = MaxTick;
123        nextTransitionTick = MaxTick;
124        deschedule(updateEvent);
125        return DrainState::Drained;
126    } else {
127        return DrainState::Draining;
128    }
129}
130
131void
132BaseTrafficGen::serialize(CheckpointOut &cp) const
133{
134    warn("%s serialization does not keep all traffic generator"
135         " internal state\n", name());
136
137    DPRINTF(Checkpoint, "Serializing BaseTrafficGen\n");
138
139    // save ticks of the graph event if it is scheduled
140    Tick nextEvent = updateEvent.scheduled() ? updateEvent.when() : 0;
141
142    DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", nextEvent);
143
144    SERIALIZE_SCALAR(nextEvent);
145
146    SERIALIZE_SCALAR(nextTransitionTick);
147
148    SERIALIZE_SCALAR(nextPacketTick);
149}
150
151void
152BaseTrafficGen::unserialize(CheckpointIn &cp)
153{
154    warn("%s serialization does not restore all traffic generator"
155         " internal state\n", name());
156
157    // restore scheduled events
158    Tick nextEvent;
159    UNSERIALIZE_SCALAR(nextEvent);
160    if (nextEvent != 0)
161        schedule(updateEvent, nextEvent);
162
163    UNSERIALIZE_SCALAR(nextTransitionTick);
164
165    UNSERIALIZE_SCALAR(nextPacketTick);
166}
167
168void
169BaseTrafficGen::update()
170{
171    // shift our progress-tracking event forward
172    reschedule(noProgressEvent, curTick() + progressCheck, true);
173
174    // if we have reached the time for the next state transition, then
175    // perform the transition
176    if (curTick() >= nextTransitionTick) {
177        transition();
178    } else {
179        assert(curTick() >= nextPacketTick);
180        // get the next packet and try to send it
181        PacketPtr pkt = activeGenerator->getNextPacket();
182
183        // If generating stream/substream IDs are enabled,
184        // try to pick and assign them to the new packet
185        if (streamGenerator) {
186            auto sid = streamGenerator->pickStreamID();
187            auto ssid = streamGenerator->pickSubStreamID();
188
189            pkt->req->setStreamId(sid);
190
191            if (streamGenerator->ssidValid()) {
192                pkt->req->setSubStreamId(ssid);
193            }
194        }
195
196        // suppress packets that are not destined for a memory, such as
197        // device accesses that could be part of a trace
198        if (pkt && system->isMemAddr(pkt->getAddr())) {
199            stats.numPackets++;
200            // Only attempts to send if not blocked by pending responses
201            blockedWaitingResp = allocateWaitingRespSlot(pkt);
202            if (blockedWaitingResp || !port.sendTimingReq(pkt)) {
203                retryPkt = pkt;
204                retryPktTick = curTick();
205            }
206        } else if (pkt) {
207            DPRINTF(TrafficGen, "Suppressed packet %s 0x%x\n",
208                    pkt->cmdString(), pkt->getAddr());
209
210            ++stats.numSuppressed;
211            if (!(static_cast<int>(stats.numSuppressed.value()) % 10000))
212                warn("%s suppressed %d packets with non-memory addresses\n",
213                     name(), stats.numSuppressed.value());
214
215            delete pkt;
216            pkt = nullptr;
217        }
218    }
219
220    // if we are waiting for a retry or for a response, do not schedule any
221    // further events, in the case of a transition or a successful send, go
222    // ahead and determine when the next update should take place
223    if (retryPkt == NULL) {
224        nextPacketTick = activeGenerator->nextPacketTick(elasticReq, 0);
225        scheduleUpdate();
226    }
227}
228
229void
230BaseTrafficGen::transition()
231{
232    if (activeGenerator)
233        activeGenerator->exit();
234
235    activeGenerator = nextGenerator();
236
237    if (activeGenerator) {
238        const Tick duration = activeGenerator->duration;
239        if (duration != MaxTick && duration != 0) {
240            // we could have been delayed and not transitioned on the
241            // exact tick when we were supposed to (due to back
242            // pressure when sending a packet)
243            nextTransitionTick = curTick() + duration;
244        } else {
245            nextTransitionTick = MaxTick;
246        }
247
248        activeGenerator->enter();
249        nextPacketTick = activeGenerator->nextPacketTick(elasticReq, 0);
250    } else {
251        nextPacketTick = MaxTick;
252        nextTransitionTick = MaxTick;
253        assert(!updateEvent.scheduled());
254    }
255}
256
257void
258BaseTrafficGen::scheduleUpdate()
259{
260    // Has the generator run out of work? In that case, force a
261    // transition if a transition period hasn't been configured.
262    while (activeGenerator &&
263           nextPacketTick == MaxTick && nextTransitionTick == MaxTick) {
264        transition();
265    }
266
267    if (!activeGenerator)
268        return;
269
270    // schedule next update event based on either the next execute
271    // tick or the next transition, which ever comes first
272    const Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
273
274    DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick);
275
276    // The next transition tick may be in the past if there was a
277    // retry, so ensure that we don't schedule anything in the past.
278    schedule(updateEvent, std::max(curTick(), nextEventTick));
279}
280
281void
282BaseTrafficGen::start()
283{
284    transition();
285    scheduleUpdate();
286}
287
288void
289BaseTrafficGen::recvReqRetry()
290{
291    DPRINTF(TrafficGen, "Received retry\n");
292    stats.numRetries++;
293    retryReq();
294}
295
296void
297BaseTrafficGen::retryReq()
298{
299    assert(retryPkt != NULL);
300    assert(retryPktTick != 0);
301    assert(!blockedWaitingResp);
302
303    // attempt to send the packet, and if we are successful start up
304    // the machinery again
305    if (port.sendTimingReq(retryPkt)) {
306        retryPkt = NULL;
307        // remember how much delay was incurred due to back-pressure
308        // when sending the request, we also use this to derive
309        // the tick for the next packet
310        Tick delay = curTick() - retryPktTick;
311        retryPktTick = 0;
312        stats.retryTicks += delay;
313
314        if (drainState() != DrainState::Draining) {
315            // packet is sent, so find out when the next one is due
316            nextPacketTick = activeGenerator->nextPacketTick(elasticReq,
317                                                             delay);
318            scheduleUpdate();
319        } else {
320            // shut things down
321            nextPacketTick = MaxTick;
322            nextTransitionTick = MaxTick;
323            signalDrainDone();
324        }
325    }
326}
327
328void
329BaseTrafficGen::noProgress()
330{
331    fatal("BaseTrafficGen %s spent %llu ticks without making progress",
332          name(), progressCheck);
333}
334
335BaseTrafficGen::StatGroup::StatGroup(Stats::Group *parent)
336    : Stats::Group(parent),
337      ADD_STAT(numSuppressed,
338               "Number of suppressed packets to non-memory space"),
339      ADD_STAT(numPackets, "Number of packets generated"),
340      ADD_STAT(numRetries, "Number of retries"),
341      ADD_STAT(retryTicks, "Time spent waiting due to back-pressure (ticks)"),
342      ADD_STAT(bytesRead, "Number of bytes read"),
343      ADD_STAT(bytesWritten, "Number of bytes written"),
344      ADD_STAT(totalReadLatency, "Total latency of read requests"),
345      ADD_STAT(totalWriteLatency, "Total latency of write requests"),
346      ADD_STAT(totalReads, "Total num of reads"),
347      ADD_STAT(totalWrites, "Total num of writes"),
348      ADD_STAT(avgReadLatency, "Avg latency of read requests",
349               totalReadLatency / totalReads),
350      ADD_STAT(avgWriteLatency, "Avg latency of write requests",
351               totalWriteLatency / totalWrites),
352      ADD_STAT(readBW, "Read bandwidth in bytes/s",
353               bytesRead / simSeconds),
354      ADD_STAT(writeBW, "Write bandwidth in bytes/s",
355               bytesWritten / simSeconds)
356{
357}
358
359std::shared_ptr<BaseGen>
360BaseTrafficGen::createIdle(Tick duration)
361{
362    return std::shared_ptr<BaseGen>(new IdleGen(*this, masterID, duration));
363}
364
365std::shared_ptr<BaseGen>
366BaseTrafficGen::createExit(Tick duration)
367{
368    return std::shared_ptr<BaseGen>(new ExitGen(*this, masterID, duration));
369}
370
371std::shared_ptr<BaseGen>
372BaseTrafficGen::createLinear(Tick duration,
373                             Addr start_addr, Addr end_addr, Addr blocksize,
374                             Tick min_period, Tick max_period,
375                             uint8_t read_percent, Addr data_limit)
376{
377    return std::shared_ptr<BaseGen>(new LinearGen(*this, masterID,
378                                                  duration, start_addr,
379                                                  end_addr, blocksize,
380                                                  system->cacheLineSize(),
381                                                  min_period, max_period,
382                                                  read_percent, data_limit));
383}
384
385std::shared_ptr<BaseGen>
386BaseTrafficGen::createRandom(Tick duration,
387                             Addr start_addr, Addr end_addr, Addr blocksize,
388                             Tick min_period, Tick max_period,
389                             uint8_t read_percent, Addr data_limit)
390{
391    return std::shared_ptr<BaseGen>(new RandomGen(*this, masterID,
392                                                  duration, start_addr,
393                                                  end_addr, blocksize,
394                                                  system->cacheLineSize(),
395                                                  min_period, max_period,
396                                                  read_percent, data_limit));
397}
398
399std::shared_ptr<BaseGen>
400BaseTrafficGen::createDram(Tick duration,
401                           Addr start_addr, Addr end_addr, Addr blocksize,
402                           Tick min_period, Tick max_period,
403                           uint8_t read_percent, Addr data_limit,
404                           unsigned int num_seq_pkts, unsigned int page_size,
405                           unsigned int nbr_of_banks_DRAM,
406                           unsigned int nbr_of_banks_util,
407                           unsigned int addr_mapping,
408                           unsigned int nbr_of_ranks)
409{
410    return std::shared_ptr<BaseGen>(new DramGen(*this, masterID,
411                                                duration, start_addr,
412                                                end_addr, blocksize,
413                                                system->cacheLineSize(),
414                                                min_period, max_period,
415                                                read_percent, data_limit,
416                                                num_seq_pkts, page_size,
417                                                nbr_of_banks_DRAM,
418                                                nbr_of_banks_util,
419                                                addr_mapping,
420                                                nbr_of_ranks));
421}
422
423std::shared_ptr<BaseGen>
424BaseTrafficGen::createDramRot(Tick duration,
425                              Addr start_addr, Addr end_addr, Addr blocksize,
426                              Tick min_period, Tick max_period,
427                              uint8_t read_percent, Addr data_limit,
428                              unsigned int num_seq_pkts,
429                              unsigned int page_size,
430                              unsigned int nbr_of_banks_DRAM,
431                              unsigned int nbr_of_banks_util,
432                              unsigned int addr_mapping,
433                              unsigned int nbr_of_ranks,
434                              unsigned int max_seq_count_per_rank)
435{
436    return std::shared_ptr<BaseGen>(new DramRotGen(*this, masterID,
437                                                   duration, start_addr,
438                                                   end_addr, blocksize,
439                                                   system->cacheLineSize(),
440                                                   min_period, max_period,
441                                                   read_percent, data_limit,
442                                                   num_seq_pkts, page_size,
443                                                   nbr_of_banks_DRAM,
444                                                   nbr_of_banks_util,
445                                                   addr_mapping,
446                                                   nbr_of_ranks,
447                                                   max_seq_count_per_rank));
448}
449
450std::shared_ptr<BaseGen>
451BaseTrafficGen::createTrace(Tick duration,
452                            const std::string& trace_file, Addr addr_offset)
453{
454#if HAVE_PROTOBUF
455    return std::shared_ptr<BaseGen>(
456        new TraceGen(*this, masterID, duration, trace_file, addr_offset));
457#else
458    panic("Can't instantiate trace generation without Protobuf support!\n");
459#endif
460}
461
462bool
463BaseTrafficGen::recvTimingResp(PacketPtr pkt)
464{
465    auto iter = waitingResp.find(pkt->req);
466
467    panic_if(iter == waitingResp.end(), "%s: "
468            "Received unexpected response [%s reqPtr=%x]\n",
469               pkt->print(), pkt->req);
470
471    assert(iter->second <= curTick());
472
473    if (pkt->isWrite()) {
474        ++stats.totalWrites;
475        stats.bytesWritten += pkt->req->getSize();
476        stats.totalWriteLatency += curTick() - iter->second;
477    } else {
478        ++stats.totalReads;
479        stats.bytesRead += pkt->req->getSize();
480        stats.totalReadLatency += curTick() - iter->second;
481    }
482
483    waitingResp.erase(iter);
484
485    delete pkt;
486
487    // Sends up the request if we were blocked
488    if (blockedWaitingResp) {
489        blockedWaitingResp = false;
490        retryReq();
491    }
492
493    return true;
494}
495