Chapter 9: SystemC CCI

CCI Callbacks, Originators, and Tools

Pre-write and post-write callbacks, originator tracking, validation, introspection tools, and safe parameter observers.

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.

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.

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:

  1. The firmware writing to a memory-mapped register (Originator: top.cpu0.iss).
  2. A TCL script executing during elaboration (Originator: TCL_Script_Engine).
  3. 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