systemc: Implement general and VCD trace support.

This doesn't include WIF trace support, but does make allowances for
adding it in the future.

Change-Id: Ifb62f40a7d8a13e94463930a44ac4b1cf41e3009
Reviewed-on: https://gem5-review.googlesource.com/c/12826
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
diff --git a/src/systemc/core/scheduler.cc b/src/systemc/core/scheduler.cc
index ae1aa89..d79abb2 100644
--- a/src/systemc/core/scheduler.cc
+++ b/src/systemc/core/scheduler.cc
@@ -36,6 +36,7 @@
 #include "systemc/ext/core/sc_main.hh"
 #include "systemc/ext/utils/sc_report.hh"
 #include "systemc/ext/utils/sc_report_handler.hh"
+#include "systemc/utils/tracefile.hh"
 
 namespace sc_gem5
 {
@@ -48,8 +49,8 @@
     starvationEvent(this, false, StarvationPriority),
     _elaborationDone(false), _started(false), _stopNow(false),
     _status(StatusOther), maxTickEvent(this, false, MaxTickPriority),
-    _numCycles(0), _changeStamp(0), _current(nullptr), initDone(false),
-    runOnce(false)
+    timeAdvancesEvent(this, false, TimeAdvancesPriority), _numCycles(0),
+    _changeStamp(0), _current(nullptr), initDone(false), runOnce(false)
 {}
 
 Scheduler::~Scheduler()
@@ -86,6 +87,8 @@
         deschedule(&starvationEvent);
     if (maxTickEvent.scheduled())
         deschedule(&maxTickEvent);
+    if (timeAdvancesEvent.scheduled())
+        deschedule(&timeAdvancesEvent);
 
     Process *p;
     while ((p = initList.getNext()))
@@ -134,6 +137,8 @@
     initDone = true;
 
     status(StatusOther);
+
+    scheduleTimeAdvancesEvent();
 }
 
 void
@@ -263,10 +268,13 @@
 void
 Scheduler::runReady()
 {
+    scheduleTimeAdvancesEvent();
+
     bool empty = readyListMethods.empty() && readyListThreads.empty();
     lastReadyTick = getCurTick();
 
     // The evaluation phase.
+    status(StatusEvaluate);
     do {
         yield();
     } while (getNextReady());
@@ -282,6 +290,8 @@
     }
 
     runUpdate();
+    if (!traceFiles.empty())
+        trace(true);
     runDelta();
 
     if (!runToTime && starved())
@@ -367,6 +377,7 @@
     }
 
     schedule(&maxTickEvent, maxTick);
+    scheduleTimeAdvancesEvent();
 
     // Return to gem5 to let it run events, etc.
     Fiber::primaryFiber()->run();
@@ -429,6 +440,13 @@
     schedule(&stopEvent);
 }
 
+void
+Scheduler::trace(bool delta)
+{
+    for (auto tf: traceFiles)
+        tf->trace(delta);
+}
+
 Scheduler scheduler;
 
 namespace {
diff --git a/src/systemc/core/scheduler.hh b/src/systemc/core/scheduler.hh
index 8015260..ebe1546 100644
--- a/src/systemc/core/scheduler.hh
+++ b/src/systemc/core/scheduler.hh
@@ -48,6 +48,8 @@
 namespace sc_gem5
 {
 
+class TraceFile;
+
 typedef NodeList<Process> ProcessList;
 typedef NodeList<Channel> ChannelList;
 
@@ -280,6 +282,7 @@
         timeSlots.erase(timeSlots.begin());
         if (!runToTime && starved())
             scheduleStarvationEvent();
+        scheduleTimeAdvancesEvent();
     }
 
     // Pending activity ignores gem5 activity, much like how a systemc
@@ -357,6 +360,9 @@
     Status status() { return _status; }
     void status(Status s) { _status = s; }
 
+    void registerTraceFile(TraceFile *tf) { traceFiles.insert(tf); }
+    void unregisterTraceFile(TraceFile *tf) { traceFiles.erase(tf); }
+
   private:
     typedef const EventBase::Priority Priority;
     static Priority DefaultPriority = EventBase::Default_Pri;
@@ -366,6 +372,7 @@
     static Priority MaxTickPriority = DefaultPriority + 2;
     static Priority ReadyPriority = DefaultPriority + 3;
     static Priority StarvationPriority = ReadyPriority;
+    static Priority TimeAdvancesPriority = EventBase::Maximum_Pri;
 
     EventQueue *eq;
 
@@ -440,6 +447,15 @@
     }
     EventWrapper<Scheduler, &Scheduler::maxTickFunc> maxTickEvent;
 
+    void timeAdvances() { trace(false); }
+    EventWrapper<Scheduler, &Scheduler::timeAdvances> timeAdvancesEvent;
+    void
+    scheduleTimeAdvancesEvent()
+    {
+        if (!traceFiles.empty() && !timeAdvancesEvent.scheduled())
+            schedule(&timeAdvancesEvent);
+    }
+
     uint64_t _numCycles;
     uint64_t _changeStamp;
 
@@ -457,6 +473,10 @@
     ChannelList updateList;
 
     std::map<::Event *, Tick> eventsToSchedule;
+
+    std::set<TraceFile *> traceFiles;
+
+    void trace(bool delta);
 };
 
 extern Scheduler scheduler;
diff --git a/src/systemc/ext/channel/sc_in.hh b/src/systemc/ext/channel/sc_in.hh
index b7170ae..a768401 100644
--- a/src/systemc/ext/channel/sc_in.hh
+++ b/src/systemc/ext/channel/sc_in.hh
@@ -34,9 +34,9 @@
 
 #include "../core/sc_event.hh"
 #include "../core/sc_port.hh"
+#include "../utils/sc_trace_file.hh"
 #include "sc_signal_in_if.hh"
 #include "sc_signal_inout_if.hh"
-#include "warn_unimpl.hh"
 
 namespace sc_core
 {
@@ -112,7 +112,14 @@
         bind(p);
     }
 
-    virtual void end_of_elaboration() { /* Implementation defined. */ }
+    virtual void
+    end_of_elaboration()
+    {
+        for (auto params: traceParamsVec)
+            sc_trace(params->tf, (*this)->read(), params->name);
+
+        traceParamsVec.clear();
+    }
 
     const T &read() const { return (*this)->read(); }
     operator const T& () const { return (*this)->read(); }
@@ -128,9 +135,17 @@
 
     virtual const char *kind() const { return "sc_in"; }
 
+    void
+    add_trace(sc_trace_file *tf, const std::string &name) const
+    {
+        traceParamsVec.push_back(new sc_trace_params(tf, name));
+    }
+
   private:
     mutable sc_event_finder_t<sc_signal_in_if<T> > _valueChangedFinder;
 
+    mutable sc_trace_params_vec traceParamsVec;
+
     // Disabled
     sc_in(const sc_in<T> &);
     sc_in<T> &operator = (const sc_in<T> &);
