Merge pull request #1368 from larsclausen/net-decl-consolidation

Support SystemVerilog net declaration assignments
This commit is contained in:
Cary R. 2026-05-17 12:16:25 -07:00 committed by GitHub
commit cc86f69a89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 144 additions and 106 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"
}
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_net_decl_assign.v",
"iverilog-args" : [ "-g2005-sv" ]
}

128
parse.y
View File

@ -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<decl_assignment_t*>*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 <wires> udp_port_decl udp_port_decls
%type <statement> udp_initial udp_init_opt
%type <wire> net_variable
%type <wires> net_variable_list
%type <text> event_variable label_opt class_declaration_endlabel_opt
%type <text> block_identifier_opt
%type <text> identifier_name
@ -731,9 +751,6 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
%type <perm_strings> loop_variables
%type <port_list> list_of_port_identifiers list_of_variable_port_identifiers
%type <decl_assignments> net_decl_assigns
%type <decl_assignment> net_decl_assign
%type <mport> port port_opt port_reference port_reference_list
%type <mport> port_declaration
%type <mports> 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 <expr> assignment_pattern expression expression_opt expr_mintypmax
%type <expr> expr_primary_or_typename expr_primary
%type <expr> class_new dynamic_array_new
%type <expr> var_decl_initializer_opt initializer_opt
%type <expr> net_decl_initializer_opt var_decl_initializer_opt initializer_opt
%type <expr> inc_or_dec_expression inside_expression lpvalue
%type <expr> branch_probe_expression streaming_concatenation
%type <expr> delay_value delay_value_simple
@ -788,8 +805,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
%type <exprs> expression_list_with_nuls expression_list_proper
%type <exprs> cont_assign cont_assign_list
%type <decl_assignment> variable_decl_assignment
%type <decl_assignments> list_of_variable_decl_assignments
%type <decl_assignment> net_decl_assign variable_decl_assignment
%type <decl_assignments> net_decl_assigns list_of_variable_decl_assignments
%type <data_type> data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void
%type <data_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 <name> = <expr> 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<PWire*> *tmp = new std::vector<PWire*>;
tmp->push_back($1);
$$ = tmp;
}
| net_variable_list ',' net_variable
{ $1->push_back($3);
$$ = $1;
}
;
event_variable
: IDENTIFIER dimensions_opt
{ if ($2) {

View File

@ -49,6 +49,10 @@
using namespace std;
static void pform_set_data_type(const struct vlltype&li, data_type_t*data_type,
std::vector<PWire*> *wires,
list<named_pexpr_t>*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
@ -2709,22 +2713,24 @@ 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();
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);
}
} 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<PWire*> *wires, NetNet::Type net_type,
list<named_pexpr_t>*attr, bool is_const)
static void pform_set_data_type(const struct vlltype&li, data_type_t*data_type,
std::vector<PWire*> *wires,
list<named_pexpr_t>*attr, bool is_const)
{
if (data_type == 0) {
VLerror(li, "internal error: data_type==0.");
@ -3289,11 +3295,6 @@ 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);

View File

@ -384,13 +384,6 @@ extern void pform_set_port_type(const struct vlltype&li,
data_type_t*dt,
std::list<named_pexpr_t>*attr);
extern void pform_set_data_type(const struct vlltype&li,
data_type_t *data_type,
std::vector<PWire*> *wires,
NetNet::Type net_type,
std::list<named_pexpr_t>*attr,
bool is_const = false);
extern void pform_set_string_type(const struct vlltype&li, const string_type_t*string_type, std::list<perm_string>*names, NetNet::Type net_type, std::list<named_pexpr_t>*attr);
extern void pform_set_class_type(const struct vlltype&li, class_type_t*class_type, std::list<perm_string>*names, NetNet::Type net_type, std::list<named_pexpr_t>*addr);