scc 2025.09
SystemC components library
vcd_trace.hh
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#ifndef _SCC_TRACE_VCD_TRACE_HH_
17#define _SCC_TRACE_VCD_TRACE_HH_
18
19#include "types.hh"
20#ifndef FWRITE
21#include <cstdio>
22#define FWRITE(BUF, SZ, LEN, FP) std::fwrite(BUF, SZ, LEN, FP)
23#define FPTR FILE*
24#endif
25#include <fmt/format.h>
26#include <scc/utilities.h>
27#include <unordered_map>
28#include <util/ities.h>
29#include <vector>
30
31namespace scc {
32namespace trace {
33
34inline void vcdEmitValueChange(FPTR os, std::string const& handle, unsigned bits, const char* val) {
35 auto buf = bits == 1 ? fmt::format("{}{}\n", *val, handle) : fmt::format("b{} {}\n", val, handle);
36 FWRITE(buf.c_str(), 1, buf.size(), os);
37}
38
39inline void vcdEmitValueChange32(FPTR os, std::string const& handle, unsigned bits, uint32_t val) {
40 auto buf = fmt::format("b{:b} {}\n", val & ((1ull << bits) - 1), handle);
41 FWRITE(buf.c_str(), 1, buf.size(), os);
42}
43
44inline void vcdEmitValueChange64(FPTR os, std::string const& handle, unsigned bits, uint64_t val) {
45 auto buf = fmt::format("b{:b} {}\n", val & ((1ul << std::min(63u, bits)) - 1), handle);
46 FWRITE(buf.c_str(), 1, buf.size(), os);
47}
48
49template <typename T> inline void vcdEmitValueChangeReal(FPTR os, std::string const& handle, unsigned bits, T val) {
50 auto buf = fmt::format("r{:.16g} {}\n", static_cast<double>(val), handle);
51 FWRITE(buf.c_str(), 1, buf.size(), os);
52}
53
54inline size_t get_buffer_size(int length) {
55 size_t sz = (static_cast<size_t>(length) + 4096) & (~static_cast<size_t>(4096 - 1));
56 return std::max<uint64_t>(1024UL, sz);
57}
58
59/*******************************************************************************************************
60 *
61 *******************************************************************************************************/
62template <typename T> struct vcd_scope_stack {
63 void add_trace(T* trace) {
64 auto hier = util::split(trace->name, '.');
65 add_trace_rec(std::begin(hier), std::end(hier), trace);
66 }
67
68 void print(FPTR os, const char* scope_name = "SystemC") {
69 auto buf = fmt::format("$scope module {} $end\n", scope_name);
70 FWRITE(buf.c_str(), 1, buf.size(), os);
71 for(auto& e : m_traces)
72 print_variable_declaration_line(os, e.first.c_str(), e.second);
73 for(auto& e : m_scopes)
74 e.second->print(os, e.first.c_str());
75 std::string end = "$upscope $end\n";
76 FWRITE(end.c_str(), 1, end.size(), os);
77 }
78
80 for(auto& s : m_scopes)
81 delete s.second;
82 }
83
84private:
85 void add_trace_rec(std::vector<std::string>::iterator beg, std::vector<std::string>::iterator const& end, T* trace) {
86 if(std::distance(beg, end) == 1) {
87 m_traces.push_back(std::make_pair(*beg, trace));
88 } else {
89 auto sc = m_scopes.find(*beg);
90 if(sc == std::end(m_scopes))
91 sc = m_scopes.insert({*beg, new vcd_scope_stack}).first;
92 sc->second->add_trace_rec(++beg, end, trace);
93 }
94 }
95 void print_variable_declaration_line(FPTR os, const char* scoped_name, T* trc) {
96 char buf[2000];
97 switch(trc->bits) {
98 case 0: {
99 std::stringstream ss;
100 ss << "'" << scoped_name << "' has 0 bits";
101 SC_REPORT_ERROR(sc_core::SC_ID_TRACING_OBJECT_IGNORED_, ss.str().c_str());
102 return;
103 }
104 case 1:
105 if(trc->type == WIRE) {
106 auto buf = fmt::format("$var wire {} {} {} $end\n", trc->bits, trc->trc_hndl, scoped_name);
107 FWRITE(buf.c_str(), 1, buf.size(), os);
108 } else {
109 auto buf = fmt::format("$var real {} {} {} $end\n", trc->bits, trc->trc_hndl, scoped_name);
110 FWRITE(buf.c_str(), 1, buf.size(), os);
111 }
112 break;
113 default: {
114 auto buf = fmt::format("$var wire {} {} {} [{}:0] $end\n", trc->bits, trc->trc_hndl, scoped_name, trc->bits - 1);
115 FWRITE(buf.c_str(), 1, buf.size(), os);
116 }
117 }
118 }
119
120 std::vector<std::pair<std::string, T*>> m_traces{0};
121 std::unordered_map<std::string, vcd_scope_stack*> m_scopes{0};
122};
123
124struct vcd_trace {
125 vcd_trace(std::string const& nm, trace_type t, unsigned bits)
126 : name{nm}
127 , bits{bits}
128 , type{t} {}
129
130 virtual void record(FPTR os) = 0;
131
132 virtual void update() = 0;
133
134 virtual uintptr_t get_hash() = 0;
135
136 virtual ~vcd_trace(){};
137
138 const std::string name;
139 std::string trc_hndl{};
140 bool is_alias{false};
141 bool is_triggered{false};
142 const unsigned bits;
143 const trace_type type;
144};
145
146namespace {
147
148inline unsigned get_bits(const char** literals) {
149 unsigned nliterals;
150 for(nliterals = 0; literals[nliterals]; nliterals++)
151 continue;
152 return scc::ilog2(nliterals);
153}
154
155struct vcd_trace_enum : public vcd_trace {
156 vcd_trace_enum(const unsigned int& object_, const std::string& name, const char** literals)
157 : vcd_trace(name, trace::WIRE, get_bits(literals))
158 , act_val(object_)
159 , old_val(object_)
160 , literals{literals} {}
161
162 uintptr_t get_hash() override { return reinterpret_cast<uintptr_t>(&act_val); }
163
164 inline bool changed() { return !is_alias && old_val != act_val; }
165
166 void update() override { old_val = act_val; }
167
168 void record(FPTR os) override { vcdEmitValueChange64(os, trc_hndl, bits, old_val); }
169
170 unsigned int old_val;
171 const unsigned int& act_val;
172 const char** literals;
173};
174
175template <typename T, typename OT = T> struct vcd_trace_t : public vcd_trace {
176 vcd_trace_t(const T& object_, const std::string& name)
177 : vcd_trace(name, trace::traits<T>::get_type(), trace::traits<T>::get_bits(object_))
178 , act_val(object_)
179 , old_val(object_) {}
180
181 uintptr_t get_hash() override { return reinterpret_cast<uintptr_t>(&act_val); }
182
183 inline bool changed() { return !is_alias && old_val != act_val; }
184
185 void update() override { old_val = act_val; }
186
187 void record(FPTR os) override;
188
189 OT old_val;
190 const T& act_val;
191};
192
193template <typename T, typename OT> void vcd_trace_t<T, OT>::record(FPTR os) {
194 if(sizeof(T) <= 4)
195 vcdEmitValueChange32(os, trc_hndl, bits, old_val);
196 else
197 vcdEmitValueChange64(os, trc_hndl, bits, old_val);
198}
199template <> void vcd_trace_t<sc_core::sc_time, sc_core::sc_time>::record(FPTR os) {
200 vcdEmitValueChange64(os, trc_hndl, bits, old_val.value());
201}
202template <> void vcd_trace_t<bool, bool>::record(FPTR os) { vcdEmitValueChange(os, trc_hndl, 1, old_val ? "1" : "0"); }
203template <> void vcd_trace_t<sc_dt::sc_bit, sc_dt::sc_bit>::record(FPTR os) { vcdEmitValueChange(os, trc_hndl, 1, old_val ? "1" : "0"); }
204template <> void vcd_trace_t<sc_dt::sc_logic, sc_dt::sc_logic>::record(FPTR os) {
205 char buf[2] = {0, 0};
206 buf[0] = old_val.to_char();
207 vcdEmitValueChange(os, trc_hndl, 1, buf);
208}
209template <> void vcd_trace_t<float, float>::record(FPTR os) { vcdEmitValueChangeReal(os, trc_hndl, 32, old_val); }
210template <> void vcd_trace_t<double, double>::record(FPTR os) { vcdEmitValueChangeReal(os, trc_hndl, 64, old_val); }
211template <> void vcd_trace_t<sc_dt::sc_int_base, sc_dt::sc_int_base>::record(FPTR os) {
212 static std::vector<char> rawdata(get_buffer_size(old_val.length()));
213 char* rawdata_ptr = &rawdata[0];
214 for(int bitindex = old_val.length() - 1; bitindex >= 0; --bitindex) {
215 *rawdata_ptr++ = '0' + old_val[bitindex].value();
216 }
217 vcdEmitValueChange(os, trc_hndl, bits, &rawdata[0]);
218}
219template <> void vcd_trace_t<sc_dt::sc_uint_base, sc_dt::sc_uint_base>::record(FPTR os) {
220 static std::vector<char> rawdata(get_buffer_size(old_val.length()));
221 char* rawdata_ptr = &rawdata[0];
222 for(int bitindex = old_val.length() - 1; bitindex >= 0; --bitindex) {
223 *rawdata_ptr++ = '0' + old_val[bitindex].value();
224 }
225 vcdEmitValueChange(os, trc_hndl, bits, &rawdata[0]);
226}
227template <> void vcd_trace_t<sc_dt::sc_signed, sc_dt::sc_signed>::record(FPTR os) {
228 static std::vector<char> rawdata(get_buffer_size(old_val.length()));
229 char* rawdata_ptr = &rawdata[0];
230 bool is_started = false;
231 int bitindex = old_val.length() - 1;
232 *rawdata_ptr++ = '0' + old_val[bitindex--].value();
233 for(; bitindex >= 0; --bitindex) {
234 auto c = '0' + old_val[bitindex].value();
235 if(is_started)
236 *rawdata_ptr++ = c;
237 else if(c == '1' || *(rawdata_ptr - 1) != c) {
238 *rawdata_ptr++ = c;
239 is_started = true;
240 }
241 }
242 vcdEmitValueChange(os, trc_hndl, bits, &rawdata[0]);
243}
244template <> void vcd_trace_t<sc_dt::sc_unsigned, sc_dt::sc_unsigned>::record(FPTR os) {
245 static std::vector<char> rawdata(get_buffer_size(old_val.length()));
246 char* rawdata_ptr = &rawdata[0];
247 bool is_started = false;
248 int bitindex = old_val.length() - 1;
249 *rawdata_ptr++ = '0' + old_val[bitindex--].value();
250 for(; bitindex >= 0; --bitindex) {
251 auto c = '0' + old_val[bitindex].value();
252 if(is_started)
253 *rawdata_ptr++ = c;
254 else if(c == '1' || *(rawdata_ptr - 1) != c) {
255 *rawdata_ptr++ = c;
256 is_started = true;
257 }
258 }
259 vcdEmitValueChange(os, trc_hndl, bits, &rawdata[0]);
260}
261template <> void vcd_trace_t<sc_dt::sc_fxval, sc_dt::sc_fxval>::record(FPTR os) { vcdEmitValueChangeReal(os, trc_hndl, bits, old_val); }
262template <> void vcd_trace_t<sc_dt::sc_fxval_fast, sc_dt::sc_fxval_fast>::record(FPTR os) {
263 vcdEmitValueChangeReal(os, trc_hndl, bits, old_val);
264}
265template <> void vcd_trace_t<sc_dt::sc_fxnum, sc_dt::sc_fxval>::record(FPTR os) { vcdEmitValueChangeReal(os, trc_hndl, bits, old_val); }
266template <> void vcd_trace_t<sc_dt::sc_fxnum_fast, sc_dt::sc_fxval_fast>::record(FPTR os) {
267 vcdEmitValueChangeReal(os, trc_hndl, bits, old_val);
268}
269template <> void vcd_trace_t<sc_dt::sc_bv_base, sc_dt::sc_bv_base>::record(FPTR os) {
270 auto str = old_val.to_string();
271 auto* cstr = str.c_str();
272 auto c = *cstr;
273 if(c != '1')
274 while(c == *(cstr + 1))
275 cstr++;
276 vcdEmitValueChange(os, trc_hndl, bits, cstr);
277}
278template <> void vcd_trace_t<sc_dt::sc_lv_base, sc_dt::sc_lv_base>::record(FPTR os) {
279 auto str = old_val.to_string();
280 auto* cstr = str.c_str();
281 auto c = *cstr;
282 if(c != '1')
283 while(c == *(cstr + 1))
284 cstr++;
285 vcdEmitValueChange(os, trc_hndl, bits, cstr);
286}
287} // namespace
288} // namespace trace
289} // namespace scc
290#endif //_SCC_TRACE_VCD_TRACE_HH_
SCC SystemC tracing utilities.
Definition fst_trace.cpp:33
SCC TLM utilities.
std::vector< std::string > split(const std::string &s, char separator)
Definition ities.h:264