scc  2024.06
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>
37 namespace scv_tr {
38 #endif
39 // clang-format on
40 // ----------------------------------------------------------------------------
41 // ----------------------------------------------------------------------------
42 using namespace std;
43 
44 // ----------------------------------------------------------------------------
45 enum EventType { BEGIN, RECORD, END };
46 struct 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 };
55 using data_type = scv_extensions_if::data_type;
56 // ----------------------------------------------------------------------------
57 namespace {
58 const 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 }};
74 class PlainWriter {
75 public:
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 };
85 class LZ4Writer {
86  std::ofstream ofs;
87  std::unique_ptr<util::lz4c_steambuf> strbuf;
88 
89 public:
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 
105 template <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 };
193 template <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 // ----------------------------------------------------------------------------
218 template <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 // ----------------------------------------------------------------------------
228 template <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 // ----------------------------------------------------------------------------
236 inline 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 // ----------------------------------------------------------------------------
251 template <typename DB>
252 inline 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 // ----------------------------------------------------------------------------
317 template <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 // ----------------------------------------------------------------------------
336 template <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 // ----------------------------------------------------------------------------
372 template <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 // ----------------------------------------------------------------------------
380 template <typename DB>
381 void 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
Definition: scv_tr_lz4.cpp:402
void scv_tr_lz4_init()
initializes the infrastructure to use a LZ4 compressed text based transaction recording database
Definition: scv_tr_lz4.cpp:394
SCC common utilities.
Definition: bit_field.h:30