Chapter 11: Advanced Core Semantics

Report Handler Internals

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

How to Read This Lesson

These core semantics are where experienced SystemC engineers earn their calm. We will name the scheduler rule, then show how the source enforces it.

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 get genuinely comfortable with 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.

Under the Hood: C++ Implementation in Accellera SystemC

What happens inside the Accellera sc_report_handler when you register a custom hook?

  1. The Core Pointer: sc_report_handler contains a static function pointer sc_report_handler_proc handler. By default, this points to sc_def_report_handler, which handles the standard terminal printing.
  2. Hooking: When you call sc_report_handler::set_handler(), you are overriding this static pointer. From that moment on, whenever any macro calls report(), your custom C++ function receives the fully constructed sc_report object.
  3. The sc_report Object: The sc_report class encapsulates the diagnostic message. It is designed to be exception-safe. When SC_THROW is resolved, the kernel uses throw *this; inside the sc_report context, propagating the C++ exception up the call stack until it is caught by a catch (const sc_core::sc_report& e) block in the user testbench or reaches the sc_simcontext boundaries (which then aborts).

Source and LRM Trail

Advanced core behavior should always be checked against Docs/LRMs/SystemC_LRM_1666-2023.pdf before source details. For implementation, read .codex-src/systemc/src/sysc/kernel and .codex-src/systemc/src/sysc/communication, especially the scheduler, events, object hierarchy, writer policy, report handler, and async update path.

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).

Here is a 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