CCI Source Architecture and Broker Internals
How the CCI reference implementation organizes brokers, handles, parameters, originators, and preset consumption.
CCI Source Architecture and Broker Internals
The SystemC Configuration, Control and Inspection (CCI) standard (IEEE 1666.1) specifies a precise architecture. Parameters register with brokers, external tools use handles, values are safely marshaled through cci_value, and every access carries originator information.
While the standard defines the interface, understanding how these components interact conceptually under the hood is critical for building robust tools.
The Broker Hierarchy
A broker implements cci::cci_broker_if. It is fundamentally a registry and policy enforcement point. It has the following responsibilities:
- Managing the lifecycle of parameters (adding, removing, finding).
- Storing unconsumed presets (configurations provided before a parameter exists).
- Providing untyped and typed handles to requesting tools.
- Managing parameter mutability and locks.
When a parameter is constructed, the CCI runtime searches the sc_core::sc_object hierarchy upwards from the parameter's parent module to find the nearest local broker. If none is found, the global fallback broker is used.
Local Brokers and cci_utils::broker
The CCI utility library provides cci_utils::broker, which can be registered locally to a specific module in the hierarchy using cci::cci_register_broker. When a local broker is registered:
- It intercepts parameter creations within its module scope.
- It can selectively hide parameters from the global broker or expose them.
- It resolves presets locally before deferring to the global broker.
Complete Example: Broker Hierarchy and Local Brokers
The following end-to-end example demonstrates how to set up a Global Broker, register a Local Broker within a subsystem, and observe how presets and parameter handles resolve through the hierarchy.
#include <systemc>
#include <cci_configuration>
#include <iostream>
#include <vector>
// 1. A Subsystem with its own local broker
class SecureSubsystem : public sc_core::sc_module {
public:
// The local broker instance
cci_utils::broker local_broker;
// Parameters managed by the local broker
cci::cci_param<int> secret_key;
cci::cci_param<int> public_id;
SC_HAS_PROCESS(SecureSubsystem);
SecureSubsystem(sc_core::sc_module_name name)
: sc_core::sc_module(name)
, local_broker("Subsystem_Broker")
, secret_key("secret_key", 0, "A hidden parameter")
, public_id("public_id", 1, "A visible parameter")
{
// Register this broker to manage all parameters in this module and below
cci::cci_register_broker(local_broker);
// Expose public_id to the parent broker, but keep secret_key strictly local
local_broker.expose.insert("top.secure_sub.public_id");
}
void print_state() {
std::cout << "[SecureSubsystem] secret_key = " << secret_key.get_value()
<< ", public_id = " << public_id.get_value() << "\n";
}
};
// 2. The Top Level Module
class Top : public sc_core::sc_module {
public:
SecureSubsystem secure_sub;
Top(sc_core::sc_module_name name)
: sc_core::sc_module(name)
, secure_sub("secure_sub")
{}
};
int sc_main(int argc, char* argv[]) {
// 3. Register the Global Broker
cci::cci_register_broker(new cci_utils::consuming_broker("Global_Broker"));
cci::cci_broker_handle global_broker = cci::cci_get_broker();
// Set presets at the global level
global_broker.set_preset_cci_value("top.secure_sub.public_id", cci::cci_value(999));
// This preset will NOT reach secret_key unless we set it directly on the local broker,
// or if the local broker was configured to pull unexposed presets from the parent.
global_broker.set_preset_cci_value("top.secure_sub.secret_key", cci::cci_value(42));
// Instantiate the hierarchy
Top top("top");
std::cout << "--- Initial State ---\n";
top.secure_sub.print_state(); // secret_key remains 0, public_id becomes 999
// 4. Inspecting via the Global Broker Handle
std::cout << "\n--- Global Broker Parameter Handles ---\n";
std::vector<cci::cci_param_untyped_handle> global_params = global_broker.get_param_handles();
for (auto& h : global_params) {
std::cout << "Global sees: " << h.name() << "\n";
}
// Note: The global broker will NOT list "top.secure_sub.secret_key" because
// it was not explicitly exposed by the local broker.
// 5. Inspecting Unconsumed Presets
std::cout << "\n--- Unconsumed Global Presets ---\n";
auto unconsumed = global_broker.get_unconsumed_preset_values();
for (const auto& preset : unconsumed) {
std::cout << "Unconsumed: " << preset.first << " (Value: " << preset.second.to_json() << ")\n";
}
// "top.secure_sub.secret_key" will be listed as unconsumed in the global broker.
sc_core::sc_start();
return 0;
}Architectural Takeaways (IEEE 1666.1)
- Isolation: Local brokers allow a module to completely isolate its internal configuration space from external tools. The global broker literally cannot see
secret_keyin the example above. - Preset Routing: Presets set on a global broker will only be consumed by local parameters if the local broker explicitly asks the parent for missing presets or exposes the parameter name to the parent.
- Handle Instantiation: When you request a handle from a broker, the broker returns a proxy object. This avoids passing raw
cci_param_if*pointers, protecting the system from dangling references if a parameter is dynamically destroyed.
Comments and Corrections