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?
- The Core Pointer:
sc_report_handlercontains a static function pointersc_report_handler_proc handler. By default, this points tosc_def_report_handler, which handles the standard terminal printing. - Hooking: When you call
sc_report_handler::set_handler(), you are overriding this static pointer. From that moment on, whenever any macro callsreport(), your custom C++ function receives the fully constructedsc_reportobject. - The
sc_reportObject: Thesc_reportclass encapsulates the diagnostic message. It is designed to be exception-safe. WhenSC_THROWis resolved, the kernel usesthrow *this;inside thesc_reportcontext, propagating the C++ exception up the call stack until it is caught by acatch (const sc_core::sc_report& e)block in the user testbench or reaches thesc_simcontextboundaries (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:
- Macro Expansion: The macro captures the file name (
__FILE__) and line number (__LINE__). - Object Creation: It instantiates an
sc_core::sc_reportobject containing the severity, message type ("ROUTER"), message text, file, and line. - Handler Dispatch: The global
sc_core::sc_report_handler::report()function is invoked. - Action Resolution: The handler looks up the configured
sc_actionfor the specific message type or severity. - 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_STOPorSC_ABORT. - 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