diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index c6a4265fc..ef718067f 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1079,7 +1079,10 @@ struct CxxrtlWorker { f << indent << "};\n"; f << indent << "if (performer) {\n"; inc_indent(); - f << indent << "performer->on_print(formatter(performer->time(), performer->realtime()));\n"; + f << indent << "static const metadata_map attributes = "; + dump_metadata_map(cell->attributes); + f << ";\n"; + f << indent << "performer->on_print(formatter(performer->time(), performer->realtime()), attributes);\n"; dec_indent(); f << indent << "} else {\n"; inc_indent(); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index d15772a3c..d3b9cf9db 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -892,33 +892,6 @@ struct fmt_part { } }; -// An object that can be passed to a `eval()` method in order to act on side effects. -struct performer { - // Called to evaluate a Verilog `$time` expression. - virtual int64_t time() const { return 0; } - - // Called to evaluate a Verilog `$realtime` expression. - virtual double realtime() const { return time(); } - - // Called when a `$print` cell is triggered. - virtual void on_print(const std::string &output) { std::cout << output; } -}; - -// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in -// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and -// a comparatively heavyweight template-based solution is justified. -struct observer { - // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks - // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and - // `base` points to the first chunk. - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {} - - // Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]` - // with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to - // the memory element chunk count and `base` points to the first chunk of the first element of the memory. - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {} -}; - template struct wire { static constexpr size_t bits = Bits; @@ -1100,6 +1073,33 @@ struct metadata { typedef std::map metadata_map; +// An object that can be passed to a `eval()` method in order to act on side effects. +struct performer { + // Called to evaluate a Verilog `$time` expression. + virtual int64_t time() const { return 0; } + + // Called to evaluate a Verilog `$realtime` expression. + virtual double realtime() const { return time(); } + + // Called when a `$print` cell is triggered. + virtual void on_print(const std::string &output, const metadata_map &attributes) { std::cout << output; } +}; + +// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in +// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and +// a comparatively heavyweight template-based solution is justified. +struct observer { + // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks + // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and + // `base` points to the first chunk. + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {} + + // Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]` + // with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to + // the memory element chunk count and `base` points to the first chunk of the first element of the memory. + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {} +}; + // Tag class to disambiguate values/wires and their aliases. struct debug_alias {};