@@ -138,9 +153,12 @@
 
 template <class T>
 inline void
-sc_trace(sc_trace_file *, const sc_in<T> &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_in<T> &i, const std::string &name)
 {
-    sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+    if (i.size())
+        sc_trace(tf, i->read(), name);
+    else
+        i.add_trace(tf, name);
 }
 
 template <>
@@ -236,7 +254,14 @@
         bind(p);
     }
 
-    virtual void end_of_elaboration() { /* Implementation defined. */ }
+    virtual void
+    end_of_elaboration()
+    {
+        for (auto params: traceParamsVec)
+            sc_trace(params->tf, (*this)->read(), params->name);
+
+        traceParamsVec.clear();
+    }
 
     const bool &read() const { return (*this)->read(); }
     operator const bool& () const { return (*this)->read(); }
@@ -268,11 +293,19 @@
 
     virtual const char *kind() const { return "sc_in"; }
 
+    void
+    add_trace(sc_trace_file *tf, const std::string &name) const
+    {
+        traceParamsVec.push_back(new sc_trace_params(tf, name));
+    }
+
   private:
     mutable sc_event_finder_t<sc_signal_in_if<bool> > _valueChangedFinder;
     mutable sc_event_finder_t<sc_signal_in_if<bool> > _posFinder;
     mutable sc_event_finder_t<sc_signal_in_if<bool> > _negFinder;
 
+    mutable sc_trace_params_vec traceParamsVec;
+
     // Disabled
     sc_in(const sc_in<bool> &);
     sc_in<bool> &operator = (const sc_in<bool> &);
@@ -280,9 +313,13 @@
 
 template <>
 inline void
-sc_trace<bool>(sc_trace_file *, const sc_in<bool> &, const std::string &)
+sc_trace<bool>(sc_trace_file *tf, const sc_in<bool> &i,
+        const std::string &name)
 {
-    sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+    if (i.size())
+        sc_trace(tf, i->read(), name);
+    else
+        i.add_trace(tf, name);
 }
 
 template <>
@@ -383,7 +420,14 @@
         bind(p);
     }
 
-    virtual void end_of_elaboration() { /* Implementation defined. */ }
+    virtual void
+    end_of_elaboration()
+    {
+        for (auto params: traceParamsVec)
+            sc_trace(params->tf, (*this)->read(), params->name);
+
+        traceParamsVec.clear();
+    }
 
     const sc_dt::sc_logic &read() const { return (*this)->read(); }
     operator const sc_dt::sc_logic& () const { return (*this)->read(); }
@@ -407,12 +451,20 @@
 
     virtual const char *kind() const { return "sc_in"; }
 
+    void
+    add_trace(sc_trace_file *tf, const std::string &name) const
+    {
+        traceParamsVec.push_back(new sc_trace_params(tf, name));
+    }
+
   private:
     mutable sc_event_finder_t<sc_signal_in_if<sc_dt::sc_logic> >
         _valueChangedFinder;
     mutable sc_event_finder_t<sc_signal_in_if<sc_dt::sc_logic> > _posFinder;
     mutable sc_event_finder_t<sc_signal_in_if<sc_dt::sc_logic> > _negFinder;
 
+    mutable sc_trace_params_vec traceParamsVec;
+
     // Disabled
     sc_in(const sc_in<sc_dt::sc_logic> &);
     sc_in<sc_dt::sc_logic> &operator = (const sc_in<sc_dt::sc_logic> &);
@@ -420,10 +472,13 @@
 
 template <>
 inline void
-sc_trace<sc_dt::sc_logic>(
-        sc_trace_file *, const sc_in<sc_dt::sc_logic> &, const std::string &)
+sc_trace<sc_dt::sc_logic>(sc_trace_file *tf, const sc_in<sc_dt::sc_logic> &i,
+        const std::string &name)
 {
-    sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+    if (i.size())
+        sc_trace(tf, i->read(), name);
+    else
+        i.add_trace(tf, name);
 }
 
 } // namespace sc_core
diff --git a/src/systemc/ext/channel/sc_inout.hh b/src/systemc/ext/channel/sc_inout.hh
index 54b0ae7..7f19443 100644
--- a/src/systemc/ext/channel/sc_inout.hh
+++ b/src/systemc/ext/channel/sc_inout.hh
@@ -35,8 +35,8 @@
 #include "../core/sc_event.hh"
 #include "../core/sc_port.hh"
 #include "../dt/bit/sc_logic.hh"
+#include "../utils/sc_trace_file.hh"
 #include "sc_signal_inout_if.hh"
-#include "warn_unimpl.hh"
 
 namespace sc_dt
 {
@@ -111,6 +111,11 @@
             delete initValue;
             initValue = nullptr;
         }
+
+        for (auto params: traceParamsVec)
+            sc_trace(params->tf, (*this)->read(), params->name);
+
+        traceParamsVec.clear();
     }
 
     const T &read() const { return (*this)->read(); }
@@ -159,19 +164,30 @@
 
     virtual const char *kind() const { return "sc_inout"; }
 
+    void
+    add_trace(sc_trace_file *tf, const std::string &name) const
+    {
+        traceParamsVec.push_back(new sc_trace_params(tf, name));
+    }
+
   private:
     T *initValue;
     mutable sc_event_finder_t<sc_signal_inout_if<T> > _valueChangedFinder;
 
+    mutable sc_trace_params_vec traceParamsVec;
+
     // Disabled
     sc_inout(const sc_inout<T> &);
 };
 
 template <class T>
 inline void
-sc_trace(sc_trace_file *, const sc_inout<T> &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_inout<T> &i, const std::string &name)
 {
-    sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+    if (i.size())
+        sc_trace(tf, i->read(), name);
+    else
+        i.add_trace(tf, name);
 }
 
 template <>
@@ -259,6 +275,11 @@
             delete initValue;
             initValue = nullptr;
         }
+
+        for (auto params: traceParamsVec)
+            sc_trace(params->tf, (*this)->read(), params->name);
+
+        traceParamsVec.clear();
     }
 
     const bool &read() const { return (*this)->read(); }
@@ -314,21 +335,32 @@
 
     virtual const char *kind() const { return "sc_inout"; }
 
+    void
+    add_trace(sc_trace_file *tf, const std::string &name) const
+    {
+        traceParamsVec.push_back(new sc_trace_params(tf, name));
+    }
+
   private:
     bool *initValue;
     mutable sc_event_finder_t<sc_signal_inout_if<bool> > _valueChangedFinder;
     mutable sc_event_finder_t<sc_signal_inout_if<bool> > _posFinder;
     mutable sc_event_finder_t<sc_signal_inout_if<bool> > _negFinder;
 
