scc 2025.09
SystemC components library
scv_tr_compressed.cpp
1// -*- C++ -*- <this line is for emacs to recognize it as C++ code>
2/*****************************************************************************
3
4 Licensed to Accellera Systems Initiative Inc. (Accellera)
5 under one or more contributor license agreements. See the
6 NOTICE file distributed with this work for additional
7 information regarding copyright ownership. Accellera licenses
8 this file to you under the Apache License, Version 2.0 (the
9 "License"); you may not use this file except in compliance
10 with the License. You may obtain a copy of the License at
11
12 http://www.apache.org/licenses/LICENSE-2.0
13
14 Unless required by applicable law or agreed to in writing,
15 software distributed under the License is distributed on an
16 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 KIND, either express or implied. See the License for the
18 specific language governing permissions and limitations
19 under the License.
20
21 *****************************************************************************/
22
23/*****************************************************************************
24
25 scv_tr_text.cpp -- This is the implementation of the transaction recording
26 text facility, which uses callbacks in scv_tr.h
27
28 Original Authors (Cadence Design Systems, Inc):
29 Norris Ip, Dean Shea, John Rose, Jasvinder Singh, William Paulsen,
30 John Pierce, Rachida Kebichi, Ted Elkind, David Bailey
31 2002-09-23
32
33 *****************************************************************************/
34
35/*****************************************************************************
36
37 MODIFICATION LOG - modifiers, enter your name, affiliation, date and
38 changes you are making here.
39
40 Name, Affiliation, Date: Torsten Maehne,
41 Universite Pierre et Marie Curie, 2013-04-30
42 Description of Modification: Fix memory leak caused by not free()-ing
43 C-strings duplicated using strdup() in the
44 function scv_tr_db_cbf() by changing the type
45 of the concerned static variable
46 my_text_file_namefrom char* to string.
47 Remove the now unused internal
48 static scv_tr_strdup() function, which is anyway
49 not exported by the linker.
50
51 *****************************************************************************/
52
53/*
54 * Here's the format of the text file:
55
56 scv_tr_stream (ID <id>, name "<full_name>", kind "<kind>")
57
58 scv_tr_generator (ID <id>, name "<name>", scv_tr_stream <id>,
59 begin_attribute (ID <id1>, name "<name1>", type <"type_name">)
60 begin_attribute (ID <id2>, name "<name2>", type <"type_name">)
61 end_attribute (ID <id3>, name "<name3>", type <"type_name">)
62 end_attribute (ID <id4>, name "<name4>", type <"type_name">)
63 ... )
64
65 tx_begin <this_transaction_id> <generator_id> <begin_time>
66 a <value>
67 a <value>
68
69 tx_end <this_transaction_id> <generator_id> <end_time>
70 a <value>
71 a <value>
72
73 tx_relation <"relation_name"> <tx_id_1> <tx_id_2>
74
75 *
76 */
77
78#include <string>
79// clang-format off
80#include <array>
81#include <zlib.h>
82#include <sstream>
83#ifdef HAS_SCV
84#include <scv.h>
85#else
86#include <scv-tr.h>
87namespace scv_tr {
88#endif
89// clang-format on
90// ----------------------------------------------------------------------------
91
92#ifdef _MSC_VER
93#define scv_tr_TEXT_LLU "%I64u"
94#define scv_tr_TEXT_LLX "%I64x"
95#else
96#define scv_tr_TEXT_LLU "%llu"
97#define scv_tr_TEXT_LLX "%llx"
98#endif
99
100// #define scv_tr_TRACE
101
102// ----------------------------------------------------------------------------
103
104static gzFile my_text_file_p = nullptr;
105
106static void scv_tr_db_cbf(const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason, void* user_data_p) {
107 // This is called from the scv_tr_db ctor.
108
109 static std::string my_text_file_name("DEFAULT_scv_tr_TEXT.txt");
110
111 switch(reason) {
112
113 case scv_tr_db::CREATE:
114 if((_scv_tr_db.get_name() != nullptr) && (strlen(_scv_tr_db.get_name()) != 0)) {
115 my_text_file_name = _scv_tr_db.get_name();
116 }
117
118 my_text_file_p = gzopen(my_text_file_name.c_str(), "wb1"); // f, h, R
119
120 if(my_text_file_p == nullptr) {
121 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open text recording file");
122 } else {
123 std::stringstream ss;
124 ss << "opening file " << my_text_file_name;
125 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL_INFO, ss.str().c_str());
126 }
127 break;
128
129 case scv_tr_db::DELETE:
130 if(my_text_file_p != nullptr) {
131 std::stringstream ss;
132 ss << "closing file " << my_text_file_name;
133 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL_INFO, ss.str().c_str());
134 gzclose(my_text_file_p);
135 my_text_file_p = nullptr;
136 }
137 break;
138
139 default:
140 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Unknown reason in scv_tr_db callback");
141 }
142}
143
144// ----------------------------------------------------------------------------
145
146static void scv_tr_stream_cbf(const scv_tr_stream& s, scv_tr_stream::callback_reason reason, void* user_data_p) {
147 if(reason == scv_tr_stream::CREATE) {
148
149 if(my_text_file_p == nullptr)
150 return;
151
152 gzprintf(my_text_file_p, "scv_tr_stream (ID " scv_tr_TEXT_LLU ", name \"%s\", kind \"%s\")\n", s.get_id(), s.get_name(),
153 s.get_stream_kind() ? s.get_stream_kind() : "<no_stream_kind>");
154 }
155}
156
157// ----------------------------------------------------------------------------
158
159// #define TRACE_DO_ATTRIBUTES
160
161static void do_attributes(bool declare_attributes, // If false then print the values
162 bool undefined_values, bool is_record_attribute, std::string& prefix_name, const std::string& exts_kind,
163 const scv_extensions_if* my_exts_p,
164 int* index) // The attribute index number
165{
166 // This function can be called recursively, for nested data types.
167 if(my_exts_p == nullptr)
168 return;
169
170 std::string full_name;
171
172 if(prefix_name == "") {
173 full_name = my_exts_p->get_name();
174 } else {
175 if((my_exts_p->get_name() == nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
176 full_name = prefix_name;
177 } else {
178 full_name = prefix_name + "." + my_exts_p->get_name();
179 }
180 }
181
182 if(full_name == "") {
183 full_name = "<anonymous>";
184 }
185
186 switch(my_exts_p->get_type()) {
187
188 case scv_extensions_if::RECORD: {
189 int num_fields = my_exts_p->get_num_fields();
190 int field_counter;
191
192 if(num_fields > 0) {
193 for(field_counter = 0; field_counter < num_fields; field_counter++) {
194
195 const scv_extensions_if* field_data_p = my_exts_p->get_field(field_counter);
196
197 do_attributes(declare_attributes, undefined_values, is_record_attribute, prefix_name, exts_kind, field_data_p, index);
198 }
199 }
200 } break;
201
202 case scv_extensions_if::ENUMERATION: {
203 if(declare_attributes) {
204 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"ENUMERATION\")\n",
205 exts_kind.c_str(), // begin_attribute or end_attribute
206 *index, full_name.c_str());
207 (*index)++;
208 } else if(undefined_values) {
209 gzprintf(my_text_file_p, "a UNDEFINED\n");
210 } else {
211 if(is_record_attribute) {
212 gzprintf(my_text_file_p, R"(%s "%s" ENUMERATION = )", exts_kind.c_str(), full_name.c_str());
213 } else {
214 gzprintf(my_text_file_p, "a ");
215 }
216 gzprintf(my_text_file_p, "\"%s\"\n", my_exts_p->get_enum_string((int)my_exts_p->get_integer()));
217 }
218 } break;
219
220 case scv_extensions_if::BOOLEAN: {
221 if(declare_attributes) {
222 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"BOOLEAN\")\n",
223 exts_kind.c_str(), // begin_attribute or end_attribute
224 *index, full_name.c_str());
225 (*index)++;
226 } else if(undefined_values) {
227 gzprintf(my_text_file_p, "a UNDEFINED\n");
228 } else {
229 if(is_record_attribute) {
230 gzprintf(my_text_file_p, R"(%s "%s" BOOLEAN = )", exts_kind.c_str(), full_name.c_str());
231 } else {
232 gzprintf(my_text_file_p, "a ");
233 }
234 gzprintf(my_text_file_p, "%s\n", my_exts_p->get_bool() ? "true" : "false");
235 }
236 } break;
237
238 case scv_extensions_if::INTEGER:
239 case scv_extensions_if::FIXED_POINT_INTEGER: {
240 if(declare_attributes) {
241 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"INTEGER\")\n",
242 exts_kind.c_str(), // begin_attribute or end_attribute
243 *index, full_name.c_str());
244 (*index)++;
245 } else if(undefined_values) {
246 gzprintf(my_text_file_p, "a UNDEFINED\n");
247 } else {
248 if(is_record_attribute) {
249 gzprintf(my_text_file_p, R"(%s "%s" INTEGER = )", exts_kind.c_str(), full_name.c_str());
250 } else {
251 gzprintf(my_text_file_p, "a ");
252 }
253 if(my_exts_p->get_bitwidth() == 64) {
254 gzprintf(my_text_file_p, scv_tr_TEXT_LLU "\n", my_exts_p->get_integer());
255 } else {
256 auto tmp_int = (int)my_exts_p->get_integer();
257 gzprintf(my_text_file_p, "%d\n", tmp_int);
258 }
259 }
260 } break;
261
262 case scv_extensions_if::UNSIGNED: {
263 if(declare_attributes) {
264 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"UNSIGNED\")\n",
265 exts_kind.c_str(), // begin_attribute or end_attribute
266 *index, full_name.c_str());
267 (*index)++;
268 } else if(undefined_values) {
269 gzprintf(my_text_file_p, "a UNDEFINED\n");
270 } else {
271 if(is_record_attribute) {
272 gzprintf(my_text_file_p, R"(%s "%s" UNSIGNED = )", exts_kind.c_str(), full_name.c_str());
273 } else {
274 gzprintf(my_text_file_p, "a ");
275 }
276 gzprintf(my_text_file_p, scv_tr_TEXT_LLU "\n", my_exts_p->get_unsigned());
277 }
278 } break;
279
280 case scv_extensions_if::POINTER: {
281 const scv_extensions_if* field_data_p = my_exts_p->get_pointer();
282
283 // Extensions are not yet implemented for pointers, so the only thing
284 // to do here is to simply print the value of the pointer.
285
286 if(declare_attributes) {
287 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"POINTER\")\n",
288 exts_kind.c_str(), // begin_attribute or end_attribute
289 *index, full_name.c_str());
290 (*index)++;
291 } else if(undefined_values) {
292 gzprintf(my_text_file_p, "a UNDEFINED\n");
293 } else {
294 if(is_record_attribute) {
295 gzprintf(my_text_file_p, R"(%s "%s" POINTER = )", exts_kind.c_str(), full_name.c_str());
296 } else {
297 gzprintf(my_text_file_p, "a ");
298 }
299 gzprintf(my_text_file_p, "%ld\n", (long)field_data_p);
300 }
301 } break;
302
303 case scv_extensions_if::STRING: {
304 if(declare_attributes) {
305 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"STRING\")\n",
306 exts_kind.c_str(), // begin_attribute or end_attribute
307 *index, full_name.c_str());
308 (*index)++;
309 } else if(undefined_values) {
310 gzprintf(my_text_file_p, "a UNDEFINED\n");
311 } else {
312 if(is_record_attribute) {
313 gzprintf(my_text_file_p, R"(%s "%s" STRING = )", exts_kind.c_str(), full_name.c_str());
314 } else {
315 gzprintf(my_text_file_p, "a ");
316 }
317 gzprintf(my_text_file_p, "\"%s\"\n", my_exts_p->get_string().c_str());
318 }
319 } break;
320
321 case scv_extensions_if::FLOATING_POINT_NUMBER: {
322 if(declare_attributes) {
323 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"FLOATING_POINT_NUMBER\")\n",
324 exts_kind.c_str(), // begin_attribute or end_attribute
325 *index, full_name.c_str());
326 (*index)++;
327 } else if(undefined_values) {
328 gzprintf(my_text_file_p, "a UNDEFINED\n");
329 } else {
330 if(is_record_attribute) {
331 gzprintf(my_text_file_p, R"(%s "%s" FLOATING_POINT_NUMBER = )", exts_kind.c_str(), full_name.c_str());
332 } else {
333 gzprintf(my_text_file_p, "a ");
334 }
335 gzprintf(my_text_file_p, "%f\n", my_exts_p->get_double());
336 }
337 } break;
338
339 case scv_extensions_if::BIT_VECTOR: {
340 if(declare_attributes) {
341 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"BIT_VECTOR[%d]\")\n",
342 exts_kind.c_str(), // begin_attribute or end_attribute
343 *index, full_name.c_str(), my_exts_p->get_bitwidth());
344 (*index)++;
345 } else if(undefined_values) {
346 gzprintf(my_text_file_p, "a UNDEFINED\n");
347 } else {
348 if(is_record_attribute) {
349 gzprintf(my_text_file_p, R"(%s "%s" BIT_VECTOR = )", exts_kind.c_str(), full_name.c_str());
350 } else {
351 gzprintf(my_text_file_p, "a ");
352 }
353 sc_bv_base tmp_bv(my_exts_p->get_bitwidth());
354 my_exts_p->get_value(tmp_bv);
355 gzprintf(my_text_file_p, "\"%s\"\n", tmp_bv.to_string().c_str());
356 }
357 } break;
358
359 case scv_extensions_if::LOGIC_VECTOR: {
360 if(declare_attributes) {
361 gzprintf(my_text_file_p, "%s (ID %d, name \"%s\", type \"LOGIC_VECTOR[%d]\")\n",
362 exts_kind.c_str(), // begin_attribute or end_attribute
363 *index, full_name.c_str(), my_exts_p->get_bitwidth());
364 (*index)++;
365 } else if(undefined_values) {
366 gzprintf(my_text_file_p, "a UNDEFINED\n");
367 } else {
368 if(is_record_attribute) {
369 gzprintf(my_text_file_p, R"(%s "%s" LOGIC_VECTOR = )", exts_kind.c_str(), full_name.c_str());
370 } else {
371 gzprintf(my_text_file_p, "a ");
372 }
373 sc_lv_base tmp_lv(my_exts_p->get_bitwidth());
374 my_exts_p->get_value(tmp_lv);
375
376 gzprintf(my_text_file_p, "\"%s\"\n", tmp_lv.to_string().c_str());
377 }
378 } break;
379
380 case scv_extensions_if::ARRAY: {
381 int array_elt_index = 0;
382
383 for(; array_elt_index < my_exts_p->get_array_size(); array_elt_index++) {
384
385 const scv_extensions_if* field_data_p = my_exts_p->get_array_elt(array_elt_index);
386
387 do_attributes(declare_attributes, undefined_values, is_record_attribute, prefix_name, exts_kind, field_data_p, index);
388 }
389 } break;
390
391 default: {
392 std::array<char, 100> tmpString{};
393 sprintf(tmpString.data(), "Unsupported attribute type = %d", my_exts_p->get_type());
394
395 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, tmpString.data());
396 }
397 }
398}
399
400// ----------------------------------------------------------------------------
401
402static void scv_tr_generator_cbf(const scv_tr_generator_base& g, scv_tr_generator_base::callback_reason reason, void* user_data_p) {
403 if(reason != scv_tr_generator_base::CREATE) {
404 return;
405 }
406
407 if(my_text_file_p == nullptr)
408 return;
409
410 gzprintf(my_text_file_p, "scv_tr_generator (ID " scv_tr_TEXT_LLU ", name \"%s\", scv_tr_stream " scv_tr_TEXT_LLU ",\n", g.get_id(),
411 g.get_name(), g.get_scv_tr_stream().get_id());
412
413 std::string exts_kind;
414 int index = 0;
415
416 const scv_extensions_if* my_begin_exts_p = g.get_begin_exts_p();
417 if(my_begin_exts_p != nullptr) {
418 exts_kind = "begin_attribute";
419 std::string tmp_str = g.get_begin_attribute_name() ? g.get_begin_attribute_name() : "";
420 do_attributes(true, false, false, tmp_str, exts_kind, my_begin_exts_p, &index);
421 }
422
423 const scv_extensions_if* my_end_exts_p = g.get_end_exts_p();
424 if(my_end_exts_p != nullptr) {
425 exts_kind = "end_attribute";
426 std::string tmp_str = g.get_end_attribute_name() ? g.get_end_attribute_name() : "";
427 do_attributes(true, false, false, tmp_str, exts_kind, my_end_exts_p, &index);
428 }
429
430 gzprintf(my_text_file_p, ")\n");
431}
432
433// ----------------------------------------------------------------------------
434
435static void scv_tr_handle_cbf(const scv_tr_handle& t, scv_tr_handle::callback_reason reason, void* user_data_p) {
436 if(my_text_file_p == nullptr)
437 return;
438
439 int i = 0;
440
441 // This callback function is called when a transaction is begun or ended,
442 // or deleted.
443
444 // First check to be sure transaction recording is enabled:
445 //
446 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
447 return;
448 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
449 return;
450
451 const scv_extensions_if* my_exts_p;
452
453 switch(reason) {
454
455 case scv_tr_handle::BEGIN: {
456 // The beginning of a transaction
457 gzprintf(my_text_file_p, "tx_begin " scv_tr_TEXT_LLU " " scv_tr_TEXT_LLU " %s\n", t.get_id(),
458 t.get_scv_tr_generator_base().get_id(), t.get_begin_sc_time().to_string().c_str());
459
460 my_exts_p = t.get_begin_exts_p();
461
462 std::string exts_kind = "begin_attributes";
463 bool default_values = false;
464
465 if(my_exts_p == nullptr) {
466 // For this transaction, the default attributes are used.
467 my_exts_p = t.get_scv_tr_generator_base().get_begin_exts_p();
468 default_values = true;
469 }
470
471 std::string tmp_str =
472 t.get_scv_tr_generator_base().get_begin_attribute_name() ? t.get_scv_tr_generator_base().get_begin_attribute_name() : "";
473
474 do_attributes(false, default_values, false, tmp_str, exts_kind, my_exts_p, &i);
475
476 } break;
477
478 case scv_tr_handle::END: {
479 // The end of a transaction
480 gzprintf(my_text_file_p, "tx_end " scv_tr_TEXT_LLU " " scv_tr_TEXT_LLU " %s\n", t.get_id(), t.get_scv_tr_generator_base().get_id(),
481 t.get_end_sc_time().to_string().c_str());
482
483 my_exts_p = t.get_end_exts_p();
484
485 std::string exts_kind = "end_attributes";
486 bool default_values = false;
487
488 if(my_exts_p == nullptr) {
489 // For this transaction, the default attributes are used.
490 my_exts_p = t.get_scv_tr_generator_base().get_end_exts_p();
491 default_values = true;
492 }
493
494 std::string tmp_str =
495 t.get_scv_tr_generator_base().get_end_attribute_name() ? t.get_scv_tr_generator_base().get_end_attribute_name() : "";
496
497 do_attributes(false, default_values, false, tmp_str, exts_kind, my_exts_p, &i);
498 } break;
499
500 default:;
501 }
502}
503
504// ----------------------------------------------------------------------------
505
506static void scv_tr_handle_record_attribute_cbf(const scv_tr_handle& t, const char* attribute_name, const scv_extensions_if* my_exts_p,
507 void* user_data_p) {
508 // First check to be sure transaction recording is enabled:
509 //
510 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
511 return;
512 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
513 return;
514
515 if(my_text_file_p == nullptr)
516 return;
517
518 std::string tmp_str;
519
520 if(attribute_name == nullptr) {
521 tmp_str = "";
522 } else {
523 tmp_str = attribute_name;
524 }
525
526 std::array<char, 100> tmp_str2{};
527 sprintf(tmp_str2.data(), "tx_record_attribute " scv_tr_TEXT_LLU, t.get_id());
528 std::string exts_kind = tmp_str2.data();
529
530 do_attributes(false, false, true, tmp_str, exts_kind, my_exts_p, nullptr);
531}
532
533// ----------------------------------------------------------------------------
534
535static void scv_tr_handle_relation_cbf(const scv_tr_handle& tr_1, const scv_tr_handle& tr_2, void* user_data_p,
536 scv_tr_relation_handle_t relation_handle) {
537 // First check to be sure transaction recording is enabled:
538 //
539 if(tr_1.get_scv_tr_stream().get_scv_tr_db() == nullptr)
540 return;
541 if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
542 return;
543
544 if(my_text_file_p == nullptr)
545 return;
546
547 if(my_text_file_p) {
548 gzprintf(my_text_file_p, "tx_relation \"%s\" " scv_tr_TEXT_LLU " " scv_tr_TEXT_LLU "\n",
549 tr_1.get_scv_tr_stream().get_scv_tr_db()->get_relation_name(relation_handle), tr_1.get_id(), tr_2.get_id());
550 }
551}
552
553// ----------------------------------------------------------------------------
554// ----------------------------------------------------------------------------
555
557 scv_tr_db::register_class_cb(scv_tr_db_cbf);
558 scv_tr_stream::register_class_cb(scv_tr_stream_cbf);
559 scv_tr_generator_base::register_class_cb(scv_tr_generator_cbf);
560 scv_tr_handle::register_class_cb(scv_tr_handle_cbf);
561 scv_tr_handle::register_record_attribute_cb(scv_tr_handle_record_attribute_cbf);
562 scv_tr_handle::register_relation_cb(scv_tr_handle_relation_cbf);
563}
564
565// ----------------------------------------------------------------------------
566#ifndef HAS_SCV
567}
568#endif
SystemC Verification Library (SCV) Transaction Recording.
void scv_tr_compressed_init()
initializes the infrastructure to use a gzip compressed text based transaction recording database