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