scc 2025.09
SystemC components library
fst_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 "fst_trace.hh"
18#include "fstapi.h"
19#include "trace/types.hh"
20#include "utilities.h"
21#include <cmath>
22#include <cstdlib>
23#include <cstring>
24#include <deque>
25#include <limits>
26#include <map>
27#include <sstream>
28#include <unordered_map>
29#include <util/ities.h>
30#include <vector>
31
32namespace scc {
33namespace trace {
34inline size_t get_buffer_size(int length) {
35 size_t sz = (static_cast<size_t>(length) + 4096) & (~static_cast<size_t>(4096 - 1));
36 return std::max<decltype(sz)>(1024UL, sz);
37}
38
39inline unsigned get_bits(const char** literals) {
40 unsigned nliterals;
41 for(nliterals = 0; literals[nliterals]; nliterals++)
42 continue;
43 return scc::ilog2(nliterals);
44}
45
46struct fst_trace {
47
48 fst_trace(std::string const& nm, trace_type type, unsigned bits)
49 : name{nm}
50 , bits{bits}
51 , type{type} {}
52
53 virtual void record(void* m_fst) = 0;
54
55 virtual void update_and_record(void* m_fst) = 0;
56
57 virtual uintptr_t get_hash() = 0;
58
59 virtual ~fst_trace(){};
60
61 const std::string name;
62 fstHandle fst_hndl{0};
63 bool is_alias{false};
64 bool is_triggered{false};
65 const unsigned bits{0};
66 const trace_type type;
67};
68
69struct fst_trace_enum : public fst_trace {
70 fst_trace_enum(const unsigned int& object_, const std::string& name, const char** literals)
71 : fst_trace(name, trace::WIRE, get_bits(literals))
72 , act_val(object_)
73 , old_val(object_)
74 , literals{literals} {}
75
76 uintptr_t get_hash() override { return reinterpret_cast<uintptr_t>(&act_val); }
77
78 inline bool changed() { return !is_alias && old_val != act_val; }
79
80 inline void update() { old_val = act_val; }
81
82 void record(void* os) override { fstWriterEmitValueChange64(os, fst_hndl, bits, old_val); }
83
84 void update_and_record(void* os) override {
85 update();
86 record(os);
87 };
88
89 unsigned int old_val;
90 const unsigned int& act_val;
91 const char** literals;
92};
93
94template <typename T, typename OT = T> struct fst_trace_t : public fst_trace {
95 fst_trace_t(const T& object_, const std::string& name, int width = -1)
96 : fst_trace(name, trace::traits<T>::get_type(), trace::traits<T>::get_bits(object_))
97 , act_val(object_)
98 , old_val(object_) {}
99
100 uintptr_t get_hash() override { return reinterpret_cast<uintptr_t>(&act_val); }
101
102 inline bool changed() { return !is_alias && old_val != act_val; }
103
104 inline void update() { old_val = act_val; }
105
106 void record(void* m_fst) override;
107
108 void update_and_record(void* m_fst) override {
109 update();
110 record(m_fst);
111 };
112
113 OT old_val;
114 const T& act_val;
115};
116
117template <typename T, typename OT> inline void fst_trace_t<T, OT>::record(void* m_fst) {
118 static std::vector<char> rawdata(65);
119 char* s = &rawdata[0];
120 for(size_t i = 0; i < 8 * sizeof(T); ++i)
121 *s++ = '0' + ((old_val >> (8 * sizeof(T) - i - 1)) & 1);
122 *s = 0;
123 fstWriterEmitValueChange(m_fst, fst_hndl, &rawdata[0]);
124}
125template <> void fst_trace_t<bool, bool>::record(void* m_fst) { fstWriterEmitValueChange(m_fst, fst_hndl, old_val ? "1" : "0"); }
126template <> void fst_trace_t<sc_dt::sc_bit, sc_dt::sc_bit>::record(void* m_fst) {
127 fstWriterEmitValueChange(m_fst, fst_hndl, old_val ? "1" : "0");
128}
129template <> void fst_trace_t<sc_dt::sc_logic, sc_dt::sc_logic>::record(void* m_fst) {
130 char buf[2] = {0, 0};
131 buf[0] = old_val.to_char();
132 fstWriterEmitValueChange(m_fst, fst_hndl, buf);
133}
134template <> void fst_trace_t<float, float>::record(void* m_fst) {
135 double val = old_val;
136 fstWriterEmitValueChange(m_fst, fst_hndl, &val);
137}
138template <> void fst_trace_t<double, double>::record(void* m_fst) { fstWriterEmitValueChange(m_fst, fst_hndl, &old_val); }
139template <> void fst_trace_t<sc_dt::sc_int_base, sc_dt::sc_int_base>::record(void* m_fst) {
140 static std::vector<char> rawdata(1024);
141 if(rawdata.size() < old_val.length() + 1)
142 rawdata.resize(old_val.length() + 1);
143 char* rawdata_ptr = &rawdata[0];
144 for(int bitindex = old_val.length() - 1; bitindex >= 0; --bitindex)
145 *rawdata_ptr++ = '0' + old_val[bitindex].value();
146 *rawdata_ptr = 0;
147 fstWriterEmitValueChange(m_fst, fst_hndl, &rawdata[0]);
148}
149template <> void fst_trace_t<sc_dt::sc_uint_base, sc_dt::sc_uint_base>::record(void* m_fst) {
150 static std::vector<char> rawdata(1024);
151 if(rawdata.size() < old_val.length() + 1)
152 rawdata.resize(old_val.length() + 1);
153 char* rawdata_ptr = &rawdata[0];
154 for(int bitindex = old_val.length() - 1; bitindex >= 0; --bitindex)
155 *rawdata_ptr++ = '0' + old_val[bitindex].value();
156 *rawdata_ptr = 0;
157 fstWriterEmitValueChange(m_fst, fst_hndl, &rawdata[0]);
158}
159template <> void fst_trace_t<sc_dt::sc_signed, sc_dt::sc_signed>::record(void* m_fst) {
160 static std::vector<char> rawdata(1024);
161 if(rawdata.size() < old_val.length() + 1)
162 rawdata.resize(old_val.length() + 1);
163 char* rawdata_ptr = &rawdata[0];
164 for(int bitindex = old_val.length() - 1; bitindex >= 0; --bitindex)
165 *rawdata_ptr++ = '0' + old_val[bitindex].value();
166 *rawdata_ptr = 0;
167 fstWriterEmitValueChange(m_fst, fst_hndl, &rawdata[0]);
168}
169template <> void fst_trace_t<sc_dt::sc_unsigned, sc_dt::sc_unsigned>::record(void* m_fst) {
170 static std::vector<char> rawdata(1024);
171 if(rawdata.size() < old_val.length() + 1)
172 rawdata.resize(old_val.length() + 1);
173 char* rawdata_ptr = &rawdata[0];
174 for(int bitindex = old_val.length() - 1; bitindex >= 0; --bitindex)
175 *rawdata_ptr++ = '0' + old_val[bitindex].value();
176 *rawdata_ptr = 0;
177 fstWriterEmitValueChange(m_fst, fst_hndl, &rawdata[0]);
178}
179template <> void fst_trace_t<sc_dt::sc_fxval, sc_dt::sc_fxval>::record(void* m_fst) {
180 auto val = old_val.to_double();
181 fstWriterEmitValueChange(m_fst, fst_hndl, &val);
182}
183template <> void fst_trace_t<sc_dt::sc_fxval_fast, sc_dt::sc_fxval_fast>::record(void* m_fst) {
184 auto val = old_val.to_double();
185 fstWriterEmitValueChange(m_fst, fst_hndl, &val);
186}
187template <> void fst_trace_t<sc_dt::sc_fxnum, sc_dt::sc_fxval>::record(void* m_fst) {
188 auto val = old_val.to_double();
189 fstWriterEmitValueChange(m_fst, fst_hndl, &val);
190}
191template <> void fst_trace_t<sc_dt::sc_fxnum_fast, sc_dt::sc_fxval_fast>::record(void* m_fst) {
192 auto val = old_val.to_double();
193 fstWriterEmitValueChange(m_fst, fst_hndl, &val);
194}
195template <> void fst_trace_t<sc_dt::sc_bv_base, sc_dt::sc_bv_base>::record(void* m_fst) {
196 auto str = old_val.to_string();
197 auto* cstr = str.c_str();
198 auto c = *cstr;
199 if(c != '1')
200 while(c == *(cstr + 1))
201 cstr++;
202 fstWriterEmitValueChange(m_fst, fst_hndl, str.c_str());
203}
204template <> void fst_trace_t<sc_dt::sc_lv_base, sc_dt::sc_lv_base>::record(void* m_fst) {
205 auto str = old_val.to_string();
206 auto* cstr = str.c_str();
207 auto c = *cstr;
208 if(c != '1')
209 while(c == *(cstr + 1))
210 cstr++;
211 fstWriterEmitValueChange(m_fst, fst_hndl, str.c_str());
212}
213} // namespace trace
214
215fst_trace_file::fst_trace_file(const char* name, std::function<bool()>& enable)
216: check_enabled(enable) {
217 std::stringstream ss;
218 ss << name << ".fst";
219 m_fst = fstWriterCreate(ss.str().c_str(), 1);
220 if(!m_fst) {
221 fprintf(stderr, "Could not open '%s', exiting.\n", ss.str().c_str());
222 exit(255);
223 }
224 fstWriterSetPackType(m_fst, FST_WR_PT_FASTLZ);
225 fstWriterSetRepackOnClose(m_fst, 1);
226 fstWriterSetParallelMode(m_fst, 0);
227 fstWriterSetTimescale(m_fst, -12); // femto seconds 1*10-12
228 fstWriterSetTimezero(m_fst, 0);
229 char tbuf[200];
230 time_t long_time;
231 time(&long_time);
232 struct tm* p_tm = localtime(&long_time);
233 strftime(tbuf, 199, "%b %d, %Y\t%H:%M:%S", p_tm);
234 fstWriterSetDate(m_fst, tbuf);
235 // fstWriterSetFileType(m_fst, FST_FT_VERILOG);
236#if SC_VERSION_MAJOR < 3
237#if defined(WITH_SC_TRACING_PHASE_CALLBACKS)
238 // remove from hierarchy
239 sc_object::detach();
240 // register regular (non-delta) callbacks
241 sc_object::register_simulation_phase_callback(SC_BEFORE_TIMESTEP);
242#else // explicitly register with simcontext
243 sc_core::sc_get_curr_simcontext()->add_trace_file(this);
244#endif
245#else
246 sc_core::sc_register_stage_callback(*this, sc_core::SC_PRE_TIMESTEP);
247#endif
248}
249
250fst_trace_file::~fst_trace_file() {
251 for(auto t : all_traces)
252 delete t.trc;
253 if(m_fst) {
254 // fstWriterFlushContext(m_fst);
255 fstWriterClose(m_fst);
256 }
257}
258
259template <typename T, typename OT = T> bool changed(trace::fst_trace* trace) {
260 if(reinterpret_cast<trace::fst_trace_t<T, OT>*>(trace)->changed()) {
261 reinterpret_cast<trace::fst_trace_t<T, OT>*>(trace)->update();
262 return true;
263 } else
264 return false;
265}
266#define DECL_TRACE_METHOD_A(tp) \
267 void fst_trace_file::trace(const tp& object, const std::string& name) { \
268 all_traces.emplace_back(this, &changed<tp>, new trace::fst_trace_t<tp>(object, name)); \
269 }
270#define DECL_TRACE_METHOD_B(tp) \
271 void fst_trace_file::trace(const tp& object, const std::string& name, int width) { \
272 all_traces.emplace_back(this, &changed<tp>, new trace::fst_trace_t<tp>(object, name)); \
273 }
274#define DECL_TRACE_METHOD_C(tp, tpo) \
275 void fst_trace_file::trace(const tp& object, const std::string& name) { \
276 all_traces.emplace_back(this, &changed<tp, tpo>, new trace::fst_trace_t<tp, tpo>(object, name)); \
277 }
278
279#if(SYSTEMC_VERSION >= 20171012) || defined(NCSC)
280void fst_trace_file::trace(const sc_core::sc_event& object, const std::string& name) {}
281void fst_trace_file::trace(const sc_core::sc_time& object, const std::string& name) {}
282#endif
283DECL_TRACE_METHOD_A(bool)
284DECL_TRACE_METHOD_A(sc_dt::sc_bit)
285DECL_TRACE_METHOD_A(sc_dt::sc_logic)
286
287DECL_TRACE_METHOD_B(unsigned char)
288DECL_TRACE_METHOD_B(unsigned short)
289DECL_TRACE_METHOD_B(unsigned int)
290DECL_TRACE_METHOD_B(unsigned long)
291#ifdef SYSTEMC_64BIT_PATCHES
292DECL_TRACE_METHOD_B(unsigned long long)
293#endif
294DECL_TRACE_METHOD_B(char)
295DECL_TRACE_METHOD_B(short)
296DECL_TRACE_METHOD_B(int)
297DECL_TRACE_METHOD_B(long)
298DECL_TRACE_METHOD_B(sc_dt::int64)
299DECL_TRACE_METHOD_B(sc_dt::uint64)
300
301DECL_TRACE_METHOD_A(float)
302DECL_TRACE_METHOD_A(double)
303DECL_TRACE_METHOD_A(sc_dt::sc_int_base)
304DECL_TRACE_METHOD_A(sc_dt::sc_uint_base)
305DECL_TRACE_METHOD_A(sc_dt::sc_signed)
306DECL_TRACE_METHOD_A(sc_dt::sc_unsigned)
307
308DECL_TRACE_METHOD_A(sc_dt::sc_fxval)
309DECL_TRACE_METHOD_A(sc_dt::sc_fxval_fast)
310DECL_TRACE_METHOD_C(sc_dt::sc_fxnum, sc_dt::sc_fxval)
311DECL_TRACE_METHOD_C(sc_dt::sc_fxnum_fast, sc_dt::sc_fxval_fast)
312
313DECL_TRACE_METHOD_A(sc_dt::sc_bv_base)
314DECL_TRACE_METHOD_A(sc_dt::sc_lv_base)
315#undef DECL_TRACE_METHOD_A
316#undef DECL_TRACE_METHOD_B
317#undef DECL_TRACE_METHOD_C
318
319void fst_trace_file::trace(const unsigned int& object, const std::string& name, const char** enum_literals) {
320 // all_traces.emplace_back(this, &changed<unsigned int>, new fst_trace_enum(object, name, enum_literals));
321}
322
323#define DECL_REGISTER_METHOD_A(tp) \
324 observer::notification_handle* fst_trace_file::observe(const tp& object, const std::string& name) { \
325 all_traces.emplace_back(this, &changed<tp>, new trace::fst_trace_t<tp>(object, name)); \
326 all_traces.back().trc->is_triggered = true; \
327 return &all_traces.back(); \
328 }
329#define DECL_REGISTER_METHOD_C(tp, tpo) \
330 observer::notification_handle* fst_trace_file::observe(const tp& object, const std::string& name) { \
331 all_traces.emplace_back(this, &changed<tp, tpo>, new trace::fst_trace_t<tp, tpo>(object, name)); \
332 all_traces.back().trc->is_triggered = true; \
333 return &all_traces.back(); \
334 }
335
336#if(SYSTEMC_VERSION >= 20171012)
337observer::notification_handle* fst_trace_file::observe(const sc_core::sc_event& object, const std::string& name) { return nullptr; }
338observer::notification_handle* fst_trace_file::observe(const sc_core::sc_time& object, const std::string& name) { return nullptr; }
339#endif
340
341DECL_REGISTER_METHOD_A(bool)
342DECL_REGISTER_METHOD_A(sc_dt::sc_bit)
343DECL_REGISTER_METHOD_A(sc_dt::sc_logic)
344
345DECL_REGISTER_METHOD_A(unsigned char)
346DECL_REGISTER_METHOD_A(unsigned short)
347DECL_REGISTER_METHOD_A(unsigned int)
348DECL_REGISTER_METHOD_A(unsigned long)
349#ifdef SYSTEMC_64BIT_PATCHES
350DECL_REGISTER_METHOD_A(unsigned long long)
351#endif
352DECL_REGISTER_METHOD_A(char)
353DECL_REGISTER_METHOD_A(short)
354DECL_REGISTER_METHOD_A(int)
355DECL_REGISTER_METHOD_A(long)
356DECL_REGISTER_METHOD_A(sc_dt::int64)
357DECL_REGISTER_METHOD_A(sc_dt::uint64)
358
359DECL_REGISTER_METHOD_A(float)
360DECL_REGISTER_METHOD_A(double)
361DECL_REGISTER_METHOD_A(sc_dt::sc_int_base)
362DECL_REGISTER_METHOD_A(sc_dt::sc_uint_base)
363DECL_REGISTER_METHOD_A(sc_dt::sc_signed)
364DECL_REGISTER_METHOD_A(sc_dt::sc_unsigned)
365
366DECL_REGISTER_METHOD_A(sc_dt::sc_fxval)
367DECL_REGISTER_METHOD_A(sc_dt::sc_fxval_fast)
368DECL_REGISTER_METHOD_C(sc_dt::sc_fxnum, sc_dt::sc_fxval)
369DECL_REGISTER_METHOD_C(sc_dt::sc_fxnum_fast, sc_dt::sc_fxval_fast)
370
371DECL_REGISTER_METHOD_A(sc_dt::sc_bv_base)
372DECL_REGISTER_METHOD_A(sc_dt::sc_lv_base)
373#undef DECL_REGISTER_METHOD_A
374#undef DECL_REGISTER_METHOD_C
375
376bool fst_trace_file::trace_entry::notify() {
377 if(!trc->is_alias && compare_and_update(trc))
378 that->triggered_traces.push_back(trc);
379 return !trc->is_alias;
380}
381
382void fst_trace_file::write_comment(const std::string& comment) {}
383namespace {
384struct scope_stack {
385 void add_trace(trace::fst_trace* trace) {
386 auto hier = util::split(trace->name, '.');
387 add_trace_rec(std::begin(hier), std::end(hier), trace);
388 }
389
390 void writeScopes(void* fst, std::unordered_map<uintptr_t, fstHandle>& alias_map, const char* scope_name = nullptr) {
391 if(m_traces.size() || scope_name) {
392 fstWriterSetScope(fst, FST_ST_VCD_SCOPE, scope_name ? scope_name : "SystemC", nullptr);
393 for(auto& e : m_traces) {
394 auto hier_tokens = util::split(e.second->name, '.');
395 auto sig_name = hier_tokens.back();
396 auto alias_it = alias_map.find(e.second->get_hash());
397 e.second->is_alias = alias_it != std::end(alias_map);
398 e.second->fst_hndl =
399 fstWriterCreateVar(fst, e.second->type == trace::REAL ? FST_VT_VCD_REAL : FST_VT_VCD_WIRE, FST_VD_IMPLICIT,
400 e.second->bits, sig_name.c_str(), e.second->is_alias ? alias_it->second : 0);
401 if(!e.second->is_alias)
402 alias_map.insert({e.second->get_hash(), e.second->fst_hndl});
403 }
404 for(auto& e : m_scopes)
405 e.second->writeScopes(fst, alias_map, e.first.c_str());
406 fstWriterSetUpscope(fst);
407 } else
408 for(auto& e : m_scopes)
409 e.second->writeScopes(fst, alias_map, e.first.c_str());
410 }
411
412 ~scope_stack() {
413 for(auto& s : m_scopes)
414 delete s.second;
415 }
416
417private:
418 void add_trace_rec(std::vector<std::string>::iterator beg, std::vector<std::string>::iterator const& end, trace::fst_trace* trace) {
419 if(std::distance(beg, end) == 1) {
420 m_traces.push_back(std::make_pair(*beg, trace));
421 } else {
422 auto sc = m_scopes.find(*beg);
423 if(sc == std::end(m_scopes))
424 sc = m_scopes.insert({*beg, new scope_stack}).first;
425 sc->second->add_trace_rec(++beg, end, trace);
426 }
427 }
428 std::vector<std::pair<std::string, trace::fst_trace*>> m_traces{0};
429 std::unordered_map<std::string, scope_stack*> m_scopes{0};
430};
431
432} // namespace
433void fst_trace_file::init() {
434 std::vector<trace_entry*> traces;
435 traces.reserve(all_traces.size());
436 scope_stack scope;
437 for(auto& e : all_traces) {
438 scope.add_trace(e.trc);
439 traces.push_back(&e);
440 }
441 std::unordered_map<uintptr_t, fstHandle> alias_map;
442 scope.writeScopes(m_fst, alias_map);
443 std::copy_if(std::begin(traces), std::end(traces), std::back_inserter(pull_traces),
444 [](trace_entry const* e) { return !(e->trc->is_alias || e->trc->is_triggered); });
445 changed_traces.reserve(pull_traces.size());
446 triggered_traces.reserve(all_traces.size());
447}
448
449#if SC_VERSION_MAJOR >= 3
450void fst_trace_file::stage_callback(const sc_core::sc_stage& stage) { cycle(false); }
451#endif
452
453void fst_trace_file::cycle(bool delta_cycle) {
454 if(delta_cycle)
455 return;
456 if(last_emitted_ts == std::numeric_limits<uint64_t>::max()) {
457 init();
458 uint64_t time_stamp = sc_core::sc_time_stamp().value() / (1_ps).value();
459 fstWriterEmitTimeChange(m_fst, time_stamp);
460 for(auto& e : all_traces)
461 if(!e.trc->is_alias)
462 e.trc->update_and_record(m_fst);
463 last_emitted_ts = time_stamp;
464 } else {
465 if(check_enabled && !check_enabled())
466 return;
467 for(auto e : pull_traces) {
468 if(e->compare_and_update(e->trc))
469 changed_traces.push_back(e->trc);
470 }
471 if(triggered_traces.size() || changed_traces.size()) {
472 uint64_t time_stamp = sc_core::sc_time_stamp().value() / (1_ps).value();
473 if(last_emitted_ts < time_stamp)
474 fstWriterEmitTimeChange(m_fst, time_stamp);
475 if(triggered_traces.size()) {
476 auto end = std::unique(std::begin(triggered_traces), std::end(triggered_traces));
477 triggered_traces.erase(end, triggered_traces.end());
478 for(auto t : triggered_traces)
479 t->record(m_fst);
480 triggered_traces.clear();
481 }
482 if(changed_traces.size()) {
483 for(auto t : changed_traces)
484 t->record(m_fst);
485 changed_traces.clear();
486 }
487 last_emitted_ts = time_stamp;
488 }
489 }
490}
491
492void fst_trace_file::set_time_unit(double v, sc_core::sc_time_unit tu) {}
493
494sc_core::sc_trace_file* create_fst_trace_file(const char* name, std::function<bool()> enable) { return new fst_trace_file(name, enable); }
495
496void close_fst_trace_file(sc_core::sc_trace_file* tf) { delete static_cast<fst_trace_file*>(tf); }
497
498} // namespace scc
SCC SystemC tracing utilities.
Definition fst_trace.cpp:33
SCC TLM utilities.
sc_core::sc_trace_file * create_fst_trace_file(const char *name, std::function< bool()> enable)
create FST file which uses pull mechanism
void close_fst_trace_file(sc_core::sc_trace_file *tf)
close the FST file
std::vector< std::string > split(const std::string &s, char separator)
Definition ities.h:264