Skip to content

Commit

Permalink
feat: add support of VCS
Browse files Browse the repository at this point in the history
  • Loading branch information
esynr3z committed Jun 29, 2024
1 parent c640abb commit d40be04
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 80 deletions.
13 changes: 11 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
include scripts/common.mk

.PHONY: all \
lint lint_verilator lint_modelsim \
lint lint_verilator lint_modelsim lint_vcs \
test test_src test_examples

export SVJSON_ROOT := $(realpath .)

all: lint test

lint: lint_verilator lint_modelsim
lint: lint_verilator lint_modelsim lint_vcs lint_xcelium

# VARHIDDEN - too strict, method arguments may overlap with class property names
# UNDRIVEN - has false positives on interface classes and custom constructors
Expand All @@ -26,6 +26,15 @@ lint_modelsim:
vlog -l log.txt -sv -warning error -f $(SVJSON_ROOT)/src/filelist.f \
)

lint_vcs:
@echo "Lint sources with VCS"
$(call run_if_exist,vcs, \
mkdir -p work_lint_vcs && \
cd work_lint_vcs && \
vcs -full64 -sverilog -l log.txt +lint=all -error=all \
-f $(SVJSON_ROOT)/src/filelist.f \
)

test: test_src test_examples

test_src:
Expand Down
2 changes: 2 additions & 0 deletions docs/modules/ROOT/pages/changelog.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ and this project adheres to {url-semantic-versioning}[Semantic Versioning].
* Add Modelsim as a linter
* Add scripts to run tests in Modelsim
* Update CI to run linting and tests in Modelsim
* Add VCS as a linter
* Add scripts to run tests in VCS

=== Fixed

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/user.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* No defines
* No plusargs

