vcd.cc revision 13245:c666c5d4996b
1/*
2 * Copyright 2018 Google, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met: redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer;
8 * redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution;
11 * neither the name of the copyright holders nor the names of its
12 * contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * Authors: Gabe Black
28 */
29
30#include "systemc/utils/vcd.hh"
31
32#include <ctime>
33#include <iomanip>
34
35#include "base/bitfield.hh"
36#include "base/cprintf.hh"
37#include "base/logging.hh"
38#include "systemc/core/scheduler.hh"
39#include "systemc/ext/core/sc_event.hh"
40#include "systemc/ext/core/sc_main.hh"
41#include "systemc/ext/core/sc_time.hh"
42#include "systemc/ext/dt/bit/sc_bv_base.hh"
43#include "systemc/ext/dt/bit/sc_logic.hh"
44#include "systemc/ext/dt/bit/sc_lv_base.hh"
45#include "systemc/ext/dt/fx/sc_fxnum.hh"
46#include "systemc/ext/dt/fx/sc_fxval.hh"
47#include "systemc/ext/dt/int/sc_int_base.hh"
48#include "systemc/ext/dt/int/sc_signed.hh"
49#include "systemc/ext/dt/int/sc_uint_base.hh"
50#include "systemc/ext/dt/int/sc_unsigned.hh"
51#include "systemc/ext/utils/functions.hh"
52
53namespace sc_gem5
54{
55
56namespace
57{
58
59std::string
60cleanName(std::string name)
61{
62    for (int i = 0; i < name.length(); i++) {
63        if (name[i] == '[')
64            name[i] = '(';
65        else if (name[i] == ']')
66            name[i] = ')';
67    }
68    return name;
69}
70
71} // anonymous namespace
72
73class VcdTraceValBase : public TraceValBase
74{
75  protected:
76    std::string _vcdName;
77
78    const char *
79    stripLeadingBits(const char *orig)
80    {
81        const char first = orig[0];
82
83        if (first != 'z' && first != 'x' && first != '0')
84            return orig;
85
86        const char *res = orig;
87        while (*++res == first) {}
88
89        if (first != '0' || *res != '1')
90            res--;
91
92        return res;
93    }
94
95    char
96    scLogicToVcdState(char in)
97    {
98        switch (in) {
99          case 'U':
100          case 'X':
101          case 'W':
102          case 'D':
103            return 'x';
104          case '0':
105          case 'L':
106            return '0';
107          case '1':
108          case 'H':
109            return '1';
110          case 'Z':
111            return 'z';
112          default:
113            return '?';
114        }
115    }
116
117    void
118    printVal(std::ostream &os, const std::string &rep)
119    {
120        switch (width()) {
121          case 0:
122            return;
123          case 1:
124            os << rep << vcdName() << std::endl;;
125            return;
126          default:
127            os << "b" << stripLeadingBits(rep.c_str()) << " " <<
128                vcdName() << std::endl;
129            return;
130        }
131    }
132
133  public:
134    VcdTraceValBase(int width) : TraceValBase(width) {}
135    ~VcdTraceValBase() {}
136
137    void vcdName(const std::string &vcd_name) { _vcdName = vcd_name; }
138    const std::string &vcdName() { return _vcdName; }
139    virtual std::string vcdType() { return "wire"; }
140
141    virtual void output(std::ostream &os) = 0;
142};
143
144void
145VcdTraceScope::addValue(const std::string &name, VcdTraceValBase *value)
146{
147    size_t pos = name.find_first_of('.');
148    if (pos == std::string::npos) {
149        values.emplace_back(name, value);
150    } else {
151        std::string sname = name.substr(0, pos);
152        auto it = scopes.find(sname);
153        if (it == scopes.end())
154            it = scopes.emplace(sname, new VcdTraceScope).first;
155        it->second->addValue(name.substr(pos + 1), value);
156    }
157}
158
159void
160VcdTraceScope::output(const std::string &name, std::ostream &os)
161{
162    os << "$scope module " << name << " $end" << std::endl;
163
164    for (auto &p: values) {
165        const std::string &name = p.first;
166        VcdTraceValBase *value = p.second;
167
168        int w = value->width();
169        if (w <= 0) {
170            std::string msg = csprintf("'%s' has 0 bits", name);
171            // The typo in this error message is intentional to match the
172            // Accellera output.
173            SC_REPORT_ERROR("(E710) object cannot not be traced", msg.c_str());
174            return;
175        }
176
177        std::string clean_name = cleanName(name);
178        if (w == 1) {
179            ccprintf(os, "$var %s  % 3d  %s  %s       $end\n",
180                     value->vcdType(), w, value->vcdName(), clean_name);
181        } else {
182            ccprintf(os, "$var %s  % 3d  %s  %s [%d:0]  $end\n",
183                     value->vcdType(), w, value->vcdName(), clean_name, w - 1);
184        }
185    }
186
187    for (auto &p: scopes)
188        p.second->output(p.first, os);
189
190    os << "$upscope $end" << std::endl;
191}
192
193template <typename T>
194class VcdTraceVal : public TraceVal<T, VcdTraceValBase>
195{
196  public:
197    typedef T TracedType;
198
199    VcdTraceVal(const T* t, const std::string &vcd_name, int width) :
200        TraceVal<T, VcdTraceValBase>(t, width)
201    {
202        this->vcdName(vcd_name);
203    }
204};
205
206std::string
207VcdTraceFile::nextSignalName()
208{
209    std::string name(_nextName);
210
211    bool carry = false;
212    int pos = NextNameChars - 1;
213    do {
214        carry = (_nextName[pos] == 'z');
215        if (carry)
216            _nextName[pos--] = 'a';
217        else
218            _nextName[pos--]++;
219    } while (carry && pos >= 0);
220
221    return name;
222}
223
224void
225VcdTraceFile::initialize()
226{
227    finalizeTime();
228
229    // Date.
230    stream() << "$date" << std::endl;
231    time_t long_time;
232    time(&long_time);
233    struct tm *p_tm = localtime(&long_time);
234    stream() << std::put_time(p_tm, "     %b %d, %Y       %H:%M:%S\n");
235    stream() << "$end" << std::endl << std::endl;
236
237    // Version.
238    stream() << "$version" << std::endl;
239    stream() << " " << ::sc_core::sc_version() << std::endl;
240    stream() << "$end" << std::endl << std::endl;
241
242    // Timescale.
243    stream() << "$timescale" << std::endl;
244    stream() << "     " << ::sc_core::sc_time::from_value(timeUnitTicks) <<
245        std::endl;
246    stream() << "$end" << std::endl << std::endl;
247
248    for (auto tv: traceVals)
249        tv->finalize();
250
251    topScope.output("SystemC", stream());
252
253    stream() << "$enddefinitions  $end" << std::endl << std::endl;
254
255    Tick now = scheduler.getCurTick();
256
257    std::string timedump_comment =
258        csprintf("All initial values are dumped below at time "
259                 "%g sec = %g timescale units.",
260                 static_cast<double>(now) / SimClock::Float::s,
261                 static_cast<double>(now / timeUnitTicks));
262    writeComment(timedump_comment);
263
264    lastPrintedTime = now / timeUnitTicks;
265
266    stream() << "$dumpvars" << std::endl;
267    for (auto tv: traceVals)
268        tv->output(stream());
269    stream() << "$end" << std::endl << std::endl;
270
271    initialized = true;
272}
273
274VcdTraceFile::~VcdTraceFile()
275{
276    for (auto tv: traceVals)
277        delete tv;
278    traceVals.clear();
279
280    if (timeUnitTicks)
281        ccprintf(stream(), "#%u\n", scheduler.getCurTick() / timeUnitTicks);
282}
283
284void
285VcdTraceFile::trace(bool delta)
286{
287    if (!delta)
288        deltasAtNow = 0;
289
290    uint64_t deltaOffset = deltasAtNow;
291
292    if (delta)
293        deltaOffset = deltasAtNow++;
294
295    if (_traceDeltas != delta)
296        return;
297
298    if (!initialized) {
299        initialize();
300        return;
301    }
302
303    Tick now = scheduler.getCurTick() / timeUnitTicks + deltaOffset;
304
305    if (now <= lastPrintedTime) {
306        // TODO warn about reversed time?
307        return;
308    }
309
310    bool time_printed = false;
311    for (auto tv: traceVals) {
312        if (tv->check()) {
313            if (!time_printed) {
314                lastPrintedTime = now;
315                ccprintf(stream(), "#%u\n", now);
316                time_printed = true;
317            }
318
319            tv->output(stream());
320        }
321    }
322    if (time_printed)
323        stream() << std::endl;
324}
325
326class VcdTraceValBool : public VcdTraceVal<bool>
327{
328  public:
329    using VcdTraceVal<bool>::VcdTraceVal;
330
331    void
332    output(std::ostream &os) override
333    {
334        printVal(os, this->value() ? "1" : "0");
335    }
336};
337
338void
339VcdTraceFile::addTraceVal(const bool *v, const std::string &name)
340{
341    addNewTraceVal<VcdTraceValBool>(v, name);
342}
343
344template <typename T>
345class VcdTraceValFloat : public VcdTraceVal<T>
346{
347  public:
348    using VcdTraceVal<T>::VcdTraceVal;
349
350    std::string vcdType() override { return "real"; }
351
352    void
353    output(std::ostream &os) override
354    {
355        ccprintf(os, "r%.16g %s\n", this->value(), this->vcdName());
356    }
357};
358
359void
360VcdTraceFile::addTraceVal(const float *v, const std::string &name)
361{
362    addNewTraceVal<VcdTraceValFloat<float>>(v, name);
363}
364void
365VcdTraceFile::addTraceVal(const double *v, const std::string &name)
366{
367    addNewTraceVal<VcdTraceValFloat<double>>(v, name);
368}
369
370class VcdTraceValScLogic : public VcdTraceVal<sc_dt::sc_logic>
371{
372  public:
373    using VcdTraceVal<sc_dt::sc_logic>::VcdTraceVal;
374
375    void
376    output(std::ostream &os) override
377    {
378        char str[2] = {
379            scLogicToVcdState(value().to_char()),
380            '\0'
381        };
382        printVal(os, str);
383    }
384};
385
386void
387VcdTraceFile::addTraceVal(const sc_dt::sc_logic *v, const std::string &name)
388{
389    addNewTraceVal<VcdTraceValScLogic>(v, name);
390}
391
392template <typename T>
393class VcdTraceValFinite : public VcdTraceVal<T>
394{
395  public:
396    using VcdTraceVal<T>::VcdTraceVal;
397
398    void
399    finalize() override
400    {
401        VcdTraceVal<T>::finalize();
402        this->_width = this->value().length();
403    }
404
405    void
406    output(std::ostream &os) override
407    {
408        std::string str;
409        const int w = this->width();
410
411        str.reserve(w);
412        for (int i = w - 1; i >= 0; i--)
413            str += this->value()[i].to_bool() ? '1' : '0';
414
415        this->printVal(os, str);
416    }
417};
418
419void
420VcdTraceFile::addTraceVal(const sc_dt::sc_int_base *v,
421                          const std::string &name)
422{
423    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_int_base>>(v, name);
424}
425void
426VcdTraceFile::addTraceVal(const sc_dt::sc_uint_base *v,
427                          const std::string &name)
428{
429    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_uint_base>>(v, name);
430}
431
432void
433VcdTraceFile::addTraceVal(const sc_dt::sc_signed *v, const std::string &name)
434{
435    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_signed>>(v, name);
436}
437void
438VcdTraceFile::addTraceVal(const sc_dt::sc_unsigned *v,
439                          const std::string &name)
440{
441    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_unsigned>>(v, name);
442}
443
444template <typename T>
445class VcdTraceValLogic : public VcdTraceVal<T>
446{
447  public:
448    using VcdTraceVal<T>::VcdTraceVal;
449
450    void
451    finalize() override
452    {
453        VcdTraceVal<T>::finalize();
454        this->_width = this->value().length();
455    }
456
457    void
458    output(std::ostream &os) override
459    {
460        this->printVal(os, this->value().to_string());
461    }
462};
463
464void
465VcdTraceFile::addTraceVal(const sc_dt::sc_bv_base *v, const std::string &name)
466{
467    addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_bv_base>>(v, name);
468}
469void
470VcdTraceFile::addTraceVal(const sc_dt::sc_lv_base *v, const std::string &name)
471{
472    addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_lv_base>>(v, name);
473}
474
475template <typename T>
476class VcdTraceValFxval : public VcdTraceVal<T>
477{
478  public:
479    using VcdTraceVal<T>::VcdTraceVal;
480
481    std::string vcdType() override { return "real"; }
482
483    void
484    output(std::ostream &os) override
485    {
486        ccprintf(os, "r%.16g %s\n",
487                this->value().to_double(), this->vcdName());
488    }
489};
490
491void
492VcdTraceFile::addTraceVal(const sc_dt::sc_fxval *v, const std::string &name)
493{
494    addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval>>(v, name);
495}
496void
497VcdTraceFile::addTraceVal(const sc_dt::sc_fxval_fast *v,
498                          const std::string &name)
499{
500    addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval_fast>>(v, name);
501}
502
503template <typename T>
504class VcdTraceValFxnum : public VcdTraceVal<T>
505{
506  public:
507    using VcdTraceVal<T>::VcdTraceVal;
508
509    void
510    output(std::ostream &os) override
511    {
512        std::string str;
513        const int w = this->width();
514
515        str.reserve(w);
516        for (int i = w - 1; i >= 0; i--)
517            str += this->value()[i] ? '1' : '0';
518
519        this->printVal(os, str);
520    }
521};
522
523void
524VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum *v, const std::string &name)
525{
526    addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum>>(v, name);
527}
528void
529VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum_fast *v,
530                          const std::string &name)
531{
532    addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum_fast>>(v, name);
533}
534
535class VcdTraceValEvent : public VcdTraceVal<::sc_core::sc_event>
536{
537  public:
538    using VcdTraceVal<::sc_core::sc_event>::VcdTraceVal;
539
540    std::string vcdType() override { return "event"; }
541
542    void
543    output(std::ostream &os) override
544    {
545        if (value())
546            printVal(os, "1");
547        else
548            os << std::endl;
549    }
550};
551
552void
553VcdTraceFile::addTraceVal(const sc_core::sc_event *v, const std::string &name)
554{
555    addNewTraceVal<VcdTraceValEvent>(v, name);
556}
557
558class VcdTraceValTime : public VcdTraceVal<::sc_core::sc_time>
559{
560  private:
561    static const int TimeWidth = 64;
562
563  public:
564    using VcdTraceVal<::sc_core::sc_time>::VcdTraceVal;
565
566    std::string vcdType() override { return "time"; }
567
568    void
569    finalize() override
570    {
571        VcdTraceVal<::sc_core::sc_time>::finalize();
572        _width = TimeWidth;
573    }
574
575    void
576    output(std::ostream &os) override
577    {
578        char str[TimeWidth + 1];
579        str[TimeWidth] = '\0';
580
581        const uint64_t val = value().value();
582        for (int i = 0; i < TimeWidth; i++)
583            str[i] = ::bits(val, TimeWidth - i - 1) ? '1' : '0';
584
585        printVal(os, str);
586    }
587};
588void
589VcdTraceFile::addTraceVal(const sc_core::sc_time *v, const std::string &name)
590{
591    addNewTraceVal<VcdTraceValTime>(v, name);
592}
593
594template <typename T>
595class VcdTraceValInt : public VcdTraceVal<T>
596{
597  public:
598    using VcdTraceVal<T>::VcdTraceVal;
599
600    void
601    output(std::ostream &os) override
602    {
603        const int w = this->width();
604        char str[w + 1];
605        str[w] = '\0';
606
607        const uint64_t val =
608            static_cast<uint64_t>(this->value()) & ::mask(sizeof(T) * 8);
609
610        if (::mask(w) < val) {
611            for (int i = 0; i < w; i++)
612                str[i] = 'x';
613        } else {
614            for (int i = 0; i < w; i++)
615                str[i] = ::bits(val, w - i - 1) ? '1' : '0';
616        }
617
618        this->printVal(os, str);
619    }
620};
621
622void
623VcdTraceFile::addTraceVal(const unsigned char *v, const std::string &name,
624                          int width)
625{
626    addNewTraceVal<VcdTraceValInt<unsigned char>>(v, name, width);
627}
628void
629VcdTraceFile::addTraceVal(const char *v, const std::string &name, int width)
630{
631    addNewTraceVal<VcdTraceValInt<char>>(v, name, width);
632}
633void
634VcdTraceFile::addTraceVal(const unsigned short *v, const std::string &name,
635                          int width)
636{
637    addNewTraceVal<VcdTraceValInt<unsigned short>>(v, name, width);
638}
639void
640VcdTraceFile::addTraceVal(const short *v, const std::string &name, int width)
641{
642    addNewTraceVal<VcdTraceValInt<short>>(v, name, width);
643}
644void
645VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name,
646                          int width)
647{
648    addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, width);
649}
650void
651VcdTraceFile::addTraceVal(const int *v, const std::string &name, int width)
652{
653    addNewTraceVal<VcdTraceValInt<int>>(v, name, width);
654}
655void
656VcdTraceFile::addTraceVal(const unsigned long *v, const std::string &name,
657                          int width)
658{
659    addNewTraceVal<VcdTraceValInt<unsigned long>>(v, name, width);
660}
661void
662VcdTraceFile::addTraceVal(const long *v, const std::string &name, int width)
663{
664    addNewTraceVal<VcdTraceValInt<long>>(v, name, width);
665}
666
667void
668VcdTraceFile::addTraceVal(const sc_dt::int64 *v, const std::string &name,
669                          int width)
670{
671    addNewTraceVal<VcdTraceValInt<sc_dt::int64>>(v, name, width);
672}
673void
674VcdTraceFile::addTraceVal(const sc_dt::uint64 *v, const std::string &name,
675                          int width)
676{
677    addNewTraceVal<VcdTraceValInt<sc_dt::uint64>>(v, name, width);
678}
679
680void
681VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name,
682                          const char **literals)
683{
684    uint64_t count = 0;
685    while (*literals++)
686        count++;
687
688    int bits = 0;
689    while (count >> bits)
690        bits++;
691
692    addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, bits);
693}
694
695void
696VcdTraceFile::writeComment(const std::string &comment)
697{
698    stream() << "$comment" << std::endl;
699    stream() << comment << std::endl;
700    stream() << "$end" << std::endl << std::endl;
701}
702
703} // namespace sc_gem5
704