scc 2025.09
SystemC components library
scv_tr_lz4.cpp
1/*******************************************************************************
2 * Copyright 2018 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#include <array>
17#include <boost/filesystem.hpp>
18#include <cmath>
19#include <cstdio>
20#include <cstring>
21#include <fcntl.h>
22#include <fmt/format.h>
23#include <fstream>
24#include <iostream>
25#include <sstream>
26#include <stdexcept>
27#include <string>
28#include <unordered_map>
29#include <unordered_set>
30#include <util/lz4_streambuf.h>
31#include <vector>
32// clang-format off
33#ifdef HAS_SCV
34#include <scv.h>
35#else
36#include <scv-tr.h>
37namespace scv_tr {
38#endif
39// clang-format on
40// ----------------------------------------------------------------------------
41// ----------------------------------------------------------------------------
42using namespace std;
43
44// ----------------------------------------------------------------------------
45enum EventType { BEGIN, RECORD, END };
46struct AttrDesc {
47 EventType const evt;
48 scv_extensions_if::data_type const type;
49 std::string const name;
50 AttrDesc(EventType evt, scv_extensions_if::data_type type, std::string const& name)
51 : evt(evt)
52 , type(type)
53 , name(name) {}
54};
55using data_type = scv_extensions_if::data_type;
56// ----------------------------------------------------------------------------
57namespace {
58const std::array<char const*, scv_extensions_if::STRING + 1> data_type_str = {{
59 "BOOLEAN", // bool
60 "ENUMERATION", // enum
61 "INTEGER", // char, short, int, long, long long, sc_int, sc_bigint
62 "UNSIGNED", // unsigned { char, short, int, long, long long }, sc_uint, sc_biguint
63 "FLOATING_POINT_NUMBER", // float, double
64 "BIT_VECTOR", // sc_bit, sc_bv
65 "LOGIC_VECTOR", // sc_logic, sc_lv
66 "FIXED_POINT_INTEGER", // sc_fixed
67 "UNSIGNED_FIXED_POINT_INTEGER", // sc_ufixed
68 "RECORD", // struct/class
69 "POINTER", // T*
70 "ARRAY", // T[N]
71 "STRING" // string, std::string
72
73}};
74class PlainWriter {
75public:
76 std::ofstream out;
77 PlainWriter(const std::string& name)
78 : out(name) {}
79 ~PlainWriter() {
80 if(out.is_open())
81 out.close();
82 }
83 bool is_open() { return out.is_open(); }
84};
85class LZ4Writer {
86 std::ofstream ofs;
87 std::unique_ptr<util::lz4c_steambuf> strbuf;
88
89public:
90 std::ostream out;
91 LZ4Writer(const std::string& name)
92 : ofs(name, std::ios::binary | std::ios::trunc)
93 , strbuf(new util::lz4c_steambuf(ofs, 8192))
94 , out(strbuf.get()) {}
95 ~LZ4Writer() {
96 if(is_open()) {
97 strbuf->close();
98 ofs.close();
99 }
100 }
101
102 bool is_open() { return ofs.is_open(); }
103};
104
105template <typename WRITER> struct Formatter {
106 std::unique_ptr<WRITER> writer;
107 Formatter(const std::string& name)
108 : writer(new WRITER(name)) {}
109
110 Formatter() {}
111
112 inline bool open(const std::string& name) {
113 writer.reset(new WRITER(name));
114 return writer->is_open();
115 }
116
117 inline void close() { delete writer.release(); }
118
119 inline void writeStream(uint64_t id, std::string const& name, std::string const& kind) {
120 auto buf = fmt::format("scv_tr_stream (ID {}, name \"{}\", kind \"{}\")\n", id, name.c_str(), kind.c_str());
121 writer->out.write(buf.c_str(), buf.size());
122 }
123
124 inline void writeGenerator(uint64_t id, std::string const& name, uint64_t stream, std::vector<AttrDesc> const& attributes) {
125 auto buf = fmt::format("scv_tr_generator (ID {}, name \"{}\", scv_tr_stream {},\n", id, name.c_str(), stream);
126 writer->out.write(buf.c_str(), buf.size());
127 auto idx = 0U;
128 for(auto attr : attributes) {
129 if(attr.evt == BEGIN) {
130 auto buf = fmt::format("begin_attribute (ID {}, name \"{}\", type \"{}\")\n", idx, attr.name, data_type_str[attr.type]);
131 writer->out.write(buf.c_str(), buf.size());
132 } else if(attr.evt == END) {
133 auto buf = fmt::format("end_attribute (ID {}, name \"{}\", type \"{}\")\n", idx, attr.name, data_type_str[attr.type]);
134 writer->out.write(buf.c_str(), buf.size());
135 }
136 ++idx;
137 }
138 writer->out.write(")\n", 2);
139 }
140
141 inline void writeTransaction(uint64_t id, uint64_t generator, EventType type, uint64_t time) {
142 auto buf = type == BEGIN ? fmt::format("tx_begin {} {} {} ps\n", id, generator, time)
143 : fmt::format("tx_end {} {} {} ps\n", id, generator, time);
144 writer->out.write(buf.c_str(), buf.size());
145 }
146
147 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
148 // data_type::BOOLEAN, data_type::ENUMERATION, data_type::BIT_VECTOR, data_type::LOGIC_VECTOR, data_type::STRING
149 auto buf = event == EventType::RECORD
150 ? fmt::format("tx_record_attribute {} \"{}\" {} = \"{}\"\n", id, name, data_type_str[type], value)
151 : fmt::format("a \"{}\"\n", value);
152 writer->out.write(buf.c_str(), buf.size());
153 }
154
155 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, int64_t value) {
156 // data_type::INTEGER, data_type::UNSIGNED, data_type::FIXED_POINT_INTEGER, data_type::UNSIGNED_FIXED_POINT_INTEGER
157 auto buf = event == EventType::RECORD ? fmt::format("tx_record_attribute {} \"{}\" {} = {}\n", id, name, data_type_str[type], value)
158 : fmt::format("a {}\n", value);
159 writer->out.write(buf.c_str(), buf.size());
160 }
161
162 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, uint64_t value) {
163 // data_type::INTEGER, data_type::UNSIGNED, data_type::FIXED_POINT_INTEGER, data_type::UNSIGNED_FIXED_POINT_INTEGER
164 auto buf = event == EventType::RECORD ? fmt::format("tx_record_attribute {} \"{}\" {} = {}\n", id, name, data_type_str[type], value)
165 : fmt::format("a {}\n", value);
166 writer->out.write(buf.c_str(), buf.size());
167 }
168
169 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, bool value) {
170 // data_type::INTEGER, data_type::UNSIGNED, data_type::FIXED_POINT_INTEGER, data_type::UNSIGNED_FIXED_POINT_INTEGER
171 auto buf = event == EventType::RECORD
172 ? fmt::format("tx_record_attribute {} \"{}\" {} = {}\n", id, name, data_type_str[type], value ? "true" : "false")
173 : fmt::format("a {}\n", value ? "true" : "false");
174 writer->out.write(buf.c_str(), buf.size());
175 }
176
177 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
178 // data_type::FLOATING_POINT_NUMBER
179 auto buf = event == EventType::RECORD ? fmt::format("tx_record_attribute {} \"{}\" {} = {}\n", id, name, data_type_str[type], value)
180 : fmt::format("a {}\n", value);
181 writer->out.write(buf.c_str(), buf.size());
182 }
183
184 inline void writeRelation(const std::string& name, uint64_t sink_id, uint64_t src_id) {
185 auto buf = fmt::format("tx_relation \"{}\" {} {}\n", name, sink_id, src_id);
186 writer->out.write(buf.c_str(), buf.size());
187 }
188 static Formatter& get() {
189 static Formatter db;
190 return db;
191 }
192};
193template <typename DB> void dbCb(const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason, void* data) {
194 // This is called from the scv_tr_db ctor.
195 static string fName("DEFAULT_scv_tr_sqlite");
196 switch(reason) {
197 case scv_tr_db::CREATE:
198 if((_scv_tr_db.get_name() != nullptr) && (strlen(_scv_tr_db.get_name()) != 0))
199 fName = _scv_tr_db.get_name();
200 try {
201 DB::get().open(fName);
202 } catch(...) {
203 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open recording file");
204 }
205 break;
206 case scv_tr_db::DELETE:
207 try {
208 DB::get().close();
209 } catch(...) {
210 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't close recording file");
211 }
212 break;
213 default:
214 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Unknown reason in scv_tr_db callback");
215 }
216}
217// ----------------------------------------------------------------------------
218template <typename DB> void streamCb(const scv_tr_stream& s, scv_tr_stream::callback_reason reason, void* data) {
219 if(reason == scv_tr_stream::CREATE) {
220 try {
221 DB::get().writeStream(s.get_id(), s.get_name(), s.get_stream_kind());
222 } catch(std::runtime_error& e) {
223 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create stream");
224 }
225 }
226}
227// ----------------------------------------------------------------------------
228template <typename DB> inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
229 try {
230 DB::get().writeAttribute(id, event, name, type, value);
231 } catch(std::runtime_error& e) {
232 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
233 }
234}
235// ----------------------------------------------------------------------------
236inline std::string get_name(const char* prefix, const scv_extensions_if* my_exts_p) {
237 string name;
238 if(!prefix || strlen(prefix) == 0) {
239 name = my_exts_p->get_name();
240 } else {
241 if((my_exts_p->get_name() == nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
242 name = prefix;
243 } else {
244 name = fmt::format("{}.{}", prefix, my_exts_p->get_name());
245 }
246 }
247 return (name == "") ? "<unnamed>" : name;
248}
249
250// ----------------------------------------------------------------------------
251template <typename DB>
252inline void recordAttributes(uint64_t id, EventType eventType, char const* prefix, const scv_extensions_if* my_exts_p) {
253 if(my_exts_p == nullptr)
254 return;
255 auto name = get_name(prefix, my_exts_p);
256 switch(my_exts_p->get_type()) {
257 case scv_extensions_if::RECORD: {
258 int num_fields = my_exts_p->get_num_fields();
259 if(num_fields > 0) {
260 for(int field_counter = 0; field_counter < num_fields; field_counter++) {
261 const scv_extensions_if* field_data_p = my_exts_p->get_field(field_counter);
262 recordAttributes<DB>(id, eventType, prefix, field_data_p);
263 }
264 }
265 } break;
266 case scv_extensions_if::POINTER:
267 if(auto ptr = my_exts_p->get_pointer()) {
268 std::stringstream ss;
269 ss << prefix << "*";
270 recordAttributes<DB>(id, eventType, ss.str().c_str(), ptr);
271 }
272 break;
273 case scv_extensions_if::ENUMERATION:
274 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::ENUMERATION,
275 my_exts_p->get_enum_string((int)(my_exts_p->get_integer())));
276 break;
277 case scv_extensions_if::BOOLEAN:
278 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool());
279 break;
280 case scv_extensions_if::INTEGER:
281 case scv_extensions_if::FIXED_POINT_INTEGER:
282 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::INTEGER, (int64_t)my_exts_p->get_integer());
283 break;
284 case scv_extensions_if::UNSIGNED:
285 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::UNSIGNED, (uint64_t)my_exts_p->get_unsigned());
286 break;
287 case scv_extensions_if::STRING:
288 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
289 break;
290 case scv_extensions_if::FLOATING_POINT_NUMBER:
291 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
292 break;
293 case scv_extensions_if::BIT_VECTOR: {
294 sc_bv_base tmp_bv(my_exts_p->get_bitwidth());
295 my_exts_p->get_value(tmp_bv);
296 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::BIT_VECTOR, tmp_bv.to_string());
297 } break;
298 case scv_extensions_if::LOGIC_VECTOR: {
299 sc_lv_base tmp_lv(my_exts_p->get_bitwidth());
300 my_exts_p->get_value(tmp_lv);
301 DB::get().writeAttribute(id, eventType, name, scv_extensions_if::LOGIC_VECTOR, tmp_lv.to_string());
302 } break;
303 case scv_extensions_if::ARRAY:
304 for(int array_elt_index = 0; array_elt_index < my_exts_p->get_array_size(); array_elt_index++) {
305 const scv_extensions_if* field_data_p = my_exts_p->get_array_elt(array_elt_index);
306 recordAttributes<DB>(id, eventType, prefix, field_data_p);
307 }
308 break;
309 default: {
310 std::array<char, 100> tmpString;
311 sprintf(tmpString.data(), "Unsupported attribute type = %d", my_exts_p->get_type());
312 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, tmpString.data());
313 }
314 }
315}
316// ----------------------------------------------------------------------------
317template <typename DB> void generatorCb(const scv_tr_generator_base& g, scv_tr_generator_base::callback_reason reason, void* data) {
318 if(reason == scv_tr_generator_base::CREATE) {
319 try {
320 std::vector<AttrDesc> attrs;
321 const scv_extensions_if* my_begin_exts_p = g.get_begin_exts_p();
322 if(my_begin_exts_p != nullptr) {
323 attrs.emplace_back(BEGIN, my_begin_exts_p->get_type(), g.get_begin_attribute_name() ? g.get_begin_attribute_name() : "");
324 }
325 const scv_extensions_if* my_end_exts_p = g.get_end_exts_p();
326 if(my_end_exts_p != nullptr) {
327 attrs.emplace_back(END, my_end_exts_p->get_type(), g.get_end_attribute_name() ? g.get_end_attribute_name() : "");
328 }
329 DB::get().writeGenerator(g.get_id(), g.get_name(), g.get_scv_tr_stream().get_id(), attrs);
330 } catch(std::runtime_error& e) {
331 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create generator entry");
332 }
333 }
334}
335// ----------------------------------------------------------------------------
336template <typename DB> void transactionCb(const scv_tr_handle& t, scv_tr_handle::callback_reason reason, void* data) {
337 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
338 return;
339 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
340 return;
341
342 uint64_t id = t.get_id();
343 vector<uint64_t>::size_type concurrencyIdx;
344 const scv_extensions_if* my_exts_p;
345 switch(reason) {
346 case scv_tr_handle::BEGIN: {
347 DB::get().writeTransaction(t.get_id(), t.get_scv_tr_generator_base().get_id(), BEGIN, t.get_begin_sc_time().value());
348 my_exts_p = t.get_begin_exts_p();
349 if(my_exts_p == nullptr)
350 my_exts_p = t.get_scv_tr_generator_base().get_begin_exts_p();
351 if(my_exts_p) {
352 auto tmp_str =
353 t.get_scv_tr_generator_base().get_begin_attribute_name() ? t.get_scv_tr_generator_base().get_begin_attribute_name() : "";
354 recordAttributes<DB>(id, BEGIN, tmp_str, my_exts_p);
355 }
356 } break;
357 case scv_tr_handle::END: {
358 DB::get().writeTransaction(t.get_id(), t.get_scv_tr_generator_base().get_id(), END, t.get_begin_sc_time().value());
359 my_exts_p = t.get_end_exts_p();
360 if(my_exts_p == nullptr)
361 my_exts_p = t.get_scv_tr_generator_base().get_end_exts_p();
362 if(my_exts_p) {
363 auto tmp_str =
364 t.get_scv_tr_generator_base().get_end_attribute_name() ? t.get_scv_tr_generator_base().get_end_attribute_name() : "";
365 recordAttributes<DB>(t.get_id(), END, tmp_str, my_exts_p);
366 }
367 } break;
368 default:;
369 }
370}
371// ----------------------------------------------------------------------------
372template <typename DB> void attributeCb(const scv_tr_handle& t, const char* name, const scv_extensions_if* ext, void* data) {
373 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
374 return;
375 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
376 return;
377 recordAttributes<DB>(t.get_id(), RECORD, name == nullptr ? "" : name, ext);
378}
379// ----------------------------------------------------------------------------
380template <typename DB>
381void relationCb(const scv_tr_handle& tr_1, const scv_tr_handle& tr_2, void* data, scv_tr_relation_handle_t relation_handle) {
382 if(tr_1.get_scv_tr_stream().get_scv_tr_db() == nullptr)
383 return;
384 if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
385 return;
386 try {
387 DB::get().writeRelation(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_relation_name(relation_handle), tr_1.get_id(), tr_2.get_id());
388 } catch(std::runtime_error& e) {
389 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction relation");
390 }
391}
392} // namespace
393// ----------------------------------------------------------------------------
395 scv_tr_db::register_class_cb(dbCb<Formatter<LZ4Writer>>);
396 scv_tr_stream::register_class_cb(streamCb<Formatter<LZ4Writer>>);
397 scv_tr_generator_base::register_class_cb(generatorCb<Formatter<LZ4Writer>>);
398 scv_tr_handle::register_class_cb(transactionCb<Formatter<LZ4Writer>>);
399 scv_tr_handle::register_record_attribute_cb(attributeCb<Formatter<LZ4Writer>>);
400 scv_tr_handle::register_relation_cb(relationCb<Formatter<LZ4Writer>>);
401}
403 scv_tr_db::register_class_cb(dbCb<Formatter<PlainWriter>>);
404 scv_tr_stream::register_class_cb(streamCb<Formatter<PlainWriter>>);
405 scv_tr_generator_base::register_class_cb(generatorCb<Formatter<PlainWriter>>);
406 scv_tr_handle::register_class_cb(transactionCb<Formatter<PlainWriter>>);
407 scv_tr_handle::register_record_attribute_cb(attributeCb<Formatter<PlainWriter>>);
408 scv_tr_handle::register_relation_cb(relationCb<Formatter<PlainWriter>>);
409}
410// ----------------------------------------------------------------------------
411#ifndef HAS_SCV
412}
413#endif
SystemC Verification Library (SCV) Transaction Recording.
void scv_tr_plain_init()
initializes the infrastructure to use a plain text based transaction recording database
void scv_tr_lz4_init()
initializes the infrastructure to use a LZ4 compressed text based transaction recording database