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