CCI Callbacks, Originators, and Tools
Pre-write and post-write callbacks, originator tracking, validation, introspection tools, and safe parameter observers.
How to Read This Lesson
CCI is about making configuration explicit and inspectable. Read every parameter as part of the platform contract, not just a convenient variable.
CCI Callbacks, Originators, and Tools
Callbacks allow a SystemC model to react when a CCI parameter changes. Originators tell the model who requested the access.
Together, they elevate CCI from a simple global variable table into a robust API capable of supporting complex Electronic Design Automation (EDA) tool flows, secure auditing, and trace generation.
Source and LRM Trail
For CCI, start with Docs/LRMs/SystemC_CCI_1_0_LRM.pdf. Then inspect .codex-src/cci/configuration/src/cci for cci_param, cci_broker_if, cci_value, originators, callbacks, and the consuming broker. The practical question is always: who owns this value, when may it change, and how can tools inspect it?
The Role of Originators
The IEEE 1666.1 standard requires every parameter modification to carry a cci::cci_originator.
- If a parameter is changed directly by a module in the SystemC hierarchy, the originator is automatically determined to be the current
sc_core::sc_object. - If an external tool (e.g., a Python script, a GUI, or a CLI parser) modifies a parameter via a handle, the tool can explicitly create a named originator (e.g.,
cci_originator("Debug_GUI")) and pass it to the setter function.
This allows models to log exactly who changed a configuration, or even reject writes from unprivileged originators in secure simulations.
Under the Hood: The cci_originator Constructor
In the C++ reference implementation, when you omit the cci_originator argument in set_value() or get_value(), the library dynamically constructs a default originator on the stack.
Inside the default constructor of cci_originator, it probes the SystemC kernel directly:
- It queries
sc_core::sc_get_current_process_handle(). If the caller is running inside anSC_THREADorSC_METHOD, it captures the exact hierarchical name of the active process. - If the simulation is not running (e.g., during elaboration or
sc_main), it falls back to inspecting the current active module scope viasc_core::sc_get_curr_simcontext()->hierarchy_curr(). - It stores a constant reference to this string or object pointer internally.
Because it is constructed extremely frequently (on every parameter access), cci_originator is optimized as a lightweight proxy class. It avoids std::string heap allocations internally, storing only pointers to existing string literals or sc_object instances already maintained by the SystemC kernel.
Complete Example: Tool Tracking via Originators and Callbacks
The following complete, compilable example demonstrates how to create custom originators, pass them through parameter handles, and use an untyped callback to generate an audit log of all configuration changes in the system.
#include <systemc>
#include <cci_configuration>
#include <iostream>
// 1. A global audit logger using an untyped callback
void audit_log_callback(const cci::cci_param_write_event<void>& ev) {
std::cout << "[AUDIT] Parameter '" << ev.param_handle.name()
<< "' changed:\n"
<< " Old Value : " << ev.old_value.to_json() << "\n"
<< " New Value : " << ev.new_value.to_json() << "\n"
<< " Changed by: " << ev.originator.name() << "\n";
}
class NetworkInterface : public sc_core::sc_module {
public:
cci::cci_param<std::string> mac_address;
SC_HAS_PROCESS(NetworkInterface);
NetworkInterface(sc_core::sc_module_name name)
: sc_core::sc_module(name)
, mac_address("mac_address", "00:00:00:00:00:00", "Device MAC")
{}
// A method simulating internal hardware behavior changing the parameter
void reset_mac_hardware() {
std::cout << "\n--- Triggering internal hardware reset ---\n";
// When set_value is called internally, the originator defaults to this sc_module.
mac_address.set_value("FF:FF:FF:FF:FF:FF");
}
};
int sc_main(int argc, char* argv[]) {
// Register broker
cci::cci_register_broker(new cci_utils::consuming_broker("Global_Broker"));
cci::cci_broker_handle broker = cci::cci_get_broker();
// Instantiate module
NetworkInterface eth0("eth0");
// 2. Attach the audit logger to the parameter
cci::cci_param_untyped_handle h_mac = broker.get_param_handle("eth0.mac_address");
h_mac.register_post_write_callback(&audit_log_callback);
// 3. Simulating an external CLI tool changing the value
std::cout << "\n--- Simulating External CLI Tool ---\n";
{
// Explicitly create an originator representing the tool
cci::cci_originator cli_originator("Command_Line_Parser");
// Pass the originator into the handle's set function
h_mac.set_cci_value(cci::cci_value("0A:1B:2C:3D:4E:5F"), cli_originator);
}
// 4. Simulating a GUI tool changing the value
std::cout << "\n--- Simulating External GUI Configurator ---\n";
{
// Explicitly create a different originator
cci::cci_originator gui_originator("Eclipse_Debug_GUI");
h_mac.set_cci_value(cci::cci_value("11:22:33:44:55:66"), gui_originator);
}
// 5. Simulating the hardware modifying itself
eth0.reset_mac_hardware();
return 0;
}Why Originators Matter for Tooling
In a 100-million-gate SoC simulation, a parameter like top.cpu0.cache.disable_prefetch might be modified by:
- The firmware writing to a memory-mapped register (Originator:
top.cpu0.iss). - A TCL script executing during elaboration (Originator:
TCL_Script_Engine). - A backend coverage tool enabling trace (Originator:
Coverage_Tool).
When debugging why the simulation performance suddenly dropped, the audit log powered by originators is the only way to prove definitively who disabled the prefetcher.
Introspection Tools
Because all parameters are registered centrally, you can build powerful introspection tools. A standard configuration dump tool will iterate over broker.get_param_handles(), querying each handle for:
name()get_cci_value().to_json()get_description()is_locked()get_value_origin().name()
This single block of introspection code can dynamically generate HTML documentation or a comprehensive JSON dump for any standard-compliant VP, without requiring custom APIs from IP vendors.
Comments and Corrections