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