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