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