Chapter 7: SystemC 1666-2023 LRM

LRM Bridge: Reports, Tracing, and Introspection

Reporting severity, report handlers, VCD tracing, object hierarchy, attributes, version information, and diagnostics.

Listen to this lessonAudiobook mode

How to Read This Lesson

This lesson is an LRM bridge. We translate standard language into the questions you actually ask while debugging and reviewing models.

Diagnostics are a formalized part of the SystemC standard surface. Standardizing error reporting and trace generation ensures interoperability between different IP vendors and testbench infrastructures. Now let's look at how the Accellera kernel implements these facilities natively.

Standard and source context

End-to-End Tracing and Reporting Example

Here is a complete example demonstrates the SC_REPORT macros, custom message types, and generating a standard Value Change Dump (VCD) trace file.

#include <systemc>
 
SC_MODULE(DiagnosticsDemo) {
    sc_core::sc_in<bool> clk;
    sc_core::sc_signal<uint32_t> counter{"counter"};
 
    SC_CTOR(DiagnosticsDemo) {
        SC_THREAD(logic_thread);
        sensitive << clk.pos();
    }
 
    void logic_thread() {
        uint32_t val = 0;
        while(true) {
            wait();
            val++;
            counter.write(val);
            
            // Standard reporting
            SC_REPORT_INFO("DiagnosticsDemo", "Counter incremented.");
 
            if (val == 3) {
                // Warning with a custom message type ID
                SC_REPORT_WARNING("LIMIT_CHECK", "Counter reached 3. Approaching limit.");
            }
            if (val == 5) {
                // Error report. By default, SC_REPORT_ERROR will stop the simulation.
                SC_REPORT_ERROR("LIMIT_CHECK", "Counter overflowed the simulated limit!");
            }
        }
    }
};
 
int sc_main(int argc, char* argv[]) {
    sc_core::sc_clock clk("clk", 10, sc_core::sc_time_unit::SC_NS);
    
    DiagnosticsDemo demo("demo");
    demo.clk(clk);
 
    // 1. Create a VCD trace file
    sc_core::sc_trace_file* tf = sc_core::sc_create_vcd_trace_file("wave_trace");
    
    // 2. Trace signals
    sc_core::sc_trace(tf, clk, "System_Clock");
    sc_core::sc_trace(tf, demo.counter, "Counter_Value");
 
    std::cout << "Starting simulation. This will terminate automatically on SC_REPORT_ERROR." << std::endl;
    
    // 3. Start Simulation
    sc_core::sc_start(100, sc_core::SC_NS);
 
    // 4. Close the trace file gracefully
    sc_core::sc_close_vcd_trace_file(tf);
 
    return 0;
}

Reports and Handling Policies

Never use std::cout or printf for structural warnings or errors, and never call std::exit() from inside a reusable IP block.

Use the standard macros:

  • SC_REPORT_INFO(msg_type, msg)
  • SC_REPORT_WARNING(msg_type, msg)
  • SC_REPORT_ERROR(msg_type, msg): By default, throws an exception and halts the scheduler.
  • SC_REPORT_FATAL(msg_type, msg): Immediately aborts execution entirely.

Under the Hood (Accellera Kernel): When you use a macro like SC_REPORT_WARNING, it generates a sc_report object containing the file, line number, time, and message. This object is passed to a global singleton sc_report_handler::report(). The handler looks up the sc_actions bitmask configured for that specific msg_type and severity. If the action bitmask includes SC_THROW, it literally throws an sc_report exception. If it contains SC_ABORT, it calls std::abort().

Report Handlers

The simulation environment (often the sc_main testbench) can override these default actions. An application can configure all warnings of msg_type "LIMIT_CHECK" to be suppressed using sc_report_handler::set_actions("LIMIT_CHECK", SC_WARNING, SC_DO_NOTHING), or demote an SC_REPORT_ERROR to merely increment an internal counter rather than halting the simulation.

Tracing

Tracing records signal activity into a file.

auto* tf = sc_create_vcd_trace_file("wave");
sc_trace(tf, signal, "signal_name");

Trace files must be created before sc_start() is called, and gracefully closed (sc_close_vcd_trace_file) at the end of sc_main().

Under the Hood: sc_create_vcd_trace_file() instantiates a vcd_trace_file object and pushes it into the sc_simcontext::m_trace_files vector. During simulation, how do values get dumped? Inside the sc_simcontext::crunch() loop, after the Evaluate and Update phases have finished for the current delta cycle, the kernel iterates over m_trace_files and calls their internal cycle(true) virtual method. This method iterates over all traced signals. If the value has changed since the last delta, it writes the formatted string mapping (e.g., b1011 $n) to the file stream.

Object Hierarchy

SystemC objects know their names and hierarchy. Hierarchy is how a large model remains navigable and debuggable. You can always query an object's location in the elaboration tree via this->name() (e.g., top.router_0.timer_1).

Under the Hood: Every sc_module and sc_signal derives from sc_object. During construction, they register themselves with sc_simcontext::m_object_manager. This manager links them into a massive tree structure mapping parent objects to their children, enabling tools to introspect the entire topology dynamically.

Source-reading checkpoint

For reports and tracing, start in the Accellera SystemC GitHub repository around report-handler and trace-file code, then follow the sc_object names surfaced by introspection.

Lesson self-check

Can you answer these clearly?

Keep moving when you can answer each question without looking back at the lesson.

Comments and Corrections