scc 2025.09
SystemC components library
vcd_mt_trace.cpp
1/*******************************************************************************
2 * Copyright 2021 MINRES Technologies GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16
17#include "vcd_mt_trace.hh"
18#include "trace/gz_writer.hh"
19#define FWRITE(BUF, SZ, LEN, FP) FP->write(BUF, SZ* LEN)
20#define FPTR gz_writer*
21#include "trace/vcd_trace.hh"
22#include "utilities.h"
23#include <cmath>
24#include <cstdlib>
25#include <cstring>
26#include <deque>
27#include <map>
28#include <sstream>
29#include <string>
30#include <unordered_map>
31#include <vector>
32
33#define FPRINT(FP, FMTSTR) FP->write_single(fmt::format(FMTSTR));
34#define FPRINTF(FP, FMTSTR, ...) FP->write_single(fmt::format(FMTSTR, __VA_ARGS__));
35
36namespace scc {
37/*******************************************************************************************************
38 *
39 *******************************************************************************************************/
40vcd_mt_trace_file::vcd_mt_trace_file(const char* name, std::function<bool()>& enable)
41: name(name)
42, check_enabled(enable) {
43 vcd_out = scc::make_unique<trace::gz_writer>(fmt::format("{}.vcd.gz", name));
44
45#if SC_VERSION_MAJOR < 3
46#if defined(WITH_SC_TRACING_PHASE_CALLBACKS)
47 // remove from hierarchy
48 sc_object::detach();
49 // register regular (non-delta) callbacks
50 sc_object::register_simulation_phase_callback(SC_BEFORE_TIMESTEP);
51#else // explicitly register with simcontext
52 sc_core::sc_get_curr_simcontext()->add_trace_file(this);
53#endif
54#endif
55}
56
57vcd_mt_trace_file::~vcd_mt_trace_file() {
58 if(vcd_out) {
59 FPRINTF(vcd_out, "#{}\n", sc_core::sc_time_stamp() / 1_ps);
60 }
61 for(auto t : all_traces)
62 delete t.trc;
63}
64
65template <typename T, typename OT = T> bool changed(trace::vcd_trace* trace) {
66 if(reinterpret_cast<trace::vcd_trace_t<T, OT>*>(trace)->changed()) {
67 reinterpret_cast<trace::vcd_trace_t<T, OT>*>(trace)->update();
68 return true;
69 } else
70 return false;
71}
72#define DECL_TRACE_METHOD_A(tp) \
73 void vcd_mt_trace_file::trace(const tp& object, const std::string& name) { \
74 all_traces.emplace_back(this, &changed<tp>, new trace::vcd_trace_t<tp>(object, name)); \
75 }
76#define DECL_TRACE_METHOD_B(tp) \
77 void vcd_mt_trace_file::trace(const tp& object, const std::string& name, int width) { \
78 all_traces.emplace_back(this, &changed<tp>, new trace::vcd_trace_t<tp>(object, name)); \
79 }
80#define DECL_TRACE_METHOD_C(tp, tpo) \
81 void vcd_mt_trace_file::trace(const tp& object, const std::string& name) { \
82 all_traces.emplace_back(this, &changed<tp, tpo>, new trace::vcd_trace_t<tp, tpo>(object, name)); \
83 }
84
85#if(SYSTEMC_VERSION >= 20171012) || defined(NCSC)
86void vcd_mt_trace_file::trace(const sc_core::sc_event& object, const std::string& name) {}
87void vcd_mt_trace_file::trace(const sc_core::sc_time& object, const std::string& name) {}
88#endif
89DECL_TRACE_METHOD_A(bool)
90DECL_TRACE_METHOD_A(sc_dt::sc_bit)
91DECL_TRACE_METHOD_A(sc_dt::sc_logic)
92
93DECL_TRACE_METHOD_B(unsigned char)
94DECL_TRACE_METHOD_B(unsigned short)
95DECL_TRACE_METHOD_B(unsigned int)
96DECL_TRACE_METHOD_B(unsigned long)
97#ifdef SYSTEMC_64BIT_PATCHES
98DECL_TRACE_METHOD_B(unsigned long long)
99#endif
100DECL_TRACE_METHOD_B(char)
101DECL_TRACE_METHOD_B(short)
102DECL_TRACE_METHOD_B(int)
103DECL_TRACE_METHOD_B(long)
104DECL_TRACE_METHOD_B(sc_dt::int64)
105DECL_TRACE_METHOD_B(sc_dt::uint64)
106
107DECL_TRACE_METHOD_A(float)
108DECL_TRACE_METHOD_A(double)
109DECL_TRACE_METHOD_A(sc_dt::sc_int_base)
110DECL_TRACE_METHOD_A(sc_dt::sc_uint_base)
111DECL_TRACE_METHOD_A(sc_dt::sc_signed)
112DECL_TRACE_METHOD_A(sc_dt::sc_unsigned)
113
114DECL_TRACE_METHOD_A(sc_dt::sc_fxval)
115DECL_TRACE_METHOD_A(sc_dt::sc_fxval_fast)
116DECL_TRACE_METHOD_C(sc_dt::sc_fxnum, sc_dt::sc_fxval)
117DECL_TRACE_METHOD_C(sc_dt::sc_fxnum_fast, sc_dt::sc_fxval_fast)
118
119DECL_TRACE_METHOD_A(sc_dt::sc_bv_base)
120DECL_TRACE_METHOD_A(sc_dt::sc_lv_base)
121#undef DECL_TRACE_METHOD_A
122#undef DECL_TRACE_METHOD_B
123#undef DECL_TRACE_METHOD_C
124
125void vcd_mt_trace_file::trace(const unsigned int& object, const std::string& name, const char** enum_literals) {
126 all_traces.emplace_back(this, &changed<unsigned int>, new trace::vcd_trace_enum(object, name, enum_literals));
127}
128
129#define DECL_REGISTER_METHOD_A(tp) \
130 observer::notification_handle* vcd_mt_trace_file::observe(const tp& object, const std::string& name) { \
131 all_traces.emplace_back(this, &changed<tp>, new trace::vcd_trace_t<tp>(object, name)); \
132 all_traces.back().trc->is_triggered = true; \
133 return &all_traces.back(); \
134 }
135#define DECL_REGISTER_METHOD_C(tp, tpo) \
136 observer::notification_handle* vcd_mt_trace_file::observe(const tp& object, const std::string& name) { \
137 all_traces.emplace_back(this, &changed<tp, tpo>, new trace::vcd_trace_t<tp, tpo>(object, name)); \
138 all_traces.back().trc->is_triggered = true; \
139 return &all_traces.back(); \
140 }
141
142#if(SYSTEMC_VERSION >= 20171012)
143observer::notification_handle* vcd_mt_trace_file::observe(const sc_core::sc_event& object, const std::string& name) { return nullptr; }
144observer::notification_handle* vcd_mt_trace_file::observe(const sc_core::sc_time& object, const std::string& name) { return nullptr; }
145#endif
146
147DECL_REGISTER_METHOD_A(bool)
148DECL_REGISTER_METHOD_A(sc_dt::sc_bit)
149DECL_REGISTER_METHOD_A(sc_dt::sc_logic)
150
151DECL_REGISTER_METHOD_A(unsigned char)
152DECL_REGISTER_METHOD_A(unsigned short)
153DECL_REGISTER_METHOD_A(unsigned int)
154DECL_REGISTER_METHOD_A(unsigned long)
155#ifdef SYSTEMC_64BIT_PATCHES
156DECL_REGISTER_METHOD_A(unsigned long long)
157#endif
158DECL_REGISTER_METHOD_A(char)
159DECL_REGISTER_METHOD_A(short)
160DECL_REGISTER_METHOD_A(int)
161DECL_REGISTER_METHOD_A(long)
162DECL_REGISTER_METHOD_A(sc_dt::int64)
163DECL_REGISTER_METHOD_A(sc_dt::uint64)
164
165DECL_REGISTER_METHOD_A(float)
166DECL_REGISTER_METHOD_A(double)
167DECL_REGISTER_METHOD_A(sc_dt::sc_int_base)
168DECL_REGISTER_METHOD_A(sc_dt::sc_uint_base)
169DECL_REGISTER_METHOD_A(sc_dt::sc_signed)
170DECL_REGISTER_METHOD_A(sc_dt::sc_unsigned)
171
172DECL_REGISTER_METHOD_A(sc_dt::sc_fxval)
173DECL_REGISTER_METHOD_A(sc_dt::sc_fxval_fast)
174DECL_REGISTER_METHOD_C(sc_dt::sc_fxnum, sc_dt::sc_fxval)
175DECL_REGISTER_METHOD_C(sc_dt::sc_fxnum_fast, sc_dt::sc_fxval_fast)
176
177DECL_REGISTER_METHOD_A(sc_dt::sc_bv_base)
178DECL_REGISTER_METHOD_A(sc_dt::sc_lv_base)
179#undef DECL_REGISTER_METHOD_A
180#undef DECL_REGISTER_METHOD_C
181
182bool vcd_mt_trace_file::trace_entry::notify() {
183 if(!trc->is_alias && compare_and_update(trc))
184 that->triggered_traces.push_back(trc);
185 return !trc->is_alias;
186}
187
188std::string vcd_mt_trace_file::obtain_name() {
189 const char first_type_used = 'a';
190 const int used_types_count = 'z' - 'a' + 1;
191 int result;
192
193 result = vcd_name_index;
194 char char6 = static_cast<char>(vcd_name_index % used_types_count);
195
196 result = result / used_types_count;
197 char char5 = static_cast<char>(result % used_types_count);
198
199 result = result / used_types_count;
200 char char4 = static_cast<char>(result % used_types_count);
201
202 result = result / used_types_count;
203 char char3 = static_cast<char>(result % used_types_count);
204
205 result = result / used_types_count;
206 char char2 = static_cast<char>(result % used_types_count);
207
208 char buf[20];
209 std::sprintf(buf, "%c%c%c%c%c", char2 + first_type_used, char3 + first_type_used, char4 + first_type_used, char5 + first_type_used,
210 char6 + first_type_used);
211 vcd_name_index++;
212 return std::string(buf);
213}
214
215void vcd_mt_trace_file::write_comment(const std::string& comment) { FPRINTF(vcd_out, "$comment\n{}\n$end\n\n", comment); }
216
217void vcd_mt_trace_file::init() {
218 std::sort(std::begin(all_traces), std::end(all_traces),
219 [](trace_entry const& a, trace_entry const& b) -> bool { return a.trc->name < b.trc->name; });
220 std::unordered_map<uintptr_t, std::string> alias_map;
221
222 trace::vcd_scope_stack<trace::vcd_trace> scope;
223 for(auto& e : all_traces) {
224 auto alias_it = alias_map.find(e.trc->get_hash());
225 e.trc->is_alias = alias_it != std::end(alias_map);
226 e.trc->trc_hndl = e.trc->is_alias ? alias_it->second : obtain_name();
227 if(!e.trc->is_alias)
228 alias_map.insert({e.trc->get_hash(), e.trc->trc_hndl});
229 scope.add_trace(e.trc);
230 }
231 std::copy_if(std::begin(all_traces), std::end(all_traces), std::back_inserter(active_traces),
232 [](trace_entry const& e) { return !(e.trc->is_alias || e.trc->is_triggered); });
233 changed_traces.reserve(active_traces.size());
234 triggered_traces.reserve(active_traces.size());
235 // date:
236 char tbuf[200];
237 time_t long_time;
238 time(&long_time);
239 struct tm* p_tm = localtime(&long_time);
240 strftime(tbuf, 199, "%b %d, %Y %H:%M:%S", p_tm);
241 FPRINTF(vcd_out, "$date\n {}\n$end\n\n", tbuf);
242 // version:
243 FPRINTF(vcd_out, "$version\n {}\n$end\n\n", sc_core::sc_version());
244 // timescale:
245 FPRINTF(vcd_out, "$timescale\n {}\n$end\n\n", (1_ps).to_string());
246 write_comment("create using SCC VCD compressed writer");
247 std::stringstream ss;
248 ss << "tracing " << active_traces.size() << " distinct traces out of " << all_traces.size() << " traces";
249 write_comment(ss.str());
250 scope.print(vcd_out.get());
251}
252
253std::string vcd_mt_trace_file::prune_name(std::string const& orig_name) {
254 static bool warned = false;
255 bool braces_removed = false;
256 std::string hier_name = orig_name;
257 for(unsigned int i = 0; i < hier_name.length(); i++) {
258 if(hier_name[i] == '[') {
259 hier_name[i] = '(';
260 braces_removed = true;
261 } else if(hier_name[i] == ']') {
262 hier_name[i] = ')';
263 braces_removed = true;
264 }
265 }
266
267 if(braces_removed && !warned) {
268 std::stringstream ss;
269 ss << name
270 << ":\n"
271 "\tTraced objects found with name containing [], which may be\n"
272 "\tinterpreted by the waveform viewer in unexpected ways.\n"
273 "\tSo the [] is automatically replaced by ().";
274
275 SC_REPORT_WARNING(sc_core::SC_ID_TRACING_OBJECT_NAME_FILTERED_, ss.str().c_str());
276 }
277 return hier_name;
278}
279
280void record_changes(FILE* vcd_out, std::vector<trace::vcd_trace*> const& changed, std::vector<trace::vcd_trace*> const& triggered) {}
281
282void vcd_mt_trace_file::cycle(bool delta_cycle) {
283 if(delta_cycle)
284 return;
285 if(!initialized) {
286 init();
287 initialized = true;
288 {
289 scc::trace::gz_writer::lock_type lock(vcd_out->writer_mtx);
290 vcd_out->write("$enddefinitions $end\n\n$dumpvars\n");
291 for(auto& e : all_traces)
292 if(!e.trc->is_alias) {
293 e.compare_and_update(e.trc);
294 e.trc->record(vcd_out.get());
295 }
296 vcd_out->write("$end\n\n");
297 }
298 } else {
299 if(check_enabled && !check_enabled())
300 return;
301 for(auto& e : active_traces) {
302 if(e.compare_and_update(e.trc))
303 changed_traces.push_back(e.trc);
304 }
305 if(triggered_traces.size() || changed_traces.size()) {
306 scc::trace::gz_writer::lock_type lock(vcd_out->writer_mtx);
307 vcd_out->write(fmt::format("#{}\n", sc_core::sc_time_stamp() / 1_ps));
308 if(triggered_traces.size()) {
309 auto end = std::unique(std::begin(triggered_traces), std::end(triggered_traces));
310 for(auto it = triggered_traces.begin(); it != end; ++it)
311 (*it)->record(vcd_out.get());
312 triggered_traces.clear();
313 }
314 if(changed_traces.size()) {
315 for(auto t : changed_traces)
316 t->record(vcd_out.get());
317 changed_traces.clear();
318 }
319 }
320 }
321}
322
323void vcd_mt_trace_file::set_time_unit(double v, sc_core::sc_time_unit tu) {}
324
325sc_core::sc_trace_file* create_vcd_mt_trace_file(const char* name, std::function<bool()> enable) {
326 return new vcd_mt_trace_file(name, enable);
327}
328
329void close_vcd_mt_trace_file(sc_core::sc_trace_file* tf) { delete static_cast<vcd_mt_trace_file*>(tf); }
330} // namespace scc
SCC TLM utilities.
void close_vcd_mt_trace_file(sc_core::sc_trace_file *tf)
close the VCD file
sc_core::sc_trace_file * create_vcd_mt_trace_file(const char *name, std::function< bool()> enable=std::function< bool()>())
create compressed VCD file which uses push mechanism and multithreading