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