CCI Brokers and Architecture
Deep dive into SystemC CCI Brokers, handling parameter registration, global vs local brokers, and preset values.
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 Brokers and Architecture
The SystemC Configuration, Control and Inspection (CCI) standard (IEEE 1666.1) introduces a powerful, standardized mechanism for configuring models. At the heart of this architecture lies the relationship between Parameters and Brokers.
If you think of a parameter as a typed variable bound to a hierarchical name, the broker is the centralized registry that manages its existence, access control, and initial configuration. It is responsible for bridging the gap between the internal SystemC module hierarchy and external tools or top-level configuration scripts.
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?
What is a Broker?
A broker (implementing cci::cci_broker_if) is an object that aggregates parameters. It provides container behaviors such as finding parameters by name, enumerating all known parameters, and managing preset values.
Rather than interacting with the raw broker interface, applications and models use a cci::cci_broker_handle. This handle acts as a proxy, safely routing requests to the underlying broker while injecting cci::cci_originator information that represents the caller.
Global vs Local Brokers
CCI defines a hierarchy of brokers that typically mirrors the sc_core::sc_module hierarchy. The LRM defines three primary layers of broker interaction:
- Global Broker: The ultimate fallback. There is exactly one global broker in the simulation, and it sits at the top level, above any
sc_modulehierarchy. It must be registered before any parameters or local brokers are created. If you fail to register a global broker before instantiating a parameter, the CCI library will enforce a default behavior, but explicit registration is strongly recommended. - Local Brokers: Modules can define their own "local" brokers to hide or manage parameters private to a sub-assembly. By registering a broker to a specific module, all parameters instantiated within that module (and its children) will default to communicating with the local broker.
- Automatic Broker Resolution: When a parameter is created inside an
sc_module, it registers with the "automatic" broker. The CCI runtime finds this broker by walking up thesc_core::sc_objecthierarchy until a local broker is found. If none is found, the global broker takes responsibility.
C++ Source Code Implementation Details: cci_broker_if and cci_originator
Under the hood, every broker must implement the pure virtual interface cci_broker_if. This interface defines the core contract for parameter management:
add_param(cci_param_if*)andremove_param(cci_param_if*): The runtime hooks where parameters inject themselves into the broker's maps upon construction and destruction.get_param_handle(name): Returns a type-erasedcci_param_handlereferencing the underlyingcci_param_if.get_preset_cci_value(name): Retrieves thecci_valueJSON-like AST payload.
When using a cci_broker_handle, the handle secretly injects a cci_originator into every call routed to the cci_broker_if. The originator is a critical security and traceability mechanism in CCI.
In the accellera-official/cci repository, when a cci_originator is constructed without arguments, it inspects the active simulation context via sc_core::sc_get_current_process_handle(). If a process is currently executing, the originator captures the hierarchical name of the process. If it is constructed during elaboration (e.g., in a module constructor), it captures the module's name. The broker uses this originator to determine if a specific entity has the permissions to lock or mutate a parameter.
Initializing Parameters with Presets
One of the most important jobs of a broker is to manage preset values. A preset allows an external tool, testbench, or top-level script to define the value of a parameter before the parameter even exists.
When a parameter is finally constructed by its owning module, it queries its broker. If the broker has a preset value for that parameter's hierarchical name, the parameter adopts the preset value instead of its hardcoded constructor default.
Unconsumed Presets
A common configuration error is a typo in the hierarchical name of a preset value. Because presets are stored in the broker until claimed by a newly constructed parameter, a preset with a typo will simply sit in the broker, unconsumed, while the target parameter silently falls back to its default value.
Brokers provide a way to detect this via get_unconsumed_preset_values(). This is typically checked at the end_of_elaboration() or start_of_simulation() phase to catch typos before runtime.
Complete Example: Brokers and Presets
The following complete, compilable example demonstrates how to set up the global broker, apply preset values, instantiate a module with parameters, and check for unconsumed presets (e.g., due to typos).
#include <systemc>
#include <cci_configuration>
#include <iostream>
// A simple module that defines CCI parameters
class MySubsystem : public sc_core::sc_module {
public:
// CCI Parameters
cci::cci_param<int> buffer_size;
cci::cci_param<bool> enable_logging;
SC_HAS_PROCESS(MySubsystem);
MySubsystem(sc_core::sc_module_name name)
: sc_core::sc_module(name)
// Initialize parameters with default values
, buffer_size("buffer_size", 256, "Size of the internal buffer")
, enable_logging("enable_logging", false, "Enable verbose logging")
{
SC_METHOD(print_config);
}
void print_config() {
std::cout << "[MySubsystem] " << name() << " Configuration:\n"
<< " buffer_size = " << buffer_size.get_value() << "\n"
<< " enable_logging = " << (enable_logging.get_value() ? "true" : "false")
<< std::endl;
}
};
int sc_main(int argc, char* argv[]) {
// 1. Register the global broker before any modules or parameters are created.
// cci_utils::consuming_broker is the standard implementation provided by the library.
cci::cci_register_broker(new cci_utils::consuming_broker("Global_Broker"));
// 2. Obtain a handle to the global broker
cci::cci_broker_handle global_broker = cci::cci_get_broker();
// 3. Set preset values for parameters that do not exist yet.
// The hierarchical path includes the module instance name ("subsystem").
global_broker.set_preset_cci_value("subsystem.buffer_size", cci::cci_value(1024));
// We intentionally introduce a typo here to demonstrate unconsumed presets:
global_broker.set_preset_cci_value("subsystem.enable_loging", cci::cci_value(true)); // Typo!
// 4. Instantiate the module.
// It will consume the "subsystem.buffer_size" preset automatically.
MySubsystem subsystem("subsystem");
// 5. Check for unconsumed presets before starting simulation.
// This is a crucial step for catching typos in configuration files or scripts.
std::vector<std::pair<std::string, cci::cci_value>> unconsumed =
global_broker.get_unconsumed_preset_values();
for (const auto& preset : unconsumed) {
SC_REPORT_WARNING("CCI",
("Unconsumed preset value detected for parameter path: " + preset.first).c_str());
}
// 6. Start simulation
sc_core::sc_start(1, sc_core::SC_NS);
return 0;
}Explanation of the Flow
cci_register_broker: Installs aconsuming_brokeras the global fallback.set_preset_cci_value: The broker records that if a parameter named"subsystem.buffer_size"is ever created, it should take the value1024instead of its hardcoded default.- Instantiation:
MySubsystemis instantiated. Whenbuffer_sizeis constructed, it queries the broker, finds the preset, and sets its initial value to1024. - Typo Detection: The preset
"subsystem.enable_loging"contains a typo. The parameterenable_loggingdoes not match, so it uses its default (false). The typo is caught by callingget_unconsumed_preset_values()and explicitly issuing anSC_REPORT_WARNING.
In the next tutorial, we will look closer at the cci_param class itself and how parameters manage mutability and originators.
Comments and Corrections