IMPORTANT: The package is developed and tested mostly 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.
IMPORTANT: The package is developed and tested mostly using Verilator 5.24. 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 {url-svjson-github-src}/filelist.f[`src/filelist.f`].

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/welcome.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The project's code is provided as a SystemVerilog package. Various EDA tools hav
|===
| Tool | Lint | Test | CI
| {url-verilator-github}[Verilator 5.24] | ✅ | ✅ | ✅
| Synopsys VCS | | | ❌
| Synopsys VCS | | | ❌
| Cadence Xcelium | ❌ | ❌ | ❌
| Siemens Modelsim | ✅ | ✅ | ✅
| Siemens Questa | ❌ | ❌ | ❌
Expand Down
54 changes: 27 additions & 27 deletions src/json_decoder.sv
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ class json_decoder;
//----------------------------------------------------------------------------
// Private properties
//----------------------------------------------------------------------------
protected typedef struct {
typedef struct {
json_value value;
int unsigned end_pos;
} parsed_s;

protected typedef json_result#(parsed_s) parser_result;
typedef json_result#(parsed_s) parser_result;

protected const byte whitespace_chars[] = '{" ", "\t", "\n", CR};

Expand Down Expand Up @@ -217,8 +217,8 @@ function json_decoder::parser_result json_decoder::parse_object(
parsed_s parsed;

int unsigned curr_pos = start_pos;
bit trailing_comma = 0;
bit exit_parsing_loop = 0;
bit trailing_comma = 1'b0;
bit exit_parsing_loop = 1'b0;

nesting_lvl++;
if (nesting_lvl >= this.nesting_limit) begin
Expand All @@ -236,7 +236,7 @@ function json_decoder::parser_result json_decoder::parse_object(
return `JSON_SYNTAX_ERR(json_error::TRAILING_COMMA, str, error.json_pos);
end
curr_pos = error.json_pos;
exit_parsing_loop = 1; // empty object parsed
exit_parsing_loop = 1'b1; // empty object parsed
end else begin
return result;
end
Expand Down Expand Up @@ -297,9 +297,9 @@ function json_decoder::parser_result json_decoder::parse_object(
result.matches_ok(parsed): begin
curr_pos = parsed.end_pos;
if (str[curr_pos] == "}") begin
exit_parsing_loop = 1; // end of object
exit_parsing_loop = 1'b1; // end of object
end else begin
trailing_comma = 1;
trailing_comma = 1'b1;
curr_pos++; // move to a symbol after comma
state = PARSE_KEY;
end
Expand Down Expand Up @@ -332,8 +332,8 @@ function json_decoder::parser_result json_decoder::parse_array(
parsed_s parsed;

int unsigned curr_pos = start_pos;
bit trailing_comma = 0;
bit exit_parsing_loop = 0;
bit trailing_comma = 1'b0;
bit exit_parsing_loop = 1'b0;

nesting_lvl++;
if (nesting_lvl >= this.nesting_limit) begin
Expand All @@ -351,7 +351,7 @@ function json_decoder::parser_result json_decoder::parse_array(
return `JSON_SYNTAX_ERR(json_error::TRAILING_COMMA, str, error.json_pos);
end
curr_pos = error.json_pos;
exit_parsing_loop = 1; // empty array parsed
exit_parsing_loop = 1'b1; // empty array parsed
end else begin
return result;
end
Expand Down Expand Up @@ -381,9 +381,9 @@ function json_decoder::parser_result json_decoder::parse_array(
result.matches_ok(parsed): begin
curr_pos = parsed.end_pos;
if (str[curr_pos] == "]") begin
exit_parsing_loop = 1; // end of array
exit_parsing_loop = 1'b1; // end of array
end else begin
trailing_comma = 1;
trailing_comma = 1'b1;
curr_pos++; // move to a symbol after comma
state = PARSE_VALUE;
end
Expand Down Expand Up @@ -414,7 +414,7 @@ function json_decoder::parser_result json_decoder::parse_string(const ref string

int unsigned curr_pos = start_pos;
int unsigned str_len = str.len();
bit exit_parsing_loop = 0;
bit exit_parsing_loop = 1'b0;

while(!exit_parsing_loop) begin
case (state)
Expand Down Expand Up @@ -443,9 +443,9 @@ function json_decoder::parser_result json_decoder::parse_string(const ref string
state = PARSE_ESCAPE;
break;
end else if (str[curr_pos] == "\"") begin
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
break;
end else if (str[curr_pos] < 'h20) begin
end else if (str[curr_pos] < byte'('h20)) begin
return `JSON_SYNTAX_ERR(json_error::INVALID_CHAR, str, curr_pos);
end else begin
value = {value, str[curr_pos++]};
Expand Down Expand Up @@ -502,7 +502,7 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string

int unsigned curr_pos = start_pos;
int unsigned str_len = str.len();
bit exit_parsing_loop = 0;
bit exit_parsing_loop = 1'b0;

while(!exit_parsing_loop) begin
case (state)
Expand Down Expand Up @@ -531,7 +531,7 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string
if (str[curr_pos - 1] == "-") begin
return `JSON_SYNTAX_ERR(json_error::INVALID_NUMBER, str, curr_pos - 1);
end
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
end
end

Expand All @@ -546,7 +546,7 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string

value = {value, str[curr_pos++]};
if (curr_pos == str_len) begin
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
end
end

Expand All @@ -556,7 +556,7 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string
end else if (str[curr_pos] inside {"e", "E"}) begin
state = EXPECT_EXPONENT_SIGN;
end else begin
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
break;
end

Expand All @@ -579,7 +579,7 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string
end else if (str[curr_pos] inside {this.digit_chars}) begin
value = {value, str[curr_pos++]};
end else begin
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
break;
end
end
Expand All @@ -588,12 +588,12 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string
if (str[curr_pos - 1] inside {".", "e", "E"}) begin
return `JSON_SYNTAX_ERR(json_error::INVALID_NUMBER, str, curr_pos - 1);
end
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
end
end

PARSE_FRACTIONAL_DIGITS: begin
is_real = 1;
is_real = 1'b1;

if (!(str[curr_pos] inside {this.digit_chars})) begin
return `JSON_SYNTAX_ERR(json_error::INVALID_NUMBER, str, curr_pos);
Expand All @@ -607,7 +607,7 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string
end else if (str[curr_pos] inside {this.digit_chars}) begin
value = {value, str[curr_pos++]};
end else begin
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
break;
end
end
Expand All @@ -616,12 +616,12 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string
if (str[curr_pos - 1] inside {"e", "E"}) begin
return `JSON_SYNTAX_ERR(json_error::INVALID_NUMBER, str, curr_pos - 1);
end
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
end
end

EXPECT_EXPONENT_SIGN: begin
is_real = 1;
is_real = 1'b1;
if (!(str[curr_pos] inside {"-", "+", this.digit_chars})) begin
return `JSON_SYNTAX_ERR(json_error::INVALID_NUMBER, str, curr_pos);
end
Expand All @@ -639,12 +639,12 @@ function json_decoder::parser_result json_decoder::parse_number(const ref string
if (str[curr_pos] inside {this.digit_chars}) begin
value = {value, str[curr_pos++]};
end else begin
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
break;
end
end
if (curr_pos == str_len) begin
exit_parsing_loop = 1;
exit_parsing_loop = 1'b1;
end
end
endcase
Expand Down
71 changes: 48 additions & 23 deletions src/json_error.sv
Original file line number Diff line number Diff line change
Expand Up @@ -76,29 +76,54 @@ endclass : json_error
function json_error::new(kind_e kind);
this.kind = kind;

this.info[EOF_VALUE] = "EOF while parsing some JSON value";
this.info[EOF_OBJECT] = "EOF while parsing an object";
this.info[EOF_ARRAY] = "EOF while parsing an array";
this.info[EOF_STRING] = "EOF while parsing a string";
this.info[EOF_LITERAL] = "EOF while parsing a literal";
this.info[EXPECTED_TOKEN] = "Current character should be some expected token";
this.info[EXPECTED_COLON] = "Current character should be ':'";
this.info[EXPECTED_OBJECT_COMMA_OR_END] = "Current character should be either ',' or '}'";
this.info[EXPECTED_ARRAY_COMMA_OR_END] = "Current character should be either ',' or ']'";
this.info[EXPECTED_DOUBLE_QUOTE] = "Current character should be '\"'";
this.info[EXPECTED_VALUE] = "Current character should start some JSON value";
this.info[INVALID_ESCAPE] = "Invaid escape code";
this.info[INVALID_CHAR] = "Unexpected control character";
this.info[INVALID_LITERAL] = "Invaid literal that should be 'true', 'false', or 'null'";
this.info[INVALID_NUMBER] = "Invaid number";
this.info[INVALID_OBJECT_KEY] = "String must be used as a key";
this.info[TRAILING_COMMA] = "Unexpected comma after the last value";
this.info[TRAILING_CHARS] = "Unexpected characters after the JSON value";
this.info[DEEP_NESTING] = "This JSON value exceeds nesing limit for a decoder";
this.info[TYPE_CONVERSION] = "Type conversion failed";
this.info[FILE_NOT_OPENED] = "File opening failed";
this.info[NOT_IMPLEMENTED] = "Feature is not implemented";
this.info[INTERNAL] = "Unspecified internal error";
this.info = '{
EOF_VALUE: "EOF while parsing some JSON value",
EOF_OBJECT: "EOF while parsing an object",
EOF_ARRAY: "EOF while parsing an array",
EOF_STRING: "EOF while parsing a string",
EOF_LITERAL: "EOF while parsing a literal",
EXPECTED_TOKEN: "Current character should be some expected token",
EXPECTED_COLON: "Current character should be ':'",
EXPECTED_OBJECT_COMMA_OR_END: "Current character should be either ',' or '}'",
EXPECTED_ARRAY_COMMA_OR_END: "Current character should be either ',' or ']'",
EXPECTED_DOUBLE_QUOTE: "Current character should be '\"'",
EXPECTED_VALUE: "Current character should start some JSON value",
INVALID_ESCAPE: "Invaid escape code",
INVALID_CHAR: "Unexpected control character",
INVALID_LITERAL: "Invaid literal that should be 'true', 'false', or 'null'",
INVALID_NUMBER: "Invaid number",
INVALID_OBJECT_KEY: "String must be used as a key",
TRAILING_COMMA: "Unexpected comma after the last value",
TRAILING_CHARS: "Unexpected characters after the JSON value",
DEEP_NESTING: "This JSON value exceeds nesing limit for a decoder",
TYPE_CONVERSION: "Type conversion failed",
FILE_NOT_OPENED: "File opening failed",
NOT_IMPLEMENTED: "Feature is not implemented",
INTERNAL: "Unspecified internal error",
EOF_VALUE: "EOF while parsing some JSON value"
};
//this.info[EOF_OBJECT] = "EOF while parsing an object";
//this.info[EOF_ARRAY] = "EOF while parsing an array";
//this.info[EOF_STRING] = "EOF while parsing a string";
//this.info[EOF_LITERAL] = "EOF while parsing a literal";
//this.info[EXPECTED_TOKEN] = "Current character should be some expected token";
//this.info[EXPECTED_COLON] = "Current character should be ':'";
//this.info[EXPECTED_OBJECT_COMMA_OR_END] = "Current character should be either ',' or '}'";
//this.info[EXPECTED_ARRAY_COMMA_OR_END] = "Current character should be either ',' or ']'";
//this.info[EXPECTED_DOUBLE_QUOTE] = "Current character should be '\"'";
//this.info[EXPECTED_VALUE] = "Current character should start some JSON value";
//this.info[INVALID_ESCAPE] = "Invaid escape code";
//this.info[INVALID_CHAR] = "Unexpected control character";
//this.info[INVALID_LITERAL] = "Invaid literal that should be 'true', 'false', or 'null'";
//this.info[INVALID_NUMBER] = "Invaid number";
//this.info[INVALID_OBJECT_KEY] = "String must be used as a key";
//this.info[TRAILING_COMMA] = "Unexpected comma after the last value";
//this.info[TRAILING_CHARS] = "Unexpected characters after the JSON value";
//this.info[DEEP_NESTING] = "This JSON value exceeds nesing limit for a decoder";
//this.info[TYPE_CONVERSION] = "Type conversion failed";
//this.info[FILE_NOT_OPENED] = "File opening failed";
//this.info[NOT_IMPLEMENTED] = "Feature is not implemented";
//this.info[INTERNAL] = "Unspecified internal error";
endfunction : new


Expand Down
2 changes: 1 addition & 1 deletion src/values/json_bits.sv
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// 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.
class json_bits #(type BITS_T) extends json_string;
class json_bits #(type BITS_T=bit) extends json_string;
typedef enum {
RADIX_DEC, // Base 10, no prefix
RADIX_BIN, // Base 2, 0b prefix
Expand Down
2 changes: 1 addition & 1 deletion src/values/json_enum.sv
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// JSON enum.
// This wrapper class represens SV enum value as standard JSON string.
// Purpose of this class is to facilitate using SV enum with JSON decoder/encoder.
class json_enum #(type ENUM_T) extends json_string;
class json_enum #(type ENUM_T=enum{DUMMY}) extends json_string;
// Internal raw value of enum
protected ENUM_T enum_value;

Expand Down
21 changes: 18 additions & 3 deletions tests/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
include ../scripts/common.mk

.PHONY: all clean test test_verilator test_modelsim
.PHONY: all clean test test_verilator test_modelsim test_vcs

extra_args ?=

Expand All @@ -12,7 +12,7 @@ export PATH := $(SVUNIT_INSTALL)/bin:$(PATH)

all: test

test: test_verilator test_modelsim
test: test_verilator test_modelsim test_vcs

test_verilator:
@echo Test sources with Verilator
Expand Down Expand Up @@ -42,5 +42,20 @@ test_modelsim:
-o work_modelsim \
)

test_vcs:
@echo Test sources with VCS
$(call run_if_exist,vcs, \
runSVUnit \
-s vcs \
-l run.log \
-c "-full64 \
+define+JSON_TEST_SUITE_INSTALL=$(JSON_TEST_SUITE_INSTALL) \
+define+GOLDEN_JSON_DIR=$(GOLDEN_JSON_DIR)" \
-f $(SVJSON_ROOT)/src/filelist.f \
-f $(SVJSON_ROOT)/tests/filelist.f \
$(extra_args) \
-o work_vcs \
)

clean:
rm -rf work_*
rm -rf work_*
Loading

0 comments on commit d40be04

Please sign in to comment.