Chapter 6: Practice

Reports, Errors, and Debugging

Using SC_REPORT_INFO, warnings, errors, hierarchy names, assertions, and debug patterns.

How to Read This Lesson

Treat this as engineering practice, not trivia. The patterns here are the ones that keep large models understandable after the original author has moved on.

Debuggability is part of the model. A virtual platform that fails with a vague C++ exception is harder to trust than one that reports the module, transaction, address, and simulated time. The IEEE 1666 LRM provides a comprehensive sc_report_handler utility for exactly this purpose.

Source and LRM Trail

Practice lessons should still cite their roots. Use Docs/LRMs/SystemC_LRM_1666-2023.pdf for behavior, .codex-src/systemc for the reference kernel, and .codex-src/systemc-common-practices for reusable modeling patterns. The goal is to turn standard rules into habits that survive real project scale.

End-to-End Reporting Example

Here is a full compilable example demonstrating SC_REPORT_INFO, SC_REPORT_WARNING, SC_REPORT_ERROR, named object tracking, and even configuring the global report handler to stop simulation on specific errors.

#include <systemc>
#include <iomanip>
#include <sstream>
 
using namespace sc_core;
 
SC_MODULE(UartModel) {
  SC_CTOR(UartModel) {
    SC_THREAD(run);
  }
 
  void run() {
    // 1. Basic Info
    SC_REPORT_INFO(name(), "UART Initialization Complete");
    wait(10, SC_NS);
 
    // 2. Warning with context
    SC_REPORT_WARNING(name(), "Buffer nearing capacity!");
    wait(10, SC_NS);
 
    // 3. Formatted Error using string stream
    uint32_t bad_address = 0xFFFFFFFF;
    std::ostringstream msg;
    msg << "Access to unmapped address 0x" 
        << std::hex << bad_address 
        << " at time " << sc_time_stamp();
        
    SC_REPORT_ERROR("AddressDecoder", msg.str().c_str());
  }
};
 
int sc_main(int argc, char* argv[]) {
  // Configure the report handler to abort simulation on the first error
  sc_report_handler::set_actions(SC_ERROR, SC_DISPLAY | SC_ABORT);
 
  UartModel uart("uart");
  sc_start(100, SC_NS);
 
  return 0;
}

SystemC Reports

SystemC provides standard report macros. Reports include severity (INFO, WARNING, ERROR, FATAL) and can include source location depending on configuration.

Use categories (the first argument to SC_REPORT_INFO) consistently so users can filter logs via sc_report_handler::set_actions().

Include Time and Path

Named hierarchy is one of SystemC's strengths. Use it. name() gives the hierarchical object name (e.g. top.bus.uart). This makes messages actionable in large systems.

Assertions

Prefer explicit SC_REPORT_ERROR or SC_REPORT_FATAL for model/user mistakes because they can carry context, time, and hierarchy strings. Plain assert() is acceptable for internal developer invariants, but it crashes the simulator abruptly, preventing graceful log flushes or user-defined intercepts.

Transaction Debugging

For TLM, log the complete transaction shape. This reveals whether a bug is in address decode, target behavior, timing, byte enables, or response propagation.

Delta-Cycle Debugging

If a signal-based model behaves one event late, print both sc_time_stamp() and relevant values before and after wait(SC_ZERO_TIME). Do not leave noisy debug logs enabled by default. Add a verbosity flag or compile-time switch.

Under the Hood: sc_report_handler

The macro SC_REPORT_INFO(msg_type, msg) expands into a call to sc_core::sc_report_handler::report(). In sysc/utils/sc_report_handler.cpp, the kernel maintains a registry of message types. When a report is triggered, it checks the configured severity (SC_INFO, SC_WARNING, SC_ERROR, SC_FATAL) and looks up the action (e.g., SC_LOG, SC_DISPLAY, SC_STOP, SC_ABORT). If the action is SC_ABORT, it calls std::abort(). If it is SC_THROW, it throws an sc_report exception. You can completely customize this behavior by overriding the report handler, directing logs to a custom GUI, a file, or a network socket.

Comments and Corrections