Chapter 9: SystemC CCI

CCI Values, JSON, and Metadata

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

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.

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.

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