Chapter 9: SystemC CCI

CCI Values, JSON, and Metadata

Using cci_value categories, JSON conversion, metadata maps, and tool-friendly configuration files.

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 Values, JSON, and Metadata

In a complex Virtual Platform, the configuration dataset is often managed externally using JSON files. The IEEE 1666.1 CCI standard is uniquely designed to interface seamlessly with text-based formats through the cci_value variant class and its built-in JSON conversion utilities.

Furthermore, parameters can store arbitrary Metadata—extra facts about the parameter that are useful for configuration tools but not strictly required by the C++ simulation logic.

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?

Why cci_value Exists

A GUI, a command-line parser, or a JSON file cannot hold a C++ template instance like cci_param<int>&. External tools require a dynamic, type-erased value format. cci::cci_value provides that bridge, offering standard categories:

  • Null (cci::CCI_NULL_VALUE)
  • Boolean (cci::CCI_BOOL_VALUE)
  • Integer / Real (cci::CCI_INTEGRAL_VALUE, cci::CCI_REAL_VALUE)
  • String (cci::CCI_STRING_VALUE)
  • Lists and Maps (cci::CCI_LIST_VALUE, cci::CCI_DICT_VALUE)

Attaching Metadata

Metadata is a dictionary (map) of cci_value instances attached to a parameter. A modeler can expose facts such as:

  • units: "ns", "bytes", "Hz"
  • valid ranges: minimum and maximum bounds.
  • UI categories: "performance_knobs", "address_map"
  • Documentation links: URLs pointing to hardware specifications.

Metadata makes the model "tool-friendly," allowing a GUI to automatically render a slider between min and max limits without hardcoding those rules in the GUI itself.

Under the Hood: The Metadata Map

In the Accellera implementation, metadata is not a special class; it leverages the exact same cci_value AST engine used for configuration values. Inside the cci_param_untyped base class, there is a dedicated std::map<std::string, cci_value> m_metadata (or a cci_value object initialized to the CCI_DICT_VALUE category).

When a module calls baud_rate.add_metadata("unit", cci::cci_value("bps")), it inserts this key-value pair directly into the parameter's internal dictionary. When an external tool calls h_baud.get_metadata(), the API returns a copy of this cci_value dictionary. Because it returns a standard cci_value, an external tool can easily call get_metadata().to_json() to instantly dump the entire metadata schema as a JSON object.

It is important to note that metadata is designed to be defined by the model owner during instantiation. External tools retrieving handles receive metadata by value (or const reference), preventing them from arbitrarily mutating the parameter's structural documentation at runtime.

Complete Example: JSON Parsers and Metadata Extraction

The following complete example demonstrates how to attach metadata to a parameter, and simulates a top-level JSON configuration tool that parses a JSON string, applies presets, and extracts metadata for a mock User Interface.

#include <systemc>
#include <cci_configuration>
#include <iostream>
#include <string>
 
class UARTController : public sc_core::sc_module {
public:
    cci::cci_param<int> baud_rate;
    cci::cci_param<bool> enable_parity;
 
    SC_HAS_PROCESS(UARTController);
    UARTController(sc_core::sc_module_name name)
        : sc_core::sc_module(name)
        , baud_rate("baud_rate", 9600, "Baud rate of the UART")
        , enable_parity("enable_parity", false, "Enable parity checking")
    {
        // Attach Metadata to assist external tools
        baud_rate.add_metadata("unit", cci::cci_value("bps"));
        baud_rate.add_metadata("min", cci::cci_value(1200));
        baud_rate.add_metadata("max", cci::cci_value(115200));
        baud_rate.add_metadata("ui_category", cci::cci_value("Timing"));
    }
 
    void print_status() {
        std::cout << "[UART] Initialized with Baud: " << baud_rate.get_value() 
                  << ", Parity: " << (enable_parity.get_value() ? "ON" : "OFF") << "\n";
    }
};
 
// Mock function simulating an external JSON Configuration Tool
void run_json_configurator(cci::cci_broker_handle broker) {
    std::cout << "--- JSON Tool Loading Presets ---\n";
    
    // A mock JSON string read from a config file
    std::string config_json = R"({
        "baud": 115200,
        "parity": true
    })";
 
    // 1. Parse JSON into a cci_value map
    cci::cci_value parsed = cci::cci_value::from_json(config_json);
    
    if (parsed.is_map()) {
        cci::cci_value::const_map_reference vmap = parsed.get_map();
        
        // 2. Set presets dynamically based on the JSON keys
        if (vmap.has_entry("baud")) {
            broker.set_preset_cci_value("top.uart.baud_rate", vmap.at("baud"));
            std::cout << "Set preset for baud_rate from JSON.\n";
        }
        if (vmap.has_entry("parity")) {
            broker.set_preset_cci_value("top.uart.enable_parity", vmap.at("parity"));
            std::cout << "Set preset for enable_parity from JSON.\n";
        }
    }
}
 
// Mock function simulating a GUI extracting Metadata to draw a slider
void run_gui_metadata_extractor(cci::cci_broker_handle broker) {
    std::cout << "\n--- GUI Tool Extracting Metadata ---\n";
    
    cci::cci_param_untyped_handle h_baud = broker.get_param_handle("top.uart.baud_rate");
    
    if (h_baud.is_valid()) {
        // Extract the metadata map
        cci::cci_value meta_val = h_baud.get_metadata();
        
        if (meta_val.is_map()) {
            cci::cci_value::const_map_reference meta = meta_val.get_map();
            
            std::cout << "Rendering UI for '" << h_baud.name() << "':\n";
            std::cout << "  Description: " << h_baud.get_description() << "\n";
            
            if (meta.has_entry("min") && meta.has_entry("max")) {
                std::cout << "  -> Drawing Slider from " 
                          << meta.at("min").get_int() << " to " 
                          << meta.at("max").get_int();
                
                if (meta.has_entry("unit")) {
                    std::cout << " " << meta.at("unit").get_string();
                }
                std::cout << "\n";
            }
        }
    }
}
 
int sc_main(int argc, char* argv[]) {
    cci::cci_register_broker(new cci_utils::consuming_broker("Global_Broker"));
    cci::cci_broker_handle broker = cci::cci_get_broker();
 
    // 1. External tool runs BEFORE module instantiation to set presets
    run_json_configurator(broker);
 
    // 2. Hierarchy is elaborated. Modules consume presets.
    struct Top : public sc_core::sc_module {
        UARTController uart;
        Top(sc_core::sc_module_name n) : sc_core::sc_module(n), uart("uart") {}
    };
    Top top("top");
 
    // 3. Print the internal state to verify JSON was applied
    std::cout << "\n--- Internal State ---\n";
    top.uart.print_status();
 
    // 4. GUI tool runs to extract metadata
    run_gui_metadata_extractor(broker);
 
    return 0;
}

Conversion Failures

When using cci_value::from_json(), invalid JSON syntax will throw a cci::cci_value_failure exception. When extracting data from a cci_value (e.g., using .get_int()), if the underlying variant does not hold an integer, an exception is also thrown.

The LRM specifies that cci_value operations strictly refuse implicit type coercion (e.g., extracting "123" as an integer). When writing tools, treat conversion failures as user-facing configuration errors, catch the exceptions gracefully, and report the specific JSON formatting mistake to the user, rather than allowing the SystemC kernel to crash.

Comments and Corrections