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