+    mutable sc_trace_params_vec traceParamsVec;
+
     // Disabled
     sc_inout(const sc_inout<bool> &);
 };
 
 template <>
 inline void sc_trace<bool>(
-        sc_trace_file *, const sc_inout<bool> &, const std::string &)
+        sc_trace_file *tf, const sc_inout<bool> &i, const std::string &name)
 {
-    sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+    if (i.size())
+        sc_trace(tf, i->read(), name);
+    else
+        i.add_trace(tf, name);
 }
 
 template <>
@@ -433,6 +465,11 @@
             delete initValue;
             initValue = nullptr;
         }
+
+        for (auto params: traceParamsVec)
+            sc_trace(params->tf, (*this)->read(), params->name);
+
+        traceParamsVec.clear();
     }
 
     const sc_dt::sc_logic &read() const { return (*this)->read(); }
@@ -488,6 +525,12 @@
 
     virtual const char *kind() const { return "sc_inout"; }
 
+    void
+    add_trace(sc_trace_file *tf, const std::string &name) const
+    {
+        traceParamsVec.push_back(new sc_trace_params(tf, name));
+    }
+
   private:
     sc_dt::sc_logic *initValue;
     mutable sc_event_finder_t<
@@ -495,16 +538,21 @@
     mutable sc_event_finder_t<sc_signal_inout_if<sc_dt::sc_logic> > _posFinder;
     mutable sc_event_finder_t<sc_signal_inout_if<sc_dt::sc_logic> > _negFinder;
 
+    mutable sc_trace_params_vec traceParamsVec;
+
     // Disabled
     sc_inout(const sc_inout<sc_dt::sc_logic> &);
 };
 
 template <>
 inline void
-sc_trace<sc_dt::sc_logic>(sc_trace_file *, const sc_inout<sc_dt::sc_logic> &,
-                          const std::string &)
+sc_trace<sc_dt::sc_logic>(sc_trace_file *tf,
+        const sc_inout<sc_dt::sc_logic> &i, const std::string &name)
 {
-    sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+    if (i.size())
+        sc_trace(tf, i->read(), name);
+    else
+        i.add_trace(tf, name);
 }
 
 } // namespace sc_core
diff --git a/src/systemc/ext/channel/sc_signal.hh b/src/systemc/ext/channel/sc_signal.hh
index bbe2d01..7c01837 100644
--- a/src/systemc/ext/channel/sc_signal.hh
+++ b/src/systemc/ext/channel/sc_signal.hh
@@ -45,22 +45,6 @@
 {
 
 class sc_port_base;
-class sc_trace_file;
-
-// Nonstandard
-// Despite having a warning "FOR INTERNAL USE ONLY!" in all caps above this
-// class definition in the Accellera implementation, it appears in their
-// examples and test programs, and so we need to have it here as well.
-struct sc_trace_params
-{
-    sc_trace_file *tf;
-    std::string name;
-
-    sc_trace_params(sc_trace_file *tf, const std::string &name) :
-        tf(tf), name(name)
-    {}
-};
-typedef std::vector<sc_trace_params *> sc_trace_params_vec;
 
 template <class T, sc_writer_policy WRITER_POLICY=SC_ONE_WRITER>
 class sc_signal : public sc_signal_inout_if<T>,
diff --git a/src/systemc/ext/core/sc_port.hh b/src/systemc/ext/core/sc_port.hh
index c9d436e..3586fec 100644
--- a/src/systemc/ext/core/sc_port.hh
+++ b/src/systemc/ext/core/sc_port.hh
@@ -47,6 +47,22 @@
 {
 
 class sc_interface;
+class sc_trace_file;
+
+// Nonstandard
+// Despite having a warning "FOR INTERNAL USE ONLY!" in all caps above this
+// class definition in the Accellera implementation, it appears in their
+// examples and test programs, and so we need to have it here as well.
+struct sc_trace_params
+{
+    sc_trace_file *tf;
+    std::string name;
+
+    sc_trace_params(sc_trace_file *tf, const std::string &name) :
+        tf(tf), name(name)
+    {}
+};
+typedef std::vector<sc_trace_params *> sc_trace_params_vec;
 
 enum sc_port_policy
 {
diff --git a/src/systemc/ext/dt/fx/sc_fxnum.hh b/src/systemc/ext/dt/fx/sc_fxnum.hh
index 1138646..8a0b7f6 100644
--- a/src/systemc/ext/dt/fx/sc_fxnum.hh
+++ b/src/systemc/ext/dt/fx/sc_fxnum.hh
@@ -66,13 +66,11 @@
 #include "sc_fxval.hh"
 #include "scfx_params.hh"
 
-namespace sc_core
+namespace sc_gem5
 {
 
-class vcd_sc_fxnum_trace;
-class vcd_sc_fxnum_fast_trace;
-class wif_sc_fxnum_trace;
-class wif_sc_fxnum_fast_trace;
+template <typename T, typename B>
+class TraceValFxnumBase;
 
 } // namespace sc_core
 
@@ -490,8 +488,8 @@
     friend class sc_fxnum_fast_bitref;
     friend class sc_fxnum_fast_subref;
 
-    friend class sc_core::vcd_sc_fxnum_trace;
-    friend class sc_core::wif_sc_fxnum_trace;
+    template <typename T, typename B>
+    friend class sc_gem5::TraceValFxnumBase;
 
   protected:
     sc_fxnum_observer *observer() const;
@@ -851,8 +849,8 @@
     friend class sc_fxnum_fast_bitref;
     friend class sc_fxnum_fast_subref;
 
-    friend class sc_core::vcd_sc_fxnum_fast_trace;
-    friend class sc_core::wif_sc_fxnum_fast_trace;
+    template <typename T, typename B>
+    friend class sc_gem5::TraceValFxnumBase;
 
   protected:
     sc_fxnum_fast_observer *observer() const;
diff --git a/src/systemc/ext/utils/sc_trace_file.hh b/src/systemc/ext/utils/sc_trace_file.hh
index 0b4b0d0..09f5bb2 100644
--- a/src/systemc/ext/utils/sc_trace_file.hh
+++ b/src/systemc/ext/utils/sc_trace_file.hh
@@ -63,7 +63,12 @@
 
 class sc_trace_file
 {
+  protected:
+    sc_trace_file();
+
   public:
+    virtual ~sc_trace_file();
+
     virtual void set_time_unit(double, sc_time_unit) = 0;
 };
 
diff --git a/src/systemc/utils/SConscript b/src/systemc/utils/SConscript
index f925293..5aaea54 100644
--- a/src/systemc/utils/SConscript
+++ b/src/systemc/utils/SConscript
@@ -33,4 +33,6 @@
     Source('sc_report_handler.cc')
     Source('sc_trace_file.cc')
     Source('sc_vector.cc')
+    Source('tracefile.cc')
+    Source('vcd.cc')
     Source('warn_unimpl.cc')
diff --git a/src/systemc/utils/sc_trace_file.cc b/src/systemc/utils/sc_trace_file.cc
index 2ac702d..cc9ceef 100644
--- a/src/systemc/utils/sc_trace_file.cc
+++ b/src/systemc/utils/sc_trace_file.cc
@@ -27,390 +27,439 @@
  * Authors: Gabe Black
  */
 
+#include <vector>
+
 #include "base/logging.hh"
+#include "systemc/core/scheduler.hh"
+#include "systemc/ext/channel/sc_signal_in_if.hh"
+#include "systemc/ext/core/sc_event.hh"
+#include "systemc/ext/core/sc_time.hh"
+#include "systemc/ext/dt/bit/sc_bv_base.hh"
+#include "systemc/ext/dt/bit/sc_logic.hh"
+#include "systemc/ext/dt/bit/sc_lv_base.hh"
+#include "systemc/ext/dt/fx/sc_fxnum.hh"
+#include "systemc/ext/dt/fx/sc_fxval.hh"
+#include "systemc/ext/dt/int/sc_int_base.hh"
+#include "systemc/ext/dt/int/sc_signed.hh"
+#include "systemc/ext/dt/int/sc_uint_base.hh"
+#include "systemc/ext/dt/int/sc_unsigned.hh"
 #include "systemc/ext/utils/sc_trace_file.hh"
+#include "systemc/utils/vcd.hh"
 
 namespace sc_core
 {
 
+sc_trace_file::sc_trace_file() {}
+sc_trace_file::~sc_trace_file() {}
+
 sc_trace_file *
 sc_create_vcd_trace_file(const char *name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
-    return nullptr;
+    auto tf = new ::sc_gem5::VcdTraceFile(name);
+    ::sc_gem5::scheduler.registerTraceFile(tf);
+    return tf;
 }
 
 void
 sc_close_vcd_trace_file(sc_trace_file *tf)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    ::sc_gem5::scheduler.unregisterTraceFile(
+            static_cast<::sc_gem5::TraceFile *>(tf));
+    delete tf;
 }
 
 void
 sc_write_comment(sc_trace_file *tf, const std::string &comment)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->writeComment(comment);
 }
 
 void
-sc_trace(sc_trace_file *, const bool &, const std::string &)
+sc_trace(sc_trace_file *tf, const bool &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const bool *, const std::string &)
+sc_trace(sc_trace_file *tf, const bool *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const float &, const std::string &)
+sc_trace(sc_trace_file *tf, const float &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const float *, const std::string &)
+sc_trace(sc_trace_file *tf, const float *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const double &, const std::string &)
+sc_trace(sc_trace_file *tf, const double &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const double *, const std::string &)
+sc_trace(sc_trace_file *tf, const double *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_logic &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_logic &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_logic *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_logic *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_int_base &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_int_base &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_int_base *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_int_base *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_uint_base &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_uint_base &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_uint_base *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_uint_base *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_signed &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_signed &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_signed *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_signed *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_unsigned &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_unsigned &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_unsigned *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_unsigned *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_bv_base &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_bv_base &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_bv_base *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_bv_base *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_lv_base &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_lv_base &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_lv_base *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_lv_base *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxval &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxval &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxval *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxval *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxval_fast &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxval_fast &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxval_fast *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxval_fast *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxnum &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxnum &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxnum *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxnum *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxnum_fast &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxnum_fast &v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::sc_fxnum_fast *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_dt::sc_fxnum_fast *v,
+        const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_event &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_event &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_event *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_event *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_time &, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_time &v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_time *, const std::string &)
+sc_trace(sc_trace_file *tf, const sc_time *v, const std::string &name)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned char &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned char &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned char *,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned char *v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned short &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned short &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned short *,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned short *v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned int &, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned int &v,
+        const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned int *, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned int *v,
+        const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned long &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned long &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned long *,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const unsigned long *v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const char &, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const char &v, const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const char *, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const char *v, const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const short &, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const short &v,
+        const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const short *, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const short *v,
+        const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const int &, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const int &v, const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const int *, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const int *v, const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const long &, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const long &v, const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const long *, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const long *v, const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::int64 &, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_dt::int64 &v,
+        const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::int64 *, const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_dt::int64 *v,
+        const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::uint64 &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_dt::uint64 &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(&v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_dt::uint64 *,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_dt::uint64 *v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->addTraceVal(v, name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_signal_in_if<char> &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_signal_in_if<char> &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->
+        addTraceVal(&v.read(), name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_signal_in_if<short> &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_signal_in_if<short> &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->
+        addTraceVal(&v.read(), name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_signal_in_if<int> &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_signal_in_if<int> &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->
+        addTraceVal(&v.read(), name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const sc_signal_in_if<long> &,
-         const std::string &, int width)
+sc_trace(sc_trace_file *tf, const sc_signal_in_if<long> &v,
+         const std::string &name, int width)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->
+        addTraceVal(&v.read(), name, width);
 }
 
 void
-sc_trace(sc_trace_file *, const unsigned int &,
-         const std::string &, const char **enum_literals)
+sc_trace(sc_trace_file *tf, const unsigned int &v,
+         const std::string &name, const char **enum_literals)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->
+        addTraceVal(&v, name, enum_literals);
 }
 
 void
-sc_trace_delta_cycles(sc_trace_file *, bool on)
+sc_trace_delta_cycles(sc_trace_file *tf, bool on)
 {
-    warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+    static_cast<::sc_gem5::TraceFile *>(tf)->traceDeltas(on);
 }
 
 } // namespace sc_core
diff --git a/src/systemc/utils/tracefile.cc b/src/systemc/utils/tracefile.cc
new file mode 100644
index 0000000..20091c3
--- /dev/null
+++ b/src/systemc/utils/tracefile.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#include "systemc/utils/tracefile.hh"
+
+#include <ctime>
+#include <iomanip>
+
+#include "base/logging.hh"
+#include "base/output.hh"
+#include "sim/core.hh"
+#include "systemc/ext/core/sc_main.hh"
+#include "systemc/ext/utils/functions.hh"
+
+namespace sc_gem5
+{
+
+TraceFile::TraceFile(const std::string &name) :
+    _os(simout.create(name, true, true)), timeUnitTicks(0),
+    timeUnitValue(1.0), timeUnitUnit(::sc_core::SC_PS), _traceDeltas(false)
+{}
+
+TraceFile::~TraceFile()
+{
+    simout.close(_os);
+}
+
+std::ostream &TraceFile::stream() { return *_os->stream(); }
+
+void
+TraceFile::set_time_unit(double d, ::sc_core::sc_time_unit tu)
+{
+    timeUnitValue = d;
+    timeUnitUnit = tu;
+}
+
+void
+TraceFile::finalizeTime()
+{
+    timeUnitTicks = ::sc_core::sc_time(timeUnitValue, timeUnitUnit).value();
+}
+
+} // namespace sc_gem5
diff --git a/src/systemc/utils/tracefile.hh b/src/systemc/utils/tracefile.hh
new file mode 100644
index 0000000..8e751e4
--- /dev/null
+++ b/src/systemc/utils/tracefile.hh
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#ifndef __SYSTEMC_UTILS_TRACEFILE_HH__
+#define __SYSTEMC_UTILS_TRACEFILE_HH__
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "systemc/core/event.hh"
+#include "systemc/ext/channel/sc_signal_in_if.hh"
+#include "systemc/ext/core/sc_event.hh"
+#include "systemc/ext/dt/fx/sc_fxnum.hh"
+#include "systemc/ext/utils/sc_trace_file.hh"
+
+class OutputStream;
+
+namespace sc_gem5
+{
+
+class TraceValBase
+{
+  protected:
+    int _width;
+
+  public:
+    TraceValBase(int _width) : _width(_width) {}
+    virtual ~TraceValBase() {}
+
+    int width() { return _width; }
+
+    virtual void finalize() {};
+    virtual bool check() = 0;
+};
+
+template <typename T, typename Base=TraceValBase>
+class TraceVal : public Base
+{
+  private:
+    const T *t;
+    T oldVal;
+
+  public:
+    TraceVal(const T *_t, int _width) : Base(_width), t(_t), oldVal(*t)
+    {}
+    ~TraceVal() {}
+
+    void finalize() override { oldVal = *t; }
+    const T &value() { return oldVal; }
+
+    bool
+    check() override
+    {
+        bool changed = (*t != oldVal);
+        oldVal = *t;
+        return changed;
+    }
+};
+
+template <typename T, typename Base>
+class TraceVal<::sc_core::sc_signal_in_if<T>, Base> : public Base
+{
+  private:
+    const ::sc_core::sc_signal_in_if<T> *iface;
+    T oldVal;
+
+  public:
+    TraceVal(const ::sc_core::sc_signal_in_if<T> *_iface, int _width) :
+        Base(_width), iface(_iface), oldVal(iface->read())
+    {}
+    ~TraceVal() {}
+
+    void finalize() override { oldVal = iface->read(); }
+    const T &value() { return oldVal; }
+
+    bool
+    check() override
+    {
+        T newVal = iface->read();
+        bool changed = (newVal != oldVal);
+        oldVal = newVal;
+        return changed;
+    }
+};
+
+template <typename Base>
+class TraceVal<::sc_core::sc_event, Base> : public Base
+{
+  private:
+    bool triggered;
+    uint64_t oldStamp;
+    const Event *event;
+
+  public:
+    TraceVal(const ::sc_core::sc_event *_event, int _width) :
+        Base(_width), triggered(false), oldStamp(0),
+        event(Event::getFromScEvent(_event))
+    {}
+    ~TraceVal() {}
+
+    bool value() { return triggered; }
+    void finalize() override { oldStamp = event->triggeredStamp(); }
+
+    bool
+    check() override
+    {
+        uint64_t newStamp = event->triggeredStamp();
+        triggered = (oldStamp != newStamp);
+        oldStamp = newStamp;
+        return triggered;
+    }
+};
+
+template <typename T, typename Base>
+class TraceValFxnumBase : public Base
+{
+  private:
+    const T *t;
+    T oldVal;
+
+  public:
+    TraceValFxnumBase(const T *_t, int _width) :
+        Base(_width), t(_t),
+        oldVal(_t->m_params.type_params(), _t->m_params.enc(),
+                _t->m_params.cast_switch(), 0)
+    {}
+    ~TraceValFxnumBase() {}
+
+    void
+    finalize() override
+    {
+        oldVal = *t;
+        this->_width = t->wl();
+    }
+
+    const T &value() { return oldVal; }
+
+    bool
+    check() override
+    {
+        bool changed = (*t != oldVal);
+        oldVal = *t;
+        return changed;
+    }
+};
+
+template <typename Base>
+class TraceVal<::sc_dt::sc_fxnum, Base> :
+    public TraceValFxnumBase<::sc_dt::sc_fxnum, Base>
+{
+  public:
+    using TraceValFxnumBase<::sc_dt::sc_fxnum, Base>::TraceValFxnumBase;
+    ~TraceVal() {}
+};
+
+template <typename Base>
+class TraceVal<::sc_dt::sc_fxnum_fast, Base> :
+    public TraceValFxnumBase<::sc_dt::sc_fxnum_fast, Base>
+{
+  public:
+    using TraceValFxnumBase<::sc_dt::sc_fxnum_fast, Base>::TraceValFxnumBase;
+    ~TraceVal() {}
+};
+
+class TraceFile : public sc_core::sc_trace_file
+{
+  protected:
+    OutputStream *_os;
+    uint64_t timeUnitTicks;
+    double timeUnitValue;
+    ::sc_core::sc_time_unit timeUnitUnit;
+
+    bool _traceDeltas;
+
+    TraceFile(const std::string &name);
+
+    std::ostream &stream();
+
+  public:
+    ~TraceFile();
+
+    void traceDeltas(bool on) { _traceDeltas = on; }
+
+    void set_time_unit(double, ::sc_core::sc_time_unit) override;
+    void finalizeTime();
+
+    virtual void trace(bool delta) = 0;
+
+    virtual void addTraceVal(const bool *v, const std::string &name) = 0;
+    virtual void addTraceVal(const float *v, const std::string &name) = 0;
+    virtual void addTraceVal(const double *v, const std::string &name) = 0;
+
+    virtual void addTraceVal(const sc_dt::sc_logic *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_int_base *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_uint_base *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_signed *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_unsigned *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_bv_base *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_lv_base *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_fxval *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_fxval_fast *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_fxnum *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_dt::sc_fxnum_fast *v,
+                             const std::string &name) = 0;
+
+    virtual void addTraceVal(const sc_core::sc_event *v,
+                             const std::string &name) = 0;
+    virtual void addTraceVal(const sc_core::sc_time *v,
+                             const std::string &name) = 0;
+
+    virtual void addTraceVal(const unsigned char *v,
+                             const std::string &name, int width) = 0;
+    virtual void addTraceVal(const char *v, const std::string &name,
+                             int width) = 0;
+    virtual void addTraceVal(const unsigned short *v,
+                             const std::string &name, int width) = 0;
+    virtual void addTraceVal(const short *v, const std::string &name,
+                             int width) = 0;
+    virtual void addTraceVal(const unsigned int *v,
+                             const std::string &name, int width) = 0;
+    virtual void addTraceVal(const int *v, const std::string &name,
+                             int width) = 0;
+    virtual void addTraceVal(const unsigned long *v,
+                             const std::string &name, int width) = 0;
+    virtual void addTraceVal(const long *v, const std::string &name,
+                             int width) = 0;
+
+    virtual void addTraceVal(const sc_dt::int64 *v,
+                             const std::string &name, int width) = 0;
+    virtual void addTraceVal(const sc_dt::uint64 *v,
+                             const std::string &name, int width) = 0;
+
+    virtual void addTraceVal(const unsigned int *,
+                             const std::string &name,
+                             const char **literals) = 0;
+
+    virtual void writeComment(const std::string &comment) = 0;
+};
+
+} // namespace sc_gem5
+
+#endif // __SYSTEMC_UTILS_TRACEFILE_HH__
diff --git a/src/systemc/utils/vcd.cc b/src/systemc/utils/vcd.cc
new file mode 100644
index 0000000..83e6b50
--- /dev/null
+++ b/src/systemc/utils/vcd.cc
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#include "systemc/utils/vcd.hh"
+
+#include <ctime>
+#include <iomanip>
+
+#include "base/bitfield.hh"
+#include "base/cprintf.hh"
+#include "base/logging.hh"
+#include "systemc/core/scheduler.hh"
+#include "systemc/ext/core/sc_event.hh"
+#include "systemc/ext/core/sc_main.hh"
+#include "systemc/ext/core/sc_time.hh"
+#include "systemc/ext/dt/bit/sc_bv_base.hh"
+#include "systemc/ext/dt/bit/sc_logic.hh"
+#include "systemc/ext/dt/bit/sc_lv_base.hh"
+#include "systemc/ext/dt/fx/sc_fxnum.hh"
+#include "systemc/ext/dt/fx/sc_fxval.hh"
+#include "systemc/ext/dt/int/sc_int_base.hh"
+#include "systemc/ext/dt/int/sc_signed.hh"
+#include "systemc/ext/dt/int/sc_uint_base.hh"
+#include "systemc/ext/dt/int/sc_unsigned.hh"
+#include "systemc/ext/utils/functions.hh"
+
+namespace sc_gem5
+{
+
+namespace
+{
+
+std::string
+cleanName(std::string name)
+{
+    for (int i = 0; i < name.length(); i++) {
+        if (name[i] == '[')
+            name[i] = '(';
+        else if (name[i] == ']')
+            name[i] = ')';
+    }
+    return name;
+}
+
+} // anonymous namespace
+
+class VcdTraceValBase : public TraceValBase
+{
+  protected:
+    std::string _vcdName;
+
+    const char *
+    stripLeadingBits(const char *orig)
+    {
+        const char first = orig[0];
+
+        if (first != 'z' && first != 'x' && first != '0')
+            return orig;
+
+        const char *res = orig;
+        while (*++res == first) {}
+
+        if (first != '0' || *res != '1')
+            res--;
+
+        return res;
+    }
+
+    char
+    scLogicToVcdState(char in)
+    {
+        switch (in) {
+          case 'U':
+          case 'X':
+          case 'W':
+          case 'D':
+            return 'x';
+          case '0':
+          case 'L':
+            return '0';
+          case '1':
+          case 'H':
+            return '1';
+          case 'Z':
+            return 'z';
+          default:
+            return '?';
+        }
+    }
+
+    void
+    printVal(std::ostream &os, const std::string &rep)
+    {
+        switch (width()) {
+          case 0:
+            return;
+          case 1:
+            os << rep << vcdName() << std::endl;;
+            return;
+          default:
+            os << "b" << stripLeadingBits(rep.c_str()) << " " <<
+                vcdName() << std::endl;
+            return;
+        }
+    }
+
+  public:
+    VcdTraceValBase(int width) : TraceValBase(width) {}
+    ~VcdTraceValBase() {}
+
+    void vcdName(const std::string &vcd_name) { _vcdName = vcd_name; }
+    const std::string &vcdName() { return _vcdName; }
+    virtual std::string vcdType() { return "wire"; }
+
+    virtual void output(std::ostream &os) = 0;
+};
+
+void
+VcdTraceScope::addValue(const std::string &name, VcdTraceValBase *value)
+{
+    size_t pos = name.find_first_of('.');
+    if (pos == std::string::npos) {
+        values.emplace_back(name, value);
+    } else {
+        std::string sname = name.substr(0, pos);
+        auto it = scopes.find(sname);
+        if (it == scopes.end())
+            it = scopes.emplace(sname, new VcdTraceScope).first;
+        it->second->addValue(name.substr(pos + 1), value);
+    }
+}
+
+void
+VcdTraceScope::output(const std::string &name, std::ostream &os)
+{
+    os << "$scope module " << name << " $end" << std::endl;
+
+    for (auto &p: values) {
+        const std::string &name = p.first;
+        VcdTraceValBase *value = p.second;
+
+        int w = value->width();
+        if (w <= 0) {
+            std::string msg = csprintf("'%s' has 0 bits", name);
+            // The typo in this error message is intentional to match the
+            // Accellera output.
+            SC_REPORT_ERROR("(E710) object cannot not be traced", msg.c_str());
+            return;
+        }
+
+        std::string clean_name = cleanName(name);
+        if (w == 1) {
+            ccprintf(os, "$var %s  % 3d  %s  %s       $end\n",
+                     value->vcdType(), w, value->vcdName(), clean_name);
+        } else {
+            ccprintf(os, "$var %s  % 3d  %s  %s [%d:0]  $end\n",
+                     value->vcdType(), w, value->vcdName(), clean_name, w - 1);
+        }
+    }
+
+    for (auto &p: scopes)
+        p.second->output(p.first, os);
+
+    os << "$upscope $end" << std::endl;
+}
+
+template <typename T>
+class VcdTraceVal : public TraceVal<T, VcdTraceValBase>
+{
+  public:
+    typedef T TracedType;
+
+    VcdTraceVal(const T* t, const std::string &vcd_name, int width) :
+        TraceVal<T, VcdTraceValBase>(t, width)
+    {
+        this->vcdName(vcd_name);
+    }
+};
+
+std::string
+VcdTraceFile::nextSignalName()
+{
+    std::string name(_nextName);
+
+    bool carry = false;
+    int pos = NextNameChars - 1;
+    do {
+        carry = (_nextName[pos] == 'z');
+        if (carry)
+            _nextName[pos--] = 'a';
+        else
+            _nextName[pos--]++;
+    } while (carry && pos >= 0);
+
+    return name;
+}
+
+void
+VcdTraceFile::initialize()
+{
+    finalizeTime();
+
+    // Date.
+    stream() << "$date" << std::endl;
+    time_t long_time;
+    time(&long_time);
+    struct tm *p_tm = localtime(&long_time);
+    stream() << std::put_time(p_tm, "     %b %d, %Y       %H:%M:%S\n");
+    stream() << "$end" << std::endl << std::endl;
+
+    // Version.
+    stream() << "$version" << std::endl;
+    stream() << " " << ::sc_core::sc_version() << std::endl;
+    stream() << "$end" << std::endl << std::endl;
+
+    // Timescale.
+    stream() << "$timescale" << std::endl;
+    stream() << "     " << ::sc_core::sc_time::from_value(timeUnitTicks) <<
+        std::endl;
+    stream() << "$end" << std::endl << std::endl;
+
+    for (auto tv: traceVals)
+        tv->finalize();
+
+    topScope.output("SystemC", stream());
+
+    stream() << "$enddefinitions  $end" << std::endl << std::endl;
+
+    Tick now = scheduler.getCurTick();
+
+    std::string timedump_comment =
+        csprintf("All initial values are dumped below at time "
+                 "%g sec = %g timescale units.",
+                 static_cast<double>(now) / SimClock::Float::s,
+                 static_cast<double>(now / timeUnitTicks));
+    writeComment(timedump_comment);
+
+    lastPrintedTime = now / timeUnitTicks;
+
+    stream() << "$dumpvars" << std::endl;
+    for (auto tv: traceVals)
+        tv->output(stream());
+    stream() << "$end" << std::endl << std::endl;
+
+    initialized = true;
+}
+
+VcdTraceFile::~VcdTraceFile()
+{
+    for (auto tv: traceVals)
+        delete tv;
+    traceVals.clear();
+
+    if (timeUnitTicks)
+        ccprintf(stream(), "#%u\n", scheduler.getCurTick() / timeUnitTicks);
+}
+
+void
+VcdTraceFile::trace(bool delta)
+{
+    if (!delta)
+        deltasAtNow = 0;
+
+    uint64_t deltaOffset = deltasAtNow;
+
+    if (delta)
+        deltaOffset = deltasAtNow++;
+
+    if (_traceDeltas != delta)
+        return;
+
+    if (!initialized) {
+        initialize();
+        return;
+    }
+
+    Tick now = scheduler.getCurTick() / timeUnitTicks + deltaOffset;
+
+    if (now <= lastPrintedTime) {
+        // TODO warn about reversed time?
+        return;
+    }
+
+    bool time_printed = false;
+    for (auto tv: traceVals) {
+        if (tv->check()) {
+            if (!time_printed) {
+                lastPrintedTime = now;
+                ccprintf(stream(), "#%u\n", now);
+                time_printed = true;
+            }
+
+            tv->output(stream());
+        }
+    }
+    if (time_printed)
+        stream() << std::endl;
+}
+
+class VcdTraceValBool : public VcdTraceVal<bool>
+{
+  public:
+    using VcdTraceVal<bool>::VcdTraceVal;
+
+    void
+    output(std::ostream &os) override
+    {
+        printVal(os, this->value() ? "1" : "0");
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const bool *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValBool>(v, name);
+}
+
+template <typename T>
+class VcdTraceValFloat : public VcdTraceVal<T>
+{
+  public:
+    using VcdTraceVal<T>::VcdTraceVal;
+
+    std::string vcdType() override { return "real"; }
+
+    void
+    output(std::ostream &os) override
+    {
+        ccprintf(os, "r%.16g %s\n", this->value(), this->vcdName());
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const float *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFloat<float>>(v, name);
+}
+void
+VcdTraceFile::addTraceVal(const double *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFloat<double>>(v, name);
+}
+
+class VcdTraceValScLogic : public VcdTraceVal<sc_dt::sc_logic>
+{
+  public:
+    using VcdTraceVal<sc_dt::sc_logic>::VcdTraceVal;
+
+    void
+    output(std::ostream &os) override
+    {
+        char str[2] = {
+            scLogicToVcdState(value().to_char()),
+            '\0'
+        };
+        printVal(os, str);
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_logic *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValScLogic>(v, name);
+}
+
+template <typename T>
+class VcdTraceValFinite : public VcdTraceVal<T>
+{
+  public:
+    using VcdTraceVal<T>::VcdTraceVal;
+
+    void
+    finalize() override
+    {
+        VcdTraceVal<T>::finalize();
+        this->_width = this->value().length();
+    }
+
+    void
+    output(std::ostream &os) override
+    {
+        std::string str;
+        const int w = this->width();
+
+        str.reserve(w);
+        for (int i = w - 1; i >= 0; i--)
+            str += this->value()[i].to_bool() ? '1' : '0';
+
+        this->printVal(os, str);
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_int_base *v,
+                          const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_int_base>>(v, name);
+}
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_uint_base *v,
+                          const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_uint_base>>(v, name);
+}
+
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_signed *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_signed>>(v, name);
+}
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_unsigned *v,
+                          const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFinite<sc_dt::sc_unsigned>>(v, name);
+}
+
+template <typename T>
+class VcdTraceValLogic : public VcdTraceVal<T>
+{
+  public:
+    using VcdTraceVal<T>::VcdTraceVal;
+
+    void
+    finalize() override
+    {
+        VcdTraceVal<T>::finalize();
+        this->_width = this->value().length();
+    }
+
+    void
+    output(std::ostream &os) override
+    {
+        this->printVal(os, this->value().to_string());
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_bv_base *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_bv_base>>(v, name);
+}
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_lv_base *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_lv_base>>(v, name);
+}
+
+template <typename T>
+class VcdTraceValFxval : public VcdTraceVal<T>
+{
+  public:
+    using VcdTraceVal<T>::VcdTraceVal;
+
+    std::string vcdType() override { return "real"; }
+
+    void
+    output(std::ostream &os) override
+    {
+        ccprintf(os, "r%.16g %s\n",
+                this->value().to_double(), this->vcdName());
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_fxval *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval>>(v, name);
+}
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_fxval_fast *v,
+                          const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval_fast>>(v, name);
+}
+
+template <typename T>
+class VcdTraceValFxnum : public VcdTraceVal<T>
+{
+  public:
+    using VcdTraceVal<T>::VcdTraceVal;
+
+    void
+    output(std::ostream &os) override
+    {
+        std::string str;
+        const int w = this->width();
+
+        str.reserve(w);
+        for (int i = w - 1; i >= 0; i--)
+            str += this->value()[i] ? '1' : '0';
+
+        this->printVal(os, str);
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum>>(v, name);
+}
+void
+VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum_fast *v,
+                          const std::string &name)
+{
+    addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum_fast>>(v, name);
+}
+
+class VcdTraceValEvent : public VcdTraceVal<::sc_core::sc_event>
+{
+  public:
+    using VcdTraceVal<::sc_core::sc_event>::VcdTraceVal;
+
+    std::string vcdType() override { return "event"; }
+
+    void
+    output(std::ostream &os) override
+    {
+        if (value())
+            printVal(os, "1");
+        else
+            os << std::endl;
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const sc_core::sc_event *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValEvent>(v, name);
+}
+
+class VcdTraceValTime : public VcdTraceVal<::sc_core::sc_time>
+{
+  private:
+    static const int TimeWidth = 64;
+
+  public:
+    using VcdTraceVal<::sc_core::sc_time>::VcdTraceVal;
+
+    std::string vcdType() override { return "time"; }
+
+    void
+    finalize() override
+    {
+        VcdTraceVal<::sc_core::sc_time>::finalize();
+        _width = TimeWidth;
+    }
+
+    void
+    output(std::ostream &os) override
+    {
+        char str[TimeWidth + 1];
+        str[TimeWidth] = '\0';
+
+        const uint64_t val = value().value();
+        for (int i = 0; i < TimeWidth; i++)
+            str[i] = ::bits(val, TimeWidth - i - 1) ? '1' : '0';
+
+        printVal(os, str);
+    }
+};
+void
+VcdTraceFile::addTraceVal(const sc_core::sc_time *v, const std::string &name)
+{
+    addNewTraceVal<VcdTraceValTime>(v, name);
+}
+
+template <typename T>
+class VcdTraceValInt : public VcdTraceVal<T>
+{
+  public:
+    using VcdTraceVal<T>::VcdTraceVal;
+
+    void
+    output(std::ostream &os) override
+    {
+        const int w = this->width();
+        char str[w + 1];
+        str[w] = '\0';
+
+        const uint64_t val =
+            static_cast<uint64_t>(this->value()) & ::mask(sizeof(T) * 8);
+
+        if (::mask(w) < val) {
+            for (int i = 0; i < w; i++)
+                str[i] = 'x';
+        } else {
+            for (int i = 0; i < w; i++)
+                str[i] = ::bits(val, w - i - 1) ? '1' : '0';
+        }
+
+        this->printVal(os, str);
+    }
+};
+
+void
+VcdTraceFile::addTraceVal(const unsigned char *v, const std::string &name,
+                          int width)
+{
+    addNewTraceVal<VcdTraceValInt<unsigned char>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const char *v, const std::string &name, int width)
+{
+    addNewTraceVal<VcdTraceValInt<char>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const unsigned short *v, const std::string &name,
+                          int width)
+{
+    addNewTraceVal<VcdTraceValInt<unsigned short>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const short *v, const std::string &name, int width)
+{
+    addNewTraceVal<VcdTraceValInt<short>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name,
+                          int width)
+{
+    addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const int *v, const std::string &name, int width)
+{
+    addNewTraceVal<VcdTraceValInt<int>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const unsigned long *v, const std::string &name,
+                          int width)
+{
+    addNewTraceVal<VcdTraceValInt<unsigned long>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const long *v, const std::string &name, int width)
+{
+    addNewTraceVal<VcdTraceValInt<long>>(v, name, width);
+}
+
+void
+VcdTraceFile::addTraceVal(const sc_dt::int64 *v, const std::string &name,
+                          int width)
+{
+    addNewTraceVal<VcdTraceValInt<sc_dt::int64>>(v, name, width);
+}
+void
+VcdTraceFile::addTraceVal(const sc_dt::uint64 *v, const std::string &name,
+                          int width)
+{
+    addNewTraceVal<VcdTraceValInt<sc_dt::uint64>>(v, name, width);
+}
+
+void
+VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name,
+                          const char **literals)
+{
+    uint64_t count = 0;
+    while (*literals++)
+        count++;
+
+    int bits = 0;
+    while (count >> bits)
+        bits++;
+
+    addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, bits);
+}
+
+void
+VcdTraceFile::writeComment(const std::string &comment)
+{
+    stream() << "$comment" << std::endl;
+    stream() << comment << std::endl;
+    stream() << "$end" << std::endl << std::endl;
+}
+
+} // namespace sc_gem5
diff --git a/src/systemc/utils/vcd.hh b/src/systemc/utils/vcd.hh
new file mode 100644
index 0000000..dce7f46
--- /dev/null
+++ b/src/systemc/utils/vcd.hh
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#ifndef __SYSTEMC_UTILS_VCD_HH__
+#define __SYSTEMC_UTILS_VCD_HH__
+
+#include <map>
+
+#include "systemc/utils/tracefile.hh"
+
+namespace sc_gem5
+{
+
+class VcdTraceValBase;
+
+class VcdTraceScope
+{
+  private:
+    std::vector<std::pair<std::string, VcdTraceValBase *>> values;
+    std::map<std::string, VcdTraceScope *> scopes;
+
+  public:
+    void addValue(const std::string &name, VcdTraceValBase *value);
+    void output(const std::string &name, std::ostream &os);
+};
+
+class VcdTraceFile : public TraceFile
+{
+  private:
+    Tick lastPrintedTime;
+    uint64_t deltasAtNow;
+
+    static const int NextNameChars = 5;
+    char _nextName[NextNameChars + 1];
+    std::string nextSignalName();
+
+    bool initialized;
+    void initialize();
+
+    std::vector<VcdTraceValBase *> traceVals;
+    VcdTraceScope topScope;
+
+  public:
+    VcdTraceFile(const std::string &name) :
+        TraceFile(name + ".vcd"), lastPrintedTime(0), deltasAtNow(0),
+        initialized(false)
+    {
+        _nextName[NextNameChars] = '\0';
+        for (int i = 0; i < NextNameChars; i++)
+            _nextName[i] = 'a';
+    }
+    ~VcdTraceFile();
+
+    void trace(bool delta) override;
+
+    template<typename TV>
+    void
+    addNewTraceVal(const typename TV::TracedType *v, const std::string &name,
+                   int width=1)
+    {
+        VcdTraceValBase *tv = new TV(v, nextSignalName(), width);
+        traceVals.push_back(tv);
+        topScope.addValue(name, tv);
+    }
+
+    void addTraceVal(const bool *v, const std::string &name) override;
+    void addTraceVal(const float *v, const std::string &name) override;
+    void addTraceVal(const double *v, const std::string &name) override;
+
+    void addTraceVal(const sc_dt::sc_logic *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_int_base *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_uint_base *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_signed *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_unsigned *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_bv_base *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_lv_base *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_fxval *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_fxval_fast *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_fxnum *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_dt::sc_fxnum_fast *v,
+                     const std::string &name) override;
+
+    void addTraceVal(const sc_core::sc_event *v,
+                     const std::string &name) override;
+    void addTraceVal(const sc_core::sc_time *v,
+                     const std::string &name) override;
+
+    void addTraceVal(const unsigned char *v,
+                     const std::string &name, int width) override;
+    void addTraceVal(const char *v, const std::string &name,
+                     int width) override;
+    void addTraceVal(const unsigned short *v,
+                     const std::string &name, int width) override;
+    void addTraceVal(const short *v, const std::string &name,
+                     int width) override;
+    void addTraceVal(const unsigned int *v,
+                     const std::string &name, int width) override;
+    void addTraceVal(const int *v, const std::string &name,
+                     int width) override;
+    void addTraceVal(const unsigned long *v,
+                     const std::string &name, int width) override;
+    void addTraceVal(const long *v, const std::string &name,
+                     int width) override;
+
+    void addTraceVal(const sc_dt::int64 *v,
+                     const std::string &name, int width) override;
+    void addTraceVal(const sc_dt::uint64 *v,
+                     const std::string &name, int width) override;
+
+    void addTraceVal(const unsigned int *, const std::string &name,
+                     const char **literals) override;
+
+    void writeComment(const std::string &comment) override;
+};
+
+} // namespace sc_gem5
+
+#endif // __SYSTEMC_UTILS_VCD_HH__