From e7934d5e66e7fe2e5959285fc8a33288b53270f0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 17 May 2026 11:05:02 -0700 Subject: [PATCH 1/4] pform_makewire(): Fix indentation The assignment handling block uses space-based indentation that does not match the surrounding code. Fix the indentation before changing the block. Signed-off-by: Lars-Peter Clausen --- pform.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pform.cc b/pform.cc index 17a6bdbac..21d386d73 100644 --- a/pform.cc +++ b/pform.cc @@ -2714,17 +2714,17 @@ void pform_makewire(const struct vlltype&li, while (! assign_list->empty()) { decl_assignment_t*first = assign_list->front(); assign_list->pop_front(); - if (PExpr*expr = first->expr.release()) { - if (type == NetNet::REG || type == NetNet::IMPLICIT_REG) { - pform_make_var_init(li, first->name, expr); - } else { - PEIdent*lval = new PEIdent(first->name.first, + if (PExpr*expr = first->expr.release()) { + if (type == NetNet::REG || type == NetNet::IMPLICIT_REG) { + pform_make_var_init(li, first->name, expr); + } else { + PEIdent*lval = new PEIdent(first->name.first, first->name.second); - FILE_NAME(lval, li); - PGAssign*ass = pform_make_pgassign(lval, expr, delay, str); - FILE_NAME(ass, li); - } - } + FILE_NAME(lval, li); + PGAssign*ass = pform_make_pgassign(lval, expr, delay, str); + FILE_NAME(ass, li); + } + } delete first; } } From 3495889112cc41d9e69ce1241b539c2983a21146 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 22 Jan 2022 17:14:20 +0100 Subject: [PATCH 2/4] Support SystemVerilog net declaration assignments SystemVerilog allows initialized and uninitialized net declaration entries to be mixed in the same declaration, e.g. `wire x, y = 1'b1`. In Verilog, either all nets need to have an initializer or non can have one. In addition SystemVerilog also allows assignments to arrays of wires during declaration. E.g. `wire a[3:0] = b;` Currently there are two different rules for net declarations, one for each of the Verilog variants. Combine these into a single rule to support SystemVerilog mixed declarations as well as the assignment to array nets. When running in Verilog mode still reject mixed initialized and uninitialized with a check after the parsing. Signed-off-by: Lars-Peter Clausen --- parse.y | 128 +++++++++++++++++++++---------------------------------- pform.cc | 12 ++++-- pform.h | 7 --- 3 files changed, 57 insertions(+), 90 deletions(-) diff --git a/parse.y b/parse.y index 54a7d2be4..8c94dd4b5 100644 --- a/parse.y +++ b/parse.y @@ -101,6 +101,29 @@ static pform_name_t* pform_create_super(void) return res; } +static void check_net_decl_assigns(const struct vlltype&loc, + const std::list*assign_list) +{ + if (gn_system_verilog()) + return; + + bool has_initializer = false; + bool has_no_initializer = false; + + for (const auto*cur : *assign_list) { + if (cur->expr) + has_initializer = true; + else + has_no_initializer = true; + + if (has_initializer && has_no_initializer) { + pform_requires_sv(loc, "Mixing initialized and uninitialized " + "net declaration entries"); + return; + } + } +} + /* The rules sometimes push attributes into a global context where sub-rules may grab them. This makes parser rules a little easier to write in some cases. */ @@ -720,9 +743,6 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type udp_port_decl udp_port_decls %type udp_initial udp_init_opt -%type net_variable -%type net_variable_list - %type event_variable label_opt class_declaration_endlabel_opt %type block_identifier_opt %type identifier_name @@ -731,9 +751,6 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type loop_variables %type list_of_port_identifiers list_of_variable_port_identifiers -%type net_decl_assigns -%type net_decl_assign - %type port port_opt port_reference port_reference_list %type port_declaration %type list_of_ports module_port_list_opt list_of_port_declarations module_attribute_foreign @@ -780,7 +797,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type assignment_pattern expression expression_opt expr_mintypmax %type expr_primary_or_typename expr_primary %type class_new dynamic_array_new -%type var_decl_initializer_opt initializer_opt +%type net_decl_initializer_opt var_decl_initializer_opt initializer_opt %type inc_or_dec_expression inside_expression lpvalue %type branch_probe_expression streaming_concatenation %type delay_value delay_value_simple @@ -788,8 +805,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type expression_list_with_nuls expression_list_proper %type cont_assign cont_assign_list -%type variable_decl_assignment -%type list_of_variable_decl_assignments +%type net_decl_assign variable_decl_assignment +%type net_decl_assigns list_of_variable_decl_assignments %type data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void %type data_type_or_implicit_no_opt @@ -4983,70 +5000,30 @@ module_item /* Modules can contain further sub-module definitions. */ : module - | attribute_list_opt net_type data_type_or_implicit delay3_opt net_variable_list ';' - - { data_type_t*data_type = $3; - pform_check_net_data_type(@2, $2, $3); - if (data_type == 0) { - data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); - FILE_NAME(data_type, @2); - } - pform_set_data_type(@2, data_type, $5, $2, $1); - if ($4 != 0) { - yyerror(@2, "sorry: Net delays not supported."); - delete $4; - } - delete $1; - } - - | attribute_list_opt K_wreal delay3 net_variable_list ';' - { real_type_t*tmpt = new real_type_t(real_type_t::REAL); - pform_set_data_type(@2, tmpt, $4, NetNet::WIRE, $1); - if ($3 != 0) { - yyerror(@3, "sorry: Net delays not supported."); - delete $3; - } - delete $1; - } - - | attribute_list_opt K_wreal net_variable_list ';' - { real_type_t*tmpt = new real_type_t(real_type_t::REAL); - pform_set_data_type(@2, tmpt, $3, NetNet::WIRE, $1); - delete $1; - } - /* Very similar to the rule above, but this takes a list of net_decl_assigns, which are = assignment declarations. */ - | attribute_list_opt net_type data_type_or_implicit delay3_opt net_decl_assigns ';' - { data_type_t*data_type = $3; - pform_check_net_data_type(@2, $2, $3); - if (data_type == 0) { - data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); - FILE_NAME(data_type, @2); - } - pform_makewire(@2, $4, str_strength, $5, $2, data_type, $1); - delete $1; - } - - /* This form doesn't have the range, but does have strengths. This - gives strength to the assignment drivers. */ - - | attribute_list_opt net_type drive_strength data_type_or_implicit net_decl_assigns ';' + | attribute_list_opt net_type drive_strength_opt data_type_or_implicit delay3_opt net_decl_assigns ';' { data_type_t*data_type = $4; pform_check_net_data_type(@2, $2, $4); + check_net_decl_assigns(@6, $6); if (data_type == 0) { data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); FILE_NAME(data_type, @2); } - pform_makewire(@2, 0, $3, $5, $2, data_type, $1); + pform_makewire(@2, $5, $3, $6, $2, data_type, $1); delete $1; } - | attribute_list_opt K_wreal net_decl_assigns ';' + | attribute_list_opt K_wreal delay3_opt net_decl_assigns ';' { real_type_t*data_type = new real_type_t(real_type_t::REAL); - pform_makewire(@2, 0, str_strength, $3, NetNet::WIRE, data_type, $1); + check_net_decl_assigns(@4, $4); + if ($3) { + yyerror(@2, "error: wreal net does not support delay."); + delete $3; + } + pform_makewire(@2, 0, str_strength, $4, NetNet::WIRE, data_type, $1); delete $1; } @@ -5550,10 +5527,21 @@ generate_block Note that the continuous assignment statement is generated as a side effect, and all I pass up is the name of the l-value. */ +net_decl_initializer_opt + : '=' expression { $$ = $2; } + | { $$ = 0; } + ; + net_decl_assign - : IDENTIFIER '=' expression + : IDENTIFIER dimensions_opt net_decl_initializer_opt { decl_assignment_t*tmp = new decl_assignment_t; tmp->name = { lex_strings.make($1), @1.lexical_pos }; + if ($2) { + tmp->index = *$2; + if ($3) + pform_requires_sv(@$, "Assignment of net array during declaration"); + delete $2; + } tmp->expr.reset($3); delete[]$1; $$ = tmp; @@ -6026,26 +6014,6 @@ dimensions } ; -net_variable - : IDENTIFIER dimensions_opt - { pform_ident_t name = { lex_strings.make($1), @1.lexical_pos }; - $$ = pform_makewire(@1, name, NetNet::IMPLICIT, $2); - delete [] $1; - } - ; - -net_variable_list - : net_variable - { std::vector *tmp = new std::vector; - tmp->push_back($1); - $$ = tmp; - } - | net_variable_list ',' net_variable - { $1->push_back($3); - $$ = $1; - } - ; - event_variable : IDENTIFIER dimensions_opt { if ($2) { diff --git a/pform.cc b/pform.cc index 21d386d73..d5de24d02 100644 --- a/pform.cc +++ b/pform.cc @@ -49,6 +49,10 @@ using namespace std; +static void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, + std::vector *wires, NetNet::Type net_type, + list*attr, bool is_const = false); + /* * The "// synthesis translate_on/off" meta-comments cause this flag * to be turned off or on. The pform_make_behavior and similar @@ -2724,6 +2728,8 @@ void pform_makewire(const struct vlltype&li, PGAssign*ass = pform_make_pgassign(lval, expr, delay, str); FILE_NAME(ass, li); } + } else if (delay) { + VLerror(li, "sorry: net delays not supported."); } delete first; } @@ -3272,9 +3278,9 @@ void pform_set_port_type(const struct vlltype&li, * This function detects the derived class for the given type and * dispatches the type to the proper subtype function. */ -void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, - std::vector *wires, NetNet::Type net_type, - list*attr, bool is_const) +static void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, + std::vector *wires, NetNet::Type net_type, + list*attr, bool is_const) { if (data_type == 0) { VLerror(li, "internal error: data_type==0."); diff --git a/pform.h b/pform.h index 781743258..5fb7e5035 100644 --- a/pform.h +++ b/pform.h @@ -384,13 +384,6 @@ extern void pform_set_port_type(const struct vlltype&li, data_type_t*dt, std::list*attr); -extern void pform_set_data_type(const struct vlltype&li, - data_type_t *data_type, - std::vector *wires, - NetNet::Type net_type, - std::list*attr, - bool is_const = false); - extern void pform_set_string_type(const struct vlltype&li, const string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); extern void pform_set_class_type(const struct vlltype&li, class_type_t*class_type, std::list*names, NetNet::Type net_type, std::list*addr); From 02fa1a99784c4725f16cf092c66c9dd294301d70 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 17 Apr 2022 20:43:05 +0200 Subject: [PATCH 3/4] pform_set_data_type(): Remove `net_type` parameter `pform_set_data_type()` is now only called on wires that already have the correct wire type set. There is no need to pass the same type to `pform_set_data_type()` and set it again. Signed-off-by: Lars-Peter Clausen --- pform.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pform.cc b/pform.cc index d5de24d02..d7c476532 100644 --- a/pform.cc +++ b/pform.cc @@ -50,7 +50,7 @@ using namespace std; static void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, - std::vector *wires, NetNet::Type net_type, + std::vector *wires, list*attr, bool is_const = false); /* @@ -2713,7 +2713,7 @@ void pform_makewire(const struct vlltype&li, wires->push_back(wire); } - pform_set_data_type(li, data_type, wires, type, attr, is_const); + pform_set_data_type(li, data_type, wires, attr, is_const); while (! assign_list->empty()) { decl_assignment_t*first = assign_list->front(); @@ -3279,7 +3279,7 @@ void pform_set_port_type(const struct vlltype&li, * dispatches the type to the proper subtype function. */ static void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, - std::vector *wires, NetNet::Type net_type, + std::vector *wires, list*attr, bool is_const) { if (data_type == 0) { @@ -3295,11 +3295,6 @@ static void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, pform_set_net_range(wire, vec_type); - // If these fail there is a bug somewhere else. pform_set_data_type() - // is only ever called on a fresh wire that already exists. - bool rc = wire->set_wire_type(net_type); - ivl_assert(li, rc); - wire->set_data_type(data_type); wire->set_const(is_const); From 28e121c040830d9bfaeb7919cc7917fd11f0d38e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 6 May 2026 23:01:47 -0700 Subject: [PATCH 4/4] Add regression tests for net declaration assignments Check that SystemVerilog net declarations can mix entries with and without initialization. Check that in SystemVerilog it is possible to do assignments within net array declarations. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_net_array_decl_assign.v | 27 +++++++++++++++ ivtest/ivltests/sv_net_decl_assign.v | 33 +++++++++++++++++++ ivtest/regress-vvp.list | 2 ++ .../vvp_tests/sv_net_array_decl_assign.json | 9 +++++ ivtest/vvp_tests/sv_net_decl_assign.json | 5 +++ 5 files changed, 76 insertions(+) create mode 100644 ivtest/ivltests/sv_net_array_decl_assign.v create mode 100644 ivtest/ivltests/sv_net_decl_assign.v create mode 100644 ivtest/vvp_tests/sv_net_array_decl_assign.json create mode 100644 ivtest/vvp_tests/sv_net_decl_assign.json diff --git a/ivtest/ivltests/sv_net_array_decl_assign.v b/ivtest/ivltests/sv_net_array_decl_assign.v new file mode 100644 index 000000000..48658d317 --- /dev/null +++ b/ivtest/ivltests/sv_net_array_decl_assign.v @@ -0,0 +1,27 @@ +// Check that net arrays can be initialized during declaration. + +module test; + + reg failed; + + wire [3:0] a[0:1] = '{4'h1, 4'h2}; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + + `check(a[0], 4'h1) + `check(a[1], 4'h2) + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_net_decl_assign.v b/ivtest/ivltests/sv_net_decl_assign.v new file mode 100644 index 000000000..31ec9b22b --- /dev/null +++ b/ivtest/ivltests/sv_net_decl_assign.v @@ -0,0 +1,33 @@ +// Check that net declarations can mix initialized and uninitialized entries. + +module test; + + reg failed; + + wire [3:0] a, b = 4'h5; + wire [3:0] c = 4'ha, d; + + assign a = b; + assign d = c; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + + `check(a, 4'h5) + `check(b, 4'h5) + `check(c, 4'ha) + `check(d, 4'ha) + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 33ab7bda9..36d22f512 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -342,6 +342,8 @@ sv_module_port1 vvp_tests/sv_module_port1.json sv_module_port2 vvp_tests/sv_module_port2.json sv_module_port3 vvp_tests/sv_module_port3.json sv_module_port4 vvp_tests/sv_module_port4.json +sv_net_array_decl_assign vvp_tests/sv_net_array_decl_assign.json +sv_net_decl_assign vvp_tests/sv_net_decl_assign.json sv_package_lifetime vvp_tests/sv_package_lifetime.json sv_package_lifetime_fail vvp_tests/sv_package_lifetime_fail.json sv_parameter_type vvp_tests/sv_parameter_type.json diff --git a/ivtest/vvp_tests/sv_net_array_decl_assign.json b/ivtest/vvp_tests/sv_net_array_decl_assign.json new file mode 100644 index 000000000..7e4cdb535 --- /dev/null +++ b/ivtest/vvp_tests/sv_net_array_decl_assign.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_net_array_decl_assign.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Array nets are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_net_decl_assign.json b/ivtest/vvp_tests/sv_net_decl_assign.json new file mode 100644 index 000000000..ded9a2672 --- /dev/null +++ b/ivtest/vvp_tests/sv_net_decl_assign.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_net_decl_assign.v", + "iverilog-args" : [ "-g2005-sv" ] +}