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