scc 2025.09
SystemC components library
tlm_recorder.h
1/*******************************************************************************
2 * Copyright 2016-2022 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
17#ifndef _TLM_NW_SCV_RECORDER_H_
18#define _TLM_NW_SCV_RECORDER_H_
19
20#include "tlm/scc/scv/tlm_recorder.h"
21#include <cci/cfg/cci_param_typed.h>
22#include <scc/report.h>
23#include <sstream>
24#include <string>
25#include <sysc/kernel/sc_dynamic_processes.h>
27#include <tlm/scc/scv/tlm_extension_recording_registry.h>
28#include <tlm/scc/scv/tlm_recording_extension.h>
29#include <tlm/scc/tlm_mm.h>
30#include <tlm_utils/peq_with_cb_and_phase.h>
31
33namespace tlm {
35namespace nw {
37namespace scv {
38
39// template <typename T> void record(SCVNS scv_tr_handle&, T const&) {}
40inline void record(SCVNS scv_tr_handle& h, tlm::tlm_phase const& e) { ::tlm::scc::scv::record(h, e); }
41inline void record(SCVNS scv_tr_handle& h, tlm::tlm_sync_enum e) { ::tlm::scc::scv::record(h, e); }
42void record(SCVNS scv_tr_handle& handle, tlm::nw::tlm_network_payload_base& o);
43
44namespace impl {
45template <typename TYPES> class tlm_recording_payload : public TYPES::tlm_payload_type {
46public:
47 using super = typename TYPES::tlm_payload_type;
48 SCVNS scv_tr_handle parent;
49 uint64_t id;
50 tlm_recording_payload& operator=(const super& x) {
51 super::operator=(x);
52 id = reinterpret_cast<uintptr_t>(&x);
53 return (*this);
54 }
55 explicit tlm_recording_payload(tlm::nw::tlm_base_mm_interface* mm)
56 : TYPES::tlm_payload_type(mm)
57 , parent()
58 , id(0) {}
59};
60template <typename TYPES = tlm::tlm_base_protocol_types> struct tlm_recording_types {
61 using tlm_payload_type = tlm_recording_payload<TYPES>;
62 using tlm_phase_type = typename TYPES::tlm_phase_type;
63};
64} // namespace impl
65template <typename TYPES> void record(SCVNS scv_tr_handle& h, impl::tlm_recording_payload<TYPES> const& e) {
66 record(h, static_cast<typename TYPES::tlm_payload_type const&>(e));
67}
68} // namespace scv
69} // namespace nw
70namespace scc {
71template <typename TYPES> struct tlm_mm_traits<tlm::nw::scv::impl::tlm_recording_types<TYPES>> {
72 using mm_if_type = tlm::nw::tlm_base_mm_interface;
73 using payload_base = tlm::nw::tlm_network_payload_base;
74};
75} // namespace scc
76namespace nw {
77namespace scv {
78
87template <typename TYPES>
88class tlm_recorder : public virtual tlm::nw::tlm_network_fw_transport_if<TYPES>,
89 public virtual tlm::nw::tlm_network_bw_transport_if<TYPES> {
90 std::string get_parent(char const* hier_name) {
91 std::string ret(hier_name);
92 auto pos = ret.rfind('.');
93 return pos == std::string::npos ? ret : ret.substr(0, pos);
94 }
95
96public:
97 using recording_types = impl::tlm_recording_types<TYPES>;
99 using payload_type = typename TYPES::tlm_payload_type;
100 using tlm_recording_payload = impl::tlm_recording_payload<TYPES>;
101
103 cci::cci_param<bool> enableBlTracing;
104
106 cci::cci_param<bool> enableNbTracing;
107
109 cci::cci_param<bool> enableTimedTracing{"enableTimedTracing", true};
110
112 cci::cci_param<bool> enableDmiTracing{"enableDmiTracing", false};
113
115 sc_core::sc_port_b<tlm::nw::tlm_network_fw_transport_if<TYPES>>& fw_port;
116
118 sc_core::sc_port_b<tlm::nw::tlm_network_bw_transport_if<TYPES>>& bw_port;
119
133 tlm_recorder(sc_core::sc_port_b<tlm::nw::tlm_network_fw_transport_if<TYPES>>& fw_port,
134 sc_core::sc_port_b<tlm::nw::tlm_network_bw_transport_if<TYPES>>& bw_port, bool recording_enabled = true,
135 SCVNS scv_tr_db* tr_db = SCVNS scv_tr_db::get_default_db())
136 : tlm_recorder(sc_core::sc_gen_unique_name("tlm_recorder"), fw_port, bw_port, recording_enabled, tr_db) {}
151 tlm_recorder(const char* name, sc_core::sc_port_b<tlm::nw::tlm_network_fw_transport_if<TYPES>>& fw_port,
152 sc_core::sc_port_b<tlm::nw::tlm_network_bw_transport_if<TYPES>>& bw_port, bool recording_enabled = true,
153 SCVNS scv_tr_db* tr_db = SCVNS scv_tr_db::get_default_db())
154 : enableBlTracing("enableBlTracing", recording_enabled)
155 , enableNbTracing("enableNbTracing", recording_enabled)
158 , m_db(tr_db)
159 , fixed_basename(name) {}
160
161 virtual ~tlm_recorder() override {
162 delete b_streamHandle;
163 for(auto* p : b_trHandle)
164 delete p; // NOLINT
165 delete b_streamHandleTimed;
166 for(auto* p : b_trTimedHandle)
167 delete p; // NOLINT
168 delete nb_streamHandle;
169 for(auto* p : nb_trHandle)
170 delete p; // NOLINT
171 delete nb_streamHandleTimed;
172 for(auto* p : nb_trTimedHandle)
173 delete p; // NOLINT
174 }
175
176 // TLM-2.0 interface methods for initiator and target sockets, surrounded with
177 // Tx Recording
187 tlm::tlm_sync_enum nb_transport_fw(payload_type& trans, typename TYPES::tlm_phase_type& phase, sc_core::sc_time& delay) override;
188
198 tlm::tlm_sync_enum nb_transport_bw(payload_type& trans, typename TYPES::tlm_phase_type& phase, sc_core::sc_time& delay) override;
199
208 void b_transport(payload_type& trans, sc_core::sc_time& delay) override;
215 unsigned int transport_dbg(payload_type& trans) override;
221 inline bool isRecordingBlockingTxEnabled() const { return m_db && enableBlTracing.get_value(); }
227 inline bool isRecordingNonBlockingTxEnabled() const { return m_db && enableNbTracing.get_value(); }
228
229private:
231 SCVNS scv_tr_db* m_db{nullptr};
233 SCVNS scv_tr_stream* b_streamHandle{nullptr};
235 std::array<SCVNS scv_tr_generator<sc_dt::uint64, sc_dt::uint64>*, 3> b_trHandle{{nullptr, nullptr, nullptr}};
237 SCVNS scv_tr_stream* b_streamHandleTimed{nullptr};
240 std::array<SCVNS scv_tr_generator<>*, 3> b_trTimedHandle{{nullptr, nullptr, nullptr}};
241
242 enum DIR { FW, BW, REQ = FW, RESP = BW };
244 SCVNS scv_tr_stream* nb_streamHandle{nullptr};
246 SCVNS scv_tr_stream* nb_streamHandleTimed{nullptr};
248 std::array<SCVNS scv_tr_generator<std::string, std::string>*, 2> nb_trHandle{{nullptr, nullptr}};
250 std::array<SCVNS scv_tr_generator<>*, 2> nb_trTimedHandle{{nullptr, nullptr}};
251 std::unordered_map<payload_type*, SCVNS scv_tr_handle> pending_fw_resp;
252 std::unordered_map<payload_type*, SCVNS scv_tr_handle> pending_bw_resp;
253
254public:
255 void initialize_streams() {
256 if(isRecordingBlockingTxEnabled() && !b_streamHandle) {
257 b_streamHandle = new SCVNS scv_tr_stream((fixed_basename + "_bl").c_str(), "[TLM][base-protocol][b]", m_db);
258 b_trHandle[tlm::TLM_READ_COMMAND] =
259 new SCVNS scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("read", *b_streamHandle, "start_delay", "end_delay");
260 b_trHandle[tlm::TLM_WRITE_COMMAND] =
261 new SCVNS scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("write", *b_streamHandle, "start_delay", "end_delay");
262 b_trHandle[tlm::TLM_IGNORE_COMMAND] =
263 new SCVNS scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("ignore", *b_streamHandle, "start_delay", "end_delay");
264 if(enableTimedTracing.get_value()) {
265 b_streamHandleTimed =
266 new SCVNS scv_tr_stream((fixed_basename + "_bl_timed").c_str(), "[TLM][base-protocol][b][timed]", m_db);
267 b_trTimedHandle[tlm::TLM_READ_COMMAND] = new SCVNS scv_tr_generator<>("read", *b_streamHandleTimed);
268 b_trTimedHandle[tlm::TLM_WRITE_COMMAND] = new SCVNS scv_tr_generator<>("write", *b_streamHandleTimed);
269 b_trTimedHandle[tlm::TLM_IGNORE_COMMAND] = new SCVNS scv_tr_generator<>("ignore", *b_streamHandleTimed);
270 }
271 }
272 if(isRecordingNonBlockingTxEnabled() && !nb_streamHandle) {
273 nb_streamHandle = new SCVNS scv_tr_stream((fixed_basename + "_nb").c_str(), "[TLM][base-protocol][nb]", m_db);
274 nb_trHandle[FW] =
275 new SCVNS scv_tr_generator<std::string, std::string>("fw", *nb_streamHandle, "tlm_phase", "tlm_phase[return_path]");
276 nb_trHandle[BW] =
277 new SCVNS scv_tr_generator<std::string, std::string>("bw", *nb_streamHandle, "tlm_phase", "tlm_phase[return_path]");
278 if(enableTimedTracing.get_value()) {
279 nb_streamHandleTimed =
280 new SCVNS scv_tr_stream((fixed_basename + "_nb_timed").c_str(), "[TLM][base-protocol][nb][timed]", m_db);
281 nb_trTimedHandle[FW] = new SCVNS scv_tr_generator<>("request", *nb_streamHandleTimed);
282 nb_trTimedHandle[BW] = new SCVNS scv_tr_generator<>("response", *nb_streamHandleTimed);
283 }
284 }
285 }
286
287private:
288 const std::string fixed_basename;
289 inline std::string phase2string(const tlm::tlm_phase& p) {
290 std::stringstream ss;
291 ss << p;
292 return ss.str();
293 }
294};
295
297// implementations of functions
299
300template <typename TYPES> void tlm_recorder<TYPES>::b_transport(payload_type& trans, sc_core::sc_time& delay) {
301 tlm_recording_payload* req{nullptr};
303 fw_port->b_transport(trans, delay);
304 return;
305 } else if(!b_streamHandle)
306 initialize_streams();
307 // Get a handle for the new transaction
308 SCVNS scv_tr_handle h =
309 b_trHandle[static_cast<unsigned>(trans.get_command())]->begin_transaction(delay.value(), sc_core::sc_time_stamp());
310 /*************************************************************************
311 * do the timed notification
312 *************************************************************************/
313 SCVNS scv_tr_handle bh;
314 if(b_streamHandleTimed) {
315 bh = b_trTimedHandle[static_cast<unsigned>(trans.get_command())]->begin_transaction(sc_core::sc_time_stamp() + delay);
316 bh.add_relation(rel_str(tlm::scc::scv::PARENT_CHILD), h);
317 }
318
319 if(trans.get_extension_count())
320 for(auto& extensionRecording : tlm::scc::scv::tlm_extension_recording_registry<TYPES>::inst().get())
321 if(extensionRecording)
322 extensionRecording->recordBeginTx(h, trans);
324
325 trans.get_extension(preExt);
326 if(preExt == nullptr) { // we are the first recording this transaction
327 preExt = new tlm::scc::scv::tlm_recording_extension(h, this);
328 if(trans.has_mm())
329 trans.set_auto_extension(preExt);
330 else
331 trans.set_extension(preExt);
332 } else {
333 h.add_relation(rel_str(tlm::scc::scv::PREDECESSOR_SUCCESSOR), preExt->txHandle);
334 }
335 SCVNS scv_tr_handle preTx{preExt->txHandle};
336 preExt->txHandle = h;
337 fw_port->b_transport(trans, delay);
338 if(preExt && preExt->get_creator() == this) {
339 // clean-up the extension if this is the original creator
340 trans.set_extension(static_cast<tlm::scc::scv::tlm_recording_extension*>(nullptr));
341 if(!trans.has_mm()) {
342 delete preExt;
343 }
344 } else {
345 preExt->txHandle = preTx;
346 }
347 record(h, trans);
348 if(trans.get_extension_count())
349 for(auto& extensionRecording : tlm::scc::scv::tlm_extension_recording_registry<TYPES>::inst().get())
350 if(extensionRecording)
351 extensionRecording->recordEndTx(h, trans);
352 // End the transaction
353 b_trHandle[static_cast<unsigned>(trans.get_command())]->end_transaction(h, delay.value(), sc_core::sc_time_stamp());
354 // and now the stuff for the timed tx
355 if(b_streamHandleTimed) {
356 record(bh, trans);
357 b_trTimedHandle[static_cast<unsigned>(trans.get_command())]->end_transaction(bh, sc_core::sc_time_stamp() + delay);
358 }
359}
360
361template <typename TYPES>
362tlm::tlm_sync_enum tlm_recorder<TYPES>::nb_transport_fw(payload_type& trans, typename TYPES::tlm_phase_type& phase,
363 sc_core::sc_time& delay) {
365 return fw_port->nb_transport_fw(trans, phase, delay);
366 else if(!nb_streamHandle)
367 initialize_streams();
368 /*************************************************************************
369 * prepare recording
370 *************************************************************************/
371 // Get a handle for the new transaction
372 SCVNS scv_tr_handle h = nb_trHandle[FW]->begin_transaction(phase2string(phase));
374 trans.get_extension(preExt);
375 if(preExt == nullptr) { // we are the first recording this transaction
376 preExt = new tlm::scc::scv::tlm_recording_extension(h, this);
377 if(trans.has_mm())
378 trans.set_auto_extension(preExt);
379 else
380 trans.set_extension(preExt);
381 } else {
382 // link handle if we have a predecessor
383 h.add_relation(rel_str(tlm::scc::scv::PREDECESSOR_SUCCESSOR), preExt->txHandle);
384 }
385 // update the extension
386 if(preExt)
387 preExt->txHandle = h;
388 h.record_attribute("delay", delay.to_string());
389 if(trans.get_extension_count())
390 for(auto& extensionRecording : tlm::scc::scv::tlm_extension_recording_registry<TYPES>::inst().get())
391 if(extensionRecording)
392 extensionRecording->recordBeginTx(h, trans);
393 /*************************************************************************
394 * do the timed notification
395 *************************************************************************/
396 SCVNS scv_tr_handle bh;
397 if(nb_streamHandleTimed) {
398 if(phase == tlm::nw::REQUEST || phase == tlm::nw::INDICATION) {
399 bh = nb_trTimedHandle[static_cast<unsigned>(trans.get_command())]->begin_transaction(sc_core::sc_time_stamp() + delay);
400 bh.add_relation(rel_str(tlm::scc::scv::PARENT_CHILD), h);
401 }
402 }
403 /*************************************************************************
404 * do the access
405 *************************************************************************/
406 tlm::tlm_sync_enum status = fw_port->nb_transport_fw(trans, phase, delay);
407 /*************************************************************************
408 * handle recording
409 *************************************************************************/
410 record(h, status);
411 h.record_attribute("delay[return_path]", delay.to_string());
412 record(h, trans);
413 if(trans.get_extension_count())
414 for(auto& extensionRecording : tlm::scc::scv::tlm_extension_recording_registry<TYPES>::inst().get())
415 if(extensionRecording)
416 extensionRecording->recordEndTx(h, trans);
417 // get the extension and free the memory if it was mine
418 if(status == tlm::TLM_COMPLETED || (status == tlm::TLM_UPDATED && (phase == tlm::nw::CONFIRM || phase == tlm::nw::RESPONSE))) {
419 trans.get_extension(preExt);
420 if(preExt && preExt->get_creator() == this) {
421 trans.set_extension(static_cast<tlm::scc::scv::tlm_recording_extension*>(nullptr));
422 if(!trans.has_mm()) {
423 delete preExt;
424 }
425 }
426 /*************************************************************************
427 * do the timed notification if req. finished here
428 *************************************************************************/
429 if(nb_streamHandleTimed) {
430 if(trans.get_command() == cxs::CXS_CMD::FLIT) {
431 record(bh, trans);
432 nb_trTimedHandle[static_cast<unsigned>(trans.get_command())]->end_transaction(bh, sc_core::sc_time_stamp() + delay);
433 } else {
434 auto it = pending_fw_resp.find(&trans);
435 if(it != pending_fw_resp.end()) {
436 nb_trTimedHandle[static_cast<unsigned>(trans.get_command())]->end_transaction(it->second,
437 sc_core::sc_time_stamp() + delay);
438 pending_fw_resp.erase(it);
439 } else {
440 SCCWARN(fixed_basename.c_str()) << "Could not find entry in pending_bw_resp";
441 }
442 }
443 }
444 } else if(nb_streamHandleTimed) {
445 if(status == tlm::TLM_ACCEPTED && (phase == tlm::nw::REQUEST || phase == tlm::nw::INDICATION))
446 pending_bw_resp.insert({&trans, bh});
447 }
448 // End the transaction
449 nb_trHandle[FW]->end_transaction(h, phase2string(phase));
450 return status;
451}
452
453template <typename TYPES>
454tlm::tlm_sync_enum tlm_recorder<TYPES>::nb_transport_bw(payload_type& trans, typename TYPES::tlm_phase_type& phase,
455 sc_core::sc_time& delay) {
457 return bw_port->nb_transport_bw(trans, phase, delay);
458 else if(!nb_streamHandle)
459 initialize_streams();
460 /*************************************************************************
461 * prepare recording
462 *************************************************************************/
464 trans.get_extension(preExt);
465 // sc_assert(preExt != nullptr && "ERROR on backward path");
466 // Get a handle for the new transaction
467 SCVNS scv_tr_handle h = nb_trHandle[BW]->begin_transaction(phase2string(phase));
468 // link handle if we have a predecessor and that's not ourself
469 if(preExt) {
470 h.add_relation(rel_str(tlm::scc::scv::PREDECESSOR_SUCCESSOR), preExt->txHandle);
471 // and set the extension handle to this transaction
472 preExt->txHandle = h;
473 }
474 h.record_attribute("delay", delay.to_string());
475 if(trans.get_extension_count())
476 for(auto& extensionRecording : tlm::scc::scv::tlm_extension_recording_registry<TYPES>::inst().get())
477 if(extensionRecording)
478 extensionRecording->recordBeginTx(h, trans);
479 /*************************************************************************
480 * do the timed notification (nothing to do)
481 *************************************************************************/
482 SCVNS scv_tr_handle bh;
483 if(nb_streamHandleTimed) {
484 if(trans.get_command() != cxs::CXS_CMD::FLIT) {
485 bh = nb_trTimedHandle[static_cast<unsigned>(trans.get_command())]->begin_transaction(sc_core::sc_time_stamp() + delay);
486 bh.add_relation(rel_str(tlm::scc::scv::PARENT_CHILD), h);
487 }
488 }
489 /*************************************************************************
490 * do the access
491 *************************************************************************/
492 tlm::tlm_sync_enum status = bw_port->nb_transport_bw(trans, phase, delay);
493 /*************************************************************************
494 * handle recording
495 *************************************************************************/
496 record(h, status);
497 h.record_attribute("delay[return_path]", delay.to_string());
498 record(h, trans);
499 if(trans.get_extension_count())
500 for(auto& extensionRecording : tlm::scc::scv::tlm_extension_recording_registry<TYPES>::inst().get())
501 if(extensionRecording)
502 extensionRecording->recordEndTx(h, trans);
503 // End the transaction
504 nb_trHandle[BW]->end_transaction(h, phase2string(phase));
505 if(status == tlm::TLM_COMPLETED || (status == tlm::TLM_UPDATED && (phase == tlm::nw::CONFIRM || tlm::nw::RESPONSE))) {
506 // the transaction is finished
507 if(preExt && preExt->get_creator() == this) {
508 // clean-up the extension if this is the original creator
509 trans.set_extension(static_cast<tlm::scc::scv::tlm_recording_extension*>(nullptr));
510 if(!trans.has_mm()) {
511 delete preExt;
512 }
513 }
514 /*************************************************************************
515 * do the timed notification if req. finished here
516 *************************************************************************/
517 if(nb_streamHandleTimed) {
518 if(trans.get_command() != cxs::CXS_CMD::FLIT) {
519 record(bh, trans);
520 nb_trTimedHandle[static_cast<unsigned>(trans.get_command())]->end_transaction(bh, sc_core::sc_time_stamp() + delay);
521 } else {
522 auto it = pending_bw_resp.find(&trans);
523 if(it != pending_bw_resp.end()) {
524 nb_trTimedHandle[static_cast<unsigned>(trans.get_command())]->end_transaction(it->second,
525 sc_core::sc_time_stamp() + delay);
526 pending_bw_resp.erase(it);
527 } else {
528 SCCWARN(fixed_basename.c_str()) << "Could not find entry in pending_bw_resp";
529 }
530 }
531 }
532 } else if(nb_streamHandleTimed) {
533 if(status == tlm::TLM_ACCEPTED && (phase == tlm::nw::REQUEST || phase == tlm::nw::INDICATION))
534 pending_fw_resp.insert({&trans, bh});
535 }
536 return status;
537}
538
545template <typename TYPES> unsigned int tlm_recorder<TYPES>::transport_dbg(payload_type& trans) {
546 unsigned int count = fw_port->transport_dbg(trans);
547 return count;
548}
549
550} // namespace scv
551} // namespace nw
552} // namespace tlm
553
554#endif /* _TLM_NW_SCV_RECORDER_H_ */
The TLM2 transaction recorder.
tlm::tlm_sync_enum nb_transport_bw(payload_type &trans, typename TYPES::tlm_phase_type &phase, sc_core::sc_time &delay) override
The non-blocking backward transport function.
sc_core::sc_port_b< tlm::nw::tlm_network_bw_transport_if< TYPES > > & bw_port
the port where bw accesses are forwarded to
bool isRecordingBlockingTxEnabled() const
get the current state of transaction recording
cci::cci_param< bool > enableBlTracing
the attribute to selectively enable/disable recording of blocking protocol tx
bool isRecordingNonBlockingTxEnabled() const
get the current state of transaction recording
cci::cci_param< bool > enableTimedTracing
the attribute to selectively enable/disable timed recording
cci::cci_param< bool > enableNbTracing
the attribute to selectively enable/disable recording of non-blocking protocol tx
sc_core::sc_port_b< tlm::nw::tlm_network_fw_transport_if< TYPES > > & fw_port
the port where fw accesses are forwarded to
void b_transport(payload_type &trans, sc_core::sc_time &delay) override
The blocking transport function.
cci::cci_param< bool > enableDmiTracing
the attribute to selectively enable/disable DMI recording
tlm::tlm_sync_enum nb_transport_fw(payload_type &trans, typename TYPES::tlm_phase_type &phase, sc_core::sc_time &delay) override
The non-blocking forward transport function.
unsigned int transport_dbg(payload_type &trans) override
The debug transportfunction.
generic payload extension class holding the handle of the last recorded SCV transaction
SCVNS scv_tr_handle txHandle
accessor to the SCV transaction handle.
void * get_creator()
accessor to the owner, the property is read only.
SCC TLM utilities.
SCC SCV4TLM classes and functions.
Definition cxs_tlm.h:546
SCC TLM utilities.
Definition cxs_tlm.h:544
SystemC TLM.
Definition dmi_mgr.h:19
A base class for TLM network payloads.
a tlm memory manager
Definition tlm_mm.h:330
The SystemC Transaction Level Model (TLM) Network TLM utilities.