scc 2025.09
SystemC components library
scv_tr_mtc.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 <fstream>
23#include <iostream>
24#include <sstream>
25#include <stdexcept>
26#include <string>
27#include <unordered_map>
28#include <unordered_set>
29#include <vector>
30// clang-format off
31#ifdef HAS_SCV
32#include <scv.h>
33#else
34#include <scv-tr.h>
35namespace scv_tr {
36#endif
37// clang-format on
38// ----------------------------------------------------------------------------
39// ----------------------------------------------------------------------------
40using namespace std;
41
42// ----------------------------------------------------------------------------
43enum EventType { BEGIN, RECORD, END };
44using data_type = scv_extensions_if::data_type;
45// ----------------------------------------------------------------------------
46namespace {
47class Base {
48protected:
49 boost::filesystem::path dir;
50 std::ofstream out;
51 bool const is_open;
52 std::array<char, 1024> buffer;
53 Base(const std::string& name)
54 : dir(name.c_str())
55 , out(name)
56 , is_open(out.is_open()) {
57 // if(boost::filesystem::exists(dir))
58 // boost::filesystem::remove_all(dir);
59 // boost::filesystem::create_directory(dir);
60 }
61 ~Base() {
62 if(is_open)
63 out.close();
64 }
65};
66
67struct Database : Base {
68 uint64_t idx{0};
69 Database(const std::string& name)
70 : Base(name) {}
71
72 inline uint64_t getIdOf(const std::string& str) { return idx; }
73
74 inline void writeStream(uint64_t id, std::string const& name, std::string const& kind) {
75 if(is_open) {
76 auto len = sprintf(buffer.data(), "scv_tr_stream (ID %lu, name \"%s\", kind \"%s\")\n", id, name.c_str(), kind.c_str());
77 out.write(buffer.data(), len);
78 }
79 }
80
81 inline void writeGenerator(uint64_t id, std::string const& name, uint64_t stream) {
82 if(is_open) {
83 auto len = sprintf(buffer.data(), "scv_tr_generator (ID %lu, name \"%s\", scv_tr_stream %lu,\n", id, name.c_str(), stream);
84 out.write(buffer.data(), len);
85 }
86 }
87
88 inline uint64_t writeTransaction(uint64_t id, uint64_t generator) {
89 return ++idx; // d.writeTx(id, generator, concurrencyLevel);
90 }
91
92 inline void writeTxTimepoint(uint64_t id, int type, uint64_t time, uint64_t file_offset) {
93 // t.append(type, time, file_offset);
94 }
95
96 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
97 // d.writeAttribute(id, event, c.getIdOf(name), type, c.getIdOf(value));
98 }
99
100 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, uint64_t value) {
101 // d.writeAttribute(id, event, c.getIdOf(name), type, value);
102 }
103
104 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
105 // int exponent;
106 // const double mantissa = frexp(value, &exponent);
107 }
108
109 inline void writeRelation(const std::string& name, uint64_t sink_id, uint64_t src_id) {
110 // d.writeRelation(c.getIdOf(name), src_id, sink_id);
111 }
112};
113
114Database* db;
115std::unordered_map<uint64_t, uint64_t> id2offset;
116
117void dbCb(const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason, void* data) {
118 // This is called from the scv_tr_db ctor.
119 static string fName("DEFAULT_scv_tr_sqlite");
120 switch(reason) {
121 case scv_tr_db::CREATE:
122 if((_scv_tr_db.get_name() != nullptr) && (strlen(_scv_tr_db.get_name()) != 0))
123 fName = _scv_tr_db.get_name();
124 try {
125 db = new Database(fName);
126 } catch(...) {
127 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open recording file");
128 }
129 break;
130 case scv_tr_db::DELETE:
131 try {
132 delete db;
133 } catch(...) {
134 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't close recording file");
135 }
136 break;
137 default:
138 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Unknown reason in scv_tr_db callback");
139 }
140}
141// ----------------------------------------------------------------------------
142void streamCb(const scv_tr_stream& s, scv_tr_stream::callback_reason reason, void* data) {
143 if(reason == scv_tr_stream::CREATE) {
144 try {
145 db->writeStream(s.get_id(), s.get_name(), s.get_stream_kind());
146 } catch(std::runtime_error& e) {
147 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create stream");
148 }
149 }
150}
151// ----------------------------------------------------------------------------
152inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
153 try {
154 db->writeAttribute(id, event, name, type, value);
155 } catch(std::runtime_error& e) {
156 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
157 }
158}
159// ----------------------------------------------------------------------------
160inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, long long value) {
161 try {
162 db->writeAttribute(id, event, name, type, static_cast<uint64_t>(value));
163 } catch(std::runtime_error& e) {
164 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
165 }
166}
167// ----------------------------------------------------------------------------
168inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
169 try {
170 db->writeAttribute(id, event, name, type, value);
171 } catch(std::runtime_error& e) {
172 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
173 }
174}
175
176inline std::string get_name(const char* prefix, const scv_extensions_if* my_exts_p) {
177 static thread_local std::unordered_map<const scv_extensions_if*, std::string> name_lut;
178 auto it = name_lut.find(my_exts_p);
179 if(it != name_lut.end())
180 return it->second;
181 string name;
182 if(!prefix || strlen(prefix) == 0) {
183 name = my_exts_p->get_name();
184 } else {
185 if((my_exts_p->get_name() == nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
186 name = prefix;
187 } else {
188 name = std::string(prefix) + "." + my_exts_p->get_name();
189 }
190 }
191 name_lut[my_exts_p] = (name == "") ? "<unnamed>" : name;
192 return (name == "") ? "<unnamed>" : name;
193}
194
195// ----------------------------------------------------------------------------
196void recordAttributes(uint64_t id, EventType eventType, char const* prefix, const scv_extensions_if* my_exts_p) {
197 if(my_exts_p == nullptr)
198 return;
199 auto name = get_name(prefix, my_exts_p);
200 switch(my_exts_p->get_type()) {
201 case scv_extensions_if::RECORD: {
202 int num_fields = my_exts_p->get_num_fields();
203 if(num_fields > 0) {
204 for(int field_counter = 0; field_counter < num_fields; field_counter++) {
205 const scv_extensions_if* field_data_p = my_exts_p->get_field(field_counter);
206 recordAttributes(id, eventType, prefix, field_data_p);
207 }
208 }
209 } break;
210 case scv_extensions_if::ENUMERATION:
211 recordAttribute(id, eventType, name, scv_extensions_if::ENUMERATION, my_exts_p->get_enum_string((int)(my_exts_p->get_integer())));
212 break;
213 case scv_extensions_if::BOOLEAN:
214 recordAttribute(id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool() ? "TRUE" : "FALSE");
215 break;
216 case scv_extensions_if::INTEGER:
217 case scv_extensions_if::FIXED_POINT_INTEGER:
218 recordAttribute(id, eventType, name, scv_extensions_if::INTEGER, my_exts_p->get_integer());
219 break;
220 case scv_extensions_if::UNSIGNED:
221 recordAttribute(id, eventType, name, scv_extensions_if::UNSIGNED, my_exts_p->get_integer());
222 break;
223 case scv_extensions_if::POINTER:
224 recordAttribute(id, eventType, name, scv_extensions_if::POINTER, (long long)my_exts_p->get_pointer());
225 break;
226 case scv_extensions_if::STRING:
227 recordAttribute(id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
228 break;
229 case scv_extensions_if::FLOATING_POINT_NUMBER:
230 recordAttribute(id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
231 break;
232 case scv_extensions_if::BIT_VECTOR: {
233 sc_bv_base tmp_bv(my_exts_p->get_bitwidth());
234 my_exts_p->get_value(tmp_bv);
235 recordAttribute(id, eventType, name, scv_extensions_if::BIT_VECTOR, tmp_bv.to_string());
236 } break;
237 case scv_extensions_if::LOGIC_VECTOR: {
238 sc_lv_base tmp_lv(my_exts_p->get_bitwidth());
239 my_exts_p->get_value(tmp_lv);
240 recordAttribute(id, eventType, name, scv_extensions_if::LOGIC_VECTOR, tmp_lv.to_string());
241 } break;
242 case scv_extensions_if::ARRAY:
243 for(int array_elt_index = 0; array_elt_index < my_exts_p->get_array_size(); array_elt_index++) {
244 const scv_extensions_if* field_data_p = my_exts_p->get_array_elt(array_elt_index);
245 recordAttributes(id, eventType, prefix, field_data_p);
246 }
247 break;
248 default: {
249 std::array<char, 100> tmpString;
250 sprintf(tmpString.data(), "Unsupported attribute type = %d", my_exts_p->get_type());
251 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, tmpString.data());
252 }
253 }
254}
255// ----------------------------------------------------------------------------
256void generatorCb(const scv_tr_generator_base& g, scv_tr_generator_base::callback_reason reason, void* data) {
257 if(reason == scv_tr_generator_base::CREATE && db) {
258 try {
259 db->writeGenerator(g.get_id(), g.get_name(), g.get_scv_tr_stream().get_id());
260 } catch(std::runtime_error& e) {
261 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create generator entry");
262 }
263 }
264}
265// ----------------------------------------------------------------------------
266void transactionCb(const scv_tr_handle& t, scv_tr_handle::callback_reason reason, void* data) {
267 if(!db)
268 return;
269 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
270 return;
271 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
272 return;
273
274 uint64_t id = t.get_id();
275 vector<uint64_t>::size_type concurrencyIdx;
276 const scv_extensions_if* my_exts_p;
277 switch(reason) {
278 case scv_tr_handle::BEGIN: {
279 my_exts_p = t.get_begin_exts_p();
280 if(my_exts_p == nullptr)
281 my_exts_p = t.get_scv_tr_generator_base().get_begin_exts_p();
282 if(my_exts_p) {
283 auto tmp_str =
284 t.get_scv_tr_generator_base().get_begin_attribute_name() ? t.get_scv_tr_generator_base().get_begin_attribute_name() : "";
285 recordAttributes(id, BEGIN, tmp_str, my_exts_p);
286 }
287 } break;
288 case scv_tr_handle::END: {
289 my_exts_p = t.get_end_exts_p();
290 if(my_exts_p == nullptr)
291 my_exts_p = t.get_scv_tr_generator_base().get_end_exts_p();
292 if(my_exts_p) {
293 auto tmp_str =
294 t.get_scv_tr_generator_base().get_end_attribute_name() ? t.get_scv_tr_generator_base().get_end_attribute_name() : "";
295 recordAttributes(t.get_id(), END, tmp_str, my_exts_p);
296 }
297 } break;
298 default:;
299 }
300}
301// ----------------------------------------------------------------------------
302void attributeCb(const scv_tr_handle& t, const char* name, const scv_extensions_if* ext, void* data) {
303 if(!db)
304 return;
305 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
306 return;
307 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
308 return;
309 recordAttributes(t.get_id(), RECORD, name == nullptr ? "" : name, ext);
310}
311// ----------------------------------------------------------------------------
312void relationCb(const scv_tr_handle& tr_1, const scv_tr_handle& tr_2, void* data, scv_tr_relation_handle_t relation_handle) {
313 if(!db)
314 return;
315 if(tr_1.get_scv_tr_stream().get_scv_tr_db() == nullptr)
316 return;
317 if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
318 return;
319 try {
320 db->writeRelation(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_relation_name(relation_handle), tr_1.get_id(), tr_2.get_id());
321 } catch(std::runtime_error& e) {
322 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction relation");
323 }
324}
325} // namespace
326// ----------------------------------------------------------------------------
328 scv_tr_db::register_class_cb(dbCb);
329 scv_tr_stream::register_class_cb(streamCb);
330 scv_tr_generator_base::register_class_cb(generatorCb);
331 scv_tr_handle::register_class_cb(transactionCb);
332 scv_tr_handle::register_record_attribute_cb(attributeCb);
333 scv_tr_handle::register_relation_cb(relationCb);
334}
335// ----------------------------------------------------------------------------
336#ifndef HAS_SCV
337}
338#endif
SystemC Verification Library (SCV) Transaction Recording.
void scv_tr_mtc_init()
initializes the infrastructure to use a compressed text based transaction recording database with a m...