User guide
Integration
-
Any simulator with extensive SystemVerilog OOP support required
-
No external dependencies
-
No defines
-
No plusargs
The package is developed and tested using Verilator 5.24 only. Support of other simulators is planned, but Verilator still will be the main simulator for project as it is the only viable option to organize CI and opensource flow. |
Sources and include directories are expressed as filelist (.f
file), which is quite standard way of describing compilation unit for many EDA tools. Filelist of the project is src/filelist.f
.
In order to make filelist portable, all paths are relative to SVJSON_ROOT
environment variable. It should point to svjson repository root in your filesystem.
As a result, integration process consists of several simple steps:
-
Clone or copy svjson repository
-
Set environment variable
SVJSON_ROOT
with a path to the repository root on your filesystem -
Add filelist to your simulator compilation options, e.g.
-f ${SVJSON_ROOT}/src/filelist.f
-
json_pkg
is ready to be compiled and used
JSON Values
According to the JSON specification there are 6 JSON value types.
The implementation follows the specification - these types are represented as a special wrapper classes of underlying SV types. However, there are some nuances. Summary table is below.
JSON value | Class | Underlying SV type | Note |
---|---|---|---|
- |
- |
Generic JSON value. Base class for all other values. |
|
Object |
Associative array |
Class representation of JSON object. |
|
Array |
Queue |
Class representation of JSON array. |
|
Number |
|
Class representation of JSON integer number. |
|
|
Class representation of JSON real number. |
||
Bool |
|
Class representation of JSON bool. |
|
String |
|
Class representation of JSON string. |
|
Parameterized enum |
Class is inherited from JSON string. Can be used to convert custom enum type to and from string during JSON manipulations. |
||
Parameterized bit vector |
Class is inherited from JSON string. Can be used to convert bit vector of custom width to and from string during JSON manipulations. This allows to use numbers of any width represented as strings in JSON. |
||
Null |
- |
|
There is no special class to represent JSON null. Native |
Inheritance tree for JSON values is shown below.
Base Value
Class json_value
is a base abstract class for all JSON values. This class is mainly used for polymorphism goals - to represent any value while decoding/encoding JSON. It has no parameters or attributes and almost all of its methods are for introspection and convenient casting to one of concrete classes.
|
Perform compare with another instance. Return 1 if instances are equal and 0 otherwise. |
|
Create a deep copy of an instance. |
|
Check if current instance is specified type. |
|
Try to cast to specified concrete class. |
|
Cast to specified concrete class and throw fatal in case of failure. |
|
Another option of trying to cast to specified concrete class. In this case, instance is an |
Object
json_object
wrapper class represens standard JSON object value using SV string
-indexed associative array of json_value
. The class basically wraps standard SV associative array methods with some additional methods required to operate as JSON value.
No additional checks are implemented for "out-of-range" accesses and similar, so you can expect that this class will operate according to behavior of an original underlying SV associative array.
SystemVerilog associative array is used to implement JSON object. As a consequence, all keys are stored in a lexicographical order (IEEE IEEE-1800-2023, ch. 7.8.2) and original order of keys within source JSON is lost. This also affects encoder, so it always prints keys in a lexicographical order. |
|
Create an instance from an associative array. |
|
Static method to create an instance from an associative array. Alternative to standard constructor. |
|
Get a value at the provided key. |
|
Set the given value for the provided key. |
|
Get all internal values as associative array. |
|
Get all keys for internal values as queue. |
|
Remove all stored values. |
|
Thin wrappers over the standard associative array methods which also mimic their signature and return values. |
Array
json_array
wrapper class represens standard JSON array value using SV queue of json_value
.
The class basically wraps standard SV queue methods with some additional methods required to operate as JSON value.
No additional checks are implemented for "out-of-range" accesses and similar, so you can expect that this class will operate according to behavior of an original underlying SV queue.
|
Create an instance from a queue. |
|
Static method to create an instance from a queue. Alternative to standard constructor. |
|
Get a value at the provided index. |
|
Set the given value for the provided index. |
|
Get all internal values as queue. |
|
Remove all stored values. |
|
Thin wrappers over the standard queue methods which also mimic their signature and return values. |
String
json_string
wrapper class represens standard JSON string value type using SV string.
\b and \u escape sequences are not supported.
|
|
Create an instance from a string. |
|
Static method to create an instance from a string. Alternative to standard constructor. |
|
Get internal string value. |
|
Set internal string value. |
Extension: Enum
json_enum#(ENUM_T)
wrapper class, that inherits from json_string
and represens SV enum
value as standard JSON string.
The class is parametrized with type ENUM_T
to work with any enumeration.
Purpose of this class is to facilitate using SV enum with JSON decoder/encoder. For example, JSON values tree can be created with json_enum
instances and then they can be seamlessly converted to strings during encoding. And vice versa for decoding.
|
Create an instance from an |
|
Static method to create an instance from an |
json_result#(json_enum#(ENUM_T)) try_from(string value) |
Static method to create an instance from a |
|
Get internal enum value as a string. |
|
Set internal enum value from a string. This function may fail due to wrong value is provided, and this fail is unrecoverable (fatal). |
|
Get internal enum value. |
|
Set internal enum value. |
Extension: Bit Vector
json_bits#(BITS_T)
wrapper class, that inherits from json_string
and represens SV bit vector value (e.g. bit[511:0]
) as standard JSON string.
The class is parametrized with type BITS_T
to work with any bit vector - any width, signed or unsigned. Packed structures can be used as well.
Purpose of this class is to facilitate using SV bit vectors of arbitrary size with JSON decoder/encoder.
As a result, any number, that cannot be represented as JSON number using longint
or real
, can be represented as a string.
There is an internal property preferred_radix
, that can take values: json_bits::RADIX_DEC
, json_bits::RADIX_BIN
or json_bits::RADIX_HEX
. This property can be changed any time and affects how bit vector is converted to the string - what base is used.
|
Create an instance from a bit vector. |
|
Static method to create an instance from a bit vector. Alternative to standard constructor. |
|
Static method to create an instance from a |
|
Get internal bit vector value as a string. |
|
Set internal bit vector value from a string. This function may fail due to wrong value is provided, and this fail is unrecoverable (fatal). |
|
Get internal bit vector value. |
|
Set internal bit vector value. |
Number
JSON standard does not specify requirements for number types, but usually it is more convenient to operate with integers and real numbers separately. Hence, several classes are used to represent JSON number.
Integer Number
json_int
wrapper class represens JSON integer number value using SV longint
.
|
Create an instance from a |
|
Static method to create an instance from a |
|
Get internal |
|
Set internal |
Real Number
json_real
wrapper class represens JSON real number value using SV real
.
|
Create an instance from a |
|
Static method to create an instance from a |
|
Get internal |
|
Set internal |
Bool
json_bool
wrapper class represens standard JSON bool value type using SV bit
.
|
Create an instance from a |
|
Static method to create an instance from a |
|
Get internal |
|
Set internal |
JSON Decoder
JSON decoder designed as an abstract class json_decoder
that allows to parse either JSON string or file using corresponding static method:
-
json_decoder::load_string(string str)
-
json_decoder::load_file(string path)
For the compatibility of EDA tools only pure ASCII character set has to be used. \b and \u escape sequences are not supported.
|
Parsing result is returned as json_result
instance, that wraps either json_error
or json_value
.
To avoid error handling and get parsed value immediately method unwrap()
can be used.
However, $fatal()
is thrown, when try to unwrap underlying error. This is described in details in JSON Error and Result section below.
In case of successful parsing, after json_value
is extracted out from the result, it can be inspected and casted to any known JSON value class. More details in JSON Values section.
Key order of any object being parsed is not preserved due to internal implementation, see the note. |
Decoder is recursive, therefore nesting depth is limited. The limit is 1024 by default and it is controllable via additional argument to any load_*
method.
Below are several examples of JSON decoding.
string data =
"{\"recipeName\":\"Vegetarian Pizza\",\"servings\":4,\"isVegan\":true}";
json_object jobject;
string recipe_name;
// Try to load string and get `json_result`, which can be either `json_error`
// or `json_value`. First unwrap() is required to get `json_value` from
// load result and avoid error handling. Second unwrap() is implicit and required
// to avoid error handling of possible unsuccessfull cast to `json_object`.
jobject = json_decoder::load_string(data).unwrap().into_object();
// Try to get a string for recipe name.
// unwrap() here is implicit to avoid error handling of possible unsuccessfull
// cast to `json_string`.
recipe_name = jobject.get("recipeName").into_string().get();
$display("Recipe name is %s", recipe_name);
// Content of pizza.json:
// {
// "recipeName": "Vegetarian Pizza",
// "servings": 4,
// "isVegan": true
// }
json_error jerror;
json_value jvalue;
// Try to load file and get `json_result`,
// which can be either `json_error` or `json_value`.
json_result#(json_value) load_res = json_decoder::load_file("pizza.json");
// Use "pattern matching" to get value
case (1)
load_res.matches_err(jerror): $fatal(jerror.to_string());
load_res.matches_ok(jvalue): begin
json_object jobject;
json_result#(json_object) cast_res = jvalue.try_into_object();
// Traditional if..else can be used as well
if (cast_res.matches_err(jerror)) begin
$fatal(jerror.to_string());
end else if (cast_res.matches_ok(jobject)) begin
$display("Keys of an object: %p", jobject.get_keys());
end
end
endcase
JSON Encoder
JSON encoder designed as an abstract class json_encoder
. It allows to dump JSON encodable value into either string or file using corresponding static methods:
-
json_encoder::dump_string(json_value_encodable obj)
-
json_encoder::dump_file(json_value_encodable obj, string path)
There is no recursion detection for encoder. |
Class json_value_encodable
is a base interface class, that defines a tree of related encodable classes. Any other class can implement one of these classes to use json_encoder
for dumping into JSON. Default JSON value classes implement them out of the box.
Dumping result is returned as json_result
instance, that wraps either string
or json_error
.
To avoid error handling and get parsed value immediately method unwrap()
can be used.
However, $fatal()
is thrown, when try to unwrap underlying error. This is described in details in JSON Error and Result section below.
Keys of any object always follow lexicographical order while dumping due to internal implementation, see the note. |
Below are several examples of JSON encoding. By default, the most compact representation style is used.
However, indent_spaces
argument can be provided to perform multiline encoding.
json_object jobject;
string data;
jobject = json_object::from(
'{
"recipeName": json_string::from("Meatballs"),
"servings": json_int::from(8),
"isVegan": json_bool::from(0)
}
);
// Try to dump to string in the most compact way and get `json_result`,
// which can be either `json_error` or `string`.
// Here unwrap() is required to get `string` and avoid error handling.
// Displays:
//{"isVegan":false,"recipeName":"Meatballs","servings":8}
data = json_encoder::dump_string(jobject).unwrap();
$display(data);
// Try to dump using 2 spaces for indentation
// Displays:
//{
// "isVegan": false,
// "recipeName": "Meatballs",
// "servings": 8
//}
data = json_encoder::dump_string(jobject, .indent_spaces(2)).unwrap();
$display(data);
json_object jobject;
json_error jerror;
json_result#(string) dump_res;
string data;
jobject = json_object::from('{"the_answer": json_int::from(42)});
// Try to dump file and get `json_result`,
// which can be either `json_error` or dumped `string`.
dump_res = json_encoder::dump_file(jobject, "answer.json");
// Use "pattern matching" to get value and handle errors
case (1)
dump_res.matches_ok(data): begin
//Dumped data is:
//{"the_answer":42}
$display("Dumped data is:\n%s", data);
end
dump_res.matches_err_eq(json_error::FILE_NOT_OPENED, jerror): begin
$display("Something wrong with a file!");
end
dump_res.matches_err(jerror): begin
$fatal(jerror.to_string());
end
endcase
JSON Encodable Interfaces
The encoder designed in such way, that it accepts object of any class if it implements one of "encodable" interface classes. Inheritance tree for these classes is shown below.
All classes require only single method to_json_encodable()
to be implemented.
It is expected that the only one of that interfaces is implemented in any other class.
Default JSON value classes implement these interfaces out of the box.
Method signatures are shown in the table below.
Class | Method |
---|---|
|
|
|
|
|
|
|
|
|
|
|
For example, there is a class some_cfg
that stores some configuration values and can be a part of any inheritance tree.
This class can implement json_object_encodable
interface, and as a result it will become encodable into JSON object.
class some_config implements json_object_encodable;
int unsigned max_addr;
bit is_active;
string id;
// Single method has to be implemented for json_object_encodable interface.
// It has to return associative array of JSON values
virtual function json_object_encodable::values_t to_json_encodable();
json_object_encodable::values_t values;
values["max_addr"] = json_int::from(longint'(this.max_addr));
values["is_active"] = json_bool::from(this.is_active);
values["id"] = json_string::from(this.id);
return values;
endfunction: to_json_encodable
endclass : some_config
// Then any `config` instance can be passed to encoder
function void dump_config(some_config cfg);
void'(json_encoder::dump_file(cfg, "config.json"));
endfunction : dump_config
JSON Error and Result
Classes json_result#(VAL_T)
and json_error
provide a robust way to manage success and error states during JSON manipulations. These classes are inspired by Rust’s Result enumeration, allowing for a clear and concise way to propagate and handle errors in SystemVerilog.
Result
The json_result#(VAL_T)
class represents the result of an operation that can either succeed with a value (Rust’s Ok
) or fail with an error (Rust’s Err
). This class is parametrized with a value type (VAL_T
) for successful results, while errors are hardcoded to use the json_error
type. By default, VAL_T
is json_value
.
The error handling mechanism is designed with pattern matching in mind to enable handling different outcomes in a structured manner. However, SystemVerilog provides true pattern matching only for tagged unions, which are still quite exotic, so pattern matching emulation with "reverse case" is suggested:
json_result#(json_value) result = json_decoder::load_file("foo.json");
json_value value;
json_error error;
case (1)
result.matches_err(error): begin
// Handle error
end
result.matches_ok(value): begin
// Use value
end
endcase
Traditional if..else
can be also used
json_result#(json_value) result = json_decoder::load_file("foo.json");
json_value value;
json_error error;
if (result.matches_err(error)) begin
// Handle error
end else if (result.matches_ok(value)) begin
// Use value
end
Pattern matching can be also more strict:
json_result#(string) result = json_encoder::dump_file(obj, path);
string value;
json_error error;
case (1)
result.matches_err_eq(json_error::TYPE_CONVERSION, error): begin
// Handle "type conversion" error
end
result.matches_err_eq(json_error::FILE_NOT_OPENED, error): begin
// Handle "file not opened" error
end
result.matches_ok(value): begin
// Use value if needed
end
endcase
The class provides methods to check whether the result is successful is_ok()
or not is_err()
without pattern matching.
Also, there is a way to skip graceful error handling and get a value immediately using unwrap()
method. However, this may lead to fatal error and stoping simulation in case of error being unwrapped.
|
Static method to create an OK result. |
|
Static method to create an error result. |
|
Checks if the result is an OK. |
|
Checks if the result is an error. |
|
Matches the result with an OK value and retrieves the value if successful. Return 1 on successful match, 0 otherwise. |
|
Matches the result with any error and retrieves the error if successful. Return 1 on successful match, 0 otherwise. |
|
Matches the result with a specific error kind and retrieves the error if successful. Return 1 on successful match, 0 otherwise. |
Error
The json_error
class encapsulates various types of errors that can occur during JSON operations.
Each error is characterized by an error kind (json_error::kind_e
) and additional context such as a description, file, line number, and the JSON string that caused the error.
The json_error::kind_e
enumeration defines the following error types:
Error Type |
Description |
|
EOF while parsing some JSON value |
|
EOF while parsing an object |
|
EOF while parsing an array |
|
EOF while parsing a string |
|
EOF while parsing a literal |
|
Current character should be some expected token |
|
Current character should be ':' |
|
Current character should be either ',' or '}' |
|
Current character should be either ',' or ']' |
|
Current character should be '\"' |
|
Current character should start some JSON value |
|
Invaid escape code |
|
Unexpected control character |
|
Invaid literal that should be 'true', 'false', or 'null' |
|
Invaid number |
|
String must be used as a key |
|
Unexpected comma after the last value |
|
Unexpected characters after the JSON value |
|
This JSON value exceeds nesing limit for a decoder |
|
Type conversion failed |
|
File opening failed |
|
Feature is not implemented |
|
Unspecified internal error |
The class have a few public methods, which can facilitate error handling and debugging.
|
Creates a new error with the specified kind and context. |
|
Logs the error. |
|
Logs the error and finishes simulation. |
|
Converts the error to a human readable string. |