Chapter 11: Advanced Core Semantics

Report Handler Internals

How SC_REPORT macros become sc_report objects, actions, severity counts, cached reports, and simulation control.

Report Handler Internals

Reports are the diagnostic backbone of a serious SystemC model. std::cout is fine for quick hacks, but a reusable, LRM-compliant model must use SC_REPORT_INFO, SC_REPORT_WARNING, SC_REPORT_ERROR, and SC_REPORT_FATAL.

To truly master SystemC reporting, you need to understand how the sc_core::sc_report_handler translates a macro call into an actionable event, how severity limits work, and how to write custom report handlers.

The Standard LRM Report Flow

When you call SC_REPORT_WARNING("ROUTER", "Unmapped access");, the LRM dictates the following sequence:

  1. Macro Expansion: The macro captures the file name (__FILE__) and line number (__LINE__).
  2. Object Creation: It instantiates an sc_core::sc_report object containing the severity, message type ("ROUTER"), message text, file, and line.
  3. Handler Dispatch: The global sc_core::sc_report_handler::report() function is invoked.
  4. Action Resolution: The handler looks up the configured sc_action for the specific message type or severity.
  5. Limit Checking: The handler increments the count for that severity/type. If the count exceeds the configured limit (e.g., maximum 10 errors allowed), the handler forces an SC_STOP or SC_ABORT.
  6. Execution: The resolved actions (SC_LOG, SC_DISPLAY, SC_THROW, etc.) are executed.

Verbosity Limits

SystemC also supports verbosity levels (from SC_NONE to SC_DEBUG). You can filter out low-level debug messages dynamically without recompiling:

sc_core::sc_report_handler::set_verbosity_level(sc_core::SC_HIGH);
// This will be suppressed if verbosity is set below SC_DEBUG
SC_REPORT_INFO_VERB("CACHE", "Cache hit on line 4", sc_core::SC_DEBUG);

Severity Limits and Quotas

You can instruct the kernel to automatically stop the simulation if too many warnings or errors occur:

// Stop simulation after 5 errors
sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, sc_core::SC_DISPLAY | sc_core::SC_STOP);
sc_core::sc_report_handler::stop_after(sc_core::SC_ERROR, 5);

Complete Example: Custom Report Handler

The ultimate power of the sc_report_handler is the ability to bypass the default printing mechanism entirely and install a custom handler hook. This is heavily used in professional verification environments (like UVM-SystemC) to pipe SystemC reports into external logging frameworks (like JSON loggers or Python test runners).

This complete sc_main demonstrates setting up a custom report handler.

#include <systemc>
#include <iostream>
#include <string>
 
// 1. Define a custom handler function matching the LRM signature
void custom_json_report_handler(const sc_core::sc_report& rep, const sc_core::sc_actions& actions) {
    // Only process if the action dictates display or log
    if (actions & sc_core::SC_DISPLAY) {
        std::cout << "{ \"severity\": \"";
        switch (rep.get_severity()) {
            case sc_core::SC_INFO:    std::cout << "INFO"; break;
            case sc_core::SC_WARNING: std::cout << "WARNING"; break;
            case sc_core::SC_ERROR:   std::cout << "ERROR"; break;
            case sc_core::SC_FATAL:   std::cout << "FATAL"; break;
        }
        std::cout << "\", \"type\": \"" << rep.get_msg_type() << "\""
                  << ", \"time\": \"" << rep.get_time() << "\""
                  << ", \"file\": \"" << rep.get_file_name() << "\""
                  << ", \"line\": " << rep.get_line_number()
                  << ", \"message\": \"" << rep.get_msg() << "\" }\n";
    }
 
    // Always respect the SC_STOP and SC_ABORT actions in a custom handler!
    if (actions & sc_core::SC_STOP) {
        sc_core::sc_stop();
    }
    if (actions & sc_core::SC_ABORT) {
        std::abort();
    }
    if (actions & sc_core::SC_THROW) {
        throw rep;
    }
}
 
SC_MODULE(CustomLoggerDemo) {
    SC_CTOR(CustomLoggerDemo) {
        SC_THREAD(run);
    }
    
    void run() {
        wait(10, sc_core::SC_NS);
        SC_REPORT_INFO("SYSTEM", "Booting kernel...");
        
        wait(15, sc_core::SC_NS);
        SC_REPORT_WARNING("MEM", "Memory usage at 90%");
        
        wait(5, sc_core::SC_NS);
        SC_REPORT_ERROR("BUS", "Timeout on AHB bus transaction!");
    }
};
 
int sc_main(int argc, char* argv[]) {
    // 2. Install the custom handler hook
    sc_core::sc_report_handler::set_handler(custom_json_report_handler);
 
    // Ensure errors don't throw, but just display (which routes to our JSON handler)
    sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, sc_core::SC_DISPLAY);
 
    CustomLoggerDemo demo("demo");
 
    sc_core::sc_start();
    
    return 0;
}

Explanation of the Execution

When run, instead of the standard SystemC text output, the custom handler generates structured JSON logs:

{ "severity": "INFO", "type": "SYSTEM", "time": "10 ns", "file": "main.cpp", "line": 42, "message": "Booting kernel..." }
{ "severity": "WARNING", "type": "MEM", "time": "25 ns", "file": "main.cpp", "line": 45, "message": "Memory usage at 90%" }
{ "severity": "ERROR", "type": "BUS", "time": "30 ns", "file": "main.cpp", "line": 48, "message": "Timeout on AHB bus transaction!" }

By hooking into sc_report_handler, you have complete control over how diagnostics are formatted and routed, allowing seamless integration with CI/CD pipelines and external databases.

Comments and Corrections