From 297dbe94c28f17c1e4133da893d81f83eaa5654c Mon Sep 17 00:00:00 2001 From: Cookie Date: Fri, 22 May 2026 14:42:26 +0800 Subject: [PATCH 001/101] Ignore configure generated files --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4547e8487..1abf677d2 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ config.h stamp-*-h /version.h /version_tag.h +/version_base.h # Directories autom4te.cache @@ -94,6 +95,7 @@ dep /vvp/dump.* /vvp/lexor.cc +/vvp/libvvp.pc /vvp/parse.cc /vvp/parse.h /vvp/parse.output @@ -103,6 +105,7 @@ dep /iverilog-vpi.man /driver-vpi/res.rc +/driver-vpi/iverilog-vpi.man /driver/iverilog.man /vvp/vvp.man @@ -118,4 +121,4 @@ dep /vvp/vvp.exp # Check output -/check.vvp +/check.vvp \ No newline at end of file From 2d3502f4b70b6ac32710266009c290288cf3a06f Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Fri, 22 May 2026 10:14:31 -0700 Subject: [PATCH 002/101] Cleanup .gitignore organization and files --- .gitignore | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 1abf677d2..dafd965e3 100644 --- a/.gitignore +++ b/.gitignore @@ -35,18 +35,20 @@ Makefile /_pli_types.h config.h /tgt-pcb/pcb_config.h -/tgt-pcb/fp.cc -/tgt-pcb/fp.h -/tgt-pcb/fp.output -/tgt-pcb/fp_lex.cc /tgt-vvp/vvp_config.h /tgt-vhdl/vhdl_config.h +/vhdlpp/vhdlpp_config.h /vpi/vpi_config.h stamp-*-h -/version.h /version_tag.h /version_base.h +/driver-vpi/iverilog-vpi.man +/driver-vpi/res.rc +/driver/iverilog.man +/vvp/libvvp.pc +/vvp/vvp.man + # Directories autom4te.cache dep @@ -57,8 +59,6 @@ dep *.vpi /cadpli/cadpli.vpl -/tgt-blif/Makefile - # lex, yacc and gperf output /driver/cflexor.c /driver/cfparse.c @@ -67,14 +67,6 @@ dep /ivlpp/lexor.c -/vhdlpp/lexor.cc -/vhdlpp/lexor_keyword.cc -/vhdlpp/parse.cc -/vhdlpp/parse.h -/vhdlpp/parse.output -/vhdlpp/vhdlpp_config.h -/vhdlpp/vhdlpp - /lexor.cc /lexor_keyword.cc /parse.cc @@ -83,6 +75,17 @@ dep /syn-rules.cc /syn-rules.output +/tgt-pcb/fp.cc +/tgt-pcb/fp.h +/tgt-pcb/fp.output +/tgt-pcb/fp_lex.cc + +/vhdlpp/lexor.cc +/vhdlpp/lexor_keyword.cc +/vhdlpp/parse.cc +/vhdlpp/parse.h +/vhdlpp/parse.output + /vpi/sdf_lexor.c /vpi/sdf_parse.c /vpi/sdf_parse.h @@ -95,7 +98,6 @@ dep /vvp/dump.* /vvp/lexor.cc -/vvp/libvvp.pc /vvp/parse.cc /vvp/parse.h /vvp/parse.output @@ -103,22 +105,18 @@ dep # Program created files /vvp/tables.cc -/iverilog-vpi.man -/driver-vpi/res.rc -/driver-vpi/iverilog-vpi.man -/driver/iverilog.man -/vvp/vvp.man - # The executables. *.exe /driver/iverilog -/iverilog-vpi +/driver-vpi/iverilog-vpi /ivl /ivlpp/ivlpp +/vhdlpp/vhdlpp /vvp/vvp /ivl.exp /vvp/vvp.exp # Check output -/check.vvp \ No newline at end of file +/check.vvp +/driver/top.vvp From e47160c6a8c9e945ea008868cb8f2ea598a396a5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 3 Apr 2022 20:59:24 +0200 Subject: [PATCH 003/101] Evaluate queue method arguments in assignment-like contexts The arguments of the queue `push_front()`, `push_back()` and `insert()` methods are passed to subroutine input ports. This makes them assignment-like contexts with the declared argument type as target type. Use `elaborate_rval_expr()` instead of `elab_and_eval()` for these arguments. This evaluates the item argument with the queue element type and the `insert()` index argument with `integer`, so target-type-dependent expressions such as assignment patterns work and enum compatibility checks use the queue element type. Signed-off-by: Lars-Peter Clausen --- elaborate.cc | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/elaborate.cc b/elaborate.cc index 988110c7f..03a9f4a18 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -47,6 +47,7 @@ # include "netenum.h" # include "netvector.h" # include "netdarray.h" +# include "netqueue.h" # include "netparray.h" # include "netscalar.h" # include "netclass.h" @@ -4099,19 +4100,7 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, << "() method requires a single argument." << endl; des->errors += 1; } - - // Get the context width if this is a logic type. - ivl_variable_type_t base_type = net->darray_type()->element_base_type(); - int context_width = -1; - switch (base_type) { - case IVL_VT_BOOL: - case IVL_VT_LOGIC: - context_width = net->darray_type()->element_width(); - break; - default: - break; - } - + ivl_type_t element_type = net->queue_type()->element_type(); vectorargv (nparms+1); argv[0] = sig; @@ -4123,8 +4112,8 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, << "() methods first argument is missing." << endl; des->errors += 1; } else { - argv[1] = elab_and_eval(des, scope, args[0], context_width, - false, false, base_type); + argv[1] = elaborate_rval_expr(des, scope, element_type, + args[0]); } } else { if (nparms == 0 || !args[0]) { @@ -4133,8 +4122,9 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, << "() methods first argument is missing." << endl; des->errors += 1; } else { - argv[1] = elab_and_eval(des, scope, args[0], context_width, - false, false, IVL_VT_LOGIC); + argv[1] = elaborate_rval_expr(des, scope, + netvector_t::integer_type(), + args[0]); } if (nparms < 2 || !args[1]) { @@ -4143,8 +4133,8 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, << "() methods second argument is missing." << endl; des->errors += 1; } else { - argv[2] = elab_and_eval(des, scope, args[1], context_width, - false, false, base_type); + argv[2] = elaborate_rval_expr(des, scope, element_type, + args[1]); } } From c7530dbcc186de2f21eacde14fd28061b885924c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 25 May 2026 20:25:07 -0700 Subject: [PATCH 004/101] Add regression test for assignment patterns as queue method arguments Check that assignment patterns are evaluated in the queue element type context when they are passed to the queue `push_front()`, `push_back()` and `insert()` methods. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_queue_ap_method.v | 33 ++++++++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/sv_queue_ap_method.json | 9 +++++++ 3 files changed, 43 insertions(+) create mode 100644 ivtest/ivltests/sv_queue_ap_method.v create mode 100644 ivtest/vvp_tests/sv_queue_ap_method.json diff --git a/ivtest/ivltests/sv_queue_ap_method.v b/ivtest/ivltests/sv_queue_ap_method.v new file mode 100644 index 000000000..41f174422 --- /dev/null +++ b/ivtest/ivltests/sv_queue_ap_method.v @@ -0,0 +1,33 @@ +// Check that assignment patterns are supported for queue method arguments. + +module test; + + bit failed; + bit [1:0][3:0] q[$]; + + `define check(val, exp) do begin \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0h, got %0h", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end \ + end while (0) + + initial begin + failed = 1'b0; + + q.push_back('{4'h1, 4'h2}); + q.push_front('{4'h3, 4'h4}); + q.insert(1, '{4'h5, 4'h6}); + + `check(q.size(), 3); + `check(q[0], 8'h34); + `check(q[1], 8'h56); + `check(q[2], 8'h12); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 09f0498cf..2475c4d3a 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -349,6 +349,7 @@ 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 +sv_queue_ap_method vvp_tests/sv_queue_ap_method.json sv_queue_assign_op vvp_tests/sv_queue_assign_op.json sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json diff --git a/ivtest/vvp_tests/sv_queue_ap_method.json b/ivtest/vvp_tests/sv_queue_ap_method.json new file mode 100644 index 000000000..1080087e9 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_ap_method.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_ap_method.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Queues are not supported", + "type" : "CE" + } +} From 1e6e69ee7fafeba8ae51a8f202a6352498e20b41 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 27 Dec 2023 08:57:20 -0800 Subject: [PATCH 005/101] Support multi-dimensional packed vectors as class properties Currently multi-dimensional packed vector class properties will cause an assert and only single dimensional or scalar vectors will pass. But just as for regular vectors there is nothing special about class property multi-dimensional vectors as they will be represented in vector form in vvp. Removing the asserts allows multi-dimensional packed vectors to be used for class properties. Indexed access to these properties is not supported yet; that requires follow-up work to elaborate packed property selects and to support partial stores to vector class properties. Signed-off-by: Lars-Peter Clausen --- tgt-vvp/draw_class.c | 9 ++------- tgt-vvp/stmt_assign.c | 6 +----- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/tgt-vvp/draw_class.c b/tgt-vvp/draw_class.c index afbc26d5c..69431d7b6 100644 --- a/tgt-vvp/draw_class.c +++ b/tgt-vvp/draw_class.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -28,7 +28,6 @@ static void show_prop_type_vector(ivl_type_t ptype) { ivl_variable_type_t data_type = ivl_type_base(ptype); unsigned packed_dimensions = ivl_type_packed_dimensions(ptype); - assert(packed_dimensions < 2); const char*signed_flag = ivl_type_signed(ptype)? "s" : ""; char code = data_type==IVL_VT_BOOL? 'b' : 'L'; @@ -37,12 +36,8 @@ static void show_prop_type_vector(ivl_type_t ptype) fprintf(vvp_out, "\"%s%c1\"", signed_flag, code); } else { - assert(packed_dimensions == 1); - assert(ivl_type_packed_lsb(ptype,0) == 0); - assert(ivl_type_packed_msb(ptype,0) >= 0); - fprintf(vvp_out, "\"%s%c%d\"", signed_flag, code, - ivl_type_packed_msb(ptype,0)+1); + ivl_type_packed_width(ptype)); } } diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index ac0a92dd3..d8c2aefec 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2025 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -1319,10 +1319,6 @@ static int show_stmt_assign_sig_cobject(ivl_statement_t net) if (ivl_type_base(prop_type) == IVL_VT_BOOL || ivl_type_base(prop_type) == IVL_VT_LOGIC) { - assert(ivl_type_packed_dimensions(prop_type) == 0 || - (ivl_type_packed_dimensions(prop_type) == 1 && - ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0))); - if (ivl_stmt_opcode(net) != 0) { fprintf(vvp_out, " %%prop/v %d;\n", prop_idx); } From 2fc9d27190faf69956dfdb19cbd99f6ba2f58a61 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 27 May 2026 21:36:37 -0700 Subject: [PATCH 006/101] Add regression test for multi-dimensional packed class properties Check that multi-dimensional packed vector class properties can be emitted, assigned, and read back through a class object. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_class_prop_packed_dims.v | 40 +++++++++++++++++++ ivtest/regress-vvp.list | 1 + .../vvp_tests/sv_class_prop_packed_dims.json | 9 +++++ 3 files changed, 50 insertions(+) create mode 100644 ivtest/ivltests/sv_class_prop_packed_dims.v create mode 100644 ivtest/vvp_tests/sv_class_prop_packed_dims.json diff --git a/ivtest/ivltests/sv_class_prop_packed_dims.v b/ivtest/ivltests/sv_class_prop_packed_dims.v new file mode 100644 index 000000000..05d97179e --- /dev/null +++ b/ivtest/ivltests/sv_class_prop_packed_dims.v @@ -0,0 +1,40 @@ +// Check that multi-dimensional packed vector class properties are supported. + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do begin \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0h, got %0h", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end \ + end while (0) + + class C; + logic [1:0][3:0] x; + bit [0:1][0:3] y; + endclass + + C c; + + initial begin + c = new; + + c.x = 8'h5a; + c.y = 8'hc3; + `check(c.x, 8'h5a); + `check(c.y, 8'hc3); + + c.x += 8'h01; + c.y ^= 8'hff; + `check(c.x, 8'h5b); + `check(c.y, 8'h3c); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 2475c4d3a..b562ceacc 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -264,6 +264,7 @@ sv_chained_constructor5 vvp_tests/sv_chained_constructor5.json sv_class_prop_assign_op1 vvp_tests/sv_class_prop_assign_op1.json sv_class_prop_assign_op2 vvp_tests/sv_class_prop_assign_op2.json sv_class_prop_logic vvp_tests/sv_class_prop_logic.json +sv_class_prop_packed_dims vvp_tests/sv_class_prop_packed_dims.json sv_class_prop_nest_darray1 vvp_tests/sv_class_prop_nest_darray1.json sv_class_prop_nest_obj1 vvp_tests/sv_class_prop_nest_obj1.json sv_class_prop_nest_real1 vvp_tests/sv_class_prop_nest_real1.json diff --git a/ivtest/vvp_tests/sv_class_prop_packed_dims.json b/ivtest/vvp_tests/sv_class_prop_packed_dims.json new file mode 100644 index 000000000..2697dc424 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_prop_packed_dims.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_prop_packed_dims.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} From aa3d804b725a8b4b4df6a23114ec6875693e7dea Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 May 2026 21:17:41 -0700 Subject: [PATCH 007/101] Fix out-of-bounds write for missing queue method arguments When a method argument is missing, the error path stores a nullptr in the argument vector for that missing slot. The vector was sized from the number of arguments that were present in the source, so calls such as `q.push_back()` or `q.insert(0)` wrote those nullptr placeholders past the end of the vector. Size the vector from the number of arguments required by the queue method instead. This gives the error path slots for the missing arguments while leaving valid calls unchanged. Signed-off-by: Lars-Peter Clausen --- elaborate.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/elaborate.cc b/elaborate.cc index 03a9f4a18..ba327a815 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4101,7 +4101,8 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, des->errors += 1; } ivl_type_t element_type = net->queue_type()->element_type(); - vectorargv (nparms+1); + unsigned expected_nparms = method_name == "insert" ? 2 : 1; + vectorargv (expected_nparms+1); argv[0] = sig; auto args = map_named_args(des, parm_names, parms_); From c5c0b09ef69ba3dc616c090bd6a8ca08e6374dd4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 May 2026 21:18:01 -0700 Subject: [PATCH 008/101] Add regression tests for queue method argument count errors Check that queue push_back(), push_front() and insert() report errors when called with too few or too many arguments. These tests are expected to fail as compile/elaboration errors. They also make sure the invalid calls do not crash during elaboration while reporting the argument count error. Signed-off-by: Lars-Peter Clausen --- .../ivltests/sv_queue_method_insert_too_few_arg_fail.v | 9 +++++++++ .../ivltests/sv_queue_method_insert_too_many_arg_fail.v | 9 +++++++++ .../sv_queue_method_push_back_too_few_arg_fail.v | 9 +++++++++ .../sv_queue_method_push_back_too_many_arg_fail.v | 9 +++++++++ .../sv_queue_method_push_front_too_few_arg_fail.v | 9 +++++++++ .../sv_queue_method_push_front_too_many_arg_fail.v | 9 +++++++++ ivtest/regress-vvp.list | 6 ++++++ .../sv_queue_method_insert_too_few_arg_fail.json | 5 +++++ .../sv_queue_method_insert_too_many_arg_fail.json | 5 +++++ .../sv_queue_method_push_back_too_few_arg_fail.json | 5 +++++ .../sv_queue_method_push_back_too_many_arg_fail.json | 5 +++++ .../sv_queue_method_push_front_too_few_arg_fail.json | 5 +++++ .../sv_queue_method_push_front_too_many_arg_fail.json | 5 +++++ 13 files changed, 90 insertions(+) create mode 100644 ivtest/ivltests/sv_queue_method_insert_too_few_arg_fail.v create mode 100644 ivtest/ivltests/sv_queue_method_insert_too_many_arg_fail.v create mode 100644 ivtest/ivltests/sv_queue_method_push_back_too_few_arg_fail.v create mode 100644 ivtest/ivltests/sv_queue_method_push_back_too_many_arg_fail.v create mode 100644 ivtest/ivltests/sv_queue_method_push_front_too_few_arg_fail.v create mode 100644 ivtest/ivltests/sv_queue_method_push_front_too_many_arg_fail.v create mode 100644 ivtest/vvp_tests/sv_queue_method_insert_too_few_arg_fail.json create mode 100644 ivtest/vvp_tests/sv_queue_method_insert_too_many_arg_fail.json create mode 100644 ivtest/vvp_tests/sv_queue_method_push_back_too_few_arg_fail.json create mode 100644 ivtest/vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json create mode 100644 ivtest/vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json create mode 100644 ivtest/vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json diff --git a/ivtest/ivltests/sv_queue_method_insert_too_few_arg_fail.v b/ivtest/ivltests/sv_queue_method_insert_too_few_arg_fail.v new file mode 100644 index 000000000..b2cb9efe6 --- /dev/null +++ b/ivtest/ivltests/sv_queue_method_insert_too_few_arg_fail.v @@ -0,0 +1,9 @@ +// Check that queue insert() rejects too few arguments. + +module test; + int q[$]; + + initial begin + q.insert(0); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_method_insert_too_many_arg_fail.v b/ivtest/ivltests/sv_queue_method_insert_too_many_arg_fail.v new file mode 100644 index 000000000..ceade7d15 --- /dev/null +++ b/ivtest/ivltests/sv_queue_method_insert_too_many_arg_fail.v @@ -0,0 +1,9 @@ +// Check that queue insert() rejects too many arguments. + +module test; + int q[$]; + + initial begin + q.insert(0, 1, 2); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_method_push_back_too_few_arg_fail.v b/ivtest/ivltests/sv_queue_method_push_back_too_few_arg_fail.v new file mode 100644 index 000000000..374e03edc --- /dev/null +++ b/ivtest/ivltests/sv_queue_method_push_back_too_few_arg_fail.v @@ -0,0 +1,9 @@ +// Check that queue push_back() rejects too few arguments. + +module test; + int q[$]; + + initial begin + q.push_back(); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_method_push_back_too_many_arg_fail.v b/ivtest/ivltests/sv_queue_method_push_back_too_many_arg_fail.v new file mode 100644 index 000000000..276004280 --- /dev/null +++ b/ivtest/ivltests/sv_queue_method_push_back_too_many_arg_fail.v @@ -0,0 +1,9 @@ +// Check that queue push_back() rejects too many arguments. + +module test; + int q[$]; + + initial begin + q.push_back(1, 2); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_method_push_front_too_few_arg_fail.v b/ivtest/ivltests/sv_queue_method_push_front_too_few_arg_fail.v new file mode 100644 index 000000000..a82bcb3cd --- /dev/null +++ b/ivtest/ivltests/sv_queue_method_push_front_too_few_arg_fail.v @@ -0,0 +1,9 @@ +// Check that queue push_front() rejects too few arguments. + +module test; + int q[$]; + + initial begin + q.push_front(); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_method_push_front_too_many_arg_fail.v b/ivtest/ivltests/sv_queue_method_push_front_too_many_arg_fail.v new file mode 100644 index 000000000..f7862a21a --- /dev/null +++ b/ivtest/ivltests/sv_queue_method_push_front_too_many_arg_fail.v @@ -0,0 +1,9 @@ +// Check that queue push_front() rejects too many arguments. + +module test; + int q[$]; + + initial begin + q.push_front(1, 2); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index b562ceacc..55fa1a553 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -352,6 +352,12 @@ sv_package_lifetime_fail vvp_tests/sv_package_lifetime_fail.json sv_parameter_type vvp_tests/sv_parameter_type.json sv_queue_ap_method vvp_tests/sv_queue_ap_method.json sv_queue_assign_op vvp_tests/sv_queue_assign_op.json +sv_queue_method_insert_too_few_arg_fail vvp_tests/sv_queue_method_insert_too_few_arg_fail.json +sv_queue_method_insert_too_many_arg_fail vvp_tests/sv_queue_method_insert_too_many_arg_fail.json +sv_queue_method_push_back_too_few_arg_fail vvp_tests/sv_queue_method_push_back_too_few_arg_fail.json +sv_queue_method_push_back_too_many_arg_fail vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json +sv_queue_method_push_front_too_few_arg_fail vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json +sv_queue_method_push_front_too_many_arg_fail vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json sv_super_member_fail vvp_tests/sv_super_member_fail.json diff --git a/ivtest/vvp_tests/sv_queue_method_insert_too_few_arg_fail.json b/ivtest/vvp_tests/sv_queue_method_insert_too_few_arg_fail.json new file mode 100644 index 000000000..98191069a --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_method_insert_too_few_arg_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_queue_method_insert_too_few_arg_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_queue_method_insert_too_many_arg_fail.json b/ivtest/vvp_tests/sv_queue_method_insert_too_many_arg_fail.json new file mode 100644 index 000000000..751dc11d1 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_method_insert_too_many_arg_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_queue_method_insert_too_many_arg_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_queue_method_push_back_too_few_arg_fail.json b/ivtest/vvp_tests/sv_queue_method_push_back_too_few_arg_fail.json new file mode 100644 index 000000000..e75cf2d2b --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_method_push_back_too_few_arg_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_queue_method_push_back_too_few_arg_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json b/ivtest/vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json new file mode 100644 index 000000000..e66971bc9 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_queue_method_push_back_too_many_arg_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json b/ivtest/vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json new file mode 100644 index 000000000..c3118f197 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_queue_method_push_front_too_few_arg_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json b/ivtest/vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json new file mode 100644 index 000000000..644c6c99a --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_queue_method_push_front_too_many_arg_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} From 5b512e4f1ec6bc20ed119d09ba0181fdc1e1c41e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 6 Jun 2026 14:18:52 -0700 Subject: [PATCH 009/101] Preserve function and final context in nested scopes Currently checks for statements that are not allowed in functions or final procedures only inspect the immediate scope. If the statement is inside a named block or a block with declarations, the current scope is the block and the context is lost. Make `NetScope::in_func()` and `NetScope::in_final()` preserve the context through begin-end, fork-join and generate block scopes. Other scope types are treated as context boundaries so function and final state does not leak across subroutine or definition scopes. Signed-off-by: Lars-Peter Clausen --- net_scope.cc | 19 ++++++++++++++++++- netlist.h | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/net_scope.cc b/net_scope.cc index cd9e923d0..be9d1c4c6 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -535,7 +535,24 @@ NetFuncDef* NetScope::func_def() bool NetScope::in_func() const { - return (type_ == FUNC) ? true : false; + if (type_ == FUNC) + return true; + + if (type_ == BEGIN_END || type_ == FORK_JOIN || type_ == GENBLOCK) + return up_ ? up_->in_func() : false; + + return false; +} + +bool NetScope::in_final() const +{ + if (in_final_) + return true; + + if (type_ == BEGIN_END || type_ == FORK_JOIN || type_ == GENBLOCK) + return up_ ? up_->in_final() : false; + + return false; } const NetFuncDef* NetScope::func_def() const diff --git a/netlist.h b/netlist.h index 86bfb9b73..d485a5be8 100644 --- a/netlist.h +++ b/netlist.h @@ -1193,7 +1193,7 @@ class NetScope : public Definitions, public Attrib { /* Is this scope elaborating a final procedure? */ void in_final(bool in_final__) { in_final_ = in_final__; }; - bool in_final() const { return in_final_; }; + bool in_final() const; const NetTaskDef* task_def() const; const NetFuncDef* func_def() const; From de415b2f03c1b41ab5b46faa9632716d98c1cd86 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 6 Jun 2026 14:19:25 -0700 Subject: [PATCH 010/101] Add regression tests for nested function and final contexts Check that statements that are not allowed in functions or final procedures are still rejected when they are placed in a named block scope. Signed-off-by: Lars-Peter Clausen --- .../ivltests/final_nested_block_task_fail.v | 12 +++++++++++ ivtest/ivltests/func_nested_block_nb_fail.v | 21 +++++++++++++++++++ ivtest/regress-vvp.list | 2 ++ .../final_nested_block_task_fail.json | 5 +++++ .../vvp_tests/func_nested_block_nb_fail.json | 4 ++++ 5 files changed, 44 insertions(+) create mode 100644 ivtest/ivltests/final_nested_block_task_fail.v create mode 100644 ivtest/ivltests/func_nested_block_nb_fail.v create mode 100644 ivtest/vvp_tests/final_nested_block_task_fail.json create mode 100644 ivtest/vvp_tests/func_nested_block_nb_fail.json diff --git a/ivtest/ivltests/final_nested_block_task_fail.v b/ivtest/ivltests/final_nested_block_task_fail.v new file mode 100644 index 000000000..b19f918f5 --- /dev/null +++ b/ivtest/ivltests/final_nested_block_task_fail.v @@ -0,0 +1,12 @@ +// Check that final context is preserved when elaborating nested blocks. + +module test; + + task t; + endtask + + final begin : nested + t; + end + +endmodule diff --git a/ivtest/ivltests/func_nested_block_nb_fail.v b/ivtest/ivltests/func_nested_block_nb_fail.v new file mode 100644 index 000000000..ad1969eb8 --- /dev/null +++ b/ivtest/ivltests/func_nested_block_nb_fail.v @@ -0,0 +1,21 @@ +// Check that function context is preserved when elaborating nested blocks. + +module test; + + reg x; + integer y; + + function integer f; + input a; + begin : nested + x <= a; + f = 0; + end + endfunction + + initial begin + y = f(1'b1); + $display("FAILED"); + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 55fa1a553..e17f69eee 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -137,9 +137,11 @@ early_sig_elab3 vvp_tests/early_sig_elab3.json eofmt_percent vvp_tests/eofmt_percent.json fdisplay3 vvp_tests/fdisplay3.json final3 vvp_tests/final3.json +final_nested_block_task_fail vvp_tests/final_nested_block_task_fail.json fmonitor1 vvp_tests/fmonitor1.json fmonitor2 vvp_tests/fmonitor2.json fread-error vvp_tests/fread-error.json +func_nested_block_nb_fail vvp_tests/func_nested_block_nb_fail.json line_directive vvp_tests/line_directive.json localparam_type vvp_tests/localparam_type.json macro_str_esc vvp_tests/macro_str_esc.json diff --git a/ivtest/vvp_tests/final_nested_block_task_fail.json b/ivtest/vvp_tests/final_nested_block_task_fail.json new file mode 100644 index 000000000..8c017453f --- /dev/null +++ b/ivtest/vvp_tests/final_nested_block_task_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "final_nested_block_task_fail.v", + "iverilog-args" : [ "-g2009" ] +} diff --git a/ivtest/vvp_tests/func_nested_block_nb_fail.json b/ivtest/vvp_tests/func_nested_block_nb_fail.json new file mode 100644 index 000000000..47984b1dd --- /dev/null +++ b/ivtest/vvp_tests/func_nested_block_nb_fail.json @@ -0,0 +1,4 @@ +{ + "type" : "CE", + "source" : "func_nested_block_nb_fail.v" +} From f96332207622ccc80861239e05a98f93e4c4dd28 Mon Sep 17 00:00:00 2001 From: Flavien Solt Date: Fri, 19 Jun 2026 16:00:16 +0800 Subject: [PATCH 011/101] Preserve sign of negative zero --- tgt-vvp/eval_real.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index a61ab0f39..0c685624f 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -202,9 +202,9 @@ static void draw_realnum_real(ivl_expr_t expr) return; } - if (value < 0) { + if (signbit(value)) { sign = 0x4000; - value *= -1; + value = -value; } fract = frexp(value, &expo); From 128c621e8540b0a68145094fa876dc5de073c9a6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 21:43:33 -0700 Subject: [PATCH 012/101] Fix width calculation for packed array bounds Variable select base normalization extends the base expression to cover the packed array bounds. The current code compared min_wid against num_bits() of each bound, but then assigned the bound value itself to min_wid. For positive bounds this can make the generated index expression much wider than required. For negative bounds the effect is much worse since min_wid is unsigned. Assigning a negative bound converts it to a huge width, causing elaboration to try to pad the expression to that size and abort or run out of memory for otherwise valid variable selects. Use the bit width of the bound instead of the bound value. Signed-off-by: Lars-Peter Clausen --- netmisc.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/netmisc.cc b/netmisc.cc index a5af12d76..2ace3f59e 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -423,8 +423,10 @@ NetExpr *normalize_variable_slice_base(const list&indices, NetExpr*base, unsigned min_wid = base->expr_width(); if ((sb < 0) && !base->has_sign()) min_wid += 1; - if (min_wid < num_bits(pcur->get_lsb())) min_wid = pcur->get_lsb(); - if (min_wid < num_bits(pcur->get_msb())) min_wid = pcur->get_msb(); + if (min_wid < num_bits(pcur->get_lsb())) + min_wid = num_bits(pcur->get_lsb()); + if (min_wid < num_bits(pcur->get_msb())) + min_wid = num_bits(pcur->get_msb()); base = pad_to_width(base, min_wid, *base); if ((sb < 0) && !base->has_sign()) { NetESelect *tmp = new NetESelect(base, 0 , min_wid); From 6326c5b1babc6d6d6d782b7cbc6780d705609d42 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 21:43:57 -0700 Subject: [PATCH 013/101] Add regression test for negative packed array bounds Check that variable selects of a packed array with negative bounds use the correct index width and can read back assigned elements. Signed-off-by: Lars-Peter Clausen --- .../ivltests/sv_partsel_var_negative_packed.v | 33 +++++++++++++++++++ ivtest/regress-vvp.list | 1 + .../sv_partsel_var_negative_packed.json | 8 +++++ 3 files changed, 42 insertions(+) create mode 100644 ivtest/ivltests/sv_partsel_var_negative_packed.v create mode 100644 ivtest/vvp_tests/sv_partsel_var_negative_packed.json diff --git a/ivtest/ivltests/sv_partsel_var_negative_packed.v b/ivtest/ivltests/sv_partsel_var_negative_packed.v new file mode 100644 index 000000000..bf9a28720 --- /dev/null +++ b/ivtest/ivltests/sv_partsel_var_negative_packed.v @@ -0,0 +1,33 @@ +// Check variable selects of packed arrays with negative bounds. + +module test; + + reg failed; + reg [-8:-1][3:0] a; + reg signed [2:0] i; + + `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; + a = '0; + + i = -1; + a[i] = 4'ha; + i = -2; + a[i] = 4'h5; + + `check(a[-1], 4'ha); + `check(a[-2], 4'h5); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index e17f69eee..1611df5d0 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -352,6 +352,7 @@ 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 +sv_partsel_var_negative_packed vvp_tests/sv_partsel_var_negative_packed.json sv_queue_ap_method vvp_tests/sv_queue_ap_method.json sv_queue_assign_op vvp_tests/sv_queue_assign_op.json sv_queue_method_insert_too_few_arg_fail vvp_tests/sv_queue_method_insert_too_few_arg_fail.json diff --git a/ivtest/vvp_tests/sv_partsel_var_negative_packed.json b/ivtest/vvp_tests/sv_partsel_var_negative_packed.json new file mode 100644 index 000000000..0d0e5f9d2 --- /dev/null +++ b/ivtest/vvp_tests/sv_partsel_var_negative_packed.json @@ -0,0 +1,8 @@ +{ + "type" : "normal", + "source" : "sv_partsel_var_negative_packed.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "iverilog-args" : [ "-pallowsigned=1" ] + } +} From 8caa3af68942360004180fb61fe89c76246b1e6c Mon Sep 17 00:00:00 2001 From: Cary R Date: Sat, 20 Jun 2026 07:55:46 -0700 Subject: [PATCH 014/101] Update to the latest GTKWave files. --- vpi/lz4.c | 70 ++++++++++++++++++++++++++++++++++++---------- vpi/lz4.h | 84 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 105 insertions(+), 49 deletions(-) diff --git a/vpi/lz4.c b/vpi/lz4.c index 0a727596b..a2f7abee1 100644 --- a/vpi/lz4.c +++ b/vpi/lz4.c @@ -124,14 +124,17 @@ # include /* only present in VS2005+ */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ +# pragma warning(disable : 6239) /* disable: C6239: ( && ) always evaluates to the result of */ +# pragma warning(disable : 6240) /* disable: C6240: ( && ) always evaluates to the result of */ +# pragma warning(disable : 6326) /* disable: C6326: Potential comparison of a constant with another constant */ #endif /* _MSC_VER */ #ifndef LZ4_FORCE_INLINE -# ifdef _MSC_VER /* Visual Studio */ +# if defined (_MSC_VER) && !defined (__clang__) /* MSVC */ # define LZ4_FORCE_INLINE static __forceinline # else # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ +# if defined (__GNUC__) || defined (__clang__) # define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) # else # define LZ4_FORCE_INLINE static inline @@ -430,7 +433,7 @@ static U16 LZ4_readLE16(const void* memPtr) return LZ4_read16(memPtr); } else { const BYTE* p = (const BYTE*)memPtr; - return (U16)((U16)p[0] + (p[1]<<8)); + return (U16)((U16)p[0] | (p[1]<<8)); } } @@ -441,7 +444,7 @@ static U32 LZ4_readLE32(const void* memPtr) return LZ4_read32(memPtr); } else { const BYTE* p = (const BYTE*)memPtr; - return (U32)p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24); + return (U32)p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); } } #endif @@ -527,7 +530,7 @@ LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) /* LZ4_memcpy_using_offset() presumes : * - dstEnd >= dstPtr + MINMATCH - * - there is at least 8 bytes available to write after dstEnd */ + * - there is at least 12 bytes available to write after dstEnd */ LZ4_FORCE_INLINE void LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) { @@ -1118,7 +1121,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( goto _last_literals; } if (litLength >= RUN_MASK) { - int len = (int)(litLength - RUN_MASK); + unsigned len = litLength - RUN_MASK; *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; @@ -1579,8 +1582,11 @@ int LZ4_freeStream (LZ4_stream_t* LZ4_stream) #endif +typedef enum { _ld_fast, _ld_slow } LoadDict_mode_e; #define HASH_UNIT sizeof(reg_t) -int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +int LZ4_loadDict_internal(LZ4_stream_t* LZ4_dict, + const char* dictionary, int dictSize, + LoadDict_mode_e _ld) { LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; const tableType_t tableType = byU32; @@ -1616,13 +1622,39 @@ int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) while (p <= dictEnd-HASH_UNIT) { U32 const h = LZ4_hashPosition(p, tableType); + /* Note: overwriting => favors positions end of dictionary */ LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType); p+=3; idx32+=3; } + if (_ld == _ld_slow) { + /* Fill hash table with additional references, to improve compression capability */ + p = dict->dictionary; + idx32 = dict->currentOffset - dict->dictSize; + while (p <= dictEnd-HASH_UNIT) { + U32 const h = LZ4_hashPosition(p, tableType); + U32 const limit = dict->currentOffset - 64 KB; + if (LZ4_getIndexOnHash(h, dict->hashTable, tableType) <= limit) { + /* Note: not overwriting => favors positions beginning of dictionary */ + LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType); + } + p++; idx32++; + } + } + return (int)dict->dictSize; } +int LZ4_loadDict(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_fast); +} + +int LZ4_loadDictSlow(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_slow); +} + void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) { const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : @@ -2042,7 +2074,7 @@ LZ4_decompress_generic( * note : fast loop may show a regression for some client arm chips. */ #if LZ4_FAST_DEC_LOOP if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { - DEBUGLOG(6, "skip fast decode loop"); + DEBUGLOG(6, "move to safe decode loop"); goto safe_decode; } @@ -2054,6 +2086,7 @@ LZ4_decompress_generic( assert(ip < iend); token = *ip++; length = token >> ML_BITS; /* literal length */ + DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); /* decode literal length */ if (length == RUN_MASK) { @@ -2083,21 +2116,23 @@ LZ4_decompress_generic( /* get offset */ offset = LZ4_readLE16(ip); ip+=2; - DEBUGLOG(6, " offset = %zu", offset); + DEBUGLOG(6, "blockPos%6u: offset = %u", (unsigned)(op-(BYTE*)dst), (unsigned)offset); match = op - offset; assert(match <= op); /* overflow check */ /* get matchlength */ length = token & ML_MASK; + DEBUGLOG(7, " match length token = %u (len==%u)", (unsigned)length, (unsigned)length+MINMATCH); if (length == ML_MASK) { size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); if (addl == rvl_error) { - DEBUGLOG(6, "error reading long match length"); + DEBUGLOG(5, "error reading long match length"); goto _output_error; } length += addl; length += MINMATCH; + DEBUGLOG(7, " long match length == %u", (unsigned)length); if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { goto safe_match_copy; @@ -2105,6 +2140,7 @@ LZ4_decompress_generic( } else { length += MINMATCH; if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + DEBUGLOG(7, "moving to safe_match_copy (ml==%u)", (unsigned)length); goto safe_match_copy; } @@ -2123,7 +2159,7 @@ LZ4_decompress_generic( } } } if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) { - DEBUGLOG(6, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match); + DEBUGLOG(5, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match); goto _output_error; } /* match starting within external dictionary */ @@ -2180,6 +2216,7 @@ LZ4_decompress_generic( assert(ip < iend); token = *ip++; length = token >> ML_BITS; /* literal length */ + DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); /* A two-stage shortcut for the most common case: * 1) If the literal length is 0..14, and there is enough space, @@ -2200,6 +2237,7 @@ LZ4_decompress_generic( /* The second stage: prepare for match copying, decode full info. * If it doesn't work out, the info won't be wasted. */ length = token & ML_MASK; /* match length */ + DEBUGLOG(7, "blockPos%6u: matchLength token = %u (len=%u)", (unsigned)(op-(BYTE*)dst), (unsigned)length, (unsigned)length + 4); offset = LZ4_readLE16(ip); ip += 2; match = op - offset; assert(match <= op); /* check overflow */ @@ -2272,9 +2310,10 @@ LZ4_decompress_generic( * so check that we exactly consume the input and don't overrun the output buffer. */ if ((ip+length != iend) || (cpy > oend)) { - DEBUGLOG(6, "should have been last run of literals") - DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); - DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); + DEBUGLOG(5, "should have been last run of literals") + DEBUGLOG(5, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); + DEBUGLOG(5, "or cpy(%p) > (oend-MFLIMIT)(%p)", cpy, oend-MFLIMIT); + DEBUGLOG(5, "after writing %u bytes / %i bytes available", (unsigned)(op-(BYTE*)dst), outputSize); goto _output_error; } } @@ -2300,6 +2339,7 @@ LZ4_decompress_generic( /* get matchlength */ length = token & ML_MASK; + DEBUGLOG(7, "blockPos%6u: matchLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); _copy_match: if (length == ML_MASK) { @@ -2389,7 +2429,7 @@ LZ4_decompress_generic( while (op < cpy) { *op++ = *match++; } } else { LZ4_memcpy(op, match, 8); - if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } + if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } } op = cpy; /* wildcopy correction */ } diff --git a/vpi/lz4.h b/vpi/lz4.h index 7a2dbfd4b..80e3e5ca0 100644 --- a/vpi/lz4.h +++ b/vpi/lz4.h @@ -129,8 +129,8 @@ extern "C" { /*------ Version ------*/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ -#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */ +#define LZ4_VERSION_MINOR 10 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 0 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) @@ -148,6 +148,7 @@ LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; **************************************/ /*! * LZ4_MEMORY_USAGE : + * Can be selected at compile time, by setting LZ4_MEMORY_USAGE. * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB) * Increasing memory usage improves compression ratio, generally at the cost of speed. * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. @@ -157,6 +158,7 @@ LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; # define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT #endif +/* These are absolute limits, they should not be changed by users */ #define LZ4_MEMORY_USAGE_MIN 10 #define LZ4_MEMORY_USAGE_DEFAULT 14 #define LZ4_MEMORY_USAGE_MAX 20 @@ -368,6 +370,51 @@ LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); */ LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); +/*! LZ4_loadDictSlow() : v1.10.0+ + * Same as LZ4_loadDict(), + * but uses a bit more cpu to reference the dictionary content more thoroughly. + * This is expected to slightly improve compression ratio. + * The extra-cpu cost is likely worth it if the dictionary is re-used across multiple sessions. + * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded) + */ +LZ4LIB_API int LZ4_loadDictSlow(LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/*! LZ4_attach_dictionary() : stable since v1.10.0 + * + * This allows efficient re-use of a static dictionary multiple times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a + * working LZ4_stream_t, this function introduces a no-copy setup mechanism, + * in which the working stream references @dictionaryStream in-place. + * + * Several assumptions are made about the state of @dictionaryStream. + * Currently, only states which have been prepared by LZ4_loadDict() or + * LZ4_loadDictSlow() should be expected to work. + * + * Alternatively, the provided @dictionaryStream may be NULL, + * in which case any existing dictionary stream is unset. + * + * If a dictionary is provided, it replaces any pre-existing stream history. + * The dictionary contents are the only history that can be referenced and + * logically immediately precede the data compressed in the first subsequent + * compression call. + * + * The dictionary will only remain attached to the working stream through the + * first compression call, at the end of which it is cleared. + * @dictionaryStream stream (and source buffer) must remain in-place / accessible / unchanged + * through the completion of the compression session. + * + * Note: there is no equivalent LZ4_attach_*() method on the decompression side + * because there is no initialization cost, hence no need to share the cost across multiple sessions. + * To decompress LZ4 blocks using dictionary, attached or not, + * just employ the regular LZ4_setStreamDecode() for streaming, + * or the stateless LZ4_decompress_safe_usingDict() for one-shot decompression. + */ +LZ4LIB_API void +LZ4_attach_dictionary(LZ4_stream_t* workingStream, + const LZ4_stream_t* dictionaryStream); + /*! LZ4_compress_fast_continue() : * Compress 'src' content using data from previously compressed blocks, for better compression ratio. * 'dst' buffer must be already allocated. @@ -563,43 +610,12 @@ LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, */ LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); -/*! LZ4_compress_destSize_extState() : +/*! LZ4_compress_destSize_extState() : introduced in v1.10.0 * Same as LZ4_compress_destSize(), but using an externally allocated state. * Also: exposes @acceleration */ int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration); -/*! LZ4_attach_dictionary() : - * This is an experimental API that allows - * efficient use of a static dictionary many times. - * - * Rather than re-loading the dictionary buffer into a working context before - * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a - * working LZ4_stream_t, this function introduces a no-copy setup mechanism, - * in which the working stream references the dictionary stream in-place. - * - * Several assumptions are made about the state of the dictionary stream. - * Currently, only streams which have been prepared by LZ4_loadDict() should - * be expected to work. - * - * Alternatively, the provided dictionaryStream may be NULL, - * in which case any existing dictionary stream is unset. - * - * If a dictionary is provided, it replaces any pre-existing stream history. - * The dictionary contents are the only history that can be referenced and - * logically immediately precede the data compressed in the first subsequent - * compression call. - * - * The dictionary will only remain attached to the working stream through the - * first compression call, at the end of which it is cleared. The dictionary - * stream (and source buffer) must remain in-place / accessible / unchanged - * through the completion of the first compression call on the stream. - */ -LZ4LIB_STATIC_API void -LZ4_attach_dictionary(LZ4_stream_t* workingStream, - const LZ4_stream_t* dictionaryStream); - - /*! In-place compression and decompression * * It's possible to have input and output sharing the same buffer, From 4b9675abd7e42c92eda0d1af2572b9a2b452ce98 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sat, 20 Jun 2026 08:52:38 -0700 Subject: [PATCH 015/101] Update unused function in lz4 --- vpi/cppcheck.sup | 88 +++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/vpi/cppcheck.sup b/vpi/cppcheck.sup index c69de0859..026317cc9 100644 --- a/vpi/cppcheck.sup +++ b/vpi/cppcheck.sup @@ -287,73 +287,75 @@ syntaxError:lz4.c unusedStructMember:lz4.c // These functions are not used by Icarus // LZ4_versionNumber() -unusedFunction:lz4.c:746 -// LZ4_versionString() -unusedFunction:lz4.c:747 -// LZ4_sizeofState() unusedFunction:lz4.c:749 +// LZ4_versionString() +unusedFunction:lz4.c:750 +// LZ4_sizeofState() +unusedFunction:lz4.c:752 // LZ4_compress_fast_extState_fastReset() -unusedFunction:lz4.c:1411 +unusedFunction:lz4.c:1414 // LZ4_compress_destSize_extState() -unusedFunction:lz4.c:1494 +unusedFunction:lz4.c:1497 // LZ4_compress_destSize() -unusedFunction:lz4.c:1503 +unusedFunction:lz4.c:1506 // LZ4_resetStream_fast() -unusedFunction:lz4.c:1567 +unusedFunction:lz4.c:1570 // LZ4_resetStream() -unusedFunction:lz4.c:1572 +unusedFunction:lz4.c:1575 // LZ4_loadDict() -unusedFunction:lz4.c:1583 +unusedFunction:lz4.c:1648 +// LZ4_loadDictSlow() +unusedFunction:lz4.c:1653 // LZ4_attach_dictionary() -unusedFunction:lz4.c:1626 +unusedFunction:lz4.c:1658 // LZ4_compress_forceExtDict() -unusedFunction:lz4.c:1755 +unusedFunction:lz4.c:1787 // LZ4_saveDict() -unusedFunction:lz4.c:1782 +unusedFunction:lz4.c:1814 // LZ4_decompress_fast_withPrefix64k() -unusedFunction:lz4.c:2456 +unusedFunction:lz4.c:2496 // LZ4_createStreamDecode() -unusedFunction:lz4.c:2529 +unusedFunction:lz4.c:2569 // LZ4_freeStreamDecode() -unusedFunction:lz4.c:2535 -// LZ4_setStreamDecode() -unusedFunction:lz4.c:2549 -// LZ4_decoderRingBufferSize() unusedFunction:lz4.c:2575 +// LZ4_setStreamDecode() +unusedFunction:lz4.c:2589 +// LZ4_decoderRingBufferSize() +unusedFunction:lz4.c:2615 // LZ4_decompress_safe_continue() -unusedFunction:lz4.c:2591 -// LZ4_decompress_fast_continue() unusedFunction:lz4.c:2631 +// LZ4_decompress_fast_continue() +unusedFunction:lz4.c:2671 // LZ4_decompress_safe_usingDict() -unusedFunction:lz4.c:2679 +unusedFunction:lz4.c:2719 // LZ4_decompress_safe_partial_usingDict() -unusedFunction:lz4.c:2694 +unusedFunction:lz4.c:2734 // LZ4_decompress_fast_usingDict() -unusedFunction:lz4.c:2709 +unusedFunction:lz4.c:2749 // LZ4_compress_limitedOutput() -unusedFunction:lz4.c:2724 +unusedFunction:lz4.c:2764 // LZ4_compress() -unusedFunction:lz4.c:2728 -// LZ4_compress_limitedOutput_withState() -unusedFunction:lz4.c:2732 -// LZ4_compress_withState() -unusedFunction:lz4.c:2736 -// LZ4_compress_limitedOutput_continue() -unusedFunction:lz4.c:2740 -// LZ4_compress_continue() -unusedFunction:lz4.c:2744 -// LZ4_uncompress() -unusedFunction:lz4.c:2755 -// LZ4_uncompress_unknownOutputSize() -unusedFunction:lz4.c:2759 -// LZ4_sizeofStreamState() -unusedFunction:lz4.c:2766 -// LZ4_resetStreamState() unusedFunction:lz4.c:2768 -// LZ4_create() +// LZ4_compress_limitedOutput_withState() +unusedFunction:lz4.c:2772 +// LZ4_compress_withState() unusedFunction:lz4.c:2776 +// LZ4_compress_limitedOutput_continue() +unusedFunction:lz4.c:2780 +// LZ4_compress_continue() +unusedFunction:lz4.c:2784 +// LZ4_uncompress() +unusedFunction:lz4.c:2795 +// LZ4_uncompress_unknownOutputSize() +unusedFunction:lz4.c:2799 +// LZ4_sizeofStreamState() +unusedFunction:lz4.c:2806 +// LZ4_resetStreamState() +unusedFunction:lz4.c:2808 +// LZ4_create() +unusedFunction:lz4.c:2816 // LZ4_slideInputBuffer() -unusedFunction:lz4.c:2783 +unusedFunction:lz4.c:2823 // The routines in sys_random.c are exact copies from IEEE1364-2005 and // they have scope warnings that we need to ignore. From 20a969bf386db85e043c8aa0062562f030a8070e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 21:43:33 -0700 Subject: [PATCH 016/101] Handle invalid old-style UDP tables An empty old-style UDP table leaves the parsed table pointer unset after the parser reports the table error. The old-style UDP creation path still passed the null pointer to process_udp_table(), which crashes. Report an invalid UDP table instead and do not register the primitive. Also keep the new-style invalid-table diagnostic formatting consistent. Signed-off-by: Lars-Peter Clausen --- ivtest/gold/br_gh1175c.gold | 1 - ivtest/gold/br_gh1175d.gold | 1 - ivtest/gold/br_gh1175e.gold | 1 - pform.cc | 16 ++++++++++++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ivtest/gold/br_gh1175c.gold b/ivtest/gold/br_gh1175c.gold index 6396fbcf3..3fe2d3526 100644 --- a/ivtest/gold/br_gh1175c.gold +++ b/ivtest/gold/br_gh1175c.gold @@ -1,4 +1,3 @@ ./ivltests/br_gh1175c.v:3: syntax error ./ivltests/br_gh1175c.v:3: errors in UDP table ./ivltests/br_gh1175c.v:1: error: Invalid table for UDP primitive id_0. - diff --git a/ivtest/gold/br_gh1175d.gold b/ivtest/gold/br_gh1175d.gold index 6c42835a7..02daa6584 100644 --- a/ivtest/gold/br_gh1175d.gold +++ b/ivtest/gold/br_gh1175d.gold @@ -1,4 +1,3 @@ ./ivltests/br_gh1175d.v:3: syntax error ./ivltests/br_gh1175d.v:3: errors in UDP table ./ivltests/br_gh1175d.v:1: error: Invalid table for UDP primitive id_0. - diff --git a/ivtest/gold/br_gh1175e.gold b/ivtest/gold/br_gh1175e.gold index 349b984e1..8ee414e94 100644 --- a/ivtest/gold/br_gh1175e.gold +++ b/ivtest/gold/br_gh1175e.gold @@ -1,4 +1,3 @@ ./ivltests/br_gh1175e.v:3: syntax error ./ivltests/br_gh1175e.v:3: errors in UDP table ./ivltests/br_gh1175e.v:1: error: Invalid table for UDP primitive id_0. - diff --git a/pform.cc b/pform.cc index 0a62724e8..59e264326 100644 --- a/pform.cc +++ b/pform.cc @@ -2079,10 +2079,18 @@ void pform_make_udp(const struct vlltype&loc, perm_string name, for (unsigned idx = 0 ; idx < pins.size() ; idx += 1) udp->ports[idx] = pins[idx]->basename(); - process_udp_table(udp, table, loc); - udp->initial = init; + if (table) { + process_udp_table(udp, table, loc); + udp->initial = init; - pform_primitives[name] = udp; + pform_primitives[name] = udp; + } else { + ostringstream msg; + msg << "error: Invalid table for UDP primitive " << name + << "."; + VLerror(loc, msg.str().c_str(), ""); + delete udp; + } } @@ -2172,7 +2180,7 @@ void pform_make_udp(const struct vlltype&loc, perm_string name, } else { ostringstream msg; msg << "error: Invalid table for UDP primitive " << name - << "." << endl; + << "."; // Some compilers warn if there is just a single C string. VLerror(loc, msg.str().c_str(), ""); } From d5a16b31a1a653d4e431dc82cb6ed4abdaf611cb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 21:43:57 -0700 Subject: [PATCH 017/101] Add regression test for empty old-style UDP table Check that an empty old-style UDP table reports the parser error and the invalid primitive error instead of crashing. Signed-off-by: Lars-Peter Clausen --- ivtest/gold/udp_empty_table_fail-iverilog-stderr.gold | 2 ++ ivtest/ivltests/udp_empty_table_fail.v | 9 +++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/udp_empty_table_fail.json | 5 +++++ 4 files changed, 17 insertions(+) create mode 100644 ivtest/gold/udp_empty_table_fail-iverilog-stderr.gold create mode 100644 ivtest/ivltests/udp_empty_table_fail.v create mode 100644 ivtest/vvp_tests/udp_empty_table_fail.json diff --git a/ivtest/gold/udp_empty_table_fail-iverilog-stderr.gold b/ivtest/gold/udp_empty_table_fail-iverilog-stderr.gold new file mode 100644 index 000000000..1ea319f87 --- /dev/null +++ b/ivtest/gold/udp_empty_table_fail-iverilog-stderr.gold @@ -0,0 +1,2 @@ +ivltests/udp_empty_table_fail.v:7: error: Empty UDP table. +ivltests/udp_empty_table_fail.v:3: error: Invalid table for UDP primitive udp_empty_table_fail. diff --git a/ivtest/ivltests/udp_empty_table_fail.v b/ivtest/ivltests/udp_empty_table_fail.v new file mode 100644 index 000000000..1dc9df054 --- /dev/null +++ b/ivtest/ivltests/udp_empty_table_fail.v @@ -0,0 +1,9 @@ +// Check that an empty old-style UDP table generates an error. + +primitive udp_empty_table_fail (o, i); + output o; + input i; + + table + endtable +endprimitive diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index e17f69eee..df582b9e6 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -389,6 +389,7 @@ test_va_math vvp_tests/test_va_math.json test_vams_math vvp_tests/test_vams_math.json timing_check_syntax vvp_tests/timing_check_syntax.json timing_check_delayed_signals vvp_tests/timing_check_delayed_signals.json +udp_empty_table_fail vvp_tests/udp_empty_table_fail.json uwire_fail2 vvp_tests/uwire_fail2.json uwire_fail3 vvp_tests/uwire_fail3.json value_range1 vvp_tests/value_range1.json diff --git a/ivtest/vvp_tests/udp_empty_table_fail.json b/ivtest/vvp_tests/udp_empty_table_fail.json new file mode 100644 index 000000000..10abc07f9 --- /dev/null +++ b/ivtest/vvp_tests/udp_empty_table_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "udp_empty_table_fail.v", + "gold" : "udp_empty_table_fail" +} From 593a97beded5d59594e73f8b8396defb0c3feb38 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 21:43:33 -0700 Subject: [PATCH 018/101] vvp: Bound VPI label scanset parsing The VPI label resolver parses word and string labels into a 32 byte temporary buffer. The scansets used by sscanf() did not specify a width, so malformed labels could write past the end of the buffer. Limit the scansets to the size of the buffer. Signed-off-by: Lars-Peter Clausen --- vvp/compile.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vvp/compile.cc b/vvp/compile.cc index 7cfa13005..d41c19d72 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -594,7 +594,7 @@ bool vpi_handle_resolv_list_s::resolve(bool mes) unsigned base, wid; size_t n = 0; char ss[32]; - if (2 == sscanf(label(), "W<%u,%[r]>%zn", &base, ss, &n) + if (2 == sscanf(label(), "W<%u,%31[r]>%zn", &base, ss, &n) && n == strlen(label())) { val.ptr = vpip_make_vthr_word(base, ss); @@ -606,7 +606,7 @@ bool vpi_handle_resolv_list_s::resolve(bool mes) val.ptr = vpip_make_vthr_str_stack(base); sym_set_value(sym_vpi, label(), val); - } else if (3 == sscanf(label(), "S<%u,vec4,%[su]%u>%zn", &base, ss, &wid, &n) + } else if (3 == sscanf(label(), "S<%u,vec4,%31[su]%u>%zn", &base, ss, &wid, &n) && n == strlen(label())) { bool signed_flag = false; From 9bc86af284ddc24cdb4d1d6d778d420451af2149 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 14 Jun 2026 13:46:25 -0700 Subject: [PATCH 019/101] elab: Elaborate enum types in nested scopes Enum types declared inside nested scopes are stored separately from typedefs. The enum sets need to be elaborated when the `NetScope` is created so enum literals are available for declarations and statements in the same scope. Module, package and class scopes already do this. Generate, task, function and named block scopes can also declare enum typedefs, but did not elaborate their enum sets. Elaborate them while setting up these scopes. Signed-off-by: Lars-Peter Clausen --- elab_scope.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/elab_scope.cc b/elab_scope.cc index bd9065fd3..4b233348d 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1322,6 +1322,8 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) collect_scope_signals(scope, wires); + elaborate_scope_enumerations(des, scope, enum_sets); + // Run through the defparams for this scope and save the result // in a table for later final override. @@ -1662,6 +1664,8 @@ void PFunction::elaborate_scope(Design*des, NetScope*scope) const collect_scope_signals(scope, wires); + elaborate_scope_enumerations(des, scope, enum_sets); + // Scan through all the named events in this scope. elaborate_scope_events_(des, scope, events); @@ -1682,6 +1686,8 @@ void PTask::elaborate_scope(Design*des, NetScope*scope) const collect_scope_signals(scope, wires); + elaborate_scope_enumerations(des, scope, enum_sets); + // Scan through all the named events in this scope. elaborate_scope_events_(des, scope, events); @@ -1732,6 +1738,8 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const collect_scope_signals(my_scope, wires); + elaborate_scope_enumerations(des, my_scope, enum_sets); + // Scan through all the named events in this scope. elaborate_scope_events_(des, my_scope, events); } From 10349287a0307c459be8b4a8b72f17a3ee1dc133 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 14 Jun 2026 13:47:19 -0700 Subject: [PATCH 020/101] Add regression tests for enum typedefs in nested scopes Check that enum literals declared by enum typedefs in generate blocks, named blocks, tasks and functions can be referenced from the same scope. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/br_gh1385.v | 39 ++++++++++++++++++++++++++++++ ivtest/ivltests/br_gh1385a.v | 37 ++++++++++++++++++++++++++++ ivtest/ivltests/br_gh1385b.v | 41 ++++++++++++++++++++++++++++++++ ivtest/ivltests/br_gh1385c.v | 39 ++++++++++++++++++++++++++++++ ivtest/regress-vvp.list | 4 ++++ ivtest/vvp_tests/br_gh1385.json | 9 +++++++ ivtest/vvp_tests/br_gh1385a.json | 9 +++++++ ivtest/vvp_tests/br_gh1385b.json | 9 +++++++ ivtest/vvp_tests/br_gh1385c.json | 9 +++++++ 9 files changed, 196 insertions(+) create mode 100644 ivtest/ivltests/br_gh1385.v create mode 100644 ivtest/ivltests/br_gh1385a.v create mode 100644 ivtest/ivltests/br_gh1385b.v create mode 100644 ivtest/ivltests/br_gh1385c.v create mode 100644 ivtest/vvp_tests/br_gh1385.json create mode 100644 ivtest/vvp_tests/br_gh1385a.json create mode 100644 ivtest/vvp_tests/br_gh1385b.json create mode 100644 ivtest/vvp_tests/br_gh1385c.json diff --git a/ivtest/ivltests/br_gh1385.v b/ivtest/ivltests/br_gh1385.v new file mode 100644 index 000000000..40a382b2f --- /dev/null +++ b/ivtest/ivltests/br_gh1385.v @@ -0,0 +1,39 @@ +// Check that enum literals declared inside generate blocks are available in +// the generated scope. + +module test; + + reg failed; + + `define check(val, exp) do begin \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end \ + end while (0) + + if (1) begin : g + typedef enum logic [3:0] { + A = 4'd1, + B = 4'd2 + } EN_T; + + EN_T e = A; + + initial begin + failed = 1'b0; + + #1; + `check(e, A); + + e = B; + `check(e, B); + + if (!failed) begin + $display("PASSED"); + end + end + end + +endmodule diff --git a/ivtest/ivltests/br_gh1385a.v b/ivtest/ivltests/br_gh1385a.v new file mode 100644 index 000000000..e0cd4d1c1 --- /dev/null +++ b/ivtest/ivltests/br_gh1385a.v @@ -0,0 +1,37 @@ +// Check that enum literals declared inside named blocks are available in +// the block scope. + +module test; + + reg failed; + + `define check(val, exp) do begin \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end \ + end while (0) + + initial begin : b + typedef enum logic [3:0] { + A = 4'd1, + B = 4'd2 + } EN_T; + + static EN_T e = A; + + failed = 1'b0; + + #1; + `check(e, A); + + e = B; + `check(e, B); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/br_gh1385b.v b/ivtest/ivltests/br_gh1385b.v new file mode 100644 index 000000000..26d15a52f --- /dev/null +++ b/ivtest/ivltests/br_gh1385b.v @@ -0,0 +1,41 @@ +// Check that enum literals declared inside tasks are available in the task +// scope. + +module test; + + reg failed; + + `define check(val, exp) do begin \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end \ + end while (0) + + task check_task_enum; + typedef enum logic [3:0] { + A = 4'd1, + B = 4'd2 + } EN_T; + + static EN_T e = A; + + `check(e, A); + + e = B; + `check(e, B); + endtask + + initial begin + failed = 1'b0; + + #1; + check_task_enum(); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/br_gh1385c.v b/ivtest/ivltests/br_gh1385c.v new file mode 100644 index 000000000..0a5117ee6 --- /dev/null +++ b/ivtest/ivltests/br_gh1385c.v @@ -0,0 +1,39 @@ +// Check that enum literals declared inside functions are available in the +// function scope. + +module test; + + reg failed; + + `define check(val, exp) do begin \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end \ + end while (0) + + function logic [3:0] check_func_enum; + typedef enum logic [3:0] { + A = 4'd1, + B = 4'd2 + } EN_T; + + static EN_T e = A; + + e = B; + check_func_enum = e; + endfunction + + initial begin + failed = 1'b0; + + #1; + `check(check_func_enum(), 4'd2); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index df582b9e6..1758a35a0 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -82,6 +82,10 @@ br_gh1258a vvp_tests/br_gh1258a.json br_gh1258b vvp_tests/br_gh1258b.json br_gh1286 vvp_tests/br_gh1286.json br_gh1323 vvp_tests/br_gh1323.json +br_gh1385 vvp_tests/br_gh1385.json +br_gh1385a vvp_tests/br_gh1385a.json +br_gh1385b vvp_tests/br_gh1385b.json +br_gh1385c vvp_tests/br_gh1385c.json ca_time_real vvp_tests/ca_time_real.json case1 vvp_tests/case1.json case2 vvp_tests/case2.json diff --git a/ivtest/vvp_tests/br_gh1385.json b/ivtest/vvp_tests/br_gh1385.json new file mode 100644 index 000000000..8cdc9e3d5 --- /dev/null +++ b/ivtest/vvp_tests/br_gh1385.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh1385.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Generate scopes are not translated", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/br_gh1385a.json b/ivtest/vvp_tests/br_gh1385a.json new file mode 100644 index 000000000..82757362e --- /dev/null +++ b/ivtest/vvp_tests/br_gh1385a.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh1385a.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Enum typedefs are not translated", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/br_gh1385b.json b/ivtest/vvp_tests/br_gh1385b.json new file mode 100644 index 000000000..c2e17a36d --- /dev/null +++ b/ivtest/vvp_tests/br_gh1385b.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh1385b.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Enum typedefs are not translated", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/br_gh1385c.json b/ivtest/vvp_tests/br_gh1385c.json new file mode 100644 index 000000000..8f0c46a12 --- /dev/null +++ b/ivtest/vvp_tests/br_gh1385c.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh1385c.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Enum typedefs are not translated", + "type" : "CE" + } +} From e9d3fe4ea39bf2478e9671af64f493b5dab4daa4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Dec 2021 21:43:57 +0100 Subject: [PATCH 021/101] Consolidate parameter up/down part select The methods for handling up and down part select are nearly identical and only differ in a hand full of lines. Consolidate them into a single method to remove the duplicated code. This makes it easier to maintain the code and add future changes. Signed-off-by: Lars-Peter Clausen --- PExpr.h | 18 +++----- elab_expr.cc | 118 ++++++++------------------------------------------- 2 files changed, 24 insertions(+), 112 deletions(-) diff --git a/PExpr.h b/PExpr.h index 290b0f093..8ad8a58b9 100644 --- a/PExpr.h +++ b/PExpr.h @@ -488,18 +488,12 @@ class PEIdent : public PExpr { const NetScope*found_in, ivl_type_t par_type, unsigned expr_wid) const; - NetExpr*elaborate_expr_param_idx_up_(Design*des, - NetScope*scope, - const NetExpr*par, - const NetScope*found_in, - ivl_type_t par_type, - bool need_const) const; - NetExpr*elaborate_expr_param_idx_do_(Design*des, - NetScope*scope, - const NetExpr*par, - const NetScope*found_in, - ivl_type_t par_type, - bool need_const) const; + NetExpr*elaborate_expr_param_idx_up_do_(Design*des, + NetScope*scope, + const NetExpr*par, + const NetScope*found_in, + ivl_type_t par_type, + bool up, bool need_const) const; NetExpr*elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, diff --git a/elab_expr.cc b/elab_expr.cc index 604e8d99d..5252e61d6 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -5381,11 +5381,11 @@ static void warn_param_ob(long par_msv, long par_lsv, bool defined, } } -NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, +NetExpr* PEIdent::elaborate_expr_param_idx_up_do_(Design*des, NetScope*scope, const NetExpr*par, const NetScope*found_in, ivl_type_t par_type, - bool need_const) const + bool up, bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); @@ -5404,12 +5404,14 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, if (debug_elaborate) cerr << get_fileline() << ": debug: Calculate part select " - << name << "[" << *base << "+:" << wid << "] from range " + << name << "[" << *base << (up ? "+:" : "-:") << wid + << "] from range " << "[" << par_msv << ":" << par_lsv << "]." << endl; if (base->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: Indexed part select base " - "expression for " << name << "[" << *base << "+:" << wid + "expression for " << name << "[" << *base + << (up ? "+:" : "-:") << wid << "] cannot be a real value." << endl; des->errors += 1; return 0; @@ -5424,107 +5426,23 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, ex->set_line(*this); if (warn_ob_select) { cerr << get_fileline() << ": warning: " << name - << "['bx+:" << wid + << "['bx" << (up ? "+" : "-") << ":" << wid << "] is always outside vector." << endl; } return ex; } long lsv = base_c->value().as_long(); long par_base = par_lsv; - - // Watch out for reversed bit numbering. We're making - // the part select from LSB to MSB. - if (par_msv < par_lsv) { - par_base = lsv; - lsv = par_lsv - wid + 1; - } - - if (warn_ob_select) { - bool defined = true; - // Check to see if the parameter has a defined range. - if (par_type == 0) { - defined = false; - } - // Get the parameter values width. - long pwid = -1; - if (par_ex->has_width()) pwid = par_ex->expr_width()-1; - warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid, - pwid, this, name, true); - } - verinum result = param_part_select_bits(par_ex->value(), wid, - lsv-par_base); - NetEConst*result_ex = new NetEConst(result); - result_ex->set_line(*this); - return result_ex; - } - - base = normalize_variable_base(base, par_msv, par_lsv, wid, true); - - /* Create a parameter reference for the variable select. */ - NetEConstParam*ptmp = new NetEConstParam(found_in, name, par_ex->value()); - ptmp->set_line(found_in->get_parameter_line_info(name)); - - NetExpr*tmp = new NetESelect(ptmp, base, wid, IVL_SEL_IDX_UP); - tmp->set_line(*this); - return tmp; -} - -NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, - const NetExpr*par, - const NetScope*found_in, - ivl_type_t par_type, - bool need_const) const -{ - const NetEConst*par_ex = dynamic_cast (par); - ivl_assert(*this, par_ex); - - long par_msv, par_lsv; - if(! calculate_param_range(*this, par_type, par_msv, par_lsv, - par_ex->value().len())) return 0; - - NetExpr*base = calculate_up_do_base_(des, scope, need_const); - if (base == 0) return 0; - - // Use the part select width already calculated by test_width(). - unsigned long wid = min_width_; - - perm_string name = peek_tail_name(path_); - - if (debug_elaborate) - cerr << get_fileline() << ": debug: Calculate part select " - << name << "[" << *base << "-:" << wid << "] from range " - << "[" << par_msv << ":" << par_lsv << "]." << endl; - - if (base->expr_type() == IVL_VT_REAL) { - cerr << get_fileline() << ": error: Indexed part select base " - "expression for " << name << "[" << *base << "-:" << wid - << "] cannot be a real value." << endl; - des->errors += 1; - return 0; - } - - // Handle the special case that the base is constant. In this - // case, just precalculate the entire constant result. - if (const NetEConst*base_c = dynamic_cast (base)) { - if (! base_c->value().is_defined()) { - NetEConst *ex; - ex = new NetEConst(verinum(verinum::Vx, wid, true)); - ex->set_line(*this); - if (warn_ob_select) { - cerr << get_fileline() << ": warning: " << name - << "['bx-:" << wid - << "] is always outside vector." << endl; - } - return ex; - } - long lsv = base_c->value().as_long(); - long par_base = par_lsv + wid - 1; + if (!up) + par_base += wid - 1; // Watch out for reversed bit numbering. We're making // the part select from LSB to MSB. if (par_msv < par_lsv) { par_base = lsv; lsv = par_lsv; + if (up) + lsv -= wid - 1; } if (warn_ob_select) { @@ -5537,7 +5455,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, long pwid = -1; if (par_ex->has_width()) pwid = par_ex->expr_width()-1; warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid, - pwid, this, name, false); + pwid, this, name, up); } verinum result = param_part_select_bits(par_ex->value(), wid, @@ -5547,13 +5465,13 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, return result_ex; } - base = normalize_variable_base(base, par_msv, par_lsv, wid, false); + base = normalize_variable_base(base, par_msv, par_lsv, wid, up); /* Create a parameter reference for the variable select. */ NetEConstParam*ptmp = new NetEConstParam(found_in, name, par_ex->value()); ptmp->set_line(found_in->get_parameter_line_info(name)); - NetExpr*tmp = new NetESelect(ptmp, base, wid, IVL_SEL_IDX_DOWN); + NetExpr*tmp = new NetESelect(ptmp, base, wid, up ? IVL_SEL_IDX_UP : IVL_SEL_IDX_DOWN); tmp->set_line(*this); return tmp; } @@ -5633,12 +5551,12 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, par_type, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) - return elaborate_expr_param_idx_up_(des, scope, par, found_in, - par_type, need_const); + return elaborate_expr_param_idx_up_do_(des, scope, par, found_in, + par_type, true, need_const); if (use_sel == index_component_t::SEL_IDX_DO) - return elaborate_expr_param_idx_do_(des, scope, par, found_in, - par_type, need_const); + return elaborate_expr_param_idx_up_do_(des, scope, par, found_in, + par_type, false, need_const); NetExpr*tmp = 0; From 03f1bbdd378d8d413038391bd0eac0dffde4375b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Dec 2021 21:47:19 +0100 Subject: [PATCH 022/101] Consolidate net up/down part select The methods for handling up and down part select are nearly identical and only differ in a hand full of lines. Consolidate them into a single method to remove the duplicated code. This makes it easier to maintain the code and add future changes. Signed-off-by: Lars-Peter Clausen --- PExpr.h | 15 ++-- elab_expr.cc | 223 ++++++++------------------------------------------- 2 files changed, 37 insertions(+), 201 deletions(-) diff --git a/PExpr.h b/PExpr.h index 8ad8a58b9..6f6eaf07e 100644 --- a/PExpr.h +++ b/PExpr.h @@ -511,16 +511,11 @@ class PEIdent : public PExpr { NetESignal*net, NetScope*found, unsigned expr_wid) const; - NetExpr*elaborate_expr_net_idx_up_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found, - bool need_const) const; - NetExpr*elaborate_expr_net_idx_do_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found, - bool need_const) const; + NetExpr*elaborate_expr_net_idx_up_do_(Design*des, + NetScope*scope, + NetESignal*net, + NetScope*found, + bool up, bool need_const) const; NetExpr*elaborate_expr_net_bit_(Design*des, NetScope*scope, NetESignal*net, diff --git a/elab_expr.cc b/elab_expr.cc index 5252e61d6..b8e32318d 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -5721,12 +5721,12 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, expr_wid); if (word_sel == index_component_t::SEL_IDX_UP) - return elaborate_expr_net_idx_up_(des, scope, res, found_in, - need_const); + return elaborate_expr_net_idx_up_do_(des, scope, res, found_in, + true, need_const); if (word_sel == index_component_t::SEL_IDX_DO) - return elaborate_expr_net_idx_do_(des, scope, res, found_in, - need_const); + return elaborate_expr_net_idx_up_do_(des, scope, res, found_in, + false, need_const); if (word_sel == index_component_t::SEL_BIT) return elaborate_expr_net_bit_(des, scope, res, found_in, @@ -5880,10 +5880,11 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, } /* - * Part select indexed up, i.e. net[ +: ] + * Part select indexed up or down, i.e. net[ +: ] */ -NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, +NetExpr* PEIdent::elaborate_expr_net_idx_up_do_(Design*des, NetScope*scope, NetESignal*net, NetScope*, + bool up, bool need_const) const { if (net->sig()->data_type() == IVL_VT_STRING) { @@ -5902,13 +5903,15 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, if (!base) return nullptr; + // Use the part select width already calculated by test_width(). unsigned long wid = min_width_; + const char *op = up ? "+" : "-"; if (base->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: Indexed part select base " "expression for " << net->sig()->name() << "[" << *base - << "+:" << wid << "] cannot be a real value." << endl; + << op << ":" << wid << "] cannot be a real value." << endl; des->errors += 1; return 0; } @@ -5919,13 +5922,13 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, if (const NetEConst*base_c = dynamic_cast (base)) { NetExpr*ex; if (base_c->value().is_defined()) { - long lsv = base_c->value().as_long(); + long msv = base_c->value().as_long(); long rel_base = 0; // Check whether an unsigned base fits in a 32 bit int. // This ensures correct results for the vlog95 target, and // for the vvp target on LLP64 platforms (Microsoft Windows). - if (!base_c->has_sign() && (int32_t)lsv < 0) { + if (!base_c->has_sign() && (int32_t)msv < 0) { // Return 'bx for a wrapped around base. ex = new NetEConst(verinum(verinum::Vx, wid, true)); ex->set_line(*this); @@ -5933,7 +5936,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, if (warn_ob_select) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; - cerr << "[" << (unsigned long)lsv << "+:" << wid + cerr << "[" << (unsigned long)msv << op << ":" << wid << "] is always outside vector." << endl; } return ex; @@ -5949,12 +5952,17 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, ivl_assert(*this, swid > 0); long loff, moff; unsigned long lwid, mwid; + long lsv = msv; + if (up) + lsv += (wid/swid)-1; + else + lsv -= (wid/swid)-1; bool lrc, mrc; - mrc = net->sig()->sb_to_slice(prefix_indices, lsv, moff, mwid); - lrc = net->sig()->sb_to_slice(prefix_indices, lsv+(wid/swid)-1, loff, lwid); + mrc = net->sig()->sb_to_slice(prefix_indices, msv, moff, mwid); + lrc = net->sig()->sb_to_slice(prefix_indices, lsv, loff, lwid); if (!mrc || !lrc) { cerr << get_fileline() << ": error: "; - cerr << "Part-select [" << lsv << "+:" << (wid/swid); + cerr << "Part-select [" << msv << op << ":" << (wid/swid); cerr << "] exceeds the declared bounds for "; cerr << net->sig()->name(); if (net->sig()->unpacked_dimensions() > 0) cerr << "[]"; @@ -5974,10 +5982,10 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, long offset = 0; // We want the last range, which is where we work. const netrange_t&rng = packed.back(); - if (rng.get_msb() < rng.get_lsb()) { + if ((rng.get_msb() > rng.get_lsb()) ^ up) { offset = -wid + 1; } - rel_base = net->sig()->sb_to_idx(prefix_indices, lsv) + offset; + rel_base = net->sig()->sb_to_idx(prefix_indices, msv) + offset; } // If the part select covers exactly the entire @@ -5988,7 +5996,6 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, net->cast_signed(false); return net; } - // Otherwise, make a part select that covers the right // range. ex = new NetEConst(verinum(rel_base)); @@ -5997,14 +6004,14 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; - cerr << "[" << lsv << "+:" << wid + cerr << "[" << msv << op << ":" << wid << "] is selecting before vector." << endl; } if (rel_base + wid > net->vector_width()) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; - cerr << "[" << lsv << "+:" << wid + cerr << "[" << msv << op << ":" << wid << "] is selecting after vector." << endl; } } @@ -6016,7 +6023,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, if (warn_ob_select) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; - cerr << "['bx+:" << wid + cerr << "['bx" << op << ":" << wid << "] is always outside vector." << endl; } return ex; @@ -6033,175 +6040,9 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, // Convert the non-constant part select index expression into // an expression that returns a canonical base. - base = normalize_variable_part_base(prefix_indices, base, net->sig(), wid, true); + base = normalize_variable_part_base(prefix_indices, base, net->sig(), wid, up); - NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_UP); - ss->set_line(*this); - - if (debug_elaborate) { - cerr << get_fileline() << ": debug: Elaborate part " - << "select base="<< *base << ", wid="<< wid << endl; - } - - return ss; -} - -/* - * Part select indexed down, i.e. net[ -: ] - */ -NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, - NetESignal*net, NetScope*, - bool need_const) const -{ - if (net->sig()->data_type() == IVL_VT_STRING) { - cerr << get_fileline() << ": error: Cannot take the index part " - "select of a string ('" << net->name() << "')." << endl; - des->errors += 1; - return 0; - } - - listprefix_indices; - bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); - if (!rc) - return 0; - - NetExpr*base = calculate_up_do_base_(des, scope, need_const); - if (!base) - return nullptr; - - // Use the part select width already calculated by test_width(). - unsigned long wid = min_width_; - - if (base->expr_type() == IVL_VT_REAL) { - cerr << get_fileline() << ": error: Indexed part select base " - "expression for " << net->sig()->name() << "[" << *base - << "-:" << wid << "] cannot be a real value." << endl; - des->errors += 1; - return 0; - } - - // Handle the special case that the base is constant as - // well. In this case it can be converted to a conventional - // part select. - if (const NetEConst*base_c = dynamic_cast (base)) { - NetExpr*ex; - if (base_c->value().is_defined()) { - long lsv = base_c->value().as_long(); - long rel_base = 0; - - // Check whether an unsigned base fits in a 32 bit int. - // This ensures correct results for the vlog95 target, and - // for the vvp target on LLP64 platforms (Microsoft Windows). - if (!base_c->has_sign() && (int32_t)lsv < 0) { - // Return 'bx for a wrapped around base. - ex = new NetEConst(verinum(verinum::Vx, wid, true)); - ex->set_line(*this); - delete base; - if (warn_ob_select) { - cerr << get_fileline() << ": warning: " << net->name(); - if (net->word_index()) cerr << "[]"; - cerr << "[" << (unsigned long)lsv << "-:" << wid - << "] is always outside vector." << endl; - } - return ex; - } - - // Get the signal range. - const netranges_t&packed = net->sig()->packed_dims(); - if (prefix_indices.size()+1 < net->sig()->packed_dims().size()) { - // Here we are selecting one or more sub-arrays. - // Make this work by finding the indexed sub-arrays and - // creating a generated slice that spans the whole range. - unsigned long swid = net->sig()->slice_width(prefix_indices.size()+1); - ivl_assert(*this, swid > 0); - long loff, moff; - unsigned long lwid, mwid; - bool lrc, mrc; - mrc = net->sig()->sb_to_slice(prefix_indices, lsv, moff, mwid); - lrc = net->sig()->sb_to_slice(prefix_indices, lsv-(wid/swid)+1, loff, lwid); - if (!mrc || !lrc) { - cerr << get_fileline() << ": error: "; - cerr << "Part-select [" << lsv << "-:" << (wid/swid); - cerr << "] exceeds the declared bounds for "; - cerr << net->sig()->name(); - if (net->sig()->unpacked_dimensions() > 0) cerr << "[]"; - cerr << "." << endl; - des->errors += 1; - return 0; - } - ivl_assert(*this, mwid == swid); - ivl_assert(*this, lwid == swid); - - if (moff > loff) { - rel_base = loff; - } else { - rel_base = moff; - } - } else { - long offset = 0; - // We want the last range, which is where we work. - const netrange_t&rng = packed.back(); - if (rng.get_msb() > rng.get_lsb()) { - offset = -wid + 1; - } - rel_base = net->sig()->sb_to_idx(prefix_indices, lsv) + offset; - } - - // If the part select covers exactly the entire - // vector, then do not bother with it. Return the - // signal itself. - if (rel_base == (long)(wid-1) && wid == net->vector_width()) { - delete base; - net->cast_signed(false); - return net; - } - - // Otherwise, make a part select that covers the right - // range. - ex = new NetEConst(verinum(rel_base)); - if (warn_ob_select) { - if (rel_base < 0) { - cerr << get_fileline() << ": warning: " - << net->name(); - if (net->word_index()) cerr << "[]"; - cerr << "[" << lsv << "-:" << wid - << "] is selecting before vector." << endl; - } - if (rel_base + wid > net->vector_width()) { - cerr << get_fileline() << ": warning: " - << net->name(); - if (net->word_index()) cerr << "[]"; - cerr << "[" << lsv << "-:" << wid - << "] is selecting after vector." << endl; - } - } - } else { - // Return 'bx for an undefined base. - ex = new NetEConst(verinum(verinum::Vx, wid, true)); - ex->set_line(*this); - delete base; - if (warn_ob_select) { - cerr << get_fileline() << ": warning: " << net->name(); - if (net->word_index()) cerr << "[]"; - cerr << "['bx-:" << wid - << "] is always outside vector." << endl; - } - return ex; - } - NetESelect*ss = new NetESelect(net, ex, wid); - ss->set_line(*this); - - delete base; - return ss; - } - - ivl_assert(*this, prefix_indices.size()+1 == net->sig()->packed_dims().size()); - - // Convert the non-constant part select index expression into - // an expression that returns a canonical base. - base = normalize_variable_part_base(prefix_indices, base, net->sig(), wid, false); - - NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_DOWN); + NetESelect*ss = new NetESelect(net, base, wid, up ? IVL_SEL_IDX_UP : IVL_SEL_IDX_DOWN); ss->set_line(*this); if (debug_elaborate) { @@ -6520,12 +6361,12 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) - return elaborate_expr_net_idx_up_(des, scope, node, found_in, - need_const); + return elaborate_expr_net_idx_up_do_(des, scope, node, found_in, + true, need_const); if (use_sel == index_component_t::SEL_IDX_DO) - return elaborate_expr_net_idx_do_(des, scope, node, found_in, - need_const); + return elaborate_expr_net_idx_up_do_(des, scope, node, found_in, + false, need_const); if (use_sel == index_component_t::SEL_BIT) return elaborate_expr_net_bit_(des, scope, node, found_in, From c124abaa3e2746897dcf5aae8c74012d1e0b0987 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 15:15:52 -0700 Subject: [PATCH 023/101] NetAssignNB: Fix dump fallback for invalid rval Currently NetAssignNB::dump() prints a malformed fallback marker when there is no rval expression. The leading '<' is missing, making it inconsistent with the blocking assignment dump output. Print the complete error marker. Signed-off-by: Lars-Peter Clausen --- design_dump.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design_dump.cc b/design_dump.cc index fe5d9edf3..ee9a1cb8e 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1138,7 +1138,7 @@ void NetAssignNB::dump(ostream&o, unsigned ind) const if (rval()) o << *rval() << ";" << endl; else - o << "rval elaboration error>;" << endl; + o << ";" << endl; } From 0637afd2849cc9b93dd9be43032c6a05f5cc9bf1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 16:12:28 -0700 Subject: [PATCH 024/101] Report error for class tasks used as expressions Class object method calls in expression context call func_def() without first checking that the resolved class method is a function. If the method is a task, func_def() triggers an assert instead of reporting a normal elaboration error. Check the method scope type before accessing the function definition and report an error for tasks. Signed-off-by: Lars-Peter Clausen --- elab_expr.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/elab_expr.cc b/elab_expr.cc index 604e8d99d..526119758 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -3410,6 +3410,14 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, return 0; } + if (method->type() != NetScope::FUNC) { + cerr << get_fileline() << ": error: Method " << method_name + << " of class " << class_type->get_name() + << " is not a function." << endl; + des->errors++; + return nullptr; + } + const NetFuncDef*def = method->func_def(); ivl_assert(*this, def); From 449abb6bdaf2394508e8945fdbfc41264f8c1855 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 16:12:45 -0700 Subject: [PATCH 025/101] Add regression test for class tasks used as expressions Check that using a class task through an object method call in expression context reports a compile/elaboration error instead of triggering an assert. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_class_task_expr_fail.v | 19 +++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/sv_class_task_expr_fail.json | 9 +++++++++ 3 files changed, 29 insertions(+) create mode 100644 ivtest/ivltests/sv_class_task_expr_fail.v create mode 100644 ivtest/vvp_tests/sv_class_task_expr_fail.json diff --git a/ivtest/ivltests/sv_class_task_expr_fail.v b/ivtest/ivltests/sv_class_task_expr_fail.v new file mode 100644 index 000000000..261f8135e --- /dev/null +++ b/ivtest/ivltests/sv_class_task_expr_fail.v @@ -0,0 +1,19 @@ +// Check that using a class task as an expression reports an error. + +module test; + + class C; + task t; + endtask + endclass + + C c; + + initial begin + int x; + c = new; + x = c.t(); + $display("FAILED"); + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index df582b9e6..3ea8e5176 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -272,6 +272,7 @@ sv_class_prop_nest_obj1 vvp_tests/sv_class_prop_nest_obj1.json sv_class_prop_nest_real1 vvp_tests/sv_class_prop_nest_real1.json sv_class_prop_nest_str1 vvp_tests/sv_class_prop_nest_str1.json sv_class_prop_nest_vec1 vvp_tests/sv_class_prop_nest_vec1.json +sv_class_task_expr_fail vvp_tests/sv_class_task_expr_fail.json sv_const1 vvp_tests/sv_const1.json sv_const2 vvp_tests/sv_const2.json sv_const3 vvp_tests/sv_const3.json diff --git a/ivtest/vvp_tests/sv_class_task_expr_fail.json b/ivtest/vvp_tests/sv_class_task_expr_fail.json new file mode 100644 index 000000000..d3c18575f --- /dev/null +++ b/ivtest/vvp_tests/sv_class_task_expr_fail.json @@ -0,0 +1,9 @@ +{ + "type" : "CE", + "source" : "sv_class_task_expr_fail.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} From 7934ab9eeb71a28f6556ef047545562ff8c83297 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 17:05:36 -0700 Subject: [PATCH 026/101] Support classes in generate blocks SystemVerilog allows class declarations as module and generate items. Currently a class declaration in a generate block triggers an assert because `pform_push_class_scope()` only records classes in `PScopeExtra` scopes. Add class storage to `PGenerate` and elaborate those classes like module and package classes. When registering task, function or class declarations, only use the current `PGenerate` object as the target if it is also the current lexical scope. This distinction matters for generated classes because `pform_cur_generate` remains set while the class body is parsed, but the current lexical scope has changed to the `PClass`. This records the class declaration in the generate block while leaving methods and constructors in the class scope. Signed-off-by: Lars-Peter Clausen --- PGenerate.h | 6 +++++- elab_scope.cc | 2 ++ elab_sig.cc | 1 + elaborate.cc | 1 + pform.cc | 27 ++++++++++++++++++--------- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/PGenerate.h b/PGenerate.h index d2d293262..24745c7dc 100644 --- a/PGenerate.h +++ b/PGenerate.h @@ -26,10 +26,12 @@ # include # include # include +# include # include "pform_types.h" class Design; class NetScope; +class PClass; class PExpr; class PFunction; class PProcess; @@ -92,9 +94,11 @@ class PGenerate : public PNamedItem, public LexicalScope { std::list gates; void add_gate(PGate*); - // Tasks instantiated within this scheme. + // Definitions instantiated within this scheme. std::map tasks; std::mapfuncs; + std::map classes; + std::vector classes_lexical; // Generate schemes can contain further generate schemes. std::list generate_schemes; diff --git a/elab_scope.cc b/elab_scope.cc index 4b233348d..43d46de3b 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1324,6 +1324,8 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) elaborate_scope_enumerations(des, scope, enum_sets); + elaborate_scope_classes(des, scope, classes_lexical); + // Run through the defparams for this scope and save the result // in a table for later final override. diff --git a/elab_sig.cc b/elab_sig.cc index f23145c22..5b752c617 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -607,6 +607,7 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); + elaborate_sig_classes(des, scope, classes); typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() diff --git a/elaborate.cc b/elaborate.cc index ba327a815..e4894fd6e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -7228,6 +7228,7 @@ bool PGenerate::elaborate_(Design*des, NetScope*scope) const if (result_flag) { elaborate_functions(des, scope, funcs); elaborate_tasks(des, scope, tasks); + elaborate_classes(des, scope, classes); for (const auto gt : gates) gt->elaborate(des, scope); diff --git a/pform.cc b/pform.cc index 59e264326..d9249910c 100644 --- a/pform.cc +++ b/pform.cc @@ -449,6 +449,11 @@ static PScopeExtra* find_nearest_scopex(LexicalScope*scope) return scopex; } +static PGenerate* current_generate_scope() +{ + return lexical_scope == pform_cur_generate ? pform_cur_generate : nullptr; +} + static void add_local_symbol(LexicalScope*scope, perm_string name, PNamedItem*item) { assert(scope); @@ -585,12 +590,16 @@ PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name) PScopeExtra*scopex = find_nearest_scopex(lexical_scope); ivl_assert(loc, scopex); - ivl_assert(loc, !pform_cur_generate); pform_set_scope_timescale(class_scope, scopex); - scopex->classes[name] = class_scope; - scopex->classes_lexical .push_back(class_scope); + if (auto generate_scope = current_generate_scope()) { + generate_scope->classes[name] = class_scope; + generate_scope->classes_lexical.push_back(class_scope); + } else { + scopex->classes[name] = class_scope; + scopex->classes_lexical.push_back(class_scope); + } lexical_scope = class_scope; return class_scope; @@ -632,9 +641,9 @@ PTask* pform_push_task_scope(const struct vlltype&loc, const char*name, pform_set_scope_timescale(task, scopex); - if (pform_cur_generate) { - add_local_symbol(pform_cur_generate, task_name, task); - pform_cur_generate->tasks[task_name] = task; + if (auto generate_scope = current_generate_scope()) { + add_local_symbol(generate_scope, task_name, task); + generate_scope->tasks[task_name] = task; } else { add_local_symbol(scopex, task_name, task); scopex->tasks[task_name] = task; @@ -667,9 +676,9 @@ PFunction* pform_push_function_scope(const struct vlltype&loc, const char*name, pform_set_scope_timescale(func, scopex); - if (pform_cur_generate) { - add_local_symbol(pform_cur_generate, func_name, func); - pform_cur_generate->funcs[func_name] = func; + if (auto generate_scope = current_generate_scope()) { + add_local_symbol(generate_scope, func_name, func); + generate_scope->funcs[func_name] = func; } else { add_local_symbol(scopex, func_name, func); From bb8b05bb5ddea26e1de3618d595e56ae33739708 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 17:05:56 -0700 Subject: [PATCH 027/101] Add regression test for classes in generate blocks Check that a class declared in a conditional generate block can be used. Also check that classes declared in a generate loop get separate class scopes for each generated instance. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/br_gh1384.v | 50 +++++++++++++++++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/br_gh1384.json | 9 ++++++ 3 files changed, 60 insertions(+) create mode 100644 ivtest/ivltests/br_gh1384.v create mode 100644 ivtest/vvp_tests/br_gh1384.json diff --git a/ivtest/ivltests/br_gh1384.v b/ivtest/ivltests/br_gh1384.v new file mode 100644 index 000000000..f58f8a283 --- /dev/null +++ b/ivtest/ivltests/br_gh1384.v @@ -0,0 +1,50 @@ +// Check that classes declared inside generate blocks can be used. + +module test; + + reg failed; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + if (1) begin : g + class C; + int value; + + function new(int value); + this.value = value; + endfunction + endclass + + C c = new(42); + end + + for (genvar i = 0; i < 2; i = i + 1) begin : h + class C; + int value; + + function new(int value); + this.value = value + i; + endfunction + endclass + + C c = new(10); + end + + initial begin + failed = 1'b0; + + `check(g.c.value, 42); + `check(h[0].c.value, 10); + `check(h[1].c.value, 11); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index d9a8f3e7b..b66ada6b5 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -82,6 +82,7 @@ br_gh1258a vvp_tests/br_gh1258a.json br_gh1258b vvp_tests/br_gh1258b.json br_gh1286 vvp_tests/br_gh1286.json br_gh1323 vvp_tests/br_gh1323.json +br_gh1384 vvp_tests/br_gh1384.json br_gh1385 vvp_tests/br_gh1385.json br_gh1385a vvp_tests/br_gh1385a.json br_gh1385b vvp_tests/br_gh1385b.json diff --git a/ivtest/vvp_tests/br_gh1384.json b/ivtest/vvp_tests/br_gh1384.json new file mode 100644 index 000000000..2f6ac5c8b --- /dev/null +++ b/ivtest/vvp_tests/br_gh1384.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh1384.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} From a5bf5e145ff9b2226872a2403c9310ebbb7bb05f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 15:29:54 -0700 Subject: [PATCH 028/101] tgt-vvp: Avoid interleaving array ports into mux output Currently draw_lpm_mux_nest() calls draw_net_input() while printing a .functor statement. For array word inputs draw_net_input() emits an .array/port statement as a side effect, which interleaves the .array/port text into the middle of the .functor line and generates invalid VVP. draw_lpm_substitute() has the same pattern. Collect the input labels before starting to print the consuming statement so any side-effect output appears as a separate statement first. Signed-off-by: Lars-Peter Clausen --- tgt-vvp/draw_mux.c | 8 ++++++-- tgt-vvp/draw_substitute.c | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tgt-vvp/draw_mux.c b/tgt-vvp/draw_mux.c index 16d793d73..ac729da53 100644 --- a/tgt-vvp/draw_mux.c +++ b/tgt-vvp/draw_mux.c @@ -83,10 +83,14 @@ static void draw_lpm_mux_nest(ivl_lpm_t net, const char*muxz) net, select_input); for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 2) { + /* draw_net_input() can emit helper statements, for example for + array words. Emit those before starting the .functor line. */ + const char*input0 = draw_net_input(ivl_lpm_data(net,idx+0)); + const char*input1 = draw_net_input(ivl_lpm_data(net,idx+1)); fprintf(vvp_out, "L_%p/0/%u .functor %s %u", net, idx/2, muxz, width); - fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+0))); - fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+1))); + fprintf(vvp_out, ", %s", input0); + fprintf(vvp_out, ", %s", input1); fprintf(vvp_out, ", L_%p/0s, C4<>;\n", net); } diff --git a/tgt-vvp/draw_substitute.c b/tgt-vvp/draw_substitute.c index 22d5c4223..72fd048c1 100644 --- a/tgt-vvp/draw_substitute.c +++ b/tgt-vvp/draw_substitute.c @@ -25,8 +25,13 @@ void draw_lpm_substitute(ivl_lpm_t net) { unsigned swidth = width_of_nexus(ivl_lpm_data(net,1)); + /* draw_net_input() can emit helper statements, for example for + array words. Emit those before starting the .substitute line. */ + const char*input0 = draw_net_input(ivl_lpm_data(net,0)); + const char*input1 = draw_net_input(ivl_lpm_data(net,1)); + fprintf(vvp_out, "L_%p .substitute %u, %u %u", net, ivl_lpm_width(net), ivl_lpm_base(net), swidth); - fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,0))); - fprintf(vvp_out, ", %s;\n", draw_net_input(ivl_lpm_data(net,1))); + fprintf(vvp_out, ", %s", input0); + fprintf(vvp_out, ", %s;\n", input1); } From 167a6bbcdb6293f7c8d4efbbc2e1b0c9b3c8348b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 15:30:10 -0700 Subject: [PATCH 029/101] Add regression test for case muxes with array word inputs Check that synthesized case statement muxes can use array words as inputs. This used to generate invalid VVP because .array/port statements were emitted in the middle of .functor statements. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/case_mux_array_word.v | 52 +++++++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/case_mux_array_word.json | 5 +++ 3 files changed, 58 insertions(+) create mode 100644 ivtest/ivltests/case_mux_array_word.v create mode 100644 ivtest/vvp_tests/case_mux_array_word.json diff --git a/ivtest/ivltests/case_mux_array_word.v b/ivtest/ivltests/case_mux_array_word.v new file mode 100644 index 000000000..800798482 --- /dev/null +++ b/ivtest/ivltests/case_mux_array_word.v @@ -0,0 +1,52 @@ +// Check that case statement muxes work with array word inputs. + +module test; + + reg [7:0] mem [0:3]; + reg [1:0] sel; + reg [7:0] out; + reg failed; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + always @* begin + case (sel) + 2'd0: out = mem[0]; + 2'd1: out = mem[1]; + 2'd2: out = mem[2]; + 2'd3: out = mem[3]; + endcase + end + + (* ivl_synthesis_off *) + initial begin + failed = 1'b0; + + mem[0] = 8'h12; + mem[1] = 8'h34; + mem[2] = 8'h56; + mem[3] = 8'h78; + + sel = 2'd0; + #1 `check(out, 8'h12); + + sel = 2'd1; + #1 `check(out, 8'h34); + + sel = 2'd2; + #1 `check(out, 8'h56); + + sel = 2'd3; + #1 `check(out, 8'h78); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index d9a8f3e7b..b516f9ce8 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -91,6 +91,7 @@ case1 vvp_tests/case1.json case2 vvp_tests/case2.json case2-S vvp_tests/case2-S.json case3 vvp_tests/case3.json +case_mux_array_word vvp_tests/case_mux_array_word.json casex_synth vvp_tests/casex_synth.json cast_int_ams vvp_tests/cast_int_ams.json cast_real_invalid1 vvp_tests/cast_real_invalid1.json diff --git a/ivtest/vvp_tests/case_mux_array_word.json b/ivtest/vvp_tests/case_mux_array_word.json new file mode 100644 index 000000000..b3303aeb1 --- /dev/null +++ b/ivtest/vvp_tests/case_mux_array_word.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "case_mux_array_word.v", + "iverilog-args" : [ "-S" ] +} From e75b0d7968d91fd8de73affd9713c26fdda4d46a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Jan 2024 10:17:23 -0800 Subject: [PATCH 030/101] tgt-vlog95: Use concatenation for unsigned expression contexts The vlog95 backend currently emits `$unsigned()` when it needs to create a self-determined unsigned expression context. `$unsigned()` is part of the optional signed expression support in this backend and is only available when the signed support flag is enabled. Concatenation is part of the baseline Verilog-95 output and also creates a self-determined unsigned expression context. Use `{expr}` for the unsigned case and keep using `$signed()` when a signed context is needed. Remove `-pallowsigned=1` from the existing vlog95 regression tests that now pass without the optional signed support flag. Signed-off-by: Lars-Peter Clausen --- ivtest/regress-vlog95.list | 16 ++++++------- tgt-vlog95/expr.c | 46 +++++++++++++++++++++----------------- tgt-vlog95/logic_lpm.c | 29 ++++++++++++++---------- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/ivtest/regress-vlog95.list b/ivtest/regress-vlog95.list index 3f82374c8..8222197cc 100644 --- a/ivtest/regress-vlog95.list +++ b/ivtest/regress-vlog95.list @@ -603,10 +603,10 @@ br_gh219 EF,-g2009,-pallowsigned=1 ivltests # creates a CA for the part select, but has nothing to connect it to. # It leaves the gate output unconnected. rise_fall_decay2 CE ivltests -# The code generator is generating unnecessary calls to $unsigned. +# The code generator emits signed casts for this test. array_packed_2d normal,-g2009,-pallowsigned=1 ivltests gold=array_packed_2d.gold -br_gh112c normal,-g2009,-pallowsigned=1 ivltests -br_gh112d normal,-g2009,-pallowsigned=1 ivltests +br_gh112c normal,-g2009 ivltests +br_gh112d normal,-g2009 ivltests # This generates a very larg (65536 bit) constant, and the parser can't cope. br_gh162 TE ivltests @@ -827,7 +827,7 @@ writemem-error normal ivltests gold=writemem-error-vlog95.gold # For Verilog 95 signed is supported as an option (-pallowsigned=1). array6 normal,-pallowsigned=1 ivltests -assign_op_oob normal,-g2009,-pallowsigned=1 ivltests +assign_op_oob normal,-g2009 ivltests assign_op_type normal,-g2009,-pallowsigned=1 ivltests bitp1 normal,-g2009,-pallowsigned=1 ivltests bits normal,-g2009,-pallowsigned=1 ivltests @@ -867,8 +867,8 @@ br_gh1237 normal,-pallowsigned=1 ivltests ca_mult normal,-pallowsigned=1 ivltests gold=ca_mult.gold cast_int normal,-pallowsigned=1 ivltests cfunc_assign_op_vec normal,-g2009,-pallowsigned=1 ivltests -constfunc4 normal,-pallowsigned=1 ivltests -constfunc6 normal,-pallowsigned=1 ivltests +constfunc4 normal ivltests +constfunc6 normal ivltests constfunc7 normal,-pallowsigned=1 ivltests constfunc13 normal,-pallowsigned=1 ivltests constfunc14 normal,-pallowsigned=1 ivltests @@ -909,7 +909,7 @@ pr1793749 normal,-pallowsigned=1 ivltests gold=pr1793749.gold pr1879226 normal,-pallowsigned=1 ivltests pr1883052 normal,-pallowsigned=1 ivltests pr1883052b normal,-pallowsigned=1 ivltests -pr1950282 normal,-pallowsigned=1 ivltests +pr1950282 normal ivltests pr1958801 normal,-pallowsigned=1 ivltests pr1993479 normal,-pallowsigned=1 ivltests gold=pr1993479.gold pr2030767 normal,-pallowsigned=1 ivltests @@ -959,7 +959,7 @@ simple_byte normal,-g2009,-pallowsigned=1 ivltests simple_int normal,-g2009,-pallowsigned=1 ivltests simple_longint normal,-g2009,-pallowsigned=1 ivltests simple_shortint normal,-g2009,-pallowsigned=1 ivltests -size_cast3 normal,-g2009,-pallowsigned=1 ivltests +size_cast3 normal,-g2009 ivltests size_cast5 normal,-g2009,-pallowsigned=1 ivltests struct_member_signed normal,-g2009,-pallowsigned=1 ivltests struct_packed_array normal,-g2009,-pallowsigned=1 ivltests diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 3b6a18623..22296de3e 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -26,7 +26,8 @@ ivl_parameter_t emitting_param = 0; /* - * Data type used to signify if a $signed or $unsigned should be emitted. + * Data type used to signify whether to emit $signed() or wrap the + * expression in a concatenation for an unsigned self-determined context. */ typedef enum expr_sign_e { NO_SIGN = 0, @@ -77,7 +78,7 @@ static expr_sign_t expr_get_binary_sign_type(ivl_expr_t expr) break; } - /* Check to see if a $signed() or $unsigned() is needed. */ + /* Check to see if the expression sign needs to be forced. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED; @@ -97,11 +98,11 @@ static expr_sign_t expr_get_select_sign_type(ivl_expr_t expr, ivl_expr_t oper1 = ivl_expr_oper1(expr); opr_sign = ivl_expr_signed(oper1); /* If the expression being padded is not a signal then don't - * skip a soft $unsigned() (from the binary operator). */ + * skip the soft unsigned context from the binary operator. */ if (ivl_expr_type(oper1) != IVL_EX_SIGNAL) can_skip_unsigned -= 1; } - /* Check to see if a $signed() or $unsigned() is needed. */ + /* Check to see if the expression sign needs to be forced. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED; @@ -115,7 +116,7 @@ static expr_sign_t expr_get_signal_sign_type(ivl_expr_t expr, int expr_sign = ivl_expr_signed(expr); expr_sign_t rtn = NO_SIGN; - /* Check to see if a $signed() or $unsigned() is needed. */ + /* Check to see if the expression sign needs to be forced. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED; @@ -210,10 +211,10 @@ static unsigned expr_is_binary_self_det(ivl_expr_t expr) } /* - * Determine if a $signed() or $unsigned() system function is needed to get + * Determine if $signed() or a single-element concatenation is needed to get * the expression sign information correct. can_skip_unsigned may be set for - * the binary/ternary operators if one of the operands will implicitly cast - * the expression to unsigned. See calc_can_skip_unsigned() for the details. + * the binary/ternary operators if one of the operands will implicitly make + * the expression unsigned. See calc_can_skip_unsigned() for the details. */ static expr_sign_t expr_get_sign_type(ivl_expr_t expr, unsigned wid, unsigned can_skip_unsigned, @@ -399,8 +400,8 @@ static unsigned calc_can_skip_unsigned(ivl_expr_t oper1, ivl_expr_t oper2) oper2_signed |= (ivl_expr_type(oper2) == IVL_EX_SELECT) && (! ivl_expr_oper2(oper2)) && (ivl_expr_signed(ivl_expr_oper1(oper2))); - /* If either operand is a hard unsigned skip adding an explicit - * $unsigned() since it will be added implicitly. */ + /* If either operand is hard unsigned, the operator result is already + * unsigned so skip adding an explicit unsigned context. */ return ! oper1_signed || ! oper2_signed; } @@ -1145,7 +1146,7 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, sign_type = expr_get_sign_type(expr, wid, can_skip_unsigned, is_full_prec); - /* Check to see if a $signed() or $unsigned() needs to be emitted + /* Check to see if a $signed() or concatenation needs to be emitted * before the expression. */ if (sign_type == NEED_SIGNED) { fprintf(vlog_out, "$signed("); @@ -1161,14 +1162,8 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, is_full_prec = 0; } if (sign_type == NEED_UNSIGNED) { - fprintf(vlog_out, "$unsigned("); - if (! allow_signed) { - fprintf(stderr, "%s:%u: vlog95 error: $unsigned() is not " - "supported.\n", - ivl_expr_file(expr), ivl_expr_lineno(expr)); - vlog_errors += 1; - } - /* A $unsigned() creates a self-determined context. */ + fprintf(vlog_out, "{"); + /* A `{x}` creates a self-determined context. */ wid = 0; /* It also clears the full precision flag. */ is_full_prec = 0; @@ -1276,6 +1271,15 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, vlog_errors += 1; break; } - /* Close the $signed() or $unsigned() if need. */ - if (sign_type != NO_SIGN) fprintf(vlog_out, ")"); + /* Close the $signed() or concatenation if needed. */ + switch (sign_type) { + case NEED_SIGNED: + fprintf(vlog_out, ")"); + break; + case NEED_UNSIGNED: + fprintf(vlog_out, "}"); + break; + default: + break; + } } diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 93c18c266..f8950bb65 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -22,7 +22,8 @@ # include "vlog95_priv.h" /* - * Data type used to signify if a $signed or $unsigned should be emitted. + * Data type used to signify whether to emit $signed() or wrap the + * expression in a concatenation for an unsigned self-determined context. */ typedef enum lpm_sign_e { NO_SIGN = 0, @@ -119,7 +120,7 @@ static lpm_sign_t lpm_get_sign_type(ivl_lpm_t lpm, break; } - /* Check to see if a $signed() or $unsigned() is needed. */ + /* Check to see if the expression sign needs to be forced. */ if (lpm_sign && ! opr_sign) rtn = NEED_SIGNED; if (! lpm_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED; @@ -1173,7 +1174,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, unsigned sign_extend) { lpm_sign_t sign_type; - /* Check to see if a $signed() or $unsigned() needs to be emitted + /* Check to see if a $signed() or concatenation needs to be emitted * before the expression. */ sign_type = lpm_get_sign_type(lpm, 0); if (sign_type == NEED_SIGNED) { @@ -1185,14 +1186,9 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, vlog_errors += 1; } } + /* A concatenation creates a self-determined unsigned context */ if (sign_type == NEED_UNSIGNED) { - fprintf(vlog_out, "$unsigned("); - if (! allow_signed) { - fprintf(stderr, "%s:%u: vlog95 error: $unsigned() is not " - "supported.\n", - ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); - vlog_errors += 1; - } + fprintf(vlog_out, "{"); } switch (ivl_lpm_type(lpm)) { @@ -1472,8 +1468,17 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, vlog_errors += 1; } - /* Close the $signed() or $unsigned() if needed. */ - if (sign_type != NO_SIGN) fprintf(vlog_out, ")"); + /* Close the $signed() or concatenation if needed. */ + switch (sign_type) { + case NEED_SIGNED: + fprintf(vlog_out, ")"); + break; + case NEED_UNSIGNED: + fprintf(vlog_out, "}"); + break; + default: + break; + } } static void emit_posedge_dff_prim(void) From 4e168a4d1fe2ac33536759a022ab70b4dad73a60 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Jun 2026 13:15:30 -0700 Subject: [PATCH 031/101] ivtest: Detect execution errors in vvp_reg.py Currently vvp_reg.py uses `returncode >= 256` to distinguish execution errors from ordinary compile or simulation failures. That matches the encoded status returned by wait(), but subprocess.run() does not expose that value. Its returncode is the decoded process exit status, or `-N` if the process was terminated by signal N. Shell wrappers can also report signal termination as `128 + N`. As a result a compiler crash can be reported as `-11` or `139`. Both values pass the old check and a CE test can be accepted as a normal compiler error. Treat negative return codes and return codes greater than or equal to 128 as execution errors before accepting CE and EF results. Also make sure that CE gold mismatches are reported as failures. Signed-off-by: Lars-Peter Clausen --- ivtest/run_ivl.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ivtest/run_ivl.py b/ivtest/run_ivl.py index 3a1aa16b7..aa2206928 100644 --- a/ivtest/run_ivl.py +++ b/ivtest/run_ivl.py @@ -129,6 +129,10 @@ def run_cmd(cmd: list) -> subprocess.CompletedProcess: res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) return res +def is_execution_error(returncode: int) -> bool: + '''Check if a command failed due to an execution error.''' + return returncode < 0 or returncode >= 128 + # pylint: disable-next=invalid-name def run_CE(options: dict, cfg: dict) -> list: @@ -154,10 +158,12 @@ def run_CE(options: dict, cfg: dict) -> list: if res.returncode == 0: return [1, "Failed - CE (no error reported)."] - if res.returncode >= 256: + if is_execution_error(res.returncode): return [1, "Failed - CE (execution error)."] - if options['gold'] is not None and not check_gold(options, log_list): - return [1, "Failed - CE (Gold file doesn't match output)."] + if options['gold'] is not None: + gold_rtn = check_gold(options, log_list) + if gold_rtn[0]: + return [1, "Failed - CE (Gold file doesn't match output)."] return [0, "Passed - CE."] @@ -248,6 +254,8 @@ def options_to_pass(options: dict) -> list: def build_ivl_return(translation_fail: bool, res: subprocess.CompletedProcess) -> list: '''Generate the return for the iverilog run.''' + if is_execution_error(res.returncode): + return [1, "Failed - iverilog execution error."] if translation_fail: if res.returncode != 0: return [0, "Passed - TE."] @@ -258,11 +266,11 @@ def build_ivl_return(translation_fail: bool, res: subprocess.CompletedProcess) - def build_vvp_return(expected_fail: bool, res: subprocess.CompletedProcess) -> list: '''Generate the return for the vvp run.''' + if is_execution_error(res.returncode): + return [1, "Failed - vvp execution error"] if res.returncode != 0 and expected_fail: return [0, "Passed - EF."] - if res.returncode >= 256: - return [1, "Failed - vvp execution error"] - if res.returncode > 0 and res.returncode < 256 and not expected_fail: + if res.returncode > 0 and not expected_fail: return [1, "Failed - vvp error, but expected to succeed"] return [] From 9004d10df44335494eab9f7a3a040be1b877477f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Jun 2026 14:36:14 -0700 Subject: [PATCH 032/101] Handle invalid l-value indexed part select bases The l-value indexed part select path elaborates the base expression with `elab_and_eval()`. If the base expression can not be bound this returns a nullptr, but the l-value path dereferenced it while checking the expression type. For example, `a[does_not_exist -: 2] = 2'b00;` reported the bind error and then crashed. Return early when base elaboration fails. This matches the r-value indexed part select path and leaves the existing bind error as the reported elaboration error. Signed-off-by: Lars-Peter Clausen --- elab_lval.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/elab_lval.cc b/elab_lval.cc index 1068954d1..4d14fdb83 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -883,8 +883,10 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, calculate_up_do_width_(des, scope, wid); NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); + if (!base) + return false; - if (base && base->expr_type() == IVL_VT_REAL) { + if (base->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: Indexed part select base " "expression for "; cerr << lv->sig()->name() << "[" << *base; From eaa719760274a8e2157e86cfa3cd40ef059dcd3d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Jun 2026 14:28:00 -0700 Subject: [PATCH 033/101] Handle bogus member l-value paths Currently the procedural l-value path asserts if symbol lookup leaves a member tail for a variable that is not a struct or class. For example, `r.bad = 1'b1;` where `r` is a scalar variable aborts during elaboration instead of reporting a normal error. Report an error for the leftover member path before the assertion. This matches the r-value path behavior for the same kind of invalid member access. Signed-off-by: Lars-Peter Clausen --- elab_lval.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/elab_lval.cc b/elab_lval.cc index 1068954d1..526b15d5b 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -298,6 +298,15 @@ NetAssign_*PEIdent::elaborate_lval_var_(Design *des, NetScope *scope, // Past this point, we should have taken care of the cases // where the name is a member/method of a struct/class. // XXXX ivl_assert(*this, method_name.nil()); + if (!tail_path.empty()) { + cerr << get_fileline() << ": error: Variable " + << reg->name() + << " does not have a field named: " + << tail_path << "." << endl; + des->errors += 1; + return nullptr; + } + ivl_assert(*this, tail_path.empty()); bool need_const_idx = is_cassign || is_force; From 9fb607d98e1b46a68dbfbff60db8d83a5cb31c96 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Jun 2026 14:37:27 -0700 Subject: [PATCH 034/101] Add regression test for indexed part select l-value bases Check that an invalid indexed part select base on a procedural l-value is reported as a normal compile error instead of crashing after the bind error. Signed-off-by: Lars-Peter Clausen --- .../ivltests/sv_lval_idx_part_invalid_base_down_fail.v | 9 +++++++++ ivtest/regress-vvp.list | 1 + .../sv_lval_idx_part_invalid_base_down_fail.json | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 ivtest/ivltests/sv_lval_idx_part_invalid_base_down_fail.v create mode 100644 ivtest/vvp_tests/sv_lval_idx_part_invalid_base_down_fail.json diff --git a/ivtest/ivltests/sv_lval_idx_part_invalid_base_down_fail.v b/ivtest/ivltests/sv_lval_idx_part_invalid_base_down_fail.v new file mode 100644 index 000000000..769a76899 --- /dev/null +++ b/ivtest/ivltests/sv_lval_idx_part_invalid_base_down_fail.v @@ -0,0 +1,9 @@ +// Check that invalid indexed part-select l-value bases report an error. + +module test; + reg [31:0] a; + + initial begin + a[does_not_exist -: 2] = 2'b00; + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index b66ada6b5..043df7b52 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -343,6 +343,7 @@ sv_lval_concat_uarray_fail1 vvp_tests/sv_lval_concat_uarray_fail1.json sv_lval_concat_uarray_fail2 vvp_tests/sv_lval_concat_uarray_fail2.json sv_lval_concat_uarray_fail3 vvp_tests/sv_lval_concat_uarray_fail3.json sv_lval_concat_uarray_fail4 vvp_tests/sv_lval_concat_uarray_fail4.json +sv_lval_idx_part_invalid_base_down_fail vvp_tests/sv_lval_idx_part_invalid_base_down_fail.json sv_mixed_assign1 vvp_tests/sv_mixed_assign1.json sv_mixed_assign2 vvp_tests/sv_mixed_assign2.json sv_mixed_assign_error1 vvp_tests/sv_mixed_assign_error1.json diff --git a/ivtest/vvp_tests/sv_lval_idx_part_invalid_base_down_fail.json b/ivtest/vvp_tests/sv_lval_idx_part_invalid_base_down_fail.json new file mode 100644 index 000000000..e04a3a978 --- /dev/null +++ b/ivtest/vvp_tests/sv_lval_idx_part_invalid_base_down_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_lval_idx_part_invalid_base_down_fail.v", + "iverilog-args" : [ "-g2001" ] +} From 3c7250eb5173bac8d2f73dfc452cb22c6ebc079f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Jun 2026 14:29:32 -0700 Subject: [PATCH 035/101] Add regression test for bogus member l-values Check that bogus member access on a procedural l-value is rejected with a normal compile error instead of aborting during elaboration. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_bad_member_lval_proc_fail.v | 9 +++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/sv_bad_member_lval_proc_fail.json | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 ivtest/ivltests/sv_bad_member_lval_proc_fail.v create mode 100644 ivtest/vvp_tests/sv_bad_member_lval_proc_fail.json diff --git a/ivtest/ivltests/sv_bad_member_lval_proc_fail.v b/ivtest/ivltests/sv_bad_member_lval_proc_fail.v new file mode 100644 index 000000000..5b43b7afe --- /dev/null +++ b/ivtest/ivltests/sv_bad_member_lval_proc_fail.v @@ -0,0 +1,9 @@ +// Check that bogus member access on procedural l-values reports an error. + +module test; + logic r; + + initial begin + r.bad = 1'b1; + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index b66ada6b5..3ad3643f1 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -254,6 +254,7 @@ sv_array_cassign10 vvp_tests/sv_array_cassign10.json sv_array_cassign_single vvp_tests/sv_array_cassign_single.json sv_array_cassign_single_fail1 vvp_tests/sv_array_cassign_single_fail1.json sv_automatic_2state vvp_tests/sv_automatic_2state.json +sv_bad_member_lval_proc_fail vvp_tests/sv_bad_member_lval_proc_fail.json sv_byte_array_string1 vvp_tests/sv_byte_array_string1.json sv_byte_array_string2 vvp_tests/sv_byte_array_string2.json sv_byte_array_string3 vvp_tests/sv_byte_array_string3.json diff --git a/ivtest/vvp_tests/sv_bad_member_lval_proc_fail.json b/ivtest/vvp_tests/sv_bad_member_lval_proc_fail.json new file mode 100644 index 000000000..dc312633a --- /dev/null +++ b/ivtest/vvp_tests/sv_bad_member_lval_proc_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_bad_member_lval_proc_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} From c53e4245b9c1fc8a31fcdfa0a3dc9c3bd056ee20 Mon Sep 17 00:00:00 2001 From: Flavien Solt Date: Mon, 22 Jun 2026 10:38:26 +0800 Subject: [PATCH 036/101] Add regression test for negative zero sign preservation Check that the vvp code generator emits a -0.0 real constant with its sign bit set, so the compiled value matches the runtime real value. The sign used to be detected with (value < 0), which is false for IEEE 754 -0.0, and a -0.0 constant was turned into +0.0. Co-Authored-By: Claude Opus 4.8 (1M context) --- ivtest/ivltests/real_negative_zero.v | 35 ++++++++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/real_negative_zero.json | 4 +++ 3 files changed, 40 insertions(+) create mode 100644 ivtest/ivltests/real_negative_zero.v create mode 100644 ivtest/vvp_tests/real_negative_zero.json diff --git a/ivtest/ivltests/real_negative_zero.v b/ivtest/ivltests/real_negative_zero.v new file mode 100644 index 000000000..03199005c --- /dev/null +++ b/ivtest/ivltests/real_negative_zero.v @@ -0,0 +1,35 @@ +// Check that the vvp code generator preserves the sign of a negative zero +// real constant. The sign used to be detected with (value < 0), which is +// false for IEEE 754 -0.0, so a -0.0 constant was emitted as +0.0 and the +// compiled value no longer matched the runtime real value. + +module test; + + real nz; + real pz; + + initial begin + nz = -0.0; + pz = 0.0; + + // The sign bit is the only thing that tells -0.0 from +0.0. + if ($realtobits(nz) !== 64'h8000_0000_0000_0000) begin + $display("FAILED: -0.0 stored as %h", $realtobits(nz)); + $finish; + end + + if ($realtobits(pz) !== 64'h0000_0000_0000_0000) begin + $display("FAILED: 0.0 stored as %h", $realtobits(pz)); + $finish; + end + + // The reciprocal makes the sign observable: 1.0/-0.0 is -inf. + if (1.0 / nz >= 0.0) begin + $display("FAILED: 1.0/-0.0 = %g", 1.0 / nz); + $finish; + end + + $display("PASSED"); + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index e17f69eee..97eaddbd0 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -207,6 +207,7 @@ pv_wr_fn_vec2 vvp_tests/pv_wr_fn_vec2.json pv_wr_fn_vec4 vvp_tests/pv_wr_fn_vec4.json queue_fail vvp_tests/queue_fail.json readmem-invalid vvp_tests/readmem-invalid.json +real_negative_zero vvp_tests/real_negative_zero.json scaled_real vvp_tests/scaled_real.json scan-invalid vvp_tests/scan-invalid.json sdf_interconnect1 vvp_tests/sdf_interconnect1.json diff --git a/ivtest/vvp_tests/real_negative_zero.json b/ivtest/vvp_tests/real_negative_zero.json new file mode 100644 index 000000000..1165f27f5 --- /dev/null +++ b/ivtest/vvp_tests/real_negative_zero.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "real_negative_zero.v" +} From 1babbc0c92314c037569cf18ba3d96a074ddb079 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 21 Jun 2026 22:50:14 -0700 Subject: [PATCH 037/101] Fix segmentation fault when the input file is missing --- ivlpp/lexor.lex | 4 ++-- main.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index 6da2aaafe..a1d64dea3 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -2368,6 +2368,8 @@ void reset_lexor(FILE* out, char* paths[]) isp->stringify_flag = 0; isp->comment = NULL; + yyout = out; + if (isp->file == 0) { perror(paths[0]); error_count += 1; @@ -2382,8 +2384,6 @@ void reset_lexor(FILE* out, char* paths[]) } } - yyout = out; - yyrestart(isp->file); assert(istack == 0); diff --git a/main.cc b/main.cc index 917e006e9..563fd8bcc 100644 --- a/main.cc +++ b/main.cc @@ -1215,7 +1215,7 @@ int main(int argc, char*argv[]) if (pre_process_fail_count) { cerr << "Preprocessor failed with " << pre_process_fail_count - << " errors." << endl; + << " error(s)." << endl; return pre_process_fail_count; } From 8c7f8f3f7ba5d843f9da5867207afc70b7224674 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 21 Jun 2026 23:03:44 -0700 Subject: [PATCH 038/101] Fix gold file to fix error message change --- ivtest/gold/br_gh72b_fail.gold | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivtest/gold/br_gh72b_fail.gold b/ivtest/gold/br_gh72b_fail.gold index 57f44c49c..80abadc09 100644 --- a/ivtest/gold/br_gh72b_fail.gold +++ b/ivtest/gold/br_gh72b_fail.gold @@ -3,4 +3,4 @@ ./ivltests/br_gh72b_fail.v:8: error: too few arguments for `macro2 ./ivltests/br_gh72b_fail.v:9: error: too few arguments for `macro2 ./ivltests/br_gh72b_fail.v:10: error: too many arguments for `macro2 -Preprocessor failed with 5 errors. +Preprocessor failed with 5 error(s). From 5311c0cd385e1a1c7f25579ef092482a68c335d9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 12:33:31 -0700 Subject: [PATCH 039/101] vvp: Add opcode for unary real minus Currently the vvp target emits unary real minus as `0.0 - value`. This is not the same operation for all real values. It loses the negative zero result for `-(+0.0)` and does not reliably flip the sign bit for NaN values whose bits are visible through `$realtobits`. Add `%neg/wr` and use it for unary real minus. This performs a direct negation of the real stack value, so zero, NaN and infinity all use the same operation as unary minus instead of a binary subtraction from zero. Signed-off-by: Lars-Peter Clausen --- Documentation/developer/guide/vvp/opcodes.rst | 4 ++++ tgt-vvp/eval_real.c | 3 +-- vvp/codes.h | 1 + vvp/compile.cc | 1 + vvp/vthread.cc | 9 +++++++++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Documentation/developer/guide/vvp/opcodes.rst b/Documentation/developer/guide/vvp/opcodes.rst index dd7e6cffc..3303e8b50 100644 --- a/Documentation/developer/guide/vvp/opcodes.rst +++ b/Documentation/developer/guide/vvp/opcodes.rst @@ -812,6 +812,10 @@ result is pushed back on the vec4 stack. This opcode multiplies two real words together. +* %neg/wr + +This opcode negates the real value on top of the real stack. + * %nand Perform the bitwise NAND of two vec4 vectors, and push the result. Each diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index 0c685624f..1a996b814 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -453,9 +453,8 @@ static void draw_unary_real(ivl_expr_t expr) } if (ivl_expr_opcode(expr) == '-') { - fprintf(vvp_out, " %%pushi/real 0, 0; load 0.0\n"); draw_eval_real(sube); - fprintf(vvp_out, " %%sub/wr;\n"); + fprintf(vvp_out, " %%neg/wr;\n"); return; } diff --git a/vvp/codes.h b/vvp/codes.h index 94a4f26b5..00594e1f0 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -164,6 +164,7 @@ extern bool of_MOD_WR(vthread_t thr, vvp_code_t code); extern bool of_MUL(vthread_t thr, vvp_code_t code); extern bool of_MULI(vthread_t thr, vvp_code_t code); extern bool of_MUL_WR(vthread_t thr, vvp_code_t code); +extern bool of_NEG_WR(vthread_t thr, vvp_code_t code); extern bool of_NAND(vthread_t thr, vvp_code_t code); extern bool of_NANDR(vthread_t thr, vvp_code_t code); extern bool of_NEW_COBJ(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index d41c19d72..fa5391717 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -219,6 +219,7 @@ static const struct opcode_table_s opcode_table[] = { { "%muli", of_MULI, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%nand", of_NAND, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%nand/r", of_NANDR, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%neg/wr", of_NEG_WR, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%new/cobj", of_NEW_COBJ, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, { "%new/darray",of_NEW_DARRAY,2, {OA_BIT1, OA_STRING,OA_NONE} }, { "%noop", of_NOOP, 0, {OA_NONE, OA_NONE, OA_NONE} }, diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 98cd41a99..2b171111f 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -4288,6 +4288,15 @@ bool of_MOD_WR(vthread_t thr, vvp_code_t) return true; } +/* + * %neg/wr + */ +bool of_NEG_WR(vthread_t thr, vvp_code_t) +{ + thr->poke_real(0, -thr->peek_real(0)); + return true; +} + /* * %pad/s */ From 0e7c62d579f3e6fbee119fe51681f3e303863761 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 12:33:52 -0700 Subject: [PATCH 040/101] Add regression tests for unary real minus special values Check that unary real minus preserves the sign or bit pattern for zero, NaN, and infinity. Each test starts with the positive value, negates it, and then negates the result back to the positive value. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/real_unary_minus_inf.v | 31 +++++++++++++++++++++ ivtest/ivltests/real_unary_minus_nan.v | 31 +++++++++++++++++++++ ivtest/ivltests/real_unary_minus_zero.v | 31 +++++++++++++++++++++ ivtest/regress-vvp.list | 3 ++ ivtest/vvp_tests/real_unary_minus_inf.json | 4 +++ ivtest/vvp_tests/real_unary_minus_nan.json | 4 +++ ivtest/vvp_tests/real_unary_minus_zero.json | 4 +++ 7 files changed, 108 insertions(+) create mode 100644 ivtest/ivltests/real_unary_minus_inf.v create mode 100644 ivtest/ivltests/real_unary_minus_nan.v create mode 100644 ivtest/ivltests/real_unary_minus_zero.v create mode 100644 ivtest/vvp_tests/real_unary_minus_inf.json create mode 100644 ivtest/vvp_tests/real_unary_minus_nan.json create mode 100644 ivtest/vvp_tests/real_unary_minus_zero.json diff --git a/ivtest/ivltests/real_unary_minus_inf.v b/ivtest/ivltests/real_unary_minus_inf.v new file mode 100644 index 000000000..543644d68 --- /dev/null +++ b/ivtest/ivltests/real_unary_minus_inf.v @@ -0,0 +1,31 @@ +// Check that unary real minus preserves infinities. + +module test; + + reg failed; + real inf; + real result; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %h, got %h", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + inf = $bitstoreal(64'h7ff0000000000000); + + result = -inf; + `check($realtobits(result), 64'hfff0000000000000); + + result = -result; + `check($realtobits(result), 64'h7ff0000000000000); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/real_unary_minus_nan.v b/ivtest/ivltests/real_unary_minus_nan.v new file mode 100644 index 000000000..f9ea679aa --- /dev/null +++ b/ivtest/ivltests/real_unary_minus_nan.v @@ -0,0 +1,31 @@ +// Check that unary real minus preserves NaN bits. + +module test; + + reg failed; + real nan; + real result; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %h, got %h", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + nan = $bitstoreal(64'h7ff8000000000001); + + result = -nan; + `check($realtobits(result), 64'hfff8000000000001); + + result = -result; + `check($realtobits(result), 64'h7ff8000000000001); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/real_unary_minus_zero.v b/ivtest/ivltests/real_unary_minus_zero.v new file mode 100644 index 000000000..1c92bc27d --- /dev/null +++ b/ivtest/ivltests/real_unary_minus_zero.v @@ -0,0 +1,31 @@ +// Check that unary real minus preserves the sign of zero. + +module test; + + reg failed; + real zero; + real result; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %h, got %h", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + zero = 0.0; + + result = -zero; + `check($realtobits(result), 64'h8000000000000000); + + result = -result; + `check($realtobits(result), 64'h0000000000000000); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 7689bca81..9655c20df 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -214,6 +214,9 @@ pv_wr_fn_vec4 vvp_tests/pv_wr_fn_vec4.json queue_fail vvp_tests/queue_fail.json readmem-invalid vvp_tests/readmem-invalid.json real_negative_zero vvp_tests/real_negative_zero.json +real_unary_minus_inf vvp_tests/real_unary_minus_inf.json +real_unary_minus_nan vvp_tests/real_unary_minus_nan.json +real_unary_minus_zero vvp_tests/real_unary_minus_zero.json scaled_real vvp_tests/scaled_real.json scan-invalid vvp_tests/scan-invalid.json sdf_interconnect1 vvp_tests/sdf_interconnect1.json diff --git a/ivtest/vvp_tests/real_unary_minus_inf.json b/ivtest/vvp_tests/real_unary_minus_inf.json new file mode 100644 index 000000000..15f4203e7 --- /dev/null +++ b/ivtest/vvp_tests/real_unary_minus_inf.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "real_unary_minus_inf.v" +} diff --git a/ivtest/vvp_tests/real_unary_minus_nan.json b/ivtest/vvp_tests/real_unary_minus_nan.json new file mode 100644 index 000000000..4dde327de --- /dev/null +++ b/ivtest/vvp_tests/real_unary_minus_nan.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "real_unary_minus_nan.v" +} diff --git a/ivtest/vvp_tests/real_unary_minus_zero.json b/ivtest/vvp_tests/real_unary_minus_zero.json new file mode 100644 index 000000000..27bf853c9 --- /dev/null +++ b/ivtest/vvp_tests/real_unary_minus_zero.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "real_unary_minus_zero.v" +} From 55d78cf1a8aef439c0f136b7889ad63423237b08 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Jun 2026 14:22:47 -0700 Subject: [PATCH 041/101] Handle missing string substr() arguments The string substr() method reports an error if it is called with the wrong number of arguments, but the error was not counted and elaboration continued with missing function arguments. A call such as `s.substr(0)` could therefore crash after printing the diagnostic. Count the arity error and fill missing internal arguments with dummy constants so elaboration can recover without building an incomplete system function call. Signed-off-by: Lars-Peter Clausen --- elab_expr.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 6c9d7b17a..1035ebe8a 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -3474,10 +3474,12 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, } if (method_name == "substr") { - if (parms_.size() != 2) + if (parms_.size() != 2) { cerr << get_fileline() << ": error: Method `substr()`" << " requires 2 arguments, got " << parms_.size() << "." << endl; + des->errors += 1; + } static const std::vector parm_names = { perm_string::literal("i"), @@ -3493,8 +3495,12 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, sys_expr->parm(0, sub_expr); for (int i = 0; i < 2; i++) { - if (!args[i]) + if (!args[i]) { + NetEConst*expr = make_const_0(32); + expr->set_line(*this); + sys_expr->parm(i + 1, expr); continue; + } auto expr = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, From d246979d2620c05e959a2cc8587d6eb98367cdc3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Jun 2026 14:23:50 -0700 Subject: [PATCH 042/101] Add regression test for string substr() arity error Check that a string substr() call with too few arguments is rejected with a normal compile error. Signed-off-by: Lars-Peter Clausen --- .../sv_string_method_substr_too_few_arg_fail.v | 10 ++++++++++ ivtest/regress-vvp.list | 1 + .../sv_string_method_substr_too_few_arg_fail.json | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 ivtest/ivltests/sv_string_method_substr_too_few_arg_fail.v create mode 100644 ivtest/vvp_tests/sv_string_method_substr_too_few_arg_fail.json diff --git a/ivtest/ivltests/sv_string_method_substr_too_few_arg_fail.v b/ivtest/ivltests/sv_string_method_substr_too_few_arg_fail.v new file mode 100644 index 000000000..d0a45e78e --- /dev/null +++ b/ivtest/ivltests/sv_string_method_substr_too_few_arg_fail.v @@ -0,0 +1,10 @@ +// Check that string substr() rejects too few arguments. + +module test; + string s; + string t; + + initial begin + t = s.substr(0); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 9655c20df..8b9eacb56 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -374,6 +374,7 @@ sv_queue_method_push_back_too_few_arg_fail vvp_tests/sv_queue_method_push_back_t sv_queue_method_push_back_too_many_arg_fail vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json sv_queue_method_push_front_too_few_arg_fail vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json sv_queue_method_push_front_too_many_arg_fail vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json +sv_string_method_substr_too_few_arg_fail vvp_tests/sv_string_method_substr_too_few_arg_fail.json sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json sv_super_member_fail vvp_tests/sv_super_member_fail.json diff --git a/ivtest/vvp_tests/sv_string_method_substr_too_few_arg_fail.json b/ivtest/vvp_tests/sv_string_method_substr_too_few_arg_fail.json new file mode 100644 index 000000000..20453dfe5 --- /dev/null +++ b/ivtest/vvp_tests/sv_string_method_substr_too_few_arg_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_string_method_substr_too_few_arg_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} From d2a97663b933e0cc158ba621127a8359a80fb573 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 26 Jun 2026 20:53:51 -0700 Subject: [PATCH 043/101] Allow type identifiers to be shadowed in more places SystemVerilog allows an identifier that is visible as a typedef to be shadowed by a declaration in a nested scope or reused as a declaration name in another namespace. The lexer can return `TYPE_IDENTIFIER` before the new name has been installed, so these grammar positions reject otherwise valid code. This is not a complete conversion of all identifier grammar sites. Only handle the trivial conflict-free cases where `IDENTIFIER` can be replaced by `identifier_name` without any surrounding grammar changes. Also stop type lookup when the current scope already has a local symbol with the same name. This makes later references to a shadowing declaration use the local symbol instead of an outer typedef. Signed-off-by: Lars-Peter Clausen --- parse.y | 89 +++++++++++++++++++++++++++++--------------------------- pform.cc | 6 ++++ 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/parse.y b/parse.y index 985afe40a..4b44d0358 100644 --- a/parse.y +++ b/parse.y @@ -747,7 +747,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type block_identifier_opt %type identifier_name %type event_variable_list -%type list_of_identifiers +%type genvar_identifier_list list_of_identifiers %type loop_variables %type list_of_port_identifiers list_of_variable_port_identifiers @@ -1688,27 +1688,27 @@ function_declaration /* IEEE1800-2005: A.2.6 */ ; genvar_iteration /* IEEE1800-2012: A.4.2 */ - : IDENTIFIER '=' expression + : identifier_name '=' expression { $$.text = $1; $$.expr = $3; } - | IDENTIFIER compressed_operator expression + | identifier_name compressed_operator expression { $$.text = $1; $$.expr = pform_genvar_compressed(@1, $1, $2, $3);; } - | IDENTIFIER K_INCR + | identifier_name K_INCR { $$.text = $1; $$.expr = pform_genvar_inc_dec(@1, $1, true); } - | IDENTIFIER K_DECR + | identifier_name K_DECR { $$.text = $1; $$.expr = pform_genvar_inc_dec(@1, $1, false); } - | K_INCR IDENTIFIER + | K_INCR identifier_name { $$.text = $2; $$.expr = pform_genvar_inc_dec(@1, $2, true); } - | K_DECR IDENTIFIER + | K_DECR identifier_name { $$.text = $2; $$.expr = pform_genvar_inc_dec(@1, $2, false); } @@ -2010,7 +2010,7 @@ variable_decl_assignment /* IEEE1800-2005 A.2.3 */ loop_variables /* IEEE1800-2005: A.6.8 */ - : loop_variables ',' IDENTIFIER + : loop_variables ',' identifier_name { std::list*tmp = $1; tmp->push_back(lex_strings.make($3)); delete[]$3; @@ -2021,7 +2021,7 @@ loop_variables /* IEEE1800-2005: A.6.8 */ tmp->push_back(perm_string()); $$ = tmp; } - | IDENTIFIER + | identifier_name { std::list*tmp = new std::list; tmp->push_back(lex_strings.make($1)); delete[]$1; @@ -2058,7 +2058,7 @@ modport_item_list ; modport_item - : IDENTIFIER + : identifier_name { pform_start_modport_item(@1, $1); } '(' modport_ports_list ')' { pform_end_modport_item(@1); } @@ -2087,7 +2087,7 @@ modport_ports_list { if (last_modport_port.type != MP_TF) yyerror(@3, "error: task/function declaration not allowed here."); } - | modport_ports_list ',' IDENTIFIER + | modport_ports_list ',' identifier_name { if (last_modport_port.type == MP_SIMPLE) { pform_add_modport_port(@3, last_modport_port.direction, lex_strings.make($3), 0); @@ -2101,7 +2101,7 @@ modport_ports_list ; modport_ports_declaration - : attribute_list_opt port_direction IDENTIFIER + : attribute_list_opt port_direction identifier_name { last_modport_port.type = MP_SIMPLE; last_modport_port.direction = $2; pform_add_modport_port(@3, $2, lex_strings.make($3), 0); @@ -2115,7 +2115,7 @@ modport_ports_declaration delete $3; delete $1; } - | attribute_list_opt import_export IDENTIFIER + | attribute_list_opt import_export identifier_name { last_modport_port.type = MP_TF; last_modport_port.is_import = $2; yyerror(@3, "sorry: modport task/function ports are not yet supported."); @@ -2128,7 +2128,7 @@ modport_ports_declaration yyerror(@3, "sorry: modport task/function ports are not yet supported."); delete $1; } - | attribute_list_opt K_clocking IDENTIFIER + | attribute_list_opt K_clocking identifier_name { last_modport_port.type = MP_CLOCKING; last_modport_port.direction = NetNet::NOT_A_PORT; yyerror(@3, "sorry: modport clocking declaration is not yet supported."); @@ -2197,16 +2197,11 @@ package_import_declaration /* IEEE1800-2005 A.2.1.3 */ ; package_import_item - : package_scope IDENTIFIER + : package_scope identifier_name { lex_in_package_scope(0); pform_package_import(@1, $1, $2); delete[]$2; } - | package_scope TYPE_IDENTIFIER - { lex_in_package_scope(0); - pform_package_import(@1, $1, $2.text); - delete[]$2.text; - } | package_scope '*' { lex_in_package_scope(0); pform_package_import(@1, $1, 0); @@ -2224,14 +2219,10 @@ package_export_declaration /* IEEE1800-2017 A.2.1.3 */ ; package_export_item - : PACKAGE_IDENTIFIER K_SCOPE_RES IDENTIFIER + : PACKAGE_IDENTIFIER K_SCOPE_RES identifier_name { pform_package_export(@2, $1, $3); delete[] $3; } - | PACKAGE_IDENTIFIER K_SCOPE_RES TYPE_IDENTIFIER - { pform_package_export(@2, $1, $3.text); - delete[] $3.text; - } | PACKAGE_IDENTIFIER K_SCOPE_RES '*' { pform_package_export(@2, $1, nullptr); } @@ -3274,7 +3265,7 @@ delay_value_simple FILE_NAME($$, @1); } } - | IDENTIFIER + | identifier_name { PEIdent*tmp = new PEIdent(lex_strings.make($1), @1.lexical_pos); FILE_NAME(tmp, @1); $$ = tmp; @@ -3351,16 +3342,16 @@ nature_item : K_units '=' STRING ';' { delete[] $3; } | K_abstol '=' expression ';' - | K_access '=' IDENTIFIER ';' + | K_access '=' identifier_name ';' { pform_nature_access(@1, $3); delete[] $3; } - | K_idt_nature '=' IDENTIFIER ';' + | K_idt_nature '=' identifier_name ';' { delete[] $3; } - | K_ddt_nature '=' IDENTIFIER ';' + | K_ddt_nature '=' identifier_name ';' { delete[] $3; } ; config_declaration - : K_config IDENTIFIER ';' + : K_config identifier_name ';' K_design lib_cell_identifiers ';' list_of_config_rule_statements K_endconfig @@ -3527,9 +3518,9 @@ event_expression or explicit as a named branch. Elaboration will check that the function name really is a nature attribute identifier. */ branch_probe_expression - : IDENTIFIER '(' IDENTIFIER ',' IDENTIFIER ')' + : identifier_name '(' identifier_name ',' identifier_name ')' { $$ = pform_make_branch_probe_expression(@1, $1, $3, $5); } - | IDENTIFIER '(' IDENTIFIER ')' + | identifier_name '(' identifier_name ')' { $$ = pform_make_branch_probe_expression(@1, $1, $3); } ; @@ -4603,6 +4594,13 @@ list_of_identifiers { $$ = list_from_identifier($1, $3, @3.lexical_pos); } ; +genvar_identifier_list + : identifier_name + { $$ = list_from_identifier($1, @1.lexical_pos); } + | genvar_identifier_list ',' identifier_name + { $$ = list_from_identifier($1, $3, @3.lexical_pos); } + ; + list_of_port_identifiers : IDENTIFIER dimensions_opt { $$ = make_port_list($1, @1.lexical_pos, $2, 0); } @@ -4809,7 +4807,7 @@ cont_assign_list items, and finally an end marker. */ module - : attribute_list_opt module_start lifetime_opt IDENTIFIER + : attribute_list_opt module_start lifetime_opt identifier_name { pform_startmodule(@2, $4, $2==K_program, $2==K_interface, $3, $1); port_declaration_context_init(); } module_package_import_list_opt @@ -4916,7 +4914,12 @@ label_opt ; module_attribute_foreign - : K_PSTAR IDENTIFIER K_integer IDENTIFIER '=' STRING ';' K_STARP { $$ = 0; } + : K_PSTAR identifier_name K_integer identifier_name '=' STRING ';' K_STARP + { delete[] $2; + delete[] $4; + delete[] $6; + $$ = 0; + } | { $$ = 0; } ; @@ -5290,10 +5293,10 @@ module_item | K_generate { check_in_gen_region(@1); } generate_item_list_opt K_endgenerate { in_gen_region = false; } - | K_genvar list_of_identifiers ';' + | K_genvar genvar_identifier_list ';' { pform_genvars(@1, $2); } - | K_for '(' K_genvar_opt IDENTIFIER '=' expression ';' + | K_for '(' K_genvar_opt identifier_name '=' expression ';' expression ';' genvar_iteration ')' { pform_start_generate_for(@2, $3, $4, $6, $8, $10.text, $10.expr); } @@ -6340,13 +6343,13 @@ specify_simple_path ; specify_path_identifiers - : IDENTIFIER + : identifier_name { std::list*tmp = new std::list; tmp->push_back(lex_strings.make($1)); $$ = tmp; delete[]$1; } - | IDENTIFIER '[' expr_primary ']' + | identifier_name '[' expr_primary ']' { if (gn_specify_blocks_flag) { yywarn(@4, "warning: Bit selects are not currently supported " "in path declarations. The declaration " @@ -6357,7 +6360,7 @@ specify_path_identifiers $$ = tmp; delete[]$1; } - | IDENTIFIER '[' expr_primary polarity_operator expr_primary ']' + | identifier_name '[' expr_primary polarity_operator expr_primary ']' { if (gn_specify_blocks_flag) { yywarn(@4, "warning: Part selects are not currently supported " "in path declarations. The declaration " @@ -6368,13 +6371,13 @@ specify_path_identifiers $$ = tmp; delete[]$1; } - | specify_path_identifiers ',' IDENTIFIER + | specify_path_identifiers ',' identifier_name { std::list*tmp = $1; tmp->push_back(lex_strings.make($3)); $$ = tmp; delete[]$3; } - | specify_path_identifiers ',' IDENTIFIER '[' expr_primary ']' + | specify_path_identifiers ',' identifier_name '[' expr_primary ']' { if (gn_specify_blocks_flag) { yywarn(@4, "warning: Bit selects are not currently supported " "in path declarations. The declaration " @@ -6385,7 +6388,7 @@ specify_path_identifiers $$ = tmp; delete[]$3; } - | specify_path_identifiers ',' IDENTIFIER '[' expr_primary polarity_operator expr_primary ']' + | specify_path_identifiers ',' identifier_name '[' expr_primary polarity_operator expr_primary ']' { if (gn_specify_blocks_flag) { yywarn(@4, "warning: Part selects are not currently supported " "in path declarations. The declaration " @@ -6399,7 +6402,7 @@ specify_path_identifiers ; specparam - : IDENTIFIER '=' expr_mintypmax + : identifier_name '=' expr_mintypmax { pform_set_specparam(@1, lex_strings.make($1), specparam_active_range, $3); delete[]$1; } diff --git a/pform.cc b/pform.cc index d9249910c..cf2f730ce 100644 --- a/pform.cc +++ b/pform.cc @@ -923,6 +923,12 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) if (cur != cur_scope->typedefs.end()) return cur->second; + // If it is defined as something else in this scope, it is not a + // data type. + auto sym = cur_scope->local_symbols.find(name); + if (sym != cur_scope->local_symbols.end()) + return nullptr; + PPackage*pkg = pform_find_potential_import(loc, cur_scope, name, false, false); if (pkg) { cur = pkg->typedefs.find(name); From 77fdcfd800671f6128d7e81b11d65b5e1d613c8f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 26 Jun 2026 20:54:14 -0700 Subject: [PATCH 044/101] Add regression tests for shadowing type identifiers Check that visible type identifiers can be shadowed by declarations in other namespaces or nested scopes. Keep each grammar category in a separate regression so failures identify the affected rule. Also check that package import and export items can name a type identifier. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_ams_name_fields.v | 18 +++++++++++ .../ivltests/sv_type_identifier_config_name.v | 13 ++++++++ .../sv_type_identifier_foreach_name.v | 29 +++++++++++++++++ .../ivltests/sv_type_identifier_genvar_name.v | 27 ++++++++++++++++ .../sv_type_identifier_modport_name.v | 19 ++++++++++++ .../ivltests/sv_type_identifier_module_name.v | 17 ++++++++++ .../sv_type_identifier_package_item.v | 31 +++++++++++++++++++ .../sv_type_identifier_specparam_name.v | 14 +++++++++ ivtest/regress-vvp.list | 8 +++++ .../sv_type_identifier_ams_name_fields.json | 5 +++ .../sv_type_identifier_config_name.json | 5 +++ .../sv_type_identifier_foreach_name.json | 9 ++++++ .../sv_type_identifier_genvar_name.json | 9 ++++++ .../sv_type_identifier_modport_name.json | 5 +++ .../sv_type_identifier_module_name.json | 5 +++ .../sv_type_identifier_package_item.json | 5 +++ .../sv_type_identifier_specparam_name.json | 5 +++ 17 files changed, 224 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_ams_name_fields.v create mode 100644 ivtest/ivltests/sv_type_identifier_config_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_foreach_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_genvar_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_modport_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_module_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_package_item.v create mode 100644 ivtest/ivltests/sv_type_identifier_specparam_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_ams_name_fields.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_config_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_foreach_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_genvar_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_modport_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_module_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_package_item.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_specparam_name.json diff --git a/ivtest/ivltests/sv_type_identifier_ams_name_fields.v b/ivtest/ivltests/sv_type_identifier_ams_name_fields.v new file mode 100644 index 000000000..d9569b764 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_ams_name_fields.v @@ -0,0 +1,18 @@ +// Check that nature name fields can shadow visible type identifiers. + +typedef int ACCESS_NAME; +typedef int IDT_NAME; +typedef int DDT_NAME; + +nature type_id_nature; + units = "V"; + access = ACCESS_NAME; + idt_nature = IDT_NAME; + ddt_nature = DDT_NAME; +endnature + +module test; + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_config_name.v b/ivtest/ivltests/sv_type_identifier_config_name.v new file mode 100644 index 000000000..e57962585 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_config_name.v @@ -0,0 +1,13 @@ +// Check that a config name can shadow a visible type identifier. + +typedef int CFG_NAME; + +config CFG_NAME; + design test; +endconfig + +module test; + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_foreach_name.v b/ivtest/ivltests/sv_type_identifier_foreach_name.v new file mode 100644 index 000000000..351326619 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_foreach_name.v @@ -0,0 +1,29 @@ +// Check that foreach loop variables can shadow visible type identifiers. + +typedef int I; +typedef int J; + +module test; + + reg failed; + int array [2][2]; + + initial begin + failed = 1'b0; + + foreach (array[I,J]) begin + array[I][J] = I * 10 + J; + end + + if (array[0][0] != 0 || array[0][1] != 1 || + array[1][0] != 10 || array[1][1] != 11) begin + $display("FAILED(%0d). foreach indices did not hide typedefs", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_genvar_name.v b/ivtest/ivltests/sv_type_identifier_genvar_name.v new file mode 100644 index 000000000..499a25712 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_genvar_name.v @@ -0,0 +1,27 @@ +// Check that a genvar name can shadow a visible type identifier. + +typedef int G; + +module test; + reg failed; + + genvar G; + generate + for (G = 0; G < 2; G = G + 1) begin : gen_block + localparam int VALUE = G; + end + endgenerate + + initial begin + failed = 1'b0; + + if (gen_block[0].VALUE != 0 || gen_block[1].VALUE != 1) begin + $display("FAILED(%0d). genvar name did not hide typedef", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_modport_name.v b/ivtest/ivltests/sv_type_identifier_modport_name.v new file mode 100644 index 000000000..f58feab4c --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_modport_name.v @@ -0,0 +1,19 @@ +// Check that a modport name can shadow a visible type identifier. + +typedef int M; + +interface type_id_modport_ifc; + logic value; + + modport M(input value); +endinterface + +module test; + + type_id_modport_ifc i_ifc(); + + initial begin + $display("PASSED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_module_name.v b/ivtest/ivltests/sv_type_identifier_module_name.v new file mode 100644 index 000000000..57425f2e6 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_module_name.v @@ -0,0 +1,17 @@ +// Check that a module name can shadow a visible type identifier. + +package p; + typedef int M; +endpackage + +import p::*; + +module M; + initial begin + $display("PASSED"); + end +endmodule + +module test; + M i(); +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_package_item.v b/ivtest/ivltests/sv_type_identifier_package_item.v new file mode 100644 index 000000000..d64259aec --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_package_item.v @@ -0,0 +1,31 @@ +// Check that package import and export items can name a type identifier. + +package type_id_name_pkg; + typedef logic [3:0] T; +endpackage + +package type_id_name_export_pkg; + import type_id_name_pkg::T; + export type_id_name_pkg::T; +endpackage + +module test; + import type_id_name_export_pkg::T; + + reg failed; + T value; + + initial begin + failed = 1'b0; + value = 4'ha; + + if ($bits(value) != 4 || value != 4'ha) begin + $display("FAILED(%0d). Imported type mismatch", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_specparam_name.v b/ivtest/ivltests/sv_type_identifier_specparam_name.v new file mode 100644 index 000000000..16572d095 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_specparam_name.v @@ -0,0 +1,14 @@ +// Check that a specparam name can shadow a visible type identifier. + +typedef int SP_DELAY; + +module test(input in, output out); + specify + specparam SP_DELAY = 1; + (in => out) = SP_DELAY; + endspecify + + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 8b9eacb56..ea188deac 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -378,6 +378,14 @@ sv_string_method_substr_too_few_arg_fail vvp_tests/sv_string_method_substr_too_f sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json sv_super_member_fail vvp_tests/sv_super_member_fail.json +sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json +sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json +sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json +sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json +sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json +sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json +sv_type_identifier_package_item vvp_tests/sv_type_identifier_package_item.json +sv_type_identifier_specparam_name vvp_tests/sv_type_identifier_specparam_name.json sv_type_param_restrict_class1 vvp_tests/sv_type_param_restrict_class1.json sv_type_param_restrict_class2 vvp_tests/sv_type_param_restrict_class2.json sv_type_param_restrict_class_fail1 vvp_tests/sv_type_param_restrict_class_fail1.json diff --git a/ivtest/vvp_tests/sv_type_identifier_ams_name_fields.json b/ivtest/vvp_tests/sv_type_identifier_ams_name_fields.json new file mode 100644 index 000000000..9323ed119 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_ams_name_fields.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_ams_name_fields.v", + "iverilog-args" : [ "-g2005-sv", "-gverilog-ams" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_config_name.json b/ivtest/vvp_tests/sv_type_identifier_config_name.json new file mode 100644 index 000000000..f31e350a6 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_config_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_config_name.v", + "iverilog-args" : [ "-g2005-sv", "-gverilog-ams" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_foreach_name.json b/ivtest/vvp_tests/sv_type_identifier_foreach_name.json new file mode 100644 index 000000000..52463b1f0 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_foreach_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_foreach_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and foreach loops are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_genvar_name.json b/ivtest/vvp_tests/sv_type_identifier_genvar_name.json new file mode 100644 index 000000000..954ef7288 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_genvar_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_genvar_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and generate blocks are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_modport_name.json b/ivtest/vvp_tests/sv_type_identifier_modport_name.json new file mode 100644 index 000000000..c02a11338 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_modport_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_modport_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_module_name.json b/ivtest/vvp_tests/sv_type_identifier_module_name.json new file mode 100644 index 000000000..587f0bd54 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_module_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_module_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_package_item.json b/ivtest/vvp_tests/sv_type_identifier_package_item.json new file mode 100644 index 000000000..8dc1e2178 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_package_item.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_package_item.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_specparam_name.json b/ivtest/vvp_tests/sv_type_identifier_specparam_name.json new file mode 100644 index 000000000..741ed0c47 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_specparam_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_specparam_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} From d5306085c53020ada2cb364bc47659ca9acd6bb4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 27 Jun 2026 17:36:17 -0700 Subject: [PATCH 045/101] vvp: Support local flag on real variables The vvp parser did not accept the local flag on `.var/real` declarations. This can happen when elaboration creates a compiler-generated real temporary, for example when a blocking intra-assignment delay is rewritten from: r = #1 1.25; to assign the right hand side to a temporary before the delay and assign the temporary to the target after the delay. Add support for the local flag. Keep a VPI symbol for the variable so `%load/real` can still resolve the label, but do not attach local real variables to the current scope. Signed-off-by: Lars-Peter Clausen --- vvp/compile.h | 2 +- vvp/parse.y | 4 ++-- vvp/words.cc | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/vvp/compile.h b/vvp/compile.h index cccc2391a..f2e949d66 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -504,7 +504,7 @@ extern void compile_variable(char*label, char*name, int msb, int lsb, int vpi_type_code, bool signed_flag, bool local_flag); -extern void compile_var_real(char*label, char*name); +extern void compile_var_real(char*label, char*name, bool local_flag); extern void compile_var_string(char*label, char*name); extern void compile_var_darray(char*label, char*name, unsigned size); extern void compile_var_cobject(char*label, char*name); diff --git a/vvp/parse.y b/vvp/parse.y index bd8dba1bf..2fc3dc93c 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -763,8 +763,8 @@ statement | T_LABEL K_VAR_2U local_flag T_STRING ',' signed_t_number signed_t_number ';' { compile_variable($1, $4, $6, $7, vpiIntVar, false, $3); } - | T_LABEL K_VAR_R T_STRING ',' signed_t_number signed_t_number ';' - { compile_var_real($1, $3); } + | T_LABEL K_VAR_R local_flag T_STRING ',' signed_t_number signed_t_number ';' + { compile_var_real($1, $4, $3); } | T_LABEL K_VAR_STR T_STRING ';' { compile_var_string($1, $3); } diff --git a/vvp/words.cc b/vvp/words.cc index 630c430b0..6291a7646 100644 --- a/vvp/words.cc +++ b/vvp/words.cc @@ -35,7 +35,8 @@ using namespace std; static void __compile_var_real(char*label, char*name, - vvp_array_t array, unsigned long array_addr) + vvp_array_t array, unsigned long array_addr, + bool local_flag) { vvp_net_t*net = new vvp_net_t; @@ -55,7 +56,8 @@ static void __compile_var_real(char*label, char*name, if (name) { assert(!array); - vpip_attach_to_current_scope(obj); + if (!local_flag) + vpip_attach_to_current_scope(obj); if (!vpip_peek_current_scope()->is_automatic()) schedule_init_vector(vvp_net_ptr_t(net,0), 0.0); } @@ -67,9 +69,9 @@ static void __compile_var_real(char*label, char*name, delete[] name; } -void compile_var_real(char*label, char*name) +void compile_var_real(char*label, char*name, bool local_flag) { - __compile_var_real(label, name, 0, 0); + __compile_var_real(label, name, 0, 0, local_flag); } void compile_var_string(char*label, char*name) From a1c333ea6e0d32a7e2655dc5bf5d354d0f03970e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 27 Jun 2026 17:43:12 -0700 Subject: [PATCH 046/101] Add regression test for delayed real assignments Check that a blocking intra-assignment delay on a real value preserves the assigned value after the delay. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/real_delay_assign.v | 20 ++++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/real_delay_assign.json | 4 ++++ 3 files changed, 25 insertions(+) create mode 100644 ivtest/ivltests/real_delay_assign.v create mode 100644 ivtest/vvp_tests/real_delay_assign.json diff --git a/ivtest/ivltests/real_delay_assign.v b/ivtest/ivltests/real_delay_assign.v new file mode 100644 index 000000000..a9d8c7231 --- /dev/null +++ b/ivtest/ivltests/real_delay_assign.v @@ -0,0 +1,20 @@ +// Check that blocking intra-assignment delays can assign real values. + +module test; + real r; + reg failed; + + initial begin + failed = 1'b0; + + r = #1 1.25; + if (r != 1.25) begin + $display("FAILED(%0d). Expected 1.25, got %0.2f", `__LINE__, r); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index ea188deac..424ce1cc6 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -213,6 +213,7 @@ pv_wr_fn_vec2 vvp_tests/pv_wr_fn_vec2.json pv_wr_fn_vec4 vvp_tests/pv_wr_fn_vec4.json queue_fail vvp_tests/queue_fail.json readmem-invalid vvp_tests/readmem-invalid.json +real_delay_assign vvp_tests/real_delay_assign.json real_negative_zero vvp_tests/real_negative_zero.json real_unary_minus_inf vvp_tests/real_unary_minus_inf.json real_unary_minus_nan vvp_tests/real_unary_minus_nan.json diff --git a/ivtest/vvp_tests/real_delay_assign.json b/ivtest/vvp_tests/real_delay_assign.json new file mode 100644 index 000000000..b4442c9fb --- /dev/null +++ b/ivtest/vvp_tests/real_delay_assign.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "real_delay_assign.v" +} From 7cffbf440dbf439718d96b0ac15c7fd1b60a57a9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 25 Jun 2026 23:10:30 -0700 Subject: [PATCH 047/101] Support declaration names shadowing type identifiers SystemVerilog allows a declaration in an inner scope to use the same name as a type identifier from an outer scope. The lexer reports such names as `TYPE_IDENTIFIER` before the new declaration has been installed, which made constructs such as `int T;`, `wire T;`, and `input T` fail when `T` was a visible typedef. The affected declaration forms have a local type/name/dimension ambiguity. For example, after `input T` or `wire T` the parser does not know whether `T` is the declared name, or whether a following identifier will make `T` the declaration type in `input T x` or `wire T x`. With dimensions, `input T [1:0]` and `wire T [1:0]` can be either a declaration named `T` with unpacked dimensions or a declaration using typedef `T` as a packed type followed by another name. Parse these declaration forms with productions that decide the first declarator and carry the selected declaration type across the rest of the list. This covers variable declarations, net declarations, ANSI and non-ANSI module port declarations, and task/function port declarations. Other identifier uses still need separate grammar changes. Signed-off-by: Lars-Peter Clausen --- parse.y | 619 +++++++++++++++++++++++++++++++++++--------------- pform_types.h | 5 + 2 files changed, 442 insertions(+), 182 deletions(-) diff --git a/parse.y b/parse.y index 4b44d0358..1c9caf208 100644 --- a/parse.y +++ b/parse.y @@ -124,6 +124,41 @@ static void check_net_decl_assigns(const struct vlltype&loc, } } +static data_type_t *pform_make_parray_type(const struct vlltype&loc, + data_type_t *base, + std::list *pdims) +{ + if (!pdims) + return base; + + data_type_t *type = new parray_type_t(base, pdims); + FILE_NAME(type, loc); + + return type; +} + +template +static void set_type_id_range(T&value, data_type_t *type, char *id, + unsigned lexical_pos, + std::list *ranges) +{ + value.type = type; + value.id = id; + value.lexical_pos = lexical_pos; + value.ranges = ranges; +} + +template +static void delete_type_id_range(T&value) +{ + delete value.type; + delete[] value.id; + delete value.ranges; + value.type = nullptr; + value.id = nullptr; + value.ranges = nullptr; +} + /* 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. */ @@ -169,25 +204,29 @@ static std::list*attributes_in_context = 0; static const struct str_pair_t pull_strength = { IVL_DR_PULL, IVL_DR_PULL }; static const struct str_pair_t str_strength = { IVL_DR_STRONG, IVL_DR_STRONG }; -static std::list* make_port_list(char*id, unsigned idn, - std::list*udims, - PExpr*expr) +static struct pform_port_list make_port_list(data_type_t *type, char*id, + unsigned idn, + std::list*udims, + PExpr*expr) { - std::list*tmp = new std::list; + struct pform_port_list list; + list.type = type; + list.ports = new std::list; pform_ident_t tmp_name = { lex_strings.make(id), idn }; - tmp->push_back(pform_port_t(tmp_name, udims, expr)); + list.ports->push_back(pform_port_t(tmp_name, udims, expr)); delete[]id; - return tmp; + return list; } -static std::list* make_port_list(list*tmp, - char*id, unsigned idn, - std::list*udims, - PExpr*expr) + +static struct pform_port_list make_port_list(struct pform_port_list list, + char*id, unsigned idn, + std::list*udims, + PExpr*expr) { pform_ident_t tmp_name = { lex_strings.make(id), idn }; - tmp->push_back(pform_port_t(tmp_name, udims, expr)); + list.ports->push_back(pform_port_t(tmp_name, udims, expr)); delete[]id; - return tmp; + return list; } static std::list* list_from_identifier(char*id, unsigned idn) @@ -206,6 +245,60 @@ static std::list* list_from_identifier(list*tmp, return tmp; } +static decl_assignment_t *pform_make_var_decl(const YYLTYPE&loc, char *id, + unsigned lexical_pos, + std::list*udims, + PExpr *init) +{ + if (init && pform_peek_scope()->var_init_needs_explicit_lifetime() && + var_lifetime == LexicalScope::INHERITED) { + cerr << loc << ": warning: Static variable initialization requires " + "explicit lifetime in this context." << endl; + warn_count += 1; + } + decl_assignment_t *decl = new decl_assignment_t; + decl->name = { lex_strings.make(id), lexical_pos }; + if (udims) { + decl->index = *udims; + delete udims; + } + decl->expr.reset(init); + delete[] id; + return decl; +} + +static decl_assignment_t *pform_make_var_decl(const YYLTYPE&loc, char *id, + std::list*udims, + PExpr *init) +{ + return pform_make_var_decl(loc, id, loc.lexical_pos, udims, init); +} + +static decl_assignment_t *pform_make_net_decl(const YYLTYPE&loc, char *id, + unsigned lexical_pos, + std::list*udims, + PExpr *init) +{ + decl_assignment_t *decl = new decl_assignment_t; + decl->name = { lex_strings.make(id), lexical_pos }; + if (udims) { + decl->index = *udims; + if (init) + pform_requires_sv(loc, "Assignment of net array during declaration"); + delete udims; + } + decl->expr.reset(init); + delete[] id; + return decl; +} + +static decl_assignment_t *pform_make_net_decl(const YYLTYPE&loc, char *id, + std::list*udims, + PExpr *init) +{ + return pform_make_net_decl(loc, id, loc.lexical_pos, udims, init); +} + template void append(vector&out, const std::vector&in) { for (size_t idx = 0 ; idx < in.size() ; idx += 1) @@ -427,14 +520,15 @@ static void port_declaration_context_init(void) } Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, - NetNet::PortType port_type, + unsigned lexical_pos, + NetNet::PortType port_type, NetNet::Type net_type, data_type_t *data_type, std::list *unpacked_dims, PExpr *default_value, std::list *attributes) { - pform_ident_t name = { lex_strings.make(id), loc.lexical_pos }; + pform_ident_t name = { lex_strings.make(id), lexical_pos }; delete[] id; Module::port_t *port = pform_module_port_reference(loc, name.first); @@ -527,8 +621,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, std::list*perm_strings; std::list*identifiers; - - std::list*port_list; + struct pform_port_list port_list; std::vector* tf_ports; @@ -583,6 +676,11 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, decl_assignment_t*decl_assignment; std::list*decl_assignments; + struct { + data_type_t *type; + std::list *decl_assignments; + } decl_assignments_with_type; + struct_member_t*struct_member; std::list*struct_members; struct_type_t*struct_type; @@ -597,6 +695,13 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, typedef_t*type; } type_identifier; + struct { + data_type_t *type; + char *id; + unsigned lexical_pos; + std::list*ranges; + } type_id_range; + struct { data_type_t*type; std::list *args; @@ -807,12 +912,15 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type net_decl_assign variable_decl_assignment %type net_decl_assigns list_of_variable_decl_assignments +%type list_of_net_decl_assignments_with_type +%type list_of_variable_decl_assignments_with_type %type data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void -%type data_type_or_implicit_no_opt -%type simple_type_or_string let_formal_type -%type packed_array_data_type -%type ps_type_identifier +%type implicit_type +%type reg_prefixed_atomic_type simple_type_or_string let_formal_type +%type packed_array_data_type atomic_type + +%type ps_type_identifier ps_type_identifier_dim %type simple_packed_type %type class_scope %type struct_union_member @@ -833,7 +941,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type net_type net_type_opt net_type_or_var net_type_or_var_opt %type gatetype switchtype %type port_direction port_direction_opt -%type integer_vector_type +%type integer_vector_type integer_vector_type_no_reg %type parameter_value_opt %type event_expression_list @@ -877,6 +985,11 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type compressed_operator %type forward_type forward_type_without_enum +%type data_type_or_implicit_plus_id_base +%type data_type_or_implicit_plus_id_dim +%type partial_port_name_dim +%type partial_port_type_plus_id_dim +%type partial_port_typedef_plus_id_dim %token K_TAND %nonassoc K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ @@ -1332,13 +1445,13 @@ data_declaration /* IEEE1800-2005: A.2.1.3 */ $1, $2); var_lifetime = LexicalScope::INHERITED; } - | attribute_list_opt K_const_opt K_var variable_lifetime_opt data_type_or_implicit list_of_variable_decl_assignments ';' - { data_type_t *data_type = $5; + | attribute_list_opt K_const_opt K_var variable_lifetime_opt list_of_variable_decl_assignments_with_type ';' + { data_type_t*data_type = $5.type; if (!data_type) { - data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); + data_type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); FILE_NAME(data_type, @3); } - pform_make_var(@3, $6, data_type, $1, $2); + pform_make_var(@3, $5.decl_assignments, data_type, $1, $2); var_lifetime = LexicalScope::INHERITED; } | attribute_list_opt K_event event_variable_list ';' @@ -1354,6 +1467,7 @@ package_scope } ; + // Type identifiers with and without attached packed dimensions. ps_type_identifier /* IEEE1800-2017: A.9.3 */ : TYPE_IDENTIFIER { pform_set_type_referenced(@1, $1.text); @@ -1369,17 +1483,33 @@ ps_type_identifier /* IEEE1800-2017: A.9.3 */ } ; +ps_type_identifier_dim /* IEEE1800-2017: A.9.3 */ + : TYPE_IDENTIFIER dimensions_opt + { pform_set_type_referenced(@1, $1.text); + data_type_t*tmp = new typeref_t($1.type); + FILE_NAME(tmp, @1); + delete[]$1.text; + $$ = pform_make_parray_type(@2, tmp, $2); + } + | package_scope TYPE_IDENTIFIER dimensions_opt + { lex_in_package_scope(nullptr); + data_type_t*tmp = new typeref_t($2.type, $1); + FILE_NAME(tmp, @2); + $$ = pform_make_parray_type(@3, tmp, $3); + delete[]$2.text; + } + ; + /* Data types that can have packed dimensions directly attached to it */ packed_array_data_type /* IEEE1800-2005: A.2.2.1 */ - : enum_data_type - { $$ = $1; } - | struct_data_type + : enum_data_type dimensions_opt + { $$ = pform_make_parray_type(@2, $1, $2); } + | struct_data_type dimensions_opt { if (!$1->packed_flag && !($1->union_flag && $1->soft_flag)) { yyerror(@1, "sorry: Unpacked structs not supported."); - } - $$ = $1; + } + $$ = pform_make_parray_type(@2, $1, $2); } - | ps_type_identifier ; simple_packed_type /* Integer and vector types */ @@ -1400,7 +1530,7 @@ simple_packed_type /* Integer and vector types */ } ; -data_type /* IEEE1800-2005: A.2.2.1 */ +atomic_type : simple_packed_type { $$ = $1; } @@ -1409,14 +1539,40 @@ data_type /* IEEE1800-2005: A.2.2.1 */ FILE_NAME(tmp, @1); $$ = tmp; } - | packed_array_data_type dimensions_opt - { if ($2) { - parray_type_t*tmp = new parray_type_t($1, $2); - FILE_NAME(tmp, @1); - $$ = tmp; - } else { - $$ = $1; - } + | packed_array_data_type { $$ = $1; } + | K_string + { string_type_t*tmp = new string_type_t; + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + +/* Data types allowed after the historical iverilog extension that permits an + extra leading `reg` in block declarations. Keep `reg` itself out of this + subset so ordinary `reg` declarations continue through the normal rules. */ +reg_prefixed_atomic_type + : integer_vector_type_no_reg unsigned_signed_opt dimensions_opt + { vector_type_t*tmp = new vector_type_t($1, $2, $3); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | atom_type signed_unsigned_opt + { atom_type_t*tmp = new atom_type_t($1, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_time unsigned_signed_opt + { atom_type_t*tmp = new atom_type_t(atom_type_t::TIME, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | non_integer_type + { real_type_t*tmp = new real_type_t($1); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | packed_array_data_type + { $$ = $1; } | K_string { string_type_t*tmp = new string_type_t; @@ -1425,6 +1581,11 @@ data_type /* IEEE1800-2005: A.2.2.1 */ } ; +data_type /* IEEE1800-2005: A.2.2.1 */ + : atomic_type { $$ = $1; } + | ps_type_identifier_dim { $$ = $1; } + ; + /* Data type or nothing, but not implicit */ data_type_opt : data_type { $$ = $1; } @@ -1436,6 +1597,11 @@ data_type_opt absent. The context may need that information to decide to resort to left context. */ +data_type_or_implicit /* IEEE1800-2005: A.2.2.1 */ + : data_type_opt { $$ = $1; } + | implicit_type { $$ = $1; } + ; + scalar_vector_opt /*IEEE1800-2005: optional support for packed array */ : K_vectored { /* Ignore */ } @@ -1445,14 +1611,8 @@ scalar_vector_opt /*IEEE1800-2005: optional support for packed array */ { /* Ignore */ } ; -data_type_or_implicit /* IEEE1800-2005: A.2.2.1 */ - : data_type_or_implicit_no_opt - | { $$ = nullptr; } - -data_type_or_implicit_no_opt - : data_type - { $$ = $1; } - | signing dimensions_opt +implicit_type + : signing dimensions_opt { vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, $1, $2); tmp->implicit_flag = true; FILE_NAME(tmp, @1); @@ -1466,7 +1626,6 @@ data_type_or_implicit_no_opt } ; - data_type_or_implicit_or_void : data_type_or_implicit { $$ = $1; } @@ -1772,7 +1931,13 @@ inside_expression /* IEEE1800-2005 A.8.3 */ integer_vector_type /* IEEE1800-2005: A.2.2.1 */ : K_reg { $$ = IVL_VT_LOGIC; } /* A synonym for logic. */ - | K_bit { $$ = IVL_VT_BOOL; } + | integer_vector_type_no_reg + { $$ = $1; + } + ; + +integer_vector_type_no_reg + : K_bit { $$ = IVL_VT_BOOL; } | K_logic { $$ = IVL_VT_LOGIC; } | K_bool { $$ = IVL_VT_BOOL; } /* Icarus Verilog xtypes extension */ ; @@ -1962,6 +2127,18 @@ loop_statement /* IEEE1800-2005: A.6.8 */ } ; +list_of_variable_decl_assignments_with_type /* IEEE1800-2005 A.2.3 */ + : data_type_or_implicit_plus_id_dim var_decl_initializer_opt + { std::list*tmp = new std::list; + tmp->push_back(pform_make_var_decl(@1, $1.id, $1.lexical_pos, $1.ranges, $2)); + $$.decl_assignments = tmp; + $$.type = $1.type; + } + | list_of_variable_decl_assignments_with_type ',' variable_decl_assignment + { $1.decl_assignments->push_back($3); + $$ = $1; + } + ; list_of_variable_decl_assignments /* IEEE1800-2005 A.2.3 */ : variable_decl_assignment @@ -1988,23 +2165,8 @@ var_decl_initializer_opt ; variable_decl_assignment /* IEEE1800-2005 A.2.3 */ - : IDENTIFIER dimensions_opt var_decl_initializer_opt - { if ($3 && pform_peek_scope()->var_init_needs_explicit_lifetime() - && (var_lifetime == LexicalScope::INHERITED)) { - cerr << @1 << ": warning: Static variable initialization requires " - "explicit lifetime in this context." << endl; - warn_count += 1; - } - - decl_assignment_t*tmp = new decl_assignment_t; - tmp->name = { lex_strings.make($1), @1.lexical_pos }; - if ($2) { - tmp->index = *$2; - delete $2; - } - tmp->expr.reset($3); - delete[]$1; - $$ = tmp; + : identifier_name dimensions_opt var_decl_initializer_opt + { $$ = pform_make_var_decl(@1, $1, $2, $3); } ; @@ -2077,7 +2239,7 @@ modport_ports_list | modport_ports_list ',' named_expression { if (last_modport_port.type == MP_SIMPLE) { pform_add_modport_port(@3, last_modport_port.direction, - $3->name, $3->parm); + $3->name, $3->parm); } else { yyerror(@3, "error: modport expression not allowed here."); } @@ -2551,11 +2713,81 @@ task_declaration /* IEEE1800-2005: A.2.7 */ tf_port_declaration /* IEEE1800-2005: A.2.7 */ - : port_direction K_var_opt data_type_or_implicit list_of_port_identifiers ';' - { $$ = pform_make_task_ports(@1, $1, $3, $4, true); + : port_direction K_var_opt list_of_port_identifiers ';' + { $$ = pform_make_task_ports(@1, $1, $3.type, $3.ports, true); } ; + // These rules only disambiguate declaration items that can be either an + // implicit declaration name or an explicit type followed by a name. Keep the + // bare `TYPE_IDENTIFIER dimensions_opt` case in the parent rule so a typedef + // name can be shadowed by an unpacked declaration name, while + // `ps_type_identifier_dim identifier_name` still parses a typedef with packed + // dimensions followed by a separate declaration name. +data_type_or_implicit_plus_id_base + : IDENTIFIER + { set_type_id_range($$, nullptr, $1, @1.lexical_pos, nullptr); + } + | atomic_type identifier_name + { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + } + | implicit_type identifier_name + { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + } + | ps_type_identifier_dim identifier_name + { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + } + ; + +data_type_or_implicit_plus_id_dim + : TYPE_IDENTIFIER dimensions_opt + { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + } + | data_type_or_implicit_plus_id_base dimensions_opt + { set_type_id_range($$, $1.type, $1.id, $1.lexical_pos, $2); + } + ; + + // Partial ANSI port declarations such as `input a, integer b` can redeclare + // the data type without repeating the direction. Keep this narrower than + // data_type_or_implicit_plus_id_dim so a bare identifier after a comma is + // still parsed as a continuation of the previous port declaration. +partial_port_type_plus_id_dim + : atomic_type identifier_name dimensions_opt + { set_type_id_range($$, $1, $2, @2.lexical_pos, $3); + } + | implicit_type identifier_name dimensions_opt + { set_type_id_range($$, $1, $2, @2.lexical_pos, $3); + } + ; + +partial_port_name_dim + : IDENTIFIER dimensions_opt + { set_type_id_range($$, nullptr, $1, @1.lexical_pos, $2); + } + | TYPE_IDENTIFIER dimensions_opt + { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + } + ; + +partial_port_typedef_plus_id_dim + : TYPE_IDENTIFIER dimensions_opt identifier_name dimensions_opt + { pform_set_type_referenced(@1, $1.text); + data_type_t*tmp = new typeref_t($1.type); + FILE_NAME(tmp, @1); + delete[]$1.text; + tmp = pform_make_parray_type(@2, tmp, $2); + set_type_id_range($$, tmp, $3, @3.lexical_pos, $4); + } + | package_scope TYPE_IDENTIFIER dimensions_opt identifier_name dimensions_opt + { lex_in_package_scope(nullptr); + data_type_t*tmp = new typeref_t($2.type, $1); + FILE_NAME(tmp, @2); + delete[]$2.text; + tmp = pform_make_parray_type(@3, tmp, $3); + set_type_id_range($$, tmp, $4, @4.lexical_pos, $5); + } + ; /* These rules for tf_port_item are slightly expanded from the strict rules in the LRM to help with LALR parsing. @@ -2567,61 +2799,62 @@ tf_port_declaration /* IEEE1800-2005: A.2.7 */ tf_port_item /* IEEE1800-2005: A.2.7 */ - : port_direction_opt K_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt + : port_direction_opt K_var_opt data_type_or_implicit_plus_id_dim initializer_opt { std::vector*tmp; NetNet::PortType use_port_type = $1; - if ((use_port_type == NetNet::PIMPLICIT) && (gn_system_verilog() || ($3 == 0))) + if ((use_port_type == NetNet::PIMPLICIT) && (gn_system_verilog() || !$3.type)) use_port_type = port_declaration_context.port_type; - list* port_list = make_port_list($4, @4.lexical_pos, $5, 0); + struct pform_port_list port_list = make_port_list($3.type, $3.id, + $3.lexical_pos, + $3.ranges, nullptr); if (use_port_type == NetNet::PIMPLICIT) { yyerror(@1, "error: Missing task/function port direction."); use_port_type = NetNet::PINPUT; // for error recovery } - if (($3 == 0) && ($1==NetNet::PIMPLICIT)) { + if (!$3.type && ($1==NetNet::PIMPLICIT)) { // Detect special case this is an undecorated // identifier and we need to get the declaration from // left context. - if ($5 != 0) { - yyerror(@5, "internal error: How can there be an unpacked range here?\n"); - } - tmp = pform_make_task_ports(@4, use_port_type, + tmp = pform_make_task_ports(@3, use_port_type, port_declaration_context.data_type, - port_list); + port_list.ports); } else { // Otherwise, the decorations for this identifier // indicate the type. Save the type for any right // context that may come later. port_declaration_context.port_type = use_port_type; - if ($3 == 0) { - $3 = new vector_type_t(IVL_VT_LOGIC, false, 0); - FILE_NAME($3, @4); + if (!$3.type) { + $3.type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); + FILE_NAME($3.type, @3); } - port_declaration_context.data_type = $3; - tmp = pform_make_task_ports(@3, use_port_type, $3, port_list); + port_declaration_context.data_type = $3.type; + tmp = pform_make_task_ports(@3, use_port_type, $3.type, + port_list.ports); } $$ = tmp; - if ($6) { - pform_requires_sv(@6, "Task/function default argument"); + if ($4) { + pform_requires_sv(@4, "Task/function default argument"); assert(tmp->size()==1); - tmp->front().defe = $6; + tmp->front().defe = $4; } } /* Rules to match error cases... */ - | port_direction_opt K_var_opt data_type_or_implicit IDENTIFIER error - { yyerror(@3, "error: Error in task/function port item after port name %s.", $4); + | port_direction_opt K_var_opt data_type_or_implicit_plus_id_dim error + { yyerror(@3, "error: Error in task/function port item after port name %s.", $3.id); + delete_type_id_range($3); yyerrok; - $$ = 0; + $$ = nullptr; } ; tf_port_list /* IEEE1800-2005: A.2.7 */ : { port_declaration_context.port_type = gn_system_verilog() ? NetNet::PINPUT : NetNet::PIMPLICIT; - port_declaration_context.data_type = 0; + port_declaration_context.data_type = nullptr; } tf_port_item_list { $$ = $2; } @@ -2816,13 +3049,13 @@ block_item_decl /* variable declarations. Note that data_type can be 0 if we are recovering from an error. */ - : K_const_opt K_var variable_lifetime_opt data_type_or_implicit list_of_variable_decl_assignments ';' - { data_type_t *data_type = $4; + : K_const_opt K_var variable_lifetime_opt list_of_variable_decl_assignments_with_type ';' + { data_type_t*data_type = $4.type; if (!data_type) { - data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); - FILE_NAME(data_type, @2); + data_type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); + FILE_NAME(data_type, @2); } - pform_make_var(@2, $5, data_type, attributes_in_context, $1); + pform_make_var(@2, $4.decl_assignments, data_type, attributes_in_context, $1); var_lifetime = LexicalScope::INHERITED; } @@ -2831,8 +3064,8 @@ block_item_decl var_lifetime = LexicalScope::INHERITED; } - /* The extra `reg` is not valid (System)Verilog, this is a iverilog extension. */ - | K_const_opt variable_lifetime_opt K_reg data_type list_of_variable_decl_assignments ';' + /* The extra `reg` is not valid (System)Verilog, this is an iverilog extension. */ + | K_const_opt variable_lifetime_opt K_reg reg_prefixed_atomic_type list_of_variable_decl_assignments ';' { if ($4) pform_make_var(@4, $5, $4, attributes_in_context, $1); var_lifetime = LexicalScope::INHERITED; } @@ -2853,11 +3086,6 @@ block_item_decl /* Recover from errors that happen within variable lists. Use the trailing semi-colon to resync the parser. */ - - | K_const_opt K_var variable_lifetime_opt data_type_or_implicit error ';' - { yyerror(@1, "error: Syntax error in variable list."); - yyerrok; - } | K_const_opt variable_lifetime_opt data_type error ';' { yyerror(@1, "error: Syntax error in variable list."); yyerrok; @@ -2945,13 +3173,8 @@ enum_base_type /* IEEE 1800-2012 A.2.2.1 */ : simple_packed_type { $$ = $1; } - | ps_type_identifier dimensions_opt - { if ($2) { - $$ = new parray_type_t($1, $2); - FILE_NAME($$, @1); - } else { - $$ = $1; - } + | ps_type_identifier_dim + { $$ = $1; } | { $$ = new atom_type_t(atom_type_t::INT, true); @@ -4602,16 +4825,16 @@ genvar_identifier_list ; list_of_port_identifiers - : IDENTIFIER dimensions_opt - { $$ = make_port_list($1, @1.lexical_pos, $2, 0); } - | list_of_port_identifiers ',' IDENTIFIER dimensions_opt - { $$ = make_port_list($1, $3, @3.lexical_pos, $4, 0); } + : data_type_or_implicit_plus_id_dim + { $$ = make_port_list($1.type, $1.id, $1.lexical_pos, $1.ranges, nullptr); } + | list_of_port_identifiers ',' identifier_name dimensions_opt + { $$ = make_port_list($1, $3, @3.lexical_pos, $4, nullptr); } ; list_of_variable_port_identifiers - : IDENTIFIER dimensions_opt initializer_opt - { $$ = make_port_list($1, @1.lexical_pos, $2, $3); } - | list_of_variable_port_identifiers ',' IDENTIFIER dimensions_opt initializer_opt + : data_type_or_implicit_plus_id_dim initializer_opt + { $$ = make_port_list($1.type, $1.id, $1.lexical_pos, $1.ranges, $2); } + | list_of_variable_port_identifiers ',' identifier_name dimensions_opt initializer_opt { $$ = make_port_list($1, $3, @3.lexical_pos, $4, $5); } ; @@ -4656,23 +4879,41 @@ list_of_port_declarations tmp->push_back($3); $$ = tmp; } - | list_of_port_declarations ',' attribute_list_opt IDENTIFIER dimensions_opt initializer_opt + | list_of_port_declarations ',' attribute_list_opt partial_port_name_dim initializer_opt { std::vector *ports = $1; Module::port_t* port; if (port_declaration_context.port_type == NetNet::NOT_A_PORT) { yyerror(@4, "error: Incomplete interface port declaration."); - delete[]$4; + delete_type_id_range($4); delete $5; - delete $6; delete $3; port = 0; } else { - port = module_declare_port(@4, $4, + port = module_declare_port(@4, $4.id, $4.lexical_pos, port_declaration_context.port_type, port_declaration_context.port_net_type, port_declaration_context.data_type, - $5, $6, $3); + $4.ranges, $5, $3); + } + ports->push_back(port); + $$ = ports; + } + | list_of_port_declarations ',' attribute_list_opt partial_port_typedef_plus_id_dim initializer_opt + { std::vector *ports = $1; + + Module::port_t* port; + if (port_declaration_context.port_type == NetNet::NOT_A_PORT) { + yyerror(@4, "error: Incomplete interface port declaration."); + delete_type_id_range($4); + delete $5; + delete $3; + port = 0; + } else { + port = module_declare_port(@4, $4.id, $4.lexical_pos, + port_declaration_context.port_type, + NetNet::IMPLICIT, $4.type, + $4.ranges, $5, $3); } ports->push_back(port); $$ = ports; @@ -4686,29 +4927,32 @@ list_of_port_declarations // All of port direction, port kind and data type are optional, but at least // one has to be specified, so we need multiple rules. port_declaration - : attribute_list_opt port_direction net_type_or_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt - { $$ = module_declare_port(@5, $5, $2, $3, $4, $6, $7, $1); + : attribute_list_opt port_direction net_type_or_var_opt data_type_or_implicit_plus_id_dim initializer_opt + { $$ = module_declare_port(@4, $4.id, $4.lexical_pos, + $2, $3, $4.type, $4.ranges, $5, $1); } - | attribute_list_opt INTERFACE_IDENTIFIER '.' IDENTIFIER IDENTIFIER dimensions_opt + | attribute_list_opt INTERFACE_IDENTIFIER '.' identifier_name identifier_name dimensions_opt { $$ = module_declare_interface_port(@5, $2, $4, $5, $6, $1); } - | attribute_list_opt INTERFACE_IDENTIFIER IDENTIFIER dimensions_opt + | attribute_list_opt INTERFACE_IDENTIFIER identifier_name dimensions_opt { $$ = module_declare_interface_port(@3, $2, 0, $3, $4, $1); } - | attribute_list_opt net_type_or_var data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt - { pform_requires_sv(@4, "Partial ANSI port declaration"); - $$ = module_declare_port(@4, $4, port_declaration_context.port_type, - $2, $3, $5, $6, $1); - } - | attribute_list_opt data_type_or_implicit_no_opt IDENTIFIER dimensions_opt initializer_opt + | attribute_list_opt net_type_or_var data_type_or_implicit_plus_id_dim initializer_opt { pform_requires_sv(@3, "Partial ANSI port declaration"); - $$ = module_declare_port(@3, $3, port_declaration_context.port_type, - NetNet::IMPLICIT, $2, $4, $5, $1); + $$ = module_declare_port(@3, $3.id, $3.lexical_pos, + port_declaration_context.port_type, + $2, $3.type, $3.ranges, $4, $1); } - | attribute_list_opt port_direction K_wreal IDENTIFIER + | attribute_list_opt partial_port_type_plus_id_dim initializer_opt + { pform_requires_sv(@2, "Partial ANSI port declaration"); + $$ = module_declare_port(@2, $2.id, $2.lexical_pos, + port_declaration_context.port_type, + NetNet::IMPLICIT, $2.type, $2.ranges, $3, $1); + } + | attribute_list_opt port_direction K_wreal identifier_name { real_type_t*real_type = new real_type_t(real_type_t::REAL); FILE_NAME(real_type, @3); - $$ = module_declare_port(@4, $4, $2, NetNet::WIRE, + $$ = module_declare_port(@4, $4, @4.lexical_pos, $2, NetNet::WIRE, real_type, nullptr, nullptr, $1); } ; @@ -5007,7 +5251,19 @@ module_item net_decl_assigns, which are = assignment declarations. */ - | attribute_list_opt net_type drive_strength_opt data_type_or_implicit delay3_opt net_decl_assigns ';' + | attribute_list_opt net_type drive_strength_opt list_of_net_decl_assignments_with_type ';' + { data_type_t*data_type = $4.type; + pform_check_net_data_type(@2, $2, data_type); + check_net_decl_assigns(@4, $4.decl_assignments); + if (!data_type) { + data_type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); + FILE_NAME(data_type, @2); + } + pform_makewire(@2, nullptr, $3, $4.decl_assignments, $2, data_type, $1); + delete $1; + } + + | attribute_list_opt net_type drive_strength_opt data_type_or_implicit delay3 net_decl_assigns ';' { data_type_t*data_type = $4; pform_check_net_data_type(@2, $2, $4); check_net_decl_assigns(@6, $6); @@ -5041,12 +5297,12 @@ module_item input wire signed [h:l] ; This creates the wire and sets the port type all at once. */ - | attribute_list_opt port_direction net_type_or_var data_type_or_implicit list_of_port_identifiers ';' - { pform_module_define_port(@2, $5, $2, $3, $4, $1); } + | attribute_list_opt port_direction net_type_or_var list_of_port_identifiers ';' + { pform_module_define_port(@2, $4.ports, $2, $3, $4.type, $1); } | attribute_list_opt port_direction K_wreal list_of_port_identifiers ';' { real_type_t*real_type = new real_type_t(real_type_t::REAL); - pform_module_define_port(@2, $4, $2, NetNet::WIRE, real_type, $1); + pform_module_define_port(@2, $4.ports, $2, NetNet::WIRE, real_type, $1); } /* The next three rules handle port declarations that include a variable @@ -5055,33 +5311,33 @@ module_item and also handle incomplete port declarations, e.g. input signed [h:l] ; */ - | attribute_list_opt K_inout data_type_or_implicit list_of_port_identifiers ';' - { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; - if (const vector_type_t*dtype = dynamic_cast ($3)) { + | attribute_list_opt K_inout list_of_port_identifiers ';' + { NetNet::Type use_type = $3.type ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3.type)) { if (dtype->implicit_flag) use_type = NetNet::NONE; } if (use_type == NetNet::NONE) - pform_set_port_type(@2, $4, NetNet::PINOUT, $3, $1); + pform_set_port_type(@2, $3.ports, NetNet::PINOUT, $3.type, $1); else - pform_module_define_port(@2, $4, NetNet::PINOUT, use_type, $3, $1); + pform_module_define_port(@2, $3.ports, NetNet::PINOUT, use_type, $3.type, $1); } - | attribute_list_opt K_input data_type_or_implicit list_of_port_identifiers ';' - { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; - if (const vector_type_t*dtype = dynamic_cast ($3)) { + | attribute_list_opt K_input list_of_port_identifiers ';' + { NetNet::Type use_type = $3.type ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3.type)) { if (dtype->implicit_flag) use_type = NetNet::NONE; } if (use_type == NetNet::NONE) - pform_set_port_type(@2, $4, NetNet::PINPUT, $3, $1); + pform_set_port_type(@2, $3.ports, NetNet::PINPUT, $3.type, $1); else - pform_module_define_port(@2, $4, NetNet::PINPUT, use_type, $3, $1); + pform_module_define_port(@2, $3.ports, NetNet::PINPUT, use_type, $3.type, $1); } - | attribute_list_opt K_output data_type_or_implicit list_of_variable_port_identifiers ';' - { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; - if (const vector_type_t*dtype = dynamic_cast ($3)) { + | attribute_list_opt K_output list_of_variable_port_identifiers ';' + { NetNet::Type use_type = $3.type ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3.type)) { if (dtype->implicit_flag) use_type = NetNet::NONE; else @@ -5091,40 +5347,36 @@ module_item // output ports are implicitly (on the inside) // variables because "reg" is not valid syntax // here. - } else if ($3) { + } else if ($3.type) { use_type = NetNet::IMPLICIT_REG; } if (use_type == NetNet::NONE) - pform_set_port_type(@2, $4, NetNet::POUTPUT, $3, $1); + pform_set_port_type(@2, $3.ports, NetNet::POUTPUT, $3.type, $1); else - pform_module_define_port(@2, $4, NetNet::POUTPUT, use_type, $3, $1); + pform_module_define_port(@2, $3.ports, NetNet::POUTPUT, use_type, $3.type, $1); } - | attribute_list_opt port_direction net_type_or_var data_type_or_implicit error ';' + | attribute_list_opt port_direction net_type_or_var error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($4) delete $4; yyerrok; } - | attribute_list_opt K_inout data_type_or_implicit error ';' + | attribute_list_opt K_inout error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($3) delete $3; yyerrok; } - | attribute_list_opt K_input data_type_or_implicit error ';' + | attribute_list_opt K_input error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($3) delete $3; yyerrok; } - | attribute_list_opt K_output data_type_or_implicit error ';' + | attribute_list_opt K_output error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($3) delete $3; yyerrok; } @@ -5535,19 +5787,22 @@ net_decl_initializer_opt | { $$ = 0; } ; +list_of_net_decl_assignments_with_type /* IEEE1800-2005 A.2.5 */ + : data_type_or_implicit_plus_id_dim net_decl_initializer_opt + { std::list*tmp = new std::list; + tmp->push_back(pform_make_net_decl(@1, $1.id, $1.lexical_pos, $1.ranges, $2)); + $$.decl_assignments = tmp; + $$.type = $1.type; + } + | list_of_net_decl_assignments_with_type ',' net_decl_assign + { $1.decl_assignments->push_back($3); + $$ = $1; + } + ; + net_decl_assign - : 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; + : identifier_name dimensions_opt net_decl_initializer_opt + { $$ = pform_make_net_decl(@1, $1, $2, $3); } ; @@ -5798,7 +6053,7 @@ port references. The port_t object gets its PWire from the port_reference, but its name from the IDENTIFIER. */ - | '.' IDENTIFIER '(' port_reference ')' + | '.' identifier_name '(' port_reference ')' { Module::port_t*tmp = $4; tmp->name = lex_strings.make($2); delete[]$2; @@ -5818,7 +6073,7 @@ port /* This attaches a name to a port reference concatenation list so that parameter passing be name is possible. */ - | '.' IDENTIFIER '(' '{' port_reference_list '}' ')' + | '.' identifier_name '(' '{' port_reference_list '}' ')' { Module::port_t*tmp = $5; tmp->name = lex_strings.make($2); delete[]$2; @@ -5921,14 +6176,14 @@ port_conn_expression_list_with_nuls port_t object to pass it up to the module declaration code. */ port_reference - : IDENTIFIER + : identifier_name { Module::port_t*ptmp; perm_string name = lex_strings.make($1); ptmp = pform_module_port_reference(@1, name); delete[]$1; $$ = ptmp; } - | IDENTIFIER '[' expression ':' expression ']' + | identifier_name '[' expression ':' expression ']' { index_component_t itmp; itmp.sel = index_component_t::SEL_PART; itmp.msb = $3; @@ -5951,7 +6206,7 @@ port_reference delete[]$1; $$ = ptmp; } - | IDENTIFIER '[' expression ']' + | identifier_name '[' expression ']' { index_component_t itmp; itmp.sel = index_component_t::SEL_BIT; itmp.msb = $3; @@ -5973,7 +6228,7 @@ port_reference delete[]$1; $$ = ptmp; } - | IDENTIFIER '[' error ']' + | identifier_name '[' error ']' { yyerror(@1, "error: Invalid port bit select"); Module::port_t*ptmp = new Module::port_t; PEIdent*wtmp = new PEIdent(lex_strings.make($1), @1.lexical_pos); diff --git a/pform_types.h b/pform_types.h index e16273cf6..021229e53 100644 --- a/pform_types.h +++ b/pform_types.h @@ -510,4 +510,9 @@ extern std::ostream& operator<< (std::ostream&out, const name_component_t&that); extern std::ostream& operator<< (std::ostream&out, const index_component_t&that); extern std::ostream& operator<< (std::ostream&out, const type_restrict_t& type); +struct pform_port_list { + std::list *ports; + data_type_t *type; +}; + #endif /* IVL_pform_types_H */ From 89b2c8bd80cd70c05d6f4b036d95191c86ba4cb0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 25 Jun 2026 23:10:50 -0700 Subject: [PATCH 048/101] Add regression tests for declaration names shadowing type identifiers Check that variable and net declaration names can shadow a visible type identifier. Check this for explicit data type declarations, `var` declarations, and net declarations. Check that task and function formal argument names can shadow a visible type identifier, and that typed arguments still use the visible typedef when an argument name follows. Check ambiguous module port declarations where a type identifier can be either the port name or the port type, with and without dimensions, and that declaration lists continue to use the type selected by the first ambiguous declarator. Cover both ANSI and non-ANSI module port declarations. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_type_identifier_net_name.v | 103 ++++++++ .../ivltests/sv_type_identifier_port_name.v | 236 ++++++++++++++++++ ...e_identifier_task_function_argument_name.v | 120 +++++++++ .../sv_type_identifier_variable_name.v | 152 +++++++++++ ivtest/regress-vvp.list | 4 + .../sv_type_identifier_net_name.json | 9 + .../sv_type_identifier_port_name.json | 9 + ...dentifier_task_function_argument_name.json | 9 + .../sv_type_identifier_variable_name.json | 9 + 9 files changed, 651 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_net_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_port_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_task_function_argument_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_variable_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_net_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_port_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_variable_name.json diff --git a/ivtest/ivltests/sv_type_identifier_net_name.v b/ivtest/ivltests/sv_type_identifier_net_name.v new file mode 100644 index 000000000..c92883ea0 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_net_name.v @@ -0,0 +1,103 @@ +// Check that net names can shadow visible type identifiers. + +typedef logic [7:0] T; +typedef int U; +typedef logic [5:0] V; + +`define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + +module net_name(output reg failed); + wire T = 1'b1; + + initial begin + failed = 1'b0; + + `check(T, 1'b1, "wire T did not declare a one-bit net"); + end +endmodule + +module net_list(output reg failed); + wire T, U; + + assign T = 1'b0; + assign U = 1'b1; + + initial begin + failed = 1'b0; + + `check(T, 1'b0, "type identifier net list first value mismatch"); + `check(U, 1'b1, "type identifier net list continuation mismatch"); + end +endmodule + +module net_array(output reg failed); + wire T [1:0]; + + assign T[0] = 1'b0; + assign T[1] = 1'b1; + + initial begin + failed = 1'b0; + + `check(T[0], 1'b0, "type identifier net array first value mismatch"); + `check(T[1], 1'b1, "type identifier net array second value mismatch"); + end +endmodule + +module net_type(output reg failed); + wire T x = 8'ha5; + wire T [1:0] y = 16'h5aa5; + wire V v0, V; + wire T T; + + assign v0 = 6'h2a; + assign V = 6'h15; + assign T = 8'h3c; + + initial begin + failed = 1'b0; + + `check($bits(x), 8, "typed net declaration width regressed"); + `check(x, 8'ha5, "typed net declaration value regressed"); + `check($bits(y), 16, "typed packed net declaration width regressed"); + `check(y, 16'h5aa5, "typed packed net declaration value regressed"); + `check($bits(v0), 6, "type identifier net declaration list first width mismatch"); + `check($bits(V), 6, "type identifier net declaration list did not allow typedef name as continuation"); + `check(V, 6'h15, "type identifier net declaration list value mismatch"); + `check($bits(T), 8, "type-name net declaration did not keep typedef type"); + `check(T, 8'h3c, "type-name net declaration value mismatch"); + end +endmodule + +module test; + reg failed; + wire f0; + wire f1; + wire f2; + wire f3; + + net_name m0(f0); + net_list m1(f1); + net_array m2(f2); + net_type m3(f3); + + initial begin + failed = 1'b0; + + #1; + + `check(f0, 1'b0, "wire T module failed"); + `check(f1, 1'b0, "wire T, U module failed"); + `check(f2, 1'b0, "wire T array module failed"); + `check(f3, 1'b0, "typed wire T module failed"); + + if (!failed) begin + $display("PASSED"); + end + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_port_name.v b/ivtest/ivltests/sv_type_identifier_port_name.v new file mode 100644 index 000000000..b5a8f5b41 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_port_name.v @@ -0,0 +1,236 @@ +// Check that module port names can shadow visible type identifiers. + +typedef logic [3:0] T; + +package p; + typedef logic [5:0] PT; +endpackage + +`define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + +module ansi_name(input T); +endmodule + +module ansi_type(input T x); +endmodule + +module ansi_name_dim(input T [1:0]); +endmodule + +module ansi_type_dim(input T [1:0] x); +endmodule + +module ansi_name_list(input T, U); +endmodule + +module ansi_type_list(input T x, y); +endmodule + +module ansi_type_list_shadow(input T x, T); +endmodule + +module ansi_type_name(input T T, output logic ok); + initial ok = ($bits(T) == 4); +endmodule + +module ansi_type_redecl(input n, T x); +endmodule + +module ansi_type_redecl_dim(input n, T [1:0] x); +endmodule + +module ansi_name_redecl_dim(input n, T [1:0]); +endmodule + +module ansi_pkg_type_redecl(input n, p::PT x); +endmodule + +module ansi_pkg_type_redecl_dim(input n, p::PT [1:0] x); +endmodule + +module decl_name(T); + input T; +endmodule + +module decl_type(x); + input T x; +endmodule + +module decl_type_dim(x); + input T [1:0] x; +endmodule + +module decl_external(.T(x)); + input x; +endmodule + +module decl_name_list(T, U); + input T, U; +endmodule + +module decl_type_list(x, y); + input T x, y; +endmodule + +module decl_type_list_shadow(x, T); + input T x, T; +endmodule + +module decl_type_name(T, ok); + input T T; + output logic ok; + + initial ok = ($bits(T) == 4); +endmodule + +module test; + + reg failed; + logic n; + T t; + logic n_dim [1:0]; + T [1:0] t_dim; + logic n0, n1; + T t0, t1; + logic rn; + T rt; + T [1:0] rt_dim; + logic rn_dim; + logic rt_name_dim [1:0]; + logic rp_n; + logic [5:0] rp_t; + logic rp_dim_n; + logic [1:0][5:0] rp_t_dim; + logic d_n; + T d_t; + T [1:0] d_t_dim; + logic d_ext; + logic d_n0, d_n1; + T d_t0, d_t1; + T s_t0, s_t1; + T d_s_t0, d_s_t1; + T p_tn; + T d_p_tn; + logic p_tn_ok; + logic d_p_tn_ok; + + ansi_name m0(n); + ansi_type m1(t); + ansi_name_dim m2(n_dim); + ansi_type_dim m3(t_dim); + ansi_name_list m4(n0, n1); + ansi_type_list m5(t0, t1); + ansi_type_list_shadow m17(s_t0, s_t1); + ansi_type_name m19(p_tn, p_tn_ok); + ansi_type_redecl m12(rn, rt); + ansi_type_redecl_dim m13(rn, rt_dim); + ansi_name_redecl_dim m14(rn_dim, rt_name_dim); + ansi_pkg_type_redecl m15(rp_n, rp_t); + ansi_pkg_type_redecl_dim m16(rp_dim_n, rp_t_dim); + decl_name m6(d_n); + decl_type m7(d_t); + decl_type_dim m8(d_t_dim); + decl_external m9(d_ext); + decl_name_list m10(d_n0, d_n1); + decl_type_list m11(d_t0, d_t1); + decl_type_list_shadow m18(d_s_t0, d_s_t1); + decl_type_name m20(d_p_tn, d_p_tn_ok); + + initial begin + failed = 1'b0; + + n = 1'b1; + t = 4'ha; + n_dim[0] = 1'b0; + n_dim[1] = 1'b1; + t_dim[0] = 4'h3; + t_dim[1] = 4'hc; + n0 = 1'b0; + n1 = 1'b1; + t0 = 4'h5; + t1 = 4'ha; + s_t0 = 4'h6; + s_t1 = 4'h9; + p_tn = 4'h3; + rn = 1'b1; + rt = 4'hc; + rt_dim[0] = 4'h3; + rt_dim[1] = 4'ha; + rn_dim = 1'b0; + rt_name_dim[0] = 1'b1; + rt_name_dim[1] = 1'b0; + rp_n = 1'b1; + rp_t = 6'h2a; + rp_dim_n = 1'b0; + rp_t_dim[0] = 6'h15; + rp_t_dim[1] = 6'h2a; + d_n = 1'b1; + d_t = 4'h6; + d_t_dim[0] = 4'h7; + d_t_dim[1] = 4'h8; + d_ext = 1'b0; + d_n0 = 1'b1; + d_n1 = 1'b0; + d_t0 = 4'h9; + d_t1 = 4'h6; + d_s_t0 = 4'h5; + d_s_t1 = 4'ha; + d_p_tn = 4'hc; + + #1; + + `check($bits(n), 1, "input T was not parsed as a port name"); + `check($bits(t), 4, "input T x was not parsed as a typed port"); + `check($bits(n_dim), 2, "input T [1:0] was not parsed as a port array"); + `check($bits(t_dim), 8, "input T [1:0] x was not parsed as a typed port array"); + `check($bits(n0), 1, "input T, U first port did not keep implicit type"); + `check($bits(n1), 1, "input T, U continuation did not keep implicit type"); + `check($bits(t0), 4, "input T x, y first port did not keep typedef type"); + `check($bits(t1), 4, "input T x, y continuation did not keep typedef type"); + `check(t0, 4'h5, "Typed port list first value mismatch"); + `check(t1, 4'ha, "Typed port list continuation mismatch"); + `check($bits(s_t0), 4, "input T x, T first port did not keep typedef type"); + `check($bits(s_t1), 4, "input T x, T continuation did not allow typedef name as port name"); + `check(s_t1, 4'h9, "Typed port list shadowing value mismatch"); + `check($bits(p_tn), 4, "input T T did not keep typedef type"); + `check(p_tn_ok, 1'b1, "input T T was not available as a typed port"); + `check($bits(rn), 1, "input n, T x first port did not keep implicit type"); + `check($bits(rt), 4, "input n, T x second port did not keep typedef type"); + `check(rt, 4'hc, "Inherited-direction typedef port value mismatch"); + `check($bits(rt_dim), 8, "input n, T [1:0] x did not keep typedef packed dimensions"); + `check(rt_dim[0], 4'h3, "Inherited-direction typedef packed array value mismatch"); + `check(rt_dim[1], 4'ha, "Inherited-direction typedef packed array value mismatch"); + `check($bits(rn_dim), 1, "input n, T [1:0] first port did not keep implicit type"); + `check($bits(rt_name_dim), 2, "input n, T [1:0] second port was not parsed as a port array"); + `check($bits(rp_t), 6, "input n, p::PT x second port did not keep package typedef type"); + `check(rp_t, 6'h2a, "Inherited-direction package typedef port value mismatch"); + `check($bits(rp_t_dim), 12, "input n, p::PT [1:0] x did not keep package typedef packed dimensions"); + `check(rp_t_dim[0], 6'h15, "Inherited-direction package typedef packed array value mismatch"); + `check(rp_t_dim[1], 6'h2a, "Inherited-direction package typedef packed array value mismatch"); + `check($bits(d_n), 1, "input T was not parsed as a port declaration name"); + `check($bits(d_t), 4, "input T x was not parsed as a typed port declaration"); + `check($bits(d_t_dim), 8, "input T [1:0] x was not parsed as a typed port declaration array"); + `check($bits(d_ext), 1, ".T(x) was not parsed as an external port name"); + `check($bits(d_n0), 1, "input T, U first declaration did not keep implicit type"); + `check($bits(d_n1), 1, "input T, U continuation declaration did not keep implicit type"); + `check($bits(d_t0), 4, "input T x, y first declaration did not keep typedef type"); + `check($bits(d_t1), 4, "input T x, y continuation declaration did not keep typedef type"); + `check(d_t0, 4'h9, "Typed port declaration list first value mismatch"); + `check(d_t1, 4'h6, "Typed port declaration list continuation mismatch"); + `check($bits(d_s_t0), 4, "input T x, T first declaration did not keep typedef type"); + `check($bits(d_s_t1), 4, "input T x, T declaration did not allow typedef name as port name"); + `check(d_s_t1, 4'ha, "Typed port declaration list shadowing value mismatch"); + `check($bits(d_p_tn), 4, "non-ANSI input T T did not keep typedef type"); + `check(d_p_tn_ok, 1'b1, "non-ANSI input T T was not available as a typed port"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_task_function_argument_name.v b/ivtest/ivltests/sv_type_identifier_task_function_argument_name.v new file mode 100644 index 000000000..835c84b82 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_task_function_argument_name.v @@ -0,0 +1,120 @@ +// Check that task and function argument names can shadow visible type identifiers. + +typedef int T; + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + function int f_name(input T); + return ($bits(T) == 1 && T === 1'b1) ? 32'd33 : -1; + endfunction + + function int f_type(input T value); + return ($bits(value) == 32) ? value : -1; + endfunction + + function int f_type_list(input T value, T); + return $bits(value) + $bits(T); + endfunction + + function int f_type_name(input T T); + return $bits(T); + endfunction + + function int f_decl_name; + input T; + return $bits(T); + endfunction + + function int f_decl_type; + input T value; + return $bits(value); + endfunction + + function int f_decl_type_name; + input T T; + return $bits(T); + endfunction + + task t_name(input T, output int value); + value = ($bits(T) == 1 && T === 1'b1) ? 32'd44 : -1; + endtask + + task t_type(input T value, output int result); + result = ($bits(value) == 32) ? value : -1; + endtask + + task t_type_list(input T value, T, output int result); + result = $bits(value) + $bits(T); + endtask + + task t_type_name(input T T, output int value); + value = $bits(T); + endtask + + task t_decl_name; + input T; + output int value; + value = $bits(T); + endtask + + task t_decl_type; + input T value; + output int result; + result = $bits(value); + endtask + + task t_decl_type_name; + input T T; + output int value; + value = $bits(T); + endtask + + initial begin + int r0; + int r1; + int r2; + int r3; + int r4; + int r5; + int r6; + + failed = 1'b0; + + t_name(1'b1, r0); + t_type(32'd55, r1); + t_type_list(32'd11, 32'd22, r2); + t_decl_name(1'b1, r3); + t_decl_type(32'd33, r4); + t_type_name(32'd44, r5); + t_decl_type_name(32'd55, r6); + + `check(f_name(1'b1), 32'd33, "Function argument did not hide typedef"); + `check(f_type(32'd66), 32'd66, "Function typed argument regressed"); + `check(f_type_list(32'd11, 32'd22), 64, "Function typed argument list shadowing mismatch"); + `check(f_type_name(32'd33), 32, "Function type-name argument did not keep typedef type"); + `check(f_decl_name(1'b1), 1, "Function non-ANSI argument did not hide typedef"); + `check(f_decl_type(32'd66), 32, "Function non-ANSI typed argument regressed"); + `check(f_decl_type_name(32'd77), 32, "Function non-ANSI type-name argument did not keep typedef type"); + `check(r0, 32'd44, "Task argument did not hide typedef"); + `check(r1, 32'd55, "Task typed argument regressed"); + `check(r2, 64, "Task typed argument list shadowing mismatch"); + `check(r3, 1, "Task non-ANSI argument did not hide typedef"); + `check(r4, 32, "Task non-ANSI typed argument regressed"); + `check(r5, 32, "Task type-name argument did not keep typedef type"); + `check(r6, 32, "Task non-ANSI type-name argument did not keep typedef type"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_variable_name.v b/ivtest/ivltests/sv_type_identifier_variable_name.v new file mode 100644 index 000000000..c4fadae33 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_variable_name.v @@ -0,0 +1,152 @@ +// Check that variable names can shadow visible type identifiers. + +typedef logic [7:0] T; +typedef int V; +typedef int W; +typedef logic [6:0] A; +typedef logic [5:0] B; +typedef logic [4:0] C; +typedef logic [3:0] D; +typedef logic [2:0] E; +typedef logic [1:0] F; +typedef logic [2:0] G; +typedef logic [3:0] H; +typedef logic [4:0] I; +typedef logic [2:0] P; +bit X; + +package p; + var C; + var D x, D; + var P P; +endpackage + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + typedef logic [3:0] X; + + T outer; + T a, b; + A a0, A; + E E; + int T, U; + var V; + var B b0, B; + var F F; + X x; + + function int f; + int T; + + T = 32'd11; + return T; + endfunction + + function int f_type_name; + H H; + + H = 4'ha; + return $bits(H) + H; + endfunction + + task t(output int value); + int T; + + T = 32'd22; + value = T; + endtask + + task t_type_name(output int value); + I I; + + I = 5'h15; + value = $bits(I) + I; + endtask + + initial begin + int r; + int r_type_name; + int tr_type_name; + var W; + + failed = 1'b0; + + outer = 8'ha5; + a = 8'h33; + b = 8'hcc; + a0 = 7'h2a; + A = 7'h15; + E = 3'h5; + T = 32'd23; + U = 32'd41; + V = 1'b1; + b0 = 6'h2a; + B = 6'h15; + F = 2'h2; + W = 1'b0; + x = 4'hc; + t(r); + t_type_name(tr_type_name); + + begin : block_scope + int T; + + T = 32'd7; + `check(T, 32'd7, "Block declaration did not hide typedef"); + end + + begin : block_type_name + G G; + + G = 3'h3; + `check($bits(G), 3, "Block type-name declaration did not keep typedef type"); + `check(G, 3'h3, "Block type-name declaration value mismatch"); + end + + r_type_name = f_type_name(); + + `check(outer, 8'ha5, "Typedef value changed"); + `check(T, 32'd23, "Module declaration did not hide typedef"); + `check(U, 32'd41, "Declaration list continuation mismatch"); + `check($bits(V), 1, "Module var declaration did not hide typedef width"); + `check(V, 1'b1, "Module var declaration did not hide typedef value"); + `check($bits(W), 1, "Block var declaration did not hide typedef width"); + `check(W, 1'b0, "Block var declaration did not hide typedef value"); + `check(a, 8'h33, "Type declaration list first value mismatch"); + `check(b, 8'hcc, "Type declaration list continuation mismatch"); + `check($bits(a0), 7, "Type declaration list did not keep typedef type"); + `check($bits(A), 7, "Type declaration list did not allow typedef name as continuation"); + `check(A, 7'h15, "Type declaration list shadowing value mismatch"); + `check($bits(E), 3, "Type-name declaration did not keep typedef type"); + `check(E, 3'h5, "Type-name declaration value mismatch"); + `check($bits(b0), 6, "Var declaration list did not keep typedef type"); + `check($bits(B), 6, "Var declaration list did not allow typedef name as continuation"); + `check(B, 6'h15, "Var declaration list shadowing value mismatch"); + `check($bits(F), 2, "Var type-name declaration did not keep typedef type"); + `check(F, 2'h2, "Var type-name declaration value mismatch"); + `check($bits(p::C), 1, "Package var declaration did not hide typedef width"); + `check($bits(p::x), 4, "Package type declaration list first width mismatch"); + `check($bits(p::D), 4, "Package type declaration list did not allow typedef name as continuation"); + `check($bits(p::P), 3, "Package type-name declaration did not keep typedef type"); + `check(f(), 32'd11, "Function declaration did not hide typedef"); + `check(r_type_name, 14, "Function type-name declaration mismatch"); + `check(r, 32'd22, "Task declaration did not hide typedef"); + `check(tr_type_name, 26, "Task type-name declaration mismatch"); + `check($bits(x), 4, "Local typedef did not hide outer identifier"); + `check(x, 4'hc, "Local typedef value mismatch"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 424ce1cc6..c25189cda 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -385,8 +385,12 @@ sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json +sv_type_identifier_net_name vvp_tests/sv_type_identifier_net_name.json sv_type_identifier_package_item vvp_tests/sv_type_identifier_package_item.json +sv_type_identifier_port_name vvp_tests/sv_type_identifier_port_name.json sv_type_identifier_specparam_name vvp_tests/sv_type_identifier_specparam_name.json +sv_type_identifier_task_function_argument_name vvp_tests/sv_type_identifier_task_function_argument_name.json +sv_type_identifier_variable_name vvp_tests/sv_type_identifier_variable_name.json sv_type_param_restrict_class1 vvp_tests/sv_type_param_restrict_class1.json sv_type_param_restrict_class2 vvp_tests/sv_type_param_restrict_class2.json sv_type_param_restrict_class_fail1 vvp_tests/sv_type_param_restrict_class_fail1.json diff --git a/ivtest/vvp_tests/sv_type_identifier_net_name.json b/ivtest/vvp_tests/sv_type_identifier_net_name.json new file mode 100644 index 000000000..5b5789167 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_net_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_net_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and SystemVerilog net types are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_port_name.json b/ivtest/vvp_tests/sv_type_identifier_port_name.json new file mode 100644 index 000000000..f94594bb3 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_port_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_port_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and ANSI ports are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json b/ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json new file mode 100644 index 000000000..a8ed1e0fd --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_task_function_argument_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and task/function arguments are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_variable_name.json b/ivtest/vvp_tests/sv_type_identifier_variable_name.json new file mode 100644 index 000000000..fde51c2a7 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_variable_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_variable_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and block/task scope are SystemVerilog", + "type" : "CE" + } +} From be298d1cca451e037af99523097b99593671b0b2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 2 Oct 2022 13:27:44 +0200 Subject: [PATCH 049/101] Allow repeat expression in event control to contain automatic terms The repeat expression of an event controlled non-blocking assignment is evaluated once when the assignment is scheduled. This means there is no risk of it being referenced when its scope has already been freed. And hence there is no need to require the repeat expression to only contain static terms. Signed-off-by: Lars-Peter Clausen --- elaborate.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/elaborate.cc b/elaborate.cc index e4894fd6e..584745249 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3251,15 +3251,6 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const NetEvWait*event = 0; if (count_ != 0 || event_ != 0) { if (count_ != 0) { - if (scope->is_auto() && count_->has_aa_term(des, scope)) { - cerr << get_fileline() << ": error: automatically " - "allocated variables may not be referenced " - "in intra-assignment event controls of " - "non-blocking assignments." << endl; - des->errors += 1; - return 0; - } - ivl_assert(*this, event_ != 0); count = elab_and_eval(des, scope, count_, -1); if (count == 0) { From f358b3fa8f2be079d6f822581ae1d2896bc09736 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 30 Jun 2026 16:31:44 -0700 Subject: [PATCH 050/101] Add regression test for automatic event control repeat counts Check that the repeat count expression of a non-blocking intra-assignment event control can reference an automatic task argument. The repeat count is evaluated when the assignment is scheduled, so the automatic variable is not referenced after the task scope is freed. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/nb_ec_repeat_auto.v | 46 +++++++++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/nb_ec_repeat_auto.json | 8 +++++ 3 files changed, 55 insertions(+) create mode 100644 ivtest/ivltests/nb_ec_repeat_auto.v create mode 100644 ivtest/vvp_tests/nb_ec_repeat_auto.json diff --git a/ivtest/ivltests/nb_ec_repeat_auto.v b/ivtest/ivltests/nb_ec_repeat_auto.v new file mode 100644 index 000000000..c9e7dff87 --- /dev/null +++ b/ivtest/ivltests/nb_ec_repeat_auto.v @@ -0,0 +1,46 @@ +// Check that non-blocking event control repeat counts can use automatic terms. + +module test; + + reg clk; + reg failed; + reg [3:0] result; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %b, got %b", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + task automatic schedule_update; + input integer count; + begin + result <= repeat(count) @(posedge clk) 4'ha; + end + endtask + + always #5 clk = ~clk; + + initial begin + clk = 1'b0; + failed = 1'b0; + result = 4'h0; + + schedule_update(2); + + @(posedge clk); + #1; + `check(result, 4'h0); + + @(posedge clk); + #1; + `check(result, 4'ha); + + if (!failed) begin + $display("PASSED"); + end + $finish; + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 424ce1cc6..353a668c7 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -174,6 +174,7 @@ module_port_array1 vvp_tests/module_port_array1.json module_port_array_fail1 vvp_tests/module_port_array_fail1.json module_port_array_init1 vvp_tests/module_port_array_init1.json monitor4 vvp_tests/monitor4.json +nb_ec_repeat_auto vvp_tests/nb_ec_repeat_auto.json named_event_edge_fail vvp_tests/named_event_edge_fail.json named_event_negedge_fail vvp_tests/named_event_negedge_fail.json named_event_posedge_fail vvp_tests/named_event_posedge_fail.json diff --git a/ivtest/vvp_tests/nb_ec_repeat_auto.json b/ivtest/vvp_tests/nb_ec_repeat_auto.json new file mode 100644 index 000000000..382425f56 --- /dev/null +++ b/ivtest/vvp_tests/nb_ec_repeat_auto.json @@ -0,0 +1,8 @@ +{ + "type" : "normal", + "source" : "nb_ec_repeat_auto.v", + "vlog95" : { + "__comment" : "Automatic tasks are not supported", + "type" : "CE" + } +} From 9bff2399df7d84cd1519102a384272e895a3c92a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 30 Jun 2026 18:05:41 -0700 Subject: [PATCH 051/101] Detect automatic terms in assignment patterns Assignment patterns contain child expressions, but currently inherit `PExpr::has_aa_term()` which always returns false. This means automatic variables inside a pattern are not caught by checks for procedural `force` and procedural continuous assignment statements. Implement `has_aa_term()` for `PEAssignPattern` and recurse into all pattern elements. Signed-off-by: Lars-Peter Clausen --- PExpr.cc | 10 ++++++++++ PExpr.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/PExpr.cc b/PExpr.cc index 8a32b0506..e9634511b 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -108,6 +108,16 @@ PEAssignPattern::~PEAssignPattern() { } +bool PEAssignPattern::has_aa_term(Design*des, NetScope*scope) const +{ + bool flag = false; + for (const auto *parm : parms_) { + if (parm) + flag = parm->has_aa_term(des, scope) || flag; + } + return flag; +} + PEBinary::PEBinary(char op, PExpr*l, PExpr*r) : op_(op), left_(l), right_(r) { diff --git a/PExpr.h b/PExpr.h index 6f6eaf07e..bf8f1e2ef 100644 --- a/PExpr.h +++ b/PExpr.h @@ -208,6 +208,8 @@ class PEAssignPattern : public PExpr { void dump(std::ostream&) const override; + virtual bool has_aa_term(Design*des, NetScope*scope) const override; + virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode) override; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const override; From 9bbdb0da0ebb2bd2d06fbfbe2cc85629d59cbd6a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 30 Jun 2026 18:07:01 -0700 Subject: [PATCH 052/101] Add regression test for automatic terms in assignment patterns Check that automatic variables referenced through an assignment pattern in a procedural `force` statement are rejected. Signed-off-by: Lars-Peter Clausen --- .../sv_assign_pattern_auto_force_fail.v | 19 +++++++++++++++++++ ivtest/regress-vvp.list | 1 + .../sv_assign_pattern_auto_force_fail.json | 5 +++++ 3 files changed, 25 insertions(+) create mode 100644 ivtest/ivltests/sv_assign_pattern_auto_force_fail.v create mode 100644 ivtest/vvp_tests/sv_assign_pattern_auto_force_fail.json diff --git a/ivtest/ivltests/sv_assign_pattern_auto_force_fail.v b/ivtest/ivltests/sv_assign_pattern_auto_force_fail.v new file mode 100644 index 000000000..d260e122b --- /dev/null +++ b/ivtest/ivltests/sv_assign_pattern_auto_force_fail.v @@ -0,0 +1,19 @@ +// Check that assignment patterns cannot reference automatic variables in +// procedural force statements. + +module test; + + reg [3:0] result; + + task automatic t; + input [3:0] value; + begin + force result = '{value[3], value[2], value[1], value[0]}; + end + endtask + + initial begin + t(4'ha); + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 424ce1cc6..d9db19f54 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -259,6 +259,7 @@ sv_array_cassign9 vvp_tests/sv_array_cassign9.json sv_array_cassign10 vvp_tests/sv_array_cassign10.json sv_array_cassign_single vvp_tests/sv_array_cassign_single.json sv_array_cassign_single_fail1 vvp_tests/sv_array_cassign_single_fail1.json +sv_assign_pattern_auto_force_fail vvp_tests/sv_assign_pattern_auto_force_fail.json sv_automatic_2state vvp_tests/sv_automatic_2state.json sv_bad_member_lval_proc_fail vvp_tests/sv_bad_member_lval_proc_fail.json sv_byte_array_string1 vvp_tests/sv_byte_array_string1.json diff --git a/ivtest/vvp_tests/sv_assign_pattern_auto_force_fail.json b/ivtest/vvp_tests/sv_assign_pattern_auto_force_fail.json new file mode 100644 index 000000000..29f301d70 --- /dev/null +++ b/ivtest/vvp_tests/sv_assign_pattern_auto_force_fail.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_assign_pattern_auto_force_fail.v", + "iverilog-args" : [ "-g2005-sv" ] +} From 5a99d0e449468630f8386d5488e9f70bbaf3bbf5 Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 1 Jul 2026 09:08:51 -0700 Subject: [PATCH 053/101] Update to the latest config.guess and config.sub files --- config.guess | 17 +++++++++++------ config.sub | 11 ++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/config.guess b/config.guess index a9d01fde4..c7f4c3294 100644 --- a/config.guess +++ b/config.guess @@ -1,10 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2025 Free Software Foundation, Inc. +# Copyright 1992-2026 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2025-07-10' +timestamp='2026-05-17' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -60,7 +60,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2025 Free Software Foundation, Inc. +Copyright 1992-2026 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -150,7 +150,7 @@ UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in -Linux|GNU|GNU/*) +Ironclad|Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build @@ -167,6 +167,8 @@ Linux|GNU|GNU/*) LIBC=gnu #elif defined(__LLVM_LIBC__) LIBC=llvm + #elif defined(__mlibc__) + LIBC=mlibc #else #include /* First heuristic to detect musl libc. */ @@ -1186,6 +1188,9 @@ EOF sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; + sw_64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; @@ -1598,10 +1603,10 @@ EOF GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; x86_64:[Ii]ronclad:*:*|i?86:[Ii]ronclad:*:*) - GUESS=$UNAME_MACHINE-pc-ironclad-mlibc + GUESS=$UNAME_MACHINE-pc-ironclad-$LIBC ;; *:[Ii]ronclad:*:*) - GUESS=$UNAME_MACHINE-unknown-ironclad-mlibc + GUESS=$UNAME_MACHINE-unknown-ironclad-$LIBC ;; esac diff --git a/config.sub b/config.sub index 3d35cde17..404aa0824 100644 --- a/config.sub +++ b/config.sub @@ -1,10 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2025 Free Software Foundation, Inc. +# Copyright 1992-2026 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale -timestamp='2025-07-10' +timestamp='2026-05-17' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -76,7 +76,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright 1992-2025 Free Software Foundation, Inc. +Copyright 1992-2026 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -1432,6 +1432,7 @@ case $cpu-$vendor in | sparcv9v \ | spu \ | sv1 \ + | sw_64 \ | sx* \ | tahoe \ | thumbv7* \ @@ -1525,7 +1526,7 @@ EOF ;; ironclad*) kernel=ironclad - os=`echo "$basic_os" | sed -e 's|ironclad|mlibc|'` + os=`echo "$basic_os" | sed -e 's|ironclad|gnu|'` ;; linux*) kernel=linux @@ -2220,7 +2221,7 @@ case $kernel-$os-$obj in ;; uclinux-uclibc*- | uclinux-gnu*- ) ;; - ironclad-mlibc*-) + ironclad-gnu*- | ironclad-mlibc*- ) ;; managarm-mlibc*- | managarm-kernel*- ) ;; From e2ab4646567a32cdc8ae47cd1f7db8abe0e381dd Mon Sep 17 00:00:00 2001 From: Ethan Sifferman Date: Thu, 2 Jul 2026 16:20:20 -0700 Subject: [PATCH 054/101] Fix unequal-length string comparison --- elab_expr.cc | 11 ++++++++ ivtest/ivltests/param_string_compare.v | 36 ++++++++++++++++++++++++++ ivtest/regress-sv.list | 1 + 3 files changed, 48 insertions(+) create mode 100644 ivtest/ivltests/param_string_compare.v diff --git a/elab_expr.cc b/elab_expr.cc index 6c9d7b17a..291db1a08 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -916,6 +916,17 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, eval_expr(lp, l_width_); eval_expr(rp, r_width_); + // test_width() does not equalize operand widths for string + // operands, but constant evaluation expects equal widths. + if (left_->expr_type() == IVL_VT_STRING || + right_->expr_type() == IVL_VT_STRING) { + if (dynamic_cast(lp) && dynamic_cast(rp)) { + unsigned use_wid = max(lp->expr_width(), rp->expr_width()); + lp = pad_to_width(lp, use_wid, false, *this); + rp = pad_to_width(rp, use_wid, false, *this); + } + } + // Handle some operand-specific special cases... switch (op_) { case 'E': /* === */ diff --git a/ivtest/ivltests/param_string_compare.v b/ivtest/ivltests/param_string_compare.v new file mode 100644 index 000000000..61e1f81e6 --- /dev/null +++ b/ivtest/ivltests/param_string_compare.v @@ -0,0 +1,36 @@ +// Regression test: comparing a `string` parameter to a string constant of +// a different length must constant-fold cleanly instead of tripping the +// "lv.len() == rv.len()" assertion in the NetEBComp evaluation functions. +// Covers ==, !=, ===, !==, ==? and !=? with the string operand on either +// side and both shorter and longer than the other operand. + +module main #(parameter string Target = "A", + parameter string Long = "ABC"); + + integer errors = 0; + + // Equality / inequality, parameter shorter than the literal. + if (Target == "AA") initial begin errors += 1; $display("FAILED: Target == \"AA\""); end + if (Target != "A") initial begin errors += 1; $display("FAILED: Target != \"A\""); end + + // Literal on the left. + if ("AA" == Target) initial begin errors += 1; $display("FAILED: \"AA\" == Target"); end + + // Parameter longer than the literal. + if (Long == "AB") initial begin errors += 1; $display("FAILED: Long == \"AB\""); end + if (Long != "ABC") initial begin errors += 1; $display("FAILED: Long != \"ABC\""); end + + // Two string parameters of different lengths. + if (Target == Long) initial begin errors += 1; $display("FAILED: Target == Long"); end + + // Case equality. + if (Target === "AA") initial begin errors += 1; $display("FAILED: Target === \"AA\""); end + if (Target !== "A") initial begin errors += 1; $display("FAILED: Target !== \"A\""); end + + // Wildcard equality. + if (Target ==? "AA") initial begin errors += 1; $display("FAILED: Target ==? \"AA\""); end + if (Target !=? "A") initial begin errors += 1; $display("FAILED: Target !=? \"A\""); end + + initial #1 if (errors == 0) $display("PASSED"); + +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index dade32337..ee2dc7757 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -416,6 +416,7 @@ net_string_fail CE,-g2005-sv ivltests package_vec_part_select normal,-g2005-sv ivltests packeda normal,-g2009 ivltests packeda2 normal,-g2009 ivltests +param_string_compare normal,-g2009 ivltests parameter_in_generate2 CE,-g2005-sv ivltests parameter_no_default normal,-g2005-sv ivltests parameter_no_default_fail1 CE ivltests From dc66f2fe7e1fc0ac799c84bd3e911f52a3c3212b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 27 Jun 2026 10:01:43 -0700 Subject: [PATCH 055/101] Support class properties shadowing type names SystemVerilog allows a class property to have the same name as a visible type. The lexer reports the visible type name as `TYPE_IDENTIFIER` before the property has been installed, which made constructs such as `typedef int T; class C; int T; endclass` fail in the class item grammar. A class property with the same name as the class itself hits the same problem. Member references such as `obj.T` or `obj.C` can also hit the same tokenization problem in hierarchical names. Parse class properties through the same declaration helper used for variables so the first type/name pair can be disambiguated. Also let hierarchical member names use `identifier_name`. Stop type lookup when a class scope already has a property with the same name. This makes method body references resolve as properties instead of visible types, including type names found through wildcard imports. Signed-off-by: Lars-Peter Clausen --- parse.y | 15 +++++---------- pform.cc | 10 ++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/parse.y b/parse.y index 1c9caf208..35e6c89fb 100644 --- a/parse.y +++ b/parse.y @@ -1172,11 +1172,11 @@ class_item /* IEEE1800-2005: A.1.8 */ /* IEEE1800-2017: A.1.9 Class items: Class properties... */ - | property_qualifier_opt data_type list_of_variable_decl_assignments ';' - { pform_class_property(@2, $1, $2, $3); } + | property_qualifier_opt list_of_variable_decl_assignments_with_type ';' + { pform_class_property(@2, $1, $2.type, $2.decl_assignments); } - | K_const class_item_qualifier_opt data_type list_of_variable_decl_assignments ';' - { pform_class_property(@1, $2 | property_qualifier_t::make_const(), $3, $4); } + | K_const class_item_qualifier_opt list_of_variable_decl_assignments_with_type ';' + { pform_class_property(@1, $2 | property_qualifier_t::make_const(), $3.type, $3.decl_assignments); } /* IEEEE1800-2017: A.1.9 Class items: class_item ::= { property_qualifier} data_declaration */ @@ -1212,11 +1212,6 @@ class_item /* IEEE1800-2005: A.1.8 */ /* Here are some error matching rules to help recover from various syntax errors within a class declaration. */ - | property_qualifier_opt data_type error ';' - { yyerror(@3, "error: Errors in variable names after data type."); - yyerrok; - } - | property_qualifier_opt IDENTIFIER error ';' { yyerror(@3, "error: %s doesn't name a type.", $2); yyerrok; @@ -4749,7 +4744,7 @@ hierarchy_identifier $$->push_back(name_component_t(lex_strings.make($1))); delete[]$1; } - | hierarchy_identifier '.' IDENTIFIER + | hierarchy_identifier '.' identifier_name { pform_name_t * tmp = $1; tmp->push_back(name_component_t(lex_strings.make($3))); delete[]$3; diff --git a/pform.cc b/pform.cc index cf2f730ce..7aead1052 100644 --- a/pform.cc +++ b/pform.cc @@ -929,6 +929,16 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) if (sym != cur_scope->local_symbols.end()) return nullptr; + // Class properties are tracked in the class type, not in + // local_symbols, but still shadow type names before lookup falls + // through to wildcard imports. + if (auto cur_class = dynamic_cast (cur_scope)) { + if (cur_class->type && + cur_class->type->properties.find(name) != + cur_class->type->properties.end()) + return nullptr; + } + PPackage*pkg = pform_find_potential_import(loc, cur_scope, name, false, false); if (pkg) { cur = pkg->typedefs.find(name); From 5364f11d1640fad6d5de9723db31050d13e8d965 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 27 Jun 2026 10:01:57 -0700 Subject: [PATCH 056/101] Add regression tests for class properties shadowing type names Check that a class property can have the same name as a type declared in an outer scope, or a type imported through a wildcard import. Also check that a class property can have the same name as the class itself. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_class_prop_class_name.v | 37 +++++++++++++++ ivtest/ivltests/sv_class_prop_type_name.v | 39 ++++++++++++++++ .../sv_class_prop_wildcard_type_name.v | 45 +++++++++++++++++++ ivtest/regress-vvp.list | 3 ++ .../vvp_tests/sv_class_prop_class_name.json | 9 ++++ ivtest/vvp_tests/sv_class_prop_type_name.json | 9 ++++ .../sv_class_prop_wildcard_type_name.json | 9 ++++ 7 files changed, 151 insertions(+) create mode 100644 ivtest/ivltests/sv_class_prop_class_name.v create mode 100644 ivtest/ivltests/sv_class_prop_type_name.v create mode 100644 ivtest/ivltests/sv_class_prop_wildcard_type_name.v create mode 100644 ivtest/vvp_tests/sv_class_prop_class_name.json create mode 100644 ivtest/vvp_tests/sv_class_prop_type_name.json create mode 100644 ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json diff --git a/ivtest/ivltests/sv_class_prop_class_name.v b/ivtest/ivltests/sv_class_prop_class_name.v new file mode 100644 index 000000000..2a8c768f7 --- /dev/null +++ b/ivtest/ivltests/sv_class_prop_class_name.v @@ -0,0 +1,37 @@ +// Check that a class property can have the same name as the class. + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int C; + + function int get; + return C; + endfunction + endclass + + C obj; + + initial begin + obj = new; + + obj.C = 23; + + `check(obj.get(), 23); + `check(obj.C, 23); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_class_prop_type_name.v b/ivtest/ivltests/sv_class_prop_type_name.v new file mode 100644 index 000000000..2ba13dea3 --- /dev/null +++ b/ivtest/ivltests/sv_class_prop_type_name.v @@ -0,0 +1,39 @@ +// Check that a class property can have the same name as an outer type. + +module test; + + typedef int T; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int T; + + function int get; + return T; + endfunction + endclass + + C obj; + + initial begin + obj = new; + + obj.T = 11; + + `check(obj.get(), 11); + `check(obj.T, 11); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_class_prop_wildcard_type_name.v b/ivtest/ivltests/sv_class_prop_wildcard_type_name.v new file mode 100644 index 000000000..f5a4a1762 --- /dev/null +++ b/ivtest/ivltests/sv_class_prop_wildcard_type_name.v @@ -0,0 +1,45 @@ +// Check that a class property can shadow a wildcard-imported type name. + +package P; + + typedef int T; + +endpackage + +module test; + + import P::*; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int T; + + function int get; + return T; + endfunction + endclass + + C obj; + + initial begin + obj = new; + + obj.T = 47; + + `check(obj.get(), 47); + `check(obj.T, 47); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 050529a95..c023763ad 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -279,8 +279,11 @@ sv_chained_constructor4 vvp_tests/sv_chained_constructor4.json sv_chained_constructor5 vvp_tests/sv_chained_constructor5.json sv_class_prop_assign_op1 vvp_tests/sv_class_prop_assign_op1.json sv_class_prop_assign_op2 vvp_tests/sv_class_prop_assign_op2.json +sv_class_prop_class_name vvp_tests/sv_class_prop_class_name.json sv_class_prop_logic vvp_tests/sv_class_prop_logic.json sv_class_prop_packed_dims vvp_tests/sv_class_prop_packed_dims.json +sv_class_prop_type_name vvp_tests/sv_class_prop_type_name.json +sv_class_prop_wildcard_type_name vvp_tests/sv_class_prop_wildcard_type_name.json sv_class_prop_nest_darray1 vvp_tests/sv_class_prop_nest_darray1.json sv_class_prop_nest_obj1 vvp_tests/sv_class_prop_nest_obj1.json sv_class_prop_nest_real1 vvp_tests/sv_class_prop_nest_real1.json diff --git a/ivtest/vvp_tests/sv_class_prop_class_name.json b/ivtest/vvp_tests/sv_class_prop_class_name.json new file mode 100644 index 000000000..4c7cfe9c8 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_prop_class_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_prop_class_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_class_prop_type_name.json b/ivtest/vvp_tests/sv_class_prop_type_name.json new file mode 100644 index 000000000..e5543bb21 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_prop_type_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_prop_type_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json b/ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json new file mode 100644 index 000000000..451678584 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_prop_wildcard_type_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} From e56c93a2be56d0459dd36c1670b8d6917ad6c81f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 26 Jun 2026 19:23:11 -0700 Subject: [PATCH 057/101] Support shadowing type identifiers in parameter declarations SystemVerilog allows a visible type identifier to be shadowed by a parameter declaration name. Parameter declarations still required the parameter name to be an `IDENTIFIER` token and rejected declarations like: typedef int P; module test; parameter int P = 1; endmodule The parameter grammar can not just accept `TYPE_IDENTIFIER` in every name position. After `parameter P` the parser does not know yet whether `P` is the parameter name, or whether a following identifier will make `P` the parameter type. Parse the optional value parameter type and the first parameter assignment together. This allows a `TYPE_IDENTIFIER` token to be interpreted as the parameter name when no explicit type is present, while still parsing a following identifier as the parameter name for typed parameters. Signed-off-by: Lars-Peter Clausen --- parse.y | 170 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 53 deletions(-) diff --git a/parse.y b/parse.y index 1c9caf208..3fc434bcd 100644 --- a/parse.y +++ b/parse.y @@ -139,12 +139,16 @@ static data_type_t *pform_make_parray_type(const struct vlltype&loc, template static void set_type_id_range(T&value, data_type_t *type, char *id, - unsigned lexical_pos, + const YYLTYPE&loc, std::list *ranges) { value.type = type; value.id = id; - value.lexical_pos = lexical_pos; + value.lexical_pos = loc.lexical_pos; + value.first_line = loc.first_line; + value.first_column = loc.first_column; + value.last_line = loc.last_line; + value.last_column = loc.last_column; value.ranges = ranges; } @@ -299,6 +303,29 @@ static decl_assignment_t *pform_make_net_decl(const YYLTYPE&loc, char *id, return pform_make_net_decl(loc, id, loc.lexical_pos, udims, init); } +static void pform_set_parameter(const YYLTYPE&loc, char *id, unsigned lexical_pos, + bool is_local, bool is_type, + type_restrict_t type_restrict, + data_type_t*data_type, const list*udims, + PExpr*expr, LexicalScope::range_t*value_range) +{ + YYLTYPE id_loc = loc; + id_loc.lexical_pos = lexical_pos; + pform_set_parameter(id_loc, lex_strings.make(id), is_local, is_type, + type_restrict, data_type, udims, expr, value_range); + delete[] id; +} + +static void pform_set_parameter(const YYLTYPE&loc, char *id, + bool is_local, bool is_type, + type_restrict_t type_restrict, + data_type_t*data_type, const list*udims, + PExpr*expr, LexicalScope::range_t*value_range) +{ + pform_set_parameter(loc, id, loc.lexical_pos, is_local, is_type, + type_restrict, data_type, udims, expr, value_range); +} + template void append(vector&out, const std::vector&in) { for (size_t idx = 0 ; idx < in.size() ; idx += 1) @@ -699,6 +726,10 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, data_type_t *type; char *id; unsigned lexical_pos; + int first_line; + int first_column; + int last_line; + int last_column; std::list*ranges; } type_id_range; @@ -2726,25 +2757,26 @@ tf_port_declaration /* IEEE1800-2005: A.2.7 */ // dimensions followed by a separate declaration name. data_type_or_implicit_plus_id_base : IDENTIFIER - { set_type_id_range($$, nullptr, $1, @1.lexical_pos, nullptr); + { set_type_id_range($$, nullptr, $1, @1, nullptr); } | atomic_type identifier_name - { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + { set_type_id_range($$, $1, $2, @2, nullptr); } | implicit_type identifier_name - { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + { set_type_id_range($$, $1, $2, @2, nullptr); } | ps_type_identifier_dim identifier_name - { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + { set_type_id_range($$, $1, $2, @2, nullptr); } ; data_type_or_implicit_plus_id_dim : TYPE_IDENTIFIER dimensions_opt - { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + { set_type_id_range($$, nullptr, $1.text, @1, $2); } | data_type_or_implicit_plus_id_base dimensions_opt - { set_type_id_range($$, $1.type, $1.id, $1.lexical_pos, $2); + { $$ = $1; + $$.ranges = $2; } ; @@ -2754,19 +2786,19 @@ data_type_or_implicit_plus_id_dim // still parsed as a continuation of the previous port declaration. partial_port_type_plus_id_dim : atomic_type identifier_name dimensions_opt - { set_type_id_range($$, $1, $2, @2.lexical_pos, $3); + { set_type_id_range($$, $1, $2, @2, $3); } | implicit_type identifier_name dimensions_opt - { set_type_id_range($$, $1, $2, @2.lexical_pos, $3); + { set_type_id_range($$, $1, $2, @2, $3); } ; partial_port_name_dim : IDENTIFIER dimensions_opt - { set_type_id_range($$, nullptr, $1, @1.lexical_pos, $2); + { set_type_id_range($$, nullptr, $1, @1, $2); } | TYPE_IDENTIFIER dimensions_opt - { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + { set_type_id_range($$, nullptr, $1.text, @1, $2); } ; @@ -2777,7 +2809,7 @@ partial_port_typedef_plus_id_dim FILE_NAME(tmp, @1); delete[]$1.text; tmp = pform_make_parray_type(@2, tmp, $2); - set_type_id_range($$, tmp, $3, @3.lexical_pos, $4); + set_type_id_range($$, tmp, $3, @3, $4); } | package_scope TYPE_IDENTIFIER dimensions_opt identifier_name dimensions_opt { lex_in_package_scope(nullptr); @@ -2785,7 +2817,7 @@ partial_port_typedef_plus_id_dim FILE_NAME(tmp, @2); delete[]$2.text; tmp = pform_make_parray_type(@3, tmp, $3); - set_type_id_range($$, tmp, $4, @4.lexical_pos, $5); + set_type_id_range($$, tmp, $4, @4, $5); } ; @@ -5207,38 +5239,27 @@ type_param ; module_parameter - : parameter param_type parameter_assign - | localparam param_type parameter_assign + : parameter parameter_assign_with_type + | localparam parameter_assign_with_type { pform_requires_sv(@1, "Local parameter in module parameter port list"); } ; module_parameter_port_list : module_parameter - | data_type_opt - { param_data_type = $1; - param_is_local = false; - param_is_type = false; - param_type_restrict = {}; - } - parameter_assign - { pform_requires_sv(@3, "Omitting initial `parameter` in parameter port " + | value_parameter_assign_with_type + { pform_requires_sv(@1, "Omitting initial `parameter` in parameter port " "list"); } | type_param { param_is_local = false; } parameter_assign | module_parameter_port_list ',' module_parameter - | module_parameter_port_list ',' data_type_opt - { if ($3) { - pform_requires_sv(@3, "Omitting `parameter`/`localparam` before " - "data type in parameter port list"); - param_data_type = $3; - param_is_type = false; - param_type_restrict = {}; - } + | module_parameter_port_list ',' parameter_assign + | module_parameter_port_list ',' value_parameter_assign_with_explicit_type + { pform_requires_sv(@3, "Omitting `parameter`/`localparam` before " + "data type in parameter port list"); } - parameter_assign | module_parameter_port_list ',' type_param parameter_assign ; @@ -5852,19 +5873,6 @@ net_type_or_var_opt | K_var { $$ = NetNet::REG; } ; - /* The param_type rule is just the data_type_or_implicit rule wrapped - with an assignment to para_data_type with the figured data type. - This is used by parameter_assign, which is found to the right of - the param_type in various rules. */ - -param_type - : data_type_or_implicit - { param_is_type = false; - param_data_type = $1; - param_type_restrict = {}; - } - | type_param - parameter : K_parameter { param_is_local = false; } @@ -5876,7 +5884,7 @@ localparam ; parameter_declaration - : parameter_or_localparam param_type parameter_assign_list ';' + : parameter_or_localparam parameter_assign_list_with_type ';' parameter_or_localparam : parameter @@ -5888,17 +5896,73 @@ parameter_or_localparam handling code. localparams parse the same as parameters, they just behave differently when someone tries to override them. */ -parameter_assign_list - : parameter_assign - | parameter_assign_list ',' parameter_assign + // The first parameter assignment is parsed together with the optional type + // so `parameter T` can shadow a typedef name, while `parameter T p` still + // parses T as the parameter type. Continuations keep the previous type, so a + // bare typedef name after a comma is still a parameter name. +parameter_assign_list_with_type + : parameter_assign_with_type + | parameter_assign_list_with_type ',' parameter_assign + ; + +parameter_assign_with_type + : value_parameter_assign_with_type + | type_param parameter_assign + ; + +value_parameter_assign_with_type + : data_type_or_implicit_plus_id_dim initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1.type; + YYLTYPE id_loc = @1; + id_loc.first_line = $1.first_line; + id_loc.first_column = $1.first_column; + id_loc.last_line = $1.last_line; + id_loc.last_column = $1.last_column; + id_loc.lexical_pos = $1.lexical_pos; + pform_set_parameter(id_loc, $1.id, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $1.ranges, $2, $3); + } + ; + + // Parameter port lists allow an explicit type after a comma without + // repeating `parameter`, e.g. `#(parameter p = 1, int q = 2)`. Keep this + // narrower than value_parameter_assign_with_type so a bare typedef name + // after a comma remains a parameter name that inherits the previous type. +value_parameter_assign_with_explicit_type + : atomic_type identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1; + pform_set_parameter(@2, $2, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $3, $4, $5); + } + | implicit_type identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1; + pform_set_parameter(@2, $2, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $3, $4, $5); + } + | ps_type_identifier_dim identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1; + pform_set_parameter(@2, $2, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $3, $4, $5); + } ; parameter_assign - : IDENTIFIER dimensions_opt initializer_opt parameter_value_ranges_opt - { pform_set_parameter(@1, lex_strings.make($1), param_is_local, + : identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { pform_set_parameter(@1, $1, param_is_local, param_is_type, param_type_restrict, param_data_type, $2, $3, $4); - delete[]$1; } ; From bc6d421ff26501496f106848757f57a3bcada2cb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 26 Jun 2026 19:24:30 -0700 Subject: [PATCH 058/101] Add regression tests for parameter declarations shadowing type identifiers Check that visible type identifiers can be shadowed by value parameter names and by type parameter names. Cover ordinary parameter declarations, typed parameter declarations, and parameter port list declarations separately. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_parameter_decl_name.v | 35 ++++++++ .../sv_type_identifier_parameter_port_name.v | 85 +++++++++++++++++++ ...type_identifier_parameter_type_decl_name.v | 41 +++++++++ ...ype_identifier_parameter_type_param_name.v | 32 +++++++ ivtest/regress-vvp.list | 4 + ...v_type_identifier_parameter_decl_name.json | 5 ++ ...v_type_identifier_parameter_port_name.json | 5 ++ ...e_identifier_parameter_type_decl_name.json | 5 ++ ..._identifier_parameter_type_param_name.json | 5 ++ 9 files changed, 217 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_decl_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_port_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json diff --git a/ivtest/ivltests/sv_type_identifier_parameter_decl_name.v b/ivtest/ivltests/sv_type_identifier_parameter_decl_name.v new file mode 100644 index 000000000..f303cfcf1 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_decl_name.v @@ -0,0 +1,35 @@ +// Check that parameter declaration names can shadow visible type identifiers. + +typedef int L; +typedef int P; +typedef int Q; +typedef int R; + +module test; + + reg failed; + + parameter P = 7, R = 13; + localparam Q = 11, L = 17; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + + `check(P, 7, "parameter name did not hide typedef"); + `check(R, 13, "parameter list continuation did not hide typedef"); + `check(Q, 11, "localparam name did not hide typedef"); + `check(L, 17, "localparam list continuation did not hide typedef"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_parameter_port_name.v b/ivtest/ivltests/sv_type_identifier_parameter_port_name.v new file mode 100644 index 000000000..76429bebd --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_port_name.v @@ -0,0 +1,85 @@ +// Check that parameter port declaration names can shadow visible type identifiers. + +typedef int P; +typedef int Q; +typedef int R; +typedef logic [7:0] T; +typedef int TP; + +package p; + typedef logic [5:0] PT; +endpackage + +`define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + +module M #( + parameter int P = 5, + Q = 9, + int R = 13, + T typed_value = 8'ha5, + T T = 8'h3c, + p::PT pkg_value = 6'h2a, + parameter type TP = logic [5:0] +) (output reg failed); + + TP type_param_value; + + initial begin + failed = 1'b0; + type_param_value = 6'h15; + + `check(P, 5, "parameter port typed value mismatch"); + `check(Q, 9, "parameter port untyped continuation mismatch"); + `check(R, 13, "parameter port atomic type continuation mismatch"); + `check($bits(typed_value), 8, "parameter port typedef type continuation width mismatch"); + `check(typed_value, 8'ha5, "parameter port typedef type continuation value mismatch"); + `check($bits(T), 8, "parameter port type-name continuation did not keep typedef type"); + `check(T, 8'h3c, "parameter port type-name continuation value mismatch"); + `check($bits(pkg_value), 6, "parameter port package type continuation width mismatch"); + `check(pkg_value, 6'h2a, "parameter port package type continuation value mismatch"); + `check($bits(type_param_value), 6, "parameter port type parameter mismatch"); + `check(type_param_value, 6'h15, "parameter port type parameter value mismatch"); + end + +endmodule + +module N #(P = 3, T typed_value = 8'h5a) (output reg failed); + + initial begin + failed = 1'b0; + + `check(P, 3, "omitted parameter keyword mismatch"); + `check($bits(typed_value), 8, "omitted parameter keyword typedef width mismatch"); + `check(typed_value, 8'h5a, "omitted parameter keyword typedef value mismatch"); + end + +endmodule + +module test; + + reg failed; + wire failed_m; + wire failed_n; + + M i_m(failed_m); + N i_n(failed_n); + + initial begin + failed = 1'b0; + + #1; + + `check(failed_m, 1'b0, "parameter port module failed"); + `check(failed_n, 1'b0, "omitted parameter keyword module failed"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v b/ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v new file mode 100644 index 000000000..857dff533 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v @@ -0,0 +1,41 @@ +// Check that typed parameter declaration names can shadow visible type identifiers. + +typedef int P; +typedef logic [7:0] T; +typedef logic [6:0] U; + +module test; + + reg failed; + + parameter int P = 13; + parameter T typed_value = 8'ha5; + parameter T T = 8'h3c; + parameter U u0 = 7'h2a, U = 7'h15; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + + `check(P, 13, "typed parameter name did not hide typedef"); + `check($bits(typed_value), 8, "typed parameter width mismatch"); + `check(typed_value, 8'ha5, "typed parameter value mismatch"); + `check($bits(T), 8, "type-name parameter did not keep typedef type"); + `check(T, 8'h3c, "type-name parameter value mismatch"); + `check($bits(u0), 7, "parameter list first declaration did not keep typedef type"); + `check(u0, 7'h2a, "parameter list first value mismatch"); + `check($bits(U), 7, "parameter list continuation did not allow typedef name as parameter name"); + `check(U, 7'h15, "parameter list continuation value mismatch"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v b/ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v new file mode 100644 index 000000000..2494d7b48 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v @@ -0,0 +1,32 @@ +// Check that type parameter declaration names can shadow visible type identifiers. + +typedef int TP; + +module test; + + reg failed; + + parameter type TP = logic [3:0]; + + TP type_param_value; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + type_param_value = 4'hc; + + `check($bits(type_param_value), 4, "type parameter name did not hide typedef"); + `check(type_param_value, 4'hc, "type parameter value mismatch"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 050529a95..f7ed30002 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -389,6 +389,10 @@ sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json sv_type_identifier_net_name vvp_tests/sv_type_identifier_net_name.json sv_type_identifier_package_item vvp_tests/sv_type_identifier_package_item.json +sv_type_identifier_parameter_decl_name vvp_tests/sv_type_identifier_parameter_decl_name.json +sv_type_identifier_parameter_port_name vvp_tests/sv_type_identifier_parameter_port_name.json +sv_type_identifier_parameter_type_decl_name vvp_tests/sv_type_identifier_parameter_type_decl_name.json +sv_type_identifier_parameter_type_param_name vvp_tests/sv_type_identifier_parameter_type_param_name.json sv_type_identifier_port_name vvp_tests/sv_type_identifier_port_name.json sv_type_identifier_specparam_name vvp_tests/sv_type_identifier_specparam_name.json sv_type_identifier_task_function_argument_name vvp_tests/sv_type_identifier_task_function_argument_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json new file mode 100644 index 000000000..9f265426c --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_decl_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json new file mode 100644 index 000000000..91cf1e3aa --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_port_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json new file mode 100644 index 000000000..d51f1988e --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_type_decl_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json new file mode 100644 index 000000000..fe48a962a --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_type_param_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} From 24743af7d521c1ca6be95b4b41a6e3bd448a13d6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 3 Jul 2026 16:50:09 -0700 Subject: [PATCH 059/101] Support task and function names shadowing type identifiers SystemVerilog allows a declaration in an inner scope to use the same name as a type identifier from an outer scope. This also applies to task and function names. The lexer reports such names as `TYPE_IDENTIFIER` before the new task or function has been installed, which made constructs such as: typedef int T; module test; function int T(input int value); return value; endfunction task T; endtask endmodule fail in the task and function declaration grammar. A class method with the same name as the class itself hits the same problem because the class name is visible as a type identifier in the class scope. The task grammar can accept `identifier_name` directly, because a task has no return type and the token after `task` and the optional lifetime is always the task name. Function declarations have a local return-type/name ambiguity. After `function T` the parser does not know yet whether `T` is the function name with no explicit return type, or whether a following identifier will make `T` the explicit return type as in `function T f`. Parse the optional function return type and function name together. This allows a `TYPE_IDENTIFIER` token to be interpreted as the function name when no separate function name follows, while still parsing typed forms and `void` return types correctly. Signed-off-by: Lars-Peter Clausen --- parse.y | 82 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/parse.y b/parse.y index 0803658e8..b2979edc7 100644 --- a/parse.y +++ b/parse.y @@ -1017,7 +1017,9 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type forward_type forward_type_without_enum %type data_type_or_implicit_plus_id_base +%type data_type_or_implicit_plus_id %type data_type_or_implicit_plus_id_dim +%type data_type_or_implicit_or_void_plus_id %type partial_port_name_dim %type partial_port_type_plus_id_dim %type partial_port_typedef_plus_id_dim @@ -1808,53 +1810,53 @@ for_step_opt definitions in the func_body to take on the scope of the function instead of the module. */ function_declaration /* IEEE1800-2005: A.2.6 */ - : K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER ';' + : K_function lifetime_opt data_type_or_implicit_or_void_plus_id ';' { assert(current_function == 0); - current_function = pform_push_function_scope(@1, $4, $2); + current_function = pform_push_function_scope(@1, $3.id, $2); } tf_item_list_opt statement_or_null_list_opt K_endfunction - { current_function->set_ports($7); - current_function->set_return($3); - current_function_set_statement($8? @8 : @4, $8); - pform_set_this_class(@4, current_function); + { current_function->set_ports($6); + current_function->set_return($3.type); + current_function_set_statement($7 ? @7 : @3, $7); + pform_set_this_class(@3, current_function); pform_pop_scope(); current_function = 0; } label_opt { // Last step: check any closing name. - check_end_label(@11, "function", $4, $11); - delete[]$4; + check_end_label(@10, "function", $3.id, $10); + delete[]$3.id; } - | K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER + | K_function lifetime_opt data_type_or_implicit_or_void_plus_id { assert(current_function == 0); - current_function = pform_push_function_scope(@1, $4, $2); + current_function = pform_push_function_scope(@1, $3.id, $2); } '(' tf_port_list_opt ')' ';' block_item_decls_opt statement_or_null_list_opt K_endfunction - { current_function->set_ports($7); - current_function->set_return($3); - current_function_set_statement($11? @11 : @4, $11); - pform_set_this_class(@4, current_function); + { current_function->set_ports($6); + current_function->set_return($3.type); + current_function_set_statement($10 ? @10 : @3, $10); + pform_set_this_class(@3, current_function); pform_pop_scope(); current_function = 0; - if ($7 == 0) { - pform_requires_sv(@4, "Functions with no ports"); + if ($6 == 0) { + pform_requires_sv(@3, "Functions with no ports"); } } label_opt { // Last step: check any closing name. - check_end_label(@14, "function", $4, $14); - delete[]$4; + check_end_label(@13, "function", $3.id, $13); + delete[]$3.id; } /* Detect and recover from some errors. */ - | K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER error K_endfunction + | K_function lifetime_opt data_type_or_implicit_or_void_plus_id error K_endfunction { /* */ if (current_function) { pform_pop_scope(); @@ -1866,8 +1868,8 @@ function_declaration /* IEEE1800-2005: A.2.6 */ } label_opt { // Last step: check any closing name. - check_end_label(@8, "function", $4, $8); - delete[]$4; + check_end_label(@7, "function", $3.id, $7); + delete[]$3.id; } ; @@ -2662,7 +2664,11 @@ streaming_concatenation /* IEEE1800-2005: A.8.1 */ task_declaration /* IEEE1800-2005: A.2.7 */ - : K_task lifetime_opt IDENTIFIER ';' + /* Tasks do not have a return type, so the leading identifier is always the + task name. Use identifier_name so a name tokenized as TYPE_IDENTIFIER can + still declare a task that shadows a visible type name. */ + + : K_task lifetime_opt identifier_name ';' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } @@ -2689,7 +2695,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ delete[]$3; } - | K_task lifetime_opt IDENTIFIER '(' + | K_task lifetime_opt identifier_name '(' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } @@ -2718,7 +2724,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ delete[]$3; } - | K_task lifetime_opt IDENTIFIER error K_endtask + | K_task lifetime_opt identifier_name error K_endtask { if (current_task) { pform_pop_scope(); @@ -2765,6 +2771,20 @@ data_type_or_implicit_plus_id_base } ; + // Function declarations have the same type/name ambiguity as other + // declarations. For `function T;`, T can be the function name even when the + // lexer returns TYPE_IDENTIFIER, while `function T f;` still uses T as the + // explicit return type. Unlike variable/net declarations, function names do + // not allow unpacked dimensions. +data_type_or_implicit_plus_id + : TYPE_IDENTIFIER + { set_type_id_range($$, nullptr, $1.text, @1, nullptr); + } + | data_type_or_implicit_plus_id_base + { $$ = $1; + } + ; + data_type_or_implicit_plus_id_dim : TYPE_IDENTIFIER dimensions_opt { set_type_id_range($$, nullptr, $1.text, @1, $2); @@ -2775,6 +2795,20 @@ data_type_or_implicit_plus_id_dim } ; + // `void` is only an explicit return type. The following identifier is still a + // function name and may be tokenized as TYPE_IDENTIFIER when it shadows a + // visible type name. +data_type_or_implicit_or_void_plus_id + : data_type_or_implicit_plus_id + { $$ = $1; + } + | K_void identifier_name + { void_type_t*tmp = new void_type_t; + FILE_NAME(tmp, @1); + set_type_id_range($$, tmp, $2, @2, nullptr); + } + ; + // Partial ANSI port declarations such as `input a, integer b` can redeclare // the data type without repeating the direction. Keep this narrower than // data_type_or_implicit_plus_id_dim so a bare identifier after a comma is From 21b0107bae5365a885004c99e9fad8627d4fbda5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 3 Jul 2026 16:50:35 -0700 Subject: [PATCH 060/101] Add regression tests for type identifier task and function names Check that function and task declarations can use a visible type identifier as the declaration name. Cover both ANSI declarations and the non-ANSI forms where the name is parsed without a separate return type or port list. Also check class method declarations where the method name is the same as the enclosing class name. Add GitHub issue #670 coverage for the `function void` case using the issue-based regression naming scheme. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/br_gh670.v | 37 +++++++++++ .../sv_type_identifier_function_name.v | 51 +++++++++++++++ .../ivltests/sv_type_identifier_task_name.v | 62 +++++++++++++++++++ ivtest/regress-vvp.list | 3 + ivtest/vvp_tests/br_gh670.json | 9 +++ .../sv_type_identifier_function_name.json | 9 +++ .../sv_type_identifier_task_name.json | 9 +++ 7 files changed, 180 insertions(+) create mode 100644 ivtest/ivltests/br_gh670.v create mode 100644 ivtest/ivltests/sv_type_identifier_function_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_task_name.v create mode 100644 ivtest/vvp_tests/br_gh670.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_function_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_task_name.json diff --git a/ivtest/ivltests/br_gh670.v b/ivtest/ivltests/br_gh670.v new file mode 100644 index 000000000..d267dc51d --- /dev/null +++ b/ivtest/ivltests/br_gh670.v @@ -0,0 +1,37 @@ +// Regression test for GitHub issue #670. +// Check that a class function can have the same name as the class. + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + class test; + int value; + + function void test(); + value = 32'd42; + endfunction + endclass + + initial begin + test tst; + + failed = 1'b0; + tst = new; + tst.test(); + + `check(tst.value, 32'd42, "Class function name did not hide class name"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_function_name.v b/ivtest/ivltests/sv_type_identifier_function_name.v new file mode 100644 index 000000000..eba36bfd8 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_function_name.v @@ -0,0 +1,51 @@ +// Check that function names can shadow visible type identifiers. + +typedef int T; +typedef int U; + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + function int T(input int value); + return value + 32'd1; + endfunction + + function U; + U = 1'b1; + endfunction + + class C; + function int T(input int value); + return value + 32'd2; + endfunction + + function int C(input int value); + return value + 32'd3; + endfunction + endclass + + initial begin + C c; + + failed = 1'b0; + c = new; + + `check(T(32'd41), 32'd42, "Function name did not hide typedef"); + `check(U(), 1'b1, "Implicit function name did not hide typedef"); + `check(c.T(32'd40), 32'd42, "Class function name did not hide typedef"); + `check(c.C(32'd39), 32'd42, "Class function name did not hide class name"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_task_name.v b/ivtest/ivltests/sv_type_identifier_task_name.v new file mode 100644 index 000000000..fefcbfc98 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_task_name.v @@ -0,0 +1,62 @@ +// Check that task names can shadow visible type identifiers. + +typedef int T; +typedef int U; + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + task T(input int value, output int result); + result = value + 32'd1; + endtask + + task U; + output int result; + + result = 32'd33; + endtask + + class C; + task T(input int value, output int result); + result = value + 32'd2; + endtask + + task C(input int value, output int result); + result = value + 32'd3; + endtask + endclass + + initial begin + C c; + int r0; + int r1; + int r2; + int r3; + + failed = 1'b0; + c = new; + + T(32'd41, r0); + U(r1); + c.T(32'd40, r2); + c.C(32'd39, r3); + + `check(r0, 32'd42, "Task name did not hide typedef"); + `check(r1, 32'd33, "Non-ANSI task name did not hide typedef"); + `check(r2, 32'd42, "Class task name did not hide typedef"); + `check(r3, 32'd42, "Class task name did not hide class name"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index b7bf85cc5..b8a152ca4 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -37,6 +37,7 @@ br_gh440 vvp_tests/br_gh440.json br_gh483a vvp_tests/br_gh483a.json br_gh483b vvp_tests/br_gh483b.json br_gh552 vvp_tests/br_gh552.json +br_gh670 vvp_tests/br_gh670.json br_gh687 vvp_tests/br_gh687.json br_gh703 vvp_tests/br_gh703.json br_gh710a vvp_tests/br_gh710a.json @@ -387,6 +388,7 @@ sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json +sv_type_identifier_function_name vvp_tests/sv_type_identifier_function_name.json sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json @@ -399,6 +401,7 @@ sv_type_identifier_parameter_type_param_name vvp_tests/sv_type_identifier_parame sv_type_identifier_port_name vvp_tests/sv_type_identifier_port_name.json sv_type_identifier_specparam_name vvp_tests/sv_type_identifier_specparam_name.json sv_type_identifier_task_function_argument_name vvp_tests/sv_type_identifier_task_function_argument_name.json +sv_type_identifier_task_name vvp_tests/sv_type_identifier_task_name.json sv_type_identifier_variable_name vvp_tests/sv_type_identifier_variable_name.json sv_type_param_restrict_class1 vvp_tests/sv_type_param_restrict_class1.json sv_type_param_restrict_class2 vvp_tests/sv_type_param_restrict_class2.json diff --git a/ivtest/vvp_tests/br_gh670.json b/ivtest/vvp_tests/br_gh670.json new file mode 100644 index 000000000..5effa2a3f --- /dev/null +++ b/ivtest/vvp_tests/br_gh670.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh670.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_function_name.json b/ivtest/vvp_tests/sv_type_identifier_function_name.json new file mode 100644 index 000000000..4e5ae1920 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_function_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_function_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_task_name.json b/ivtest/vvp_tests/sv_type_identifier_task_name.json new file mode 100644 index 000000000..b95d47644 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_task_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_task_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} From 0c7341be3714ed27c2e9246ecff8784593a11ff1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 3 Jul 2026 18:10:54 -0700 Subject: [PATCH 061/101] Support enum items shadowing type identifiers SystemVerilog allows a declaration in an inner scope to use the same name as a type identifier from an outer scope. This also applies to enum item names. The lexer reports such names as `TYPE_IDENTIFIER` before the enum item has been installed, which made constructs such as: typedef int T; module test; enum { T = 1 } e; endmodule fail in the enum item grammar. Enum item declarations do not have the local type/name ambiguity that exists for variable, net, or parameter declarations. The name in each `enum_name` production is always the enum item name, including the sequence forms like `T[2]` and `T[1:2]`. Use `identifier_name` for these names so they can shadow a visible type identifier. Signed-off-by: Lars-Peter Clausen --- parse.y | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/parse.y b/parse.y index 0803658e8..c7c655661 100644 --- a/parse.y +++ b/parse.y @@ -3242,20 +3242,23 @@ pos_neg_number } ; + /* Enum items are declaration names. Use identifier_name so an enum item can + shadow a visible type identifier without introducing a type/name + ambiguity. */ enum_name - : IDENTIFIER initializer_opt + : identifier_name initializer_opt { perm_string name = lex_strings.make($1); delete[]$1; $$ = make_named_number(@$, name, $2); } - | IDENTIFIER '[' pos_neg_number ']' initializer_opt + | identifier_name '[' pos_neg_number ']' initializer_opt { perm_string name = lex_strings.make($1); long count = check_enum_seq_value(@1, $3, false); $$ = make_named_numbers(@$, name, 0, count-1, $5); delete[]$1; delete $3; } - | IDENTIFIER '[' pos_neg_number ':' pos_neg_number ']' initializer_opt + | identifier_name '[' pos_neg_number ':' pos_neg_number ']' initializer_opt { perm_string name = lex_strings.make($1); $$ = make_named_numbers(@$, name, check_enum_seq_value(@1, $3, true), check_enum_seq_value(@1, $5, true), $7); From 43817251f4c55f4f7cc14fa661a1626d0b1b6b0a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 3 Jul 2026 18:13:51 -0700 Subject: [PATCH 062/101] Add regression test for enum items shadowing type identifiers Check that enum item names can shadow visible type identifiers. Cover plain enum items as well as the counted and ranged enum item sequence forms. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_enum_item_name.v | 40 +++++++++++++++++++ ivtest/regress-vvp.list | 1 + .../sv_type_identifier_enum_item_name.json | 9 +++++ 3 files changed, 50 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_enum_item_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_enum_item_name.json diff --git a/ivtest/ivltests/sv_type_identifier_enum_item_name.v b/ivtest/ivltests/sv_type_identifier_enum_item_name.v new file mode 100644 index 000000000..9be765a20 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_enum_item_name.v @@ -0,0 +1,40 @@ +// Check that enum item names can shadow visible type identifiers. + +typedef int T; +typedef int U; +typedef int V; + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + enum { + T = 3, + U[2] = 5, + V[3:4] = 9 + } e; + + initial begin + failed = 1'b0; + + e = T; + + `check(e, 3, "Enum item name did not hide typedef"); + `check(U0, 5, "Enum item sequence name did not hide typedef"); + `check(U1, 6, "Enum item sequence value mismatch"); + `check(V3, 9, "Enum item range name did not hide typedef"); + `check(V4, 10, "Enum item range value mismatch"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index b7bf85cc5..2c8699b64 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -386,6 +386,7 @@ sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json +sv_type_identifier_enum_item_name vvp_tests/sv_type_identifier_enum_item_name.json sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_enum_item_name.json b/ivtest/vvp_tests/sv_type_identifier_enum_item_name.json new file mode 100644 index 000000000..25adc3e46 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_enum_item_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_enum_item_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Enums are SystemVerilog", + "type" : "CE" + } +} From 8d4faf27441057eae8ff832cc70cb5e6546d77db Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jul 2026 12:44:52 -0700 Subject: [PATCH 063/101] Report UDP declaration errors instead of asserting Malformed UDP declarations can reach `pform_make_udp()` with conflicting duplicate port declarations or with an initial value on an output that was not declared as a register. These cases currently trigger internal assertions instead of reporting normal compile errors. The ANSI-style UDP output initializer path also treats the initializer expression as if it was the old-style `initial out = value` assignment statement. This makes a valid `output reg out = 1'b0` initializer assert as well. Report errors for the invalid declarations and read the ANSI-style initializer value directly from the initializer expression. Signed-off-by: Lars-Peter Clausen --- pform.cc | 115 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 24 deletions(-) diff --git a/pform.cc b/pform.cc index 7aead1052..17b8ebfdf 100644 --- a/pform.cc +++ b/pform.cc @@ -1947,11 +1947,25 @@ void pform_make_udp(const struct vlltype&loc, perm_string name, ivl_assert(loc, (*decl)[idx]); if ((*decl)[idx]->get_port_type() != NetNet::PIMPLICIT) { bool rc = cur->set_port_type((*decl)[idx]->get_port_type()); - ivl_assert(loc, rc); + if (!rc) { + cerr << loc << ": error: " + << "Port " << port_name << " of primitive " + << name << " has conflicting port declarations." + << endl; + error_count += 1; + local_errors += 1; + } } if ((*decl)[idx]->get_wire_type() != NetNet::IMPLICIT) { bool rc = cur->set_wire_type((*decl)[idx]->get_wire_type()); - ivl_assert(loc, rc); + if (!rc) { + cerr << loc << ": error: " + << "Port " << port_name << " of primitive " + << name << " has conflicting data declarations." + << endl; + error_count += 1; + local_errors += 1; + } } } else { @@ -1959,6 +1973,14 @@ void pform_make_udp(const struct vlltype&loc, perm_string name, } } + if (local_errors > 0) { + delete parms; + delete decl; + delete table; + delete init_expr; + return; + } + /* Put the parameters into a vector of wire descriptions. Look in the map for the definitions of the name. In this loop, @@ -2070,22 +2092,56 @@ void pform_make_udp(const struct vlltype&loc, perm_string name, registered. Then save the initial value that I get. */ verinum::V init = verinum::Vx; if (init_expr) { - // XXXX - ivl_assert(loc, pins[0]->get_wire_type() == NetNet::REG); + if (pins[0]->get_wire_type() != NetNet::REG) { + cerr << init_expr->get_fileline() << ": error: " + << "Initial value for primitive " << name + << " requires a registered output port." << endl; + error_count += 1; + local_errors += 1; + } - const PAssign*pa = dynamic_cast(init_expr); - ivl_assert(*init_expr, pa); + auto*pa = dynamic_cast(init_expr); + if (!pa) { + cerr << init_expr->get_fileline() << ": error: " + << "Invalid initial statement for primitive " + << name << "." << endl; + error_count += 1; + local_errors += 1; + } - const PEIdent*id = dynamic_cast(pa->lval()); - ivl_assert(*init_expr, id); + if (pa) { + auto*id = dynamic_cast(pa->lval()); + if (!id) { + cerr << init_expr->get_fileline() << ": error: " + << "Invalid initial statement for primitive " + << name << "." << endl; + error_count += 1; + local_errors += 1; + } - // XXXX - //ivl_assert(*init_expr, id->name() == pins[0]->name()); + // XXXX + //ivl_assert(*init_expr, id->name() == pins[0]->name()); - const PENumber*np = dynamic_cast(pa->rval()); - ivl_assert(*init_expr, np); + auto*np = dynamic_cast(pa->rval()); + if (!np) { + cerr << init_expr->get_fileline() << ": error: " + << "Invalid initial value for primitive " + << name << "." << endl; + error_count += 1; + local_errors += 1; + } else { + init = np->value()[0]; + } + } + + if (local_errors > 0) { + delete parms; + delete decl; + delete table; + delete init_expr; + return; + } - init = np->value()[0]; } // Put the primitive into the primitives table @@ -2159,20 +2215,31 @@ void pform_make_udp(const struct vlltype&loc, perm_string name, registered. Then save the initial value that I get. */ verinum::V init = verinum::Vx; if (init_expr) { - // XXXX - ivl_assert(*init_expr, pins[0]->get_wire_type() == NetNet::REG); + bool local_errors = false; - const PAssign*pa = dynamic_cast(init_expr); - ivl_assert(*init_expr, pa); + if (pins[0]->get_wire_type() != NetNet::REG) { + cerr << init_expr->get_fileline() << ": error: " + << "Initial value for primitive " << name + << " requires a registered output port." << endl; + error_count += 1; + local_errors = true; + } - const PEIdent*id = dynamic_cast(pa->lval()); - ivl_assert(*init_expr, id); + auto*np = dynamic_cast(init_expr); + if (!np) { + cerr << init_expr->get_fileline() << ": error: " + << "Invalid initial value for primitive " + << name << "." << endl; + error_count += 1; + local_errors = true; + } - // XXXX - //ivl_assert(*init_expr, id->name() == pins[0]->name()); - - const PENumber*np = dynamic_cast(pa->rval()); - ivl_assert(*init_expr, np); + if (local_errors) { + delete parms; + delete table; + delete init_expr; + return; + } init = np->value()[0]; } From c8568998af616e7b701688f24c92503214cf1d2d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jul 2026 12:45:15 -0700 Subject: [PATCH 064/101] Add regression tests for UDP declaration diagnostics Check that UDP initial values on non-registered outputs generate compile errors for both old-style and ANSI-style UDP declarations. Also check that conflicting UDP port declarations generate an error and that a valid ANSI-style `output reg` initializer is accepted. Signed-off-by: Lars-Peter Clausen --- .../ivltests/udp_ansi_initial_nonreg_fail.v | 8 +++++++ ivtest/ivltests/udp_ansi_initial_reg.v | 21 +++++++++++++++++++ ivtest/ivltests/udp_initial_nonreg_fail.v | 12 +++++++++++ ivtest/ivltests/udp_port_decl_conflict_fail.v | 12 +++++++++++ ivtest/regress-vvp.list | 4 ++++ .../udp_ansi_initial_nonreg_fail.json | 4 ++++ ivtest/vvp_tests/udp_ansi_initial_reg.json | 4 ++++ ivtest/vvp_tests/udp_initial_nonreg_fail.json | 4 ++++ .../udp_port_decl_conflict_fail.json | 4 ++++ 9 files changed, 73 insertions(+) create mode 100644 ivtest/ivltests/udp_ansi_initial_nonreg_fail.v create mode 100644 ivtest/ivltests/udp_ansi_initial_reg.v create mode 100644 ivtest/ivltests/udp_initial_nonreg_fail.v create mode 100644 ivtest/ivltests/udp_port_decl_conflict_fail.v create mode 100644 ivtest/vvp_tests/udp_ansi_initial_nonreg_fail.json create mode 100644 ivtest/vvp_tests/udp_ansi_initial_reg.json create mode 100644 ivtest/vvp_tests/udp_initial_nonreg_fail.json create mode 100644 ivtest/vvp_tests/udp_port_decl_conflict_fail.json diff --git a/ivtest/ivltests/udp_ansi_initial_nonreg_fail.v b/ivtest/ivltests/udp_ansi_initial_nonreg_fail.v new file mode 100644 index 000000000..25ce9da6d --- /dev/null +++ b/ivtest/ivltests/udp_ansi_initial_nonreg_fail.v @@ -0,0 +1,8 @@ +// Check that an ANSI-style UDP output initializer requires a reg output. + +primitive udp_ansi_initial_nonreg_fail (output o = 1'b0, input i); + table + 0 : 0; + 1 : 1; + endtable +endprimitive diff --git a/ivtest/ivltests/udp_ansi_initial_reg.v b/ivtest/ivltests/udp_ansi_initial_reg.v new file mode 100644 index 000000000..cfbcdd6b1 --- /dev/null +++ b/ivtest/ivltests/udp_ansi_initial_reg.v @@ -0,0 +1,21 @@ +// Check that an ANSI-style UDP reg output can have an initializer. + +primitive udp_ansi_initial_reg (output reg o = 1'b0, input i); + table + 0 : ? : 0; + 1 : ? : 1; + endtable +endprimitive + +module test; + reg i; + wire o; + + udp_ansi_initial_reg i_udp(o, i); + + initial begin + i = 1'b0; + #1; + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/udp_initial_nonreg_fail.v b/ivtest/ivltests/udp_initial_nonreg_fail.v new file mode 100644 index 000000000..689b63e45 --- /dev/null +++ b/ivtest/ivltests/udp_initial_nonreg_fail.v @@ -0,0 +1,12 @@ +// Check that an old-style UDP initial statement requires a reg output. + +primitive udp_initial_nonreg_fail (o, i); + output o; + input i; + initial o = 1'b0; + + table + 0 : 0; + 1 : 1; + endtable +endprimitive diff --git a/ivtest/ivltests/udp_port_decl_conflict_fail.v b/ivtest/ivltests/udp_port_decl_conflict_fail.v new file mode 100644 index 000000000..00ebdfe55 --- /dev/null +++ b/ivtest/ivltests/udp_port_decl_conflict_fail.v @@ -0,0 +1,12 @@ +// Check that conflicting UDP port declarations generate an error. + +primitive udp_port_decl_conflict_fail (o, i); + output o; + input o; + input i; + + table + 0 : 0; + 1 : 1; + endtable +endprimitive diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index e81949a7b..85cfa1dda 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -430,7 +430,11 @@ test_va_math vvp_tests/test_va_math.json test_vams_math vvp_tests/test_vams_math.json timing_check_syntax vvp_tests/timing_check_syntax.json timing_check_delayed_signals vvp_tests/timing_check_delayed_signals.json +udp_ansi_initial_nonreg_fail vvp_tests/udp_ansi_initial_nonreg_fail.json +udp_ansi_initial_reg vvp_tests/udp_ansi_initial_reg.json udp_empty_table_fail vvp_tests/udp_empty_table_fail.json +udp_initial_nonreg_fail vvp_tests/udp_initial_nonreg_fail.json +udp_port_decl_conflict_fail vvp_tests/udp_port_decl_conflict_fail.json uwire_fail2 vvp_tests/uwire_fail2.json uwire_fail3 vvp_tests/uwire_fail3.json value_range1 vvp_tests/value_range1.json diff --git a/ivtest/vvp_tests/udp_ansi_initial_nonreg_fail.json b/ivtest/vvp_tests/udp_ansi_initial_nonreg_fail.json new file mode 100644 index 000000000..1a984c32f --- /dev/null +++ b/ivtest/vvp_tests/udp_ansi_initial_nonreg_fail.json @@ -0,0 +1,4 @@ +{ + "type" : "CE", + "source" : "udp_ansi_initial_nonreg_fail.v" +} diff --git a/ivtest/vvp_tests/udp_ansi_initial_reg.json b/ivtest/vvp_tests/udp_ansi_initial_reg.json new file mode 100644 index 000000000..5f72fea49 --- /dev/null +++ b/ivtest/vvp_tests/udp_ansi_initial_reg.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "udp_ansi_initial_reg.v" +} diff --git a/ivtest/vvp_tests/udp_initial_nonreg_fail.json b/ivtest/vvp_tests/udp_initial_nonreg_fail.json new file mode 100644 index 000000000..735d978fd --- /dev/null +++ b/ivtest/vvp_tests/udp_initial_nonreg_fail.json @@ -0,0 +1,4 @@ +{ + "type" : "CE", + "source" : "udp_initial_nonreg_fail.v" +} diff --git a/ivtest/vvp_tests/udp_port_decl_conflict_fail.json b/ivtest/vvp_tests/udp_port_decl_conflict_fail.json new file mode 100644 index 000000000..56a5fdb42 --- /dev/null +++ b/ivtest/vvp_tests/udp_port_decl_conflict_fail.json @@ -0,0 +1,4 @@ +{ + "type" : "CE", + "source" : "udp_port_decl_conflict_fail.v" +} From 56c42dcf370a51cf83e9afe9a35a6d7fa4fa3ba3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jul 2026 12:09:47 -0700 Subject: [PATCH 065/101] Support UDP names shadowing type identifiers SystemVerilog allows a UDP primitive or UDP port declaration to use the same name as a visible type identifier from another namespace or outer scope. The lexer reports such names as `TYPE_IDENTIFIER` before the UDP name has been installed, which made constructs such as: typedef int T; primitive T (Q, A); output Q; input A; table 0 : 0; endtable endprimitive fail in the UDP grammar. UDP primitive and port names do not have the local type/name ambiguity that exists for variable, net, or parameter declarations. Use `identifier_name` for the primitive name, the UDP port list, UDP port declarations, and the UDP initial target so a `TYPE_IDENTIFIER` token can be accepted as the UDP name. Signed-off-by: Lars-Peter Clausen --- parse.y | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/parse.y b/parse.y index 4736ce90f..1a67658d2 100644 --- a/parse.y +++ b/parse.y @@ -7650,7 +7650,7 @@ udp_sequ_entry ; udp_initial - : K_initial IDENTIFIER '=' number ';' + : K_initial identifier_name '=' number ';' { PExpr*etmp = new PENumber($4); PEIdent*itmp = new PEIdent(lex_strings.make($2), @2.lexical_pos); PAssign*atmp = new PAssign(itmp, etmp); @@ -7728,9 +7728,9 @@ udp_output_sym makes for these ports are scoped within the UDP, so there is no hierarchy involved. */ udp_port_decl - : K_input list_of_identifiers ';' + : K_input udp_port_list ';' { $$ = pform_make_udp_input_ports($2); } - | K_output IDENTIFIER ';' + | K_output identifier_name ';' { perm_string pname = lex_strings.make($2); PWire*pp = new PWire(pname, @2.lexical_pos, NetNet::IMPLICIT, NetNet::POUTPUT); vector*tmp = new std::vector(1); @@ -7738,7 +7738,7 @@ udp_port_decl $$ = tmp; delete[]$2; } - | K_reg IDENTIFIER ';' + | K_reg identifier_name ';' { perm_string pname = lex_strings.make($2); PWire*pp = new PWire(pname, @2.lexical_pos, NetNet::REG, NetNet::PIMPLICIT); vector*tmp = new std::vector(1); @@ -7746,7 +7746,7 @@ udp_port_decl $$ = tmp; delete[]$2; } - | K_output K_reg IDENTIFIER ';' + | K_output K_reg identifier_name ';' { perm_string pname = lex_strings.make($3); PWire*pp = new PWire(pname, @3.lexical_pos, NetNet::REG, NetNet::POUTPUT); vector*tmp = new std::vector(1); @@ -7771,9 +7771,9 @@ udp_port_decls ; udp_port_list - : IDENTIFIER + : identifier_name { $$ = list_from_identifier($1, @1.lexical_pos); } - | udp_port_list ',' IDENTIFIER + | udp_port_list ',' identifier_name { $$ = list_from_identifier($1, $3, @3.lexical_pos); } ; @@ -7782,9 +7782,9 @@ udp_reg_opt | { $$ = false; }; udp_input_declaration_list - : K_input IDENTIFIER + : K_input identifier_name { $$ = list_from_identifier($2, @2.lexical_pos); } - | udp_input_declaration_list ',' K_input IDENTIFIER + | udp_input_declaration_list ',' K_input identifier_name { $$ = list_from_identifier($1, $4, @4.lexical_pos); } ; @@ -7793,7 +7793,7 @@ udp_primitive format. The ports are simply names in the port list, and the declarations are in the body. */ - : K_primitive IDENTIFIER '(' udp_port_list ')' ';' + : K_primitive identifier_name '(' udp_port_list ')' ';' udp_port_decls udp_init_opt udp_body @@ -7807,8 +7807,8 @@ udp_primitive /* This is the syntax for IEEE1364-2001 format definitions. The port names and declarations are all in the parameter list. */ - | K_primitive IDENTIFIER - '(' K_output udp_reg_opt IDENTIFIER initializer_opt ',' + | K_primitive identifier_name + '(' K_output udp_reg_opt identifier_name initializer_opt ',' udp_input_declaration_list ')' ';' udp_body K_endprimitive label_opt From 2b67d9f7540191454a91361c0e5f220b5364bd5a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jul 2026 12:17:25 -0700 Subject: [PATCH 066/101] Add regression tests for UDP names shadowing type identifiers Check that UDP primitive and port names can shadow visible type identifiers. Cover old-style UDP declarations, including input and output declarations and the initial target, as well as ANSI-style UDP port declarations. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_udp_ansi_name.v | 25 ++++++++++++++++ ivtest/ivltests/sv_type_identifier_udp_name.v | 29 +++++++++++++++++++ ivtest/regress-vvp.list | 2 ++ .../sv_type_identifier_udp_ansi_name.json | 5 ++++ .../sv_type_identifier_udp_name.json | 5 ++++ 5 files changed, 66 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_udp_ansi_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_udp_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_udp_ansi_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_udp_name.json diff --git a/ivtest/ivltests/sv_type_identifier_udp_ansi_name.v b/ivtest/ivltests/sv_type_identifier_udp_ansi_name.v new file mode 100644 index 000000000..4b44eee0d --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_udp_ansi_name.v @@ -0,0 +1,25 @@ +// Check that ANSI-style UDP names can shadow visible type identifiers. + +package p; + typedef int ANSI_UDP; + typedef int Y; + typedef int I0; + typedef int I1; +endpackage + +import p::*; + +primitive ANSI_UDP (output Y, input I0, input I1); + table + 00 : 0; + 01 : 0; + 10 : 0; + 11 : 1; + endtable +endprimitive + +module test; + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_udp_name.v b/ivtest/ivltests/sv_type_identifier_udp_name.v new file mode 100644 index 000000000..666dd4053 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_udp_name.v @@ -0,0 +1,29 @@ +// Check that old-style UDP names can shadow visible type identifiers. + +package p; + typedef int OLD_UDP; + typedef int Q; + typedef int A; + typedef int B; +endpackage + +import p::*; + +primitive OLD_UDP (Q, A, B); + output reg Q; + input A, B; + initial Q = 0; + + table + 00 : ? : 0; + 01 : ? : 0; + 10 : ? : 0; + 11 : ? : 1; + endtable +endprimitive + +module test; + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 85cfa1dda..26594699e 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -403,6 +403,8 @@ sv_type_identifier_port_name vvp_tests/sv_type_identifier_port_name.json sv_type_identifier_specparam_name vvp_tests/sv_type_identifier_specparam_name.json sv_type_identifier_task_function_argument_name vvp_tests/sv_type_identifier_task_function_argument_name.json sv_type_identifier_task_name vvp_tests/sv_type_identifier_task_name.json +sv_type_identifier_udp_ansi_name vvp_tests/sv_type_identifier_udp_ansi_name.json +sv_type_identifier_udp_name vvp_tests/sv_type_identifier_udp_name.json sv_type_identifier_variable_name vvp_tests/sv_type_identifier_variable_name.json sv_type_param_restrict_class1 vvp_tests/sv_type_param_restrict_class1.json sv_type_param_restrict_class2 vvp_tests/sv_type_param_restrict_class2.json diff --git a/ivtest/vvp_tests/sv_type_identifier_udp_ansi_name.json b/ivtest/vvp_tests/sv_type_identifier_udp_ansi_name.json new file mode 100644 index 000000000..dd6b8e056 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_udp_ansi_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_udp_ansi_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_udp_name.json b/ivtest/vvp_tests/sv_type_identifier_udp_name.json new file mode 100644 index 000000000..8291d8798 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_udp_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_udp_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} From ff0b4a3154913fdf2af164ab5e5746f65ede2cec Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Jul 2026 21:17:10 -0700 Subject: [PATCH 067/101] parser: Fix parameter assignment grammar conflict The parameter declaration grammar allows a visible type identifier to be used as a parameter name. The assignment continuation rule still used `identifier_name`, which made Bison reduce a `TYPE_IDENTIFIER` before it had seen whether following dimensions belonged to the parameter name or to an explicit type identifier. Match ordinary and type identifiers directly in `parameter_assign` so the parser can shift dimensions before deciding between a parameter name and an explicit parameter type. Fixes: e56c93a2be56 ("Support shadowing type identifiers in parameter declarations") Signed-off-by: Lars-Peter Clausen --- parse.y | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/parse.y b/parse.y index 1a67658d2..f94ceccf1 100644 --- a/parse.y +++ b/parse.y @@ -5991,10 +5991,17 @@ value_parameter_assign_with_explicit_type ; parameter_assign - : identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt - { pform_set_parameter(@1, $1, param_is_local, + : IDENTIFIER dimensions_opt initializer_opt parameter_value_ranges_opt + { pform_set_parameter(@1, lex_strings.make($1), param_is_local, param_is_type, param_type_restrict, param_data_type, $2, $3, $4); + delete[]$1; + } + | TYPE_IDENTIFIER dimensions_opt initializer_opt parameter_value_ranges_opt + { pform_set_parameter(@1, lex_strings.make($1.text), param_is_local, + param_is_type, param_type_restrict, + param_data_type, $2, $3, $4); + delete[]$1.text; } ; From 2cef311be2a038f8098affeb0983e982f4d396bf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Jul 2026 21:10:15 -0700 Subject: [PATCH 068/101] Make Bison parser conflicts errors Bison reports shift/reduce and reduce/reduce parser conflicts as warnings by default. This allows parser changes to introduce new conflicts while the normal build still succeeds. Pass the conflict warning classes as errors to Bison when generating the parsers. This makes the regular build fail if either parser has unresolved conflicts. Signed-off-by: Lars-Peter Clausen --- Makefile.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 2503db9c7..f97cd0743 100644 --- a/Makefile.in +++ b/Makefile.in @@ -85,6 +85,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ YACC = @YACC@ +YACC_CONFLICT_FLAGS = -Werror=conflicts-sr -Werror=conflicts-rr MAN = @MAN@ PS2PDF = @PS2PDF@ GROFF = @GROFF@ @@ -241,10 +242,10 @@ parse.o: parse.cc # Use pattern rules to avoid parallel build issues (see pr3462585) parse%cc parse%h: $(srcdir)/parse%y - $(YACC) --verbose -t -p VL --defines=parse.h -o parse.cc $< + $(YACC) --verbose $(YACC_CONFLICT_FLAGS) -t -p VL --defines=parse.h -o parse.cc $< syn-rules.cc: $(srcdir)/syn-rules.y - $(YACC) --verbose -t -p syn_ -o $@ $< + $(YACC) --verbose $(YACC_CONFLICT_FLAGS) -t -p syn_ -o $@ $< lexor.cc: $(srcdir)/lexor.lex $(LEX) -s -t $< > $@ From ca5c6fc59f47eb69767d8bffd0f1e77609ab3f67 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Jul 2026 14:39:43 -0700 Subject: [PATCH 069/101] Support package names shadowing type identifiers SystemVerilog allows a package declaration to use a name that is also visible as a type identifier. The lexer reports such names as `TYPE_IDENTIFIER` before the package has been installed, which made constructs such as: package p; typedef int T; endpackage import p::*; package T; endpackage fail in the package declaration grammar. Package declarations do not have the local type/name ambiguity that exists for variable, net, or parameter declarations. After the optional lifetime the next token is always the package name. Use `identifier_name` so a `TYPE_IDENTIFIER` token can be accepted as the package name. Signed-off-by: Lars-Peter Clausen --- parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse.y b/parse.y index f94ceccf1..b7c71c8bf 100644 --- a/parse.y +++ b/parse.y @@ -2359,7 +2359,7 @@ open_range_list /* IEEE1800-2005 A.2.11 */ ; package_declaration /* IEEE1800-2005 A.1.2 */ - : K_package lifetime_opt IDENTIFIER ';' + : K_package lifetime_opt identifier_name ';' { pform_start_package_declaration(@1, $3, $2); } timeunits_declaration_opt { pform_set_scope_timescale(@1); } From 15277c9fa480def3b435d456b020ba819df31902 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Jul 2026 14:39:50 -0700 Subject: [PATCH 070/101] Add regression test for package names shadowing type identifiers Check that a package declaration can use a visible type identifier as its package name. Also check that the resulting package scope can be selected with a scope-qualified reference. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_package_name.v | 30 +++++++++++++++++++ ivtest/regress-vvp.list | 1 + .../sv_type_identifier_package_name.json | 5 ++++ 3 files changed, 36 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_package_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_package_name.json diff --git a/ivtest/ivltests/sv_type_identifier_package_name.v b/ivtest/ivltests/sv_type_identifier_package_name.v new file mode 100644 index 000000000..3a470d562 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_package_name.v @@ -0,0 +1,30 @@ +// Check that package names can shadow visible type identifiers. + +package p; + typedef int T; +endpackage + +import p::*; + +package T; + parameter int VALUE = 23; +endpackage + +module test; + + reg failed; + + initial begin + failed = 1'b0; + + if (T::VALUE !== 23) begin + $display("FAILED(%0d). Package name did not hide typedef", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 26594699e..ed072cda6 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -395,6 +395,7 @@ sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json sv_type_identifier_net_name vvp_tests/sv_type_identifier_net_name.json sv_type_identifier_package_item vvp_tests/sv_type_identifier_package_item.json +sv_type_identifier_package_name vvp_tests/sv_type_identifier_package_name.json sv_type_identifier_parameter_decl_name vvp_tests/sv_type_identifier_parameter_decl_name.json sv_type_identifier_parameter_port_name vvp_tests/sv_type_identifier_parameter_port_name.json sv_type_identifier_parameter_type_decl_name vvp_tests/sv_type_identifier_parameter_type_decl_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_package_name.json b/ivtest/vvp_tests/sv_type_identifier_package_name.json new file mode 100644 index 000000000..dd4076748 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_package_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_package_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} From 06740d0ddaab4b1d526ca57f508ed466a317ece4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jul 2026 12:22:18 -0700 Subject: [PATCH 071/101] Support block labels shadowing type identifiers SystemVerilog allows a named block label in an inner scope to use the same name as a visible type identifier from an outer scope. The lexer reports such names as `TYPE_IDENTIFIER` before the label has been installed, which made constructs such as: typedef int T; module test; initial begin begin : T end : T end endmodule fail in the block label grammar. The affected grammar positions are label names, not declarations with an adjacent type/name ambiguity. Use `identifier_name` for `label_opt` and for the anachronistic named generate begin form so a token returned as `TYPE_IDENTIFIER` can still be accepted as the label name. With `label_opt` able to handle `TYPE_IDENTIFIER`, the separate class end-label rule is no longer needed. Signed-off-by: Lars-Peter Clausen --- parse.y | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/parse.y b/parse.y index b7c71c8bf..3c1daeb31 100644 --- a/parse.y +++ b/parse.y @@ -879,7 +879,7 @@ 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 event_variable label_opt class_declaration_endlabel_opt +%type event_variable label_opt %type block_identifier_opt %type identifier_name %type event_variable_list @@ -1126,7 +1126,7 @@ class_declaration /* IEEE1800-2005: A.1.2 */ { // Process a class. pform_end_class_declaration(@9); } - class_declaration_endlabel_opt + label_opt { // Wrap up the class. check_end_label(@11, "class", $4, $11); delete[] $4; @@ -1145,14 +1145,6 @@ identifier_name | TYPE_IDENTIFIER { $$ = $1.text; } ; - /* The endlabel after a class declaration is a little tricky because - the class name is detected by the lexor as a TYPE_IDENTIFIER if it - does indeed match a name. */ -class_declaration_endlabel_opt - : ':' identifier_name { $$ = $2; } - | { $$ = 0; } - ; - /* This rule implements [ extends class_type ] in the class_declaration. It is not a rule of its own in the LRM. @@ -5217,7 +5209,7 @@ module_end ; label_opt - : ':' IDENTIFIER { $$ = $2; } + : ':' identifier_name { $$ = $2; } | { $$ = 0; } ; @@ -5787,7 +5779,7 @@ generate_item cerr << @1 << ": warning: Anachronistic use of begin/end to surround generate schemes." << endl; } } - | K_begin ':' IDENTIFIER + | K_begin ':' identifier_name { pform_start_generate_nblock(@1, $3); } generate_item_list_opt K_end { /* Detect and warn about anachronistic named begin/end use */ From f6848300b713a6b2182051958d543439a36139ca Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jul 2026 12:26:22 -0700 Subject: [PATCH 072/101] Add regression tests for block labels shadowing type identifiers Check that visible type identifiers can be reused as named block labels. Cover procedural `begin` blocks, fork blocks, and conditional generate blocks. Also check matching end labels where the grammar consumes the shared `label_opt` rule. The generate test keeps the existing vlog95 compile-error expectation because named generate scopes are not translated by the vlog95 target. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_block_label_name.v | 28 +++++++++++++++++++ .../sv_type_identifier_fork_label_name.v | 28 +++++++++++++++++++ .../sv_type_identifier_generate_label_name.v | 25 +++++++++++++++++ ivtest/regress-vvp.list | 3 ++ .../sv_type_identifier_block_label_name.json | 5 ++++ .../sv_type_identifier_fork_label_name.json | 5 ++++ ...v_type_identifier_generate_label_name.json | 9 ++++++ 7 files changed, 103 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_block_label_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_fork_label_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_generate_label_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_block_label_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_fork_label_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_generate_label_name.json diff --git a/ivtest/ivltests/sv_type_identifier_block_label_name.v b/ivtest/ivltests/sv_type_identifier_block_label_name.v new file mode 100644 index 000000000..36dc7fc7c --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_block_label_name.v @@ -0,0 +1,28 @@ +// Check that block labels can shadow visible type identifiers. + +typedef int T; + +module test; + + reg failed; + reg value; + + initial begin + failed = 1'b0; + value = 0; + + begin : T + value = value + 1; + end : T + + if (value !== 1) begin + $display("FAILED(%0d). block labels did not hide typedefs", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_fork_label_name.v b/ivtest/ivltests/sv_type_identifier_fork_label_name.v new file mode 100644 index 000000000..507312c78 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_fork_label_name.v @@ -0,0 +1,28 @@ +// Check that fork block labels can shadow visible type identifiers. + +typedef int T; + +module test; + + reg failed; + reg value; + + initial begin + failed = 1'b0; + value = 1'b0; + + fork : T + value = 1'b1; + join : T + + if (value !== 1'b1) begin + $display("FAILED(%0d). fork label did not hide typedef", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_generate_label_name.v b/ivtest/ivltests/sv_type_identifier_generate_label_name.v new file mode 100644 index 000000000..01ac046e8 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_generate_label_name.v @@ -0,0 +1,25 @@ +// Check that generate block labels can shadow visible type identifiers. + +typedef int G; +module test; + + reg failed; + + if (1) begin : G + localparam int VALUE = 1; + end : G + + initial begin + failed = 1'b0; + + if (G.VALUE != 1) begin + $display("FAILED(%0d). generate labels did not hide typedefs", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index ed072cda6..19f2caa98 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -386,10 +386,13 @@ sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json +sv_type_identifier_block_label_name vvp_tests/sv_type_identifier_block_label_name.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json sv_type_identifier_enum_item_name vvp_tests/sv_type_identifier_enum_item_name.json sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json +sv_type_identifier_fork_label_name vvp_tests/sv_type_identifier_fork_label_name.json sv_type_identifier_function_name vvp_tests/sv_type_identifier_function_name.json +sv_type_identifier_generate_label_name vvp_tests/sv_type_identifier_generate_label_name.json sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_block_label_name.json b/ivtest/vvp_tests/sv_type_identifier_block_label_name.json new file mode 100644 index 000000000..badf56ebb --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_block_label_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_block_label_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_fork_label_name.json b/ivtest/vvp_tests/sv_type_identifier_fork_label_name.json new file mode 100644 index 000000000..2aca16cd9 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_fork_label_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_fork_label_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_generate_label_name.json b/ivtest/vvp_tests/sv_type_identifier_generate_label_name.json new file mode 100644 index 000000000..488dea150 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_generate_label_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_generate_label_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Named generate scopes are not translated by vlog95", + "type" : "CE" + } +} From 17ca0948f409adcb4aeda621877086a27cadd8a1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Jul 2026 14:39:43 -0700 Subject: [PATCH 073/101] Support event names shadowing type identifiers SystemVerilog allows a declaration in an inner scope to use the same name as a type identifier from an outer scope. This also applies to named event declarations. The lexer reports such names as `TYPE_IDENTIFIER` before the event has been installed, which made constructs such as: typedef int T; module test; event T; endmodule fail in the event declaration grammar. Event declarations do not have the local type/name ambiguity that exists for variable, net, or parameter declarations. The name in `event_variable` is always the event name. Use `identifier_name` so a `TYPE_IDENTIFIER` token can be accepted as the event name. Signed-off-by: Lars-Peter Clausen --- parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse.y b/parse.y index 3c1daeb31..6f9edcf15 100644 --- a/parse.y +++ b/parse.y @@ -6368,7 +6368,7 @@ dimensions ; event_variable - : IDENTIFIER dimensions_opt + : identifier_name dimensions_opt { if ($2) { yyerror(@2, "sorry: event arrays are not supported."); delete $2; From abfb4e83bc642fe84d96bbd2791e2cfc9d1d2c79 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Jul 2026 14:39:50 -0700 Subject: [PATCH 074/101] Add regression test for event names shadowing type identifiers Check that event declarations can use visible type identifiers as event names. Also check that the resulting named event can be triggered and waited on. Signed-off-by: Lars-Peter Clausen --- .../ivltests/sv_type_identifier_event_name.v | 31 +++++++++++++++++++ ivtest/regress-vvp.list | 1 + .../sv_type_identifier_event_name.json | 5 +++ 3 files changed, 37 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_event_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_event_name.json diff --git a/ivtest/ivltests/sv_type_identifier_event_name.v b/ivtest/ivltests/sv_type_identifier_event_name.v new file mode 100644 index 000000000..b6937562c --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_event_name.v @@ -0,0 +1,31 @@ +// Check that event names can shadow visible type identifiers. + +typedef int T; + +module test; + + reg failed; + reg seen; + + event T; + + initial begin + failed = 1'b0; + seen = 1'b0; + + #1 -> T; + #1; + + if (seen !== 1'b1) begin + $display("FAILED(%0d). Event T was not triggered", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + + always @T seen = 1'b1; + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 19f2caa98..e8d021038 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -389,6 +389,7 @@ sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields. sv_type_identifier_block_label_name vvp_tests/sv_type_identifier_block_label_name.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json sv_type_identifier_enum_item_name vvp_tests/sv_type_identifier_enum_item_name.json +sv_type_identifier_event_name vvp_tests/sv_type_identifier_event_name.json sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json sv_type_identifier_fork_label_name vvp_tests/sv_type_identifier_fork_label_name.json sv_type_identifier_function_name vvp_tests/sv_type_identifier_function_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_event_name.json b/ivtest/vvp_tests/sv_type_identifier_event_name.json new file mode 100644 index 000000000..c4bb339f3 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_event_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_event_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} From 9880fb8d5e56e480cb2f9aa15d6374c71b1e9d1c Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Wed, 1 Jul 2026 12:45:16 +0500 Subject: [PATCH 075/101] SV: class queue/darray property foundation Fold in Windows VPI routing for vpip_format_pretty, fix queue method argument elaboration via elaborate_rval_expr, reject class tasks used as expressions cleanly, and update br1005 now that class queues compile. Review follow-up: move vpip_format_pretty to vpip_format.cc with diagnostic return strings, restore NetNet-based queue method elaboration with a separate property path, drop spurious /devel/ from .gitignore, and bump copyright years on touched files. Formatting pass per inline review: brace style for multi-line if bodies, ||/&& at end-of-line continuations, switch/case indentation, single-line if returns, NetNet-based sys_task_method_ again, and aligned extern decls. --- PExpr.h | 12 +- Statement.h | 28 ++ elab_expr.cc | 354 +++++++++++++++--- elab_sig.cc | 5 - elaborate.cc | 241 ++++++++++-- eval_tree.cc | 20 + ivl.def | 1 + ivl_target.h | 4 + ivtest/gold/br1005.gold | 23 +- ivtest/ivltests/sv_class_queue_prop_methods.v | 39 ++ ivtest/regress-ivl1.list | 1 - ivtest/regress-vvp.list | 3 +- .../sv_class_queue_prop_methods.json | 9 + t-dll-api.cc | 14 +- tgt-vvp/draw_class.c | 1 + tgt-vvp/draw_vpi.c | 34 +- tgt-vvp/eval_real.c | 11 +- tgt-vvp/eval_string.c | 11 +- tgt-vvp/eval_vec4.c | 83 +++- tgt-vvp/stmt_assign.c | 11 +- tgt-vvp/vvp_process.c | 98 +++++ vpi/libvpi.c | 5 + vpi/sys_display.c | 30 ++ vpi_user.h | 7 +- vvp/codes.h | 19 + vvp/compile.cc | 29 +- vvp/compile.h | 4 + vvp/lexor.lex | 1 + vvp/parse.y | 4 +- vvp/vpi_darray.cc | 113 ++++-- vvp/vpi_priv.cc | 1 + vvp/vpi_priv.h | 26 +- vvp/vpip_format.cc | 67 +++- vvp/vthread.cc | 316 ++++++++++++++++ vvp/vvp.def | 1 + 35 files changed, 1470 insertions(+), 156 deletions(-) create mode 100644 ivtest/ivltests/sv_class_queue_prop_methods.v create mode 100644 ivtest/vvp_tests/sv_class_queue_prop_methods.json diff --git a/PExpr.h b/PExpr.h index 6f6eaf07e..5cd233726 100644 --- a/PExpr.h +++ b/PExpr.h @@ -37,6 +37,7 @@ class NetExpr; class NetScope; class PPackage; struct symbol_search_results; +class netclass_t; /* * The PExpr class hierarchy supports the description of @@ -974,7 +975,16 @@ class PECallFunction : public PExpr { unsigned elaborate_arguments_(Design*des, NetScope*scope, const NetFuncDef*def, bool need_const, std::vector&parms, - unsigned parm_off) const; + unsigned parm_off, + const std::vector*src_parms = nullptr) const; + + NetExpr* elaborate_class_method_net_(Design*des, NetScope*scope, + NetNet*net, const netclass_t*class_type, + perm_string method_name, + const std::vector*src_parms) const; + + NetExpr* elaborate_expr_method_chained_(Design*des, NetScope*scope, + symbol_search_results&search_results) const; }; /* diff --git a/Statement.h b/Statement.h index d410caaef..03fee609b 100644 --- a/Statement.h +++ b/Statement.h @@ -40,6 +40,8 @@ class NetCAssign; class NetDeassign; class NetForce; class NetScope; +class NetNet; +class netdarray_t; /* * The PProcess is the root of a behavioral process. Each process gets @@ -261,16 +263,42 @@ class PCallTask : public Statement { perm_string method_name, const char *sys_task_name, const std::vector &parm_names = {}) const; + NetProc*elaborate_sys_task_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names = {}) const; NetProc*elaborate_queue_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, const char *sys_task_name, const std::vector &parm_names) const; + NetProc*elaborate_queue_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const; NetProc*elaborate_method_func_(NetScope*scope, NetNet*net, ivl_type_t type, perm_string method_name, const char*sys_task_name) const; + NetProc*elaborate_method_property_func_(NetScope*scope, + NetNet*net, int property_idx, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const; + NetProc*elaborate_queue_method_expr_(Design*des, NetScope*scope, + NetExpr*queue_base, + const netdarray_t*use_darray, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const; + NetProc*elaborate_method_func_expr_(NetScope*scope, + NetExpr*queue_base, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const; bool test_task_calls_ok_(Design*des, const NetScope*scope) const; PPackage*package_; diff --git a/elab_expr.cc b/elab_expr.cc index 1035ebe8a..0a03bb3f9 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -461,7 +461,6 @@ NetExpr* PEAssignPattern::elaborate_expr(Design*des, NetScope*, unsigned, unsign cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; - ivl_assert(*this, 0); return 0; } @@ -1540,12 +1539,36 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, << "search_results.net->net_type: " << *search_results.net->net_type() << endl; } - // Don't support multiple chained methods yet. + // Chained class methods: obj.m1().m2() — width is that of the last call. if (search_results.path_tail.size() > 1) { + if (search_results.net && search_results.net->data_type()==IVL_VT_CLASS) { + const netclass_t* cur_class = dynamic_cast(search_results.type); + if (cur_class) { + size_t ntail = search_results.path_tail.size(); + size_t idx = 0; + for (auto it = search_results.path_tail.begin() + ; it != search_results.path_tail.end() ; ++it, ++idx) { + perm_string mname = it->name; + NetScope*meth = cur_class->method_from_name(mname); + if (meth == 0) return 0; + const NetNet*res = meth->find_signal(meth->basename()); + if (res == 0) return 0; + if (idx + 1 == ntail) { + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + return expr_width_; + } + cur_class = dynamic_cast(res->net_type()); + if (cur_class == 0) return 0; + } + } + } if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width_method_: " << "Chained path tail (" << search_results.path_tail - << ") not supported." << endl; + << ") not supported for this expression type." << endl; } return 0; } @@ -1582,8 +1605,8 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, // the expr_width for the return value of the queue method. For example: // .x.size(); // In this example, x is a queue. - if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE - && search_results.path_head.back().index.empty()) { + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.empty()) { const NetNet*net = search_results.net; const netdarray_t*darray = net->darray_type(); @@ -2732,7 +2755,6 @@ NetExpr* PEIdent::elaborate_expr_class_field_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { - const netclass_t *class_type = dynamic_cast(sr.type); const name_component_t comp = sr.path_tail.front(); @@ -2810,6 +2832,27 @@ NetExpr* PEIdent::elaborate_expr_class_field_(Design*des, NetScope*scope, canon_index = make_canonical_index(des, scope, this, comp.index, tmp_ua, false); } + } else if (dynamic_cast(tmp_type)) { + /* Queue or dynamic-array property: optional index, e.g. c.q[i] + * or whole-container reference c.q. */ + const std::list*idx_list = &comp.index; + if (idx_list->empty() && !path_.back().index.empty()) { + idx_list = &path_.back().index; + } + if (idx_list->size() == 0) { + canon_index = nullptr; + } else if (idx_list->size() != 1) { + cerr << get_fileline() << ": error: " + << "Got " << idx_list->size() << " indices, " + << "expecting 0 or 1 for the queue/dynamic-array property " + << class_type->get_prop_name(pidx) << "." << endl; + des->errors++; + } else { + const index_component_t&use_index = idx_list->back(); + ivl_assert(*this, use_index.msb != 0); + ivl_assert(*this, use_index.lsb == 0); + canon_index = elab_and_eval(des, scope, use_index.msb, -1, false); + } } if (debug_elaborate && canon_index) { @@ -3128,13 +3171,16 @@ NetExpr* PECallFunction::elaborate_base_(Design*des, NetScope*scope, NetScope*ds unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, const NetFuncDef*def, bool need_const, vector&parms, - unsigned parm_off) const + unsigned parm_off, + const vector*src_parms) const { unsigned parm_errors = 0; unsigned missing_parms = 0; + const vector&use_parms = src_parms ? *src_parms : parms_; + const unsigned parm_count = parms.size() - parm_off; - const unsigned actual_count = parms_.size(); + const unsigned actual_count = use_parms.size(); if (parm_count == 0 && actual_count == 0) return 0; @@ -3147,7 +3193,7 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, des->errors += 1; } - auto args = map_named_args(des, def, parms_, parm_off); + auto args = map_named_args(des, def, use_parms, parm_off); for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { unsigned pidx = idx + parm_off; @@ -3202,6 +3248,168 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, return parm_errors; } +/* + * Elaborate a call to a single class method, optionally using an alternate + * argument vector (for chained calls where inner methods use no user args). + */ +NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope, + NetNet*net, const netclass_t*class_type, + perm_string method_name, + const vector*src_parms) const +{ + NetScope*method = class_type->method_from_name(method_name); + + if (method == 0) { + cerr << get_fileline() << ": Error: " << method_name + << " is not a method of class " << class_type->get_name() + << "." << endl; + des->errors += 1; + return 0; + } + + if (method->type() != NetScope::FUNC) { + cerr << get_fileline() << ": error: Method " << method_name + << " of class " << class_type->get_name() + << " is not a function." << endl; + des->errors += 1; + return 0; + } + + const NetFuncDef*def = method->func_def(); + ivl_assert(*this, def); + + NetNet*res = method->find_signal(method->basename()); + ivl_assert(*this, res); + + vector parms(def->port_count()); + ivl_assert(*this, def->port_count() >= 1); + + NetESignal*ethis = new NetESignal(net); + ethis->set_line(*this); + parms[0] = ethis; + + elaborate_arguments_(des, scope, def, false, parms, 1, src_parms); + + NetESignal*eres = new NetESignal(res); + NetEUFunc*call = new NetEUFunc(scope, method, eres, parms, false); + call->set_line(*this); + return call; +} + +/* + * Handle obj.m1().m2(args): arguments apply only to the last call; intermediate + * methods must be class methods returning class handles. + */ +NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*scope, + symbol_search_results&search_results) const +{ + static const vector no_parms; + + if (search_results.par_val && search_results.type) { + cerr << get_fileline() << ": sorry: " + << "Method name nesting is not supported for parameter methods yet." << endl; + des->errors += 1; + return 0; + } + + NetExpr* sub_expr = 0; + if (search_results.net) { + NetESignal*tmp = new NetESignal(search_results.net); + tmp->set_line(*this); + sub_expr = tmp; + } + + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.size()==1) { + + const NetNet*net = search_results.net; + const netdarray_t*darray = net->darray_type(); + const index_component_t&use_index = search_results.path_head.back().index.back(); + ivl_assert(*this, use_index.msb != 0); + ivl_assert(*this, use_index.lsb == 0); + + NetExpr*mux = elab_and_eval(des, scope, use_index.msb, -1, false); + if (!mux) + return 0; + + NetESelect*tmp = new NetESelect(sub_expr, mux, darray->element_width(), darray->element_type()); + tmp->set_line(*this); + sub_expr = tmp; + } + + if (!sub_expr) { + cerr << get_fileline() << ": internal error: " + << "Method chain elaborate lost base sub-expression." << endl; + des->errors += 1; + return 0; + } + + if (sub_expr->expr_type() != IVL_VT_CLASS) { + cerr << get_fileline() << ": sorry: " + << "Method name nesting for this expression type is not supported yet " + << "(only class handle chains)." << endl; + cerr << get_fileline() << ": : " + << "method path: " << search_results.path_tail << endl; + des->errors += 1; + return 0; + } + + NetNet* cur_net = search_results.net; + const netclass_t* cur_class = dynamic_cast(search_results.type); + if (cur_class == 0) + cur_class = dynamic_cast(cur_net->net_type()); + if (cur_class == 0) { + cerr << get_fileline() << ": internal error: " + << "IVL_VT_CLASS net without netclass_t type." << endl; + des->errors += 1; + return 0; + } + + size_t chain_len = search_results.path_tail.size() - 1; + size_t step_idx = 0; + for (auto it = search_results.path_tail.begin() + ; step_idx < chain_len ; ++it, ++step_idx) { + perm_string method_name = it->name; + + NetExpr* step = elaborate_class_method_net_(des, scope, cur_net, cur_class, + method_name, &no_parms); + if (step == 0) { + return 0; + } + + NetEUFunc*uf = dynamic_cast (step); + if (uf == 0) { + cerr << get_fileline() << ": internal error: " + << "expected class method call to be NetEUFunc." << endl; + des->errors += 1; + return 0; + } + + const NetESignal*rs = uf->result_sig(); + ivl_assert(*this, rs); + cur_net = const_cast(rs->sig()); + ivl_assert(*this, cur_net); + cur_class = dynamic_cast(cur_net->net_type()); + if (cur_class == 0) { + cerr << get_fileline() << ": sorry: " + << "Method chaining requires intermediate results to be class handles; " + << "after `" << method_name << "` the type is not a class." << endl; + des->errors += 1; + return 0; + } + } + + symbol_search_results tail_sr; + tail_sr.scope = search_results.scope; + tail_sr.path_head = search_results.path_head; + tail_sr.path_tail.clear(); + tail_sr.path_tail.push_back(search_results.path_tail.back()); + tail_sr.net = cur_net; + tail_sr.type = cur_class; + + return elaborate_expr_method_(des, scope, tail_sr); +} + /* * Look for a method of a given object. The search_results gives us the * information we need to look into this case: The net is the object that will @@ -3225,12 +3433,82 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, return 0; } + // e.g. c.q.size() — path_tail is {q, size}; q is a queue-typed property + if (search_results.path_tail.size() == 2) { + const netclass_t*cls = dynamic_cast(search_results.type); + if (!cls && search_results.net && search_results.net->net_type()) { + cls = dynamic_cast(search_results.net->net_type()); + } + if (cls && search_results.net) { + perm_string prop_name = search_results.path_tail.front().name; + int pidx = cls->property_idx_from_name(prop_name); + if (pidx >= 0) { + ivl_type_t ptype = cls->get_prop_type(pidx); + if (ptype && dynamic_cast(ptype)) { + NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr); + prop->set_line(*this); + perm_string method_name = search_results.path_tail.back().name; + const netqueue_t*queue = dynamic_cast(ptype); + ivl_assert(*this, queue); + ivl_type_t element_type = queue->element_type(); + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "pop_back") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_back() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_back", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "pop_front") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_front() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_front", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + } + if (ptype && ptype->base_type() == IVL_VT_DARRAY) { + NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr); + prop->set_line(*this); + perm_string method_name = search_results.path_tail.back().name; + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + } + } + } + } + if (search_results.path_tail.size() > 1) { - cerr << get_fileline() << ": sorry: " - << "Method name nesting is not supported yet." << endl; - cerr << get_fileline() << ": : " - << "method path: " << search_results.path_tail << endl; - return 0; + return elaborate_expr_method_chained_(des, scope, search_results); } if (debug_elaborate) { @@ -3271,8 +3549,8 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // .x[e].len() // If x is a queue of strings, then x[e] is a string. Elaborate the x[e] // expression and pass that to the len() method. - if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE - && search_results.path_head.back().index.size()==1) { + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.size()==1) { const NetNet*net = search_results.net; const netdarray_t*darray = net->darray_type(); @@ -3326,8 +3604,8 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // Queue methods. This handles the case that the located signal is a // QUEUE object, and there is a method. - if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE - && search_results.path_head.back().index.size()==0) { + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.size()==0) { // Get the method name that we are looking for. perm_string method_name = search_results.path_tail.back().name; @@ -3394,49 +3672,13 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // Class methods. Generate function call to the class method. if (sub_expr->expr_type()==IVL_VT_CLASS) { - // Get the method name that we are looking for. perm_string method_name = search_results.path_tail.back().name; NetNet*net = search_results.net; const netclass_t*class_type = dynamic_cast(search_results.type); ivl_assert(*this, class_type); - NetScope*method = class_type->method_from_name(method_name); - - if (method == 0) { - cerr << get_fileline() << ": Error: " << method_name - << " is not a method of class " << class_type->get_name() - << "." << endl; - des->errors += 1; - return 0; - } - - if (method->type() != NetScope::FUNC) { - cerr << get_fileline() << ": error: Method " << method_name - << " of class " << class_type->get_name() - << " is not a function." << endl; - des->errors++; - return nullptr; - } - - const NetFuncDef*def = method->func_def(); - ivl_assert(*this, def); - - NetNet*res = method->find_signal(method->basename()); - ivl_assert(*this, res); - - vector parms(def->port_count()); - ivl_assert(*this, def->port_count() >= 1); - - NetESignal*ethis = new NetESignal(net); - ethis->set_line(*this); - parms[0] = ethis; - - elaborate_arguments_(des, scope, def, false, parms, 1); - - NetESignal*eres = new NetESignal(res); - NetEUFunc*call = new NetEUFunc(scope, method, eres, parms, false); - call->set_line(*this); - return call; + return elaborate_class_method_net_(des, scope, net, class_type, + method_name, nullptr); } // String methods. diff --git a/elab_sig.cc b/elab_sig.cc index 5b752c617..f8cb069ef 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -404,11 +404,6 @@ void netclass_t::elaborate_sig(Design*des, PClass*pclass) << " type=" << *use_type << endl; } - if (dynamic_cast (use_type)) { - cerr << cur->second.get_fileline() << ": sorry: " - << "Queues inside classes are not yet supported." << endl; - des->errors++; - } set_property(cur->first, cur->second.qual, use_type); if (! cur->second.qual.test_static()) diff --git a/elaborate.cc b/elaborate.cc index e4894fd6e..4d48fa546 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4074,19 +4074,66 @@ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, return sys; } -/* - * This private method is called to elaborate queue push methods. The - * sys_task_name is the internal system-task name to use. - */ -NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, - NetNet*net, - perm_string method_name, - const char *sys_task_name, - const std::vector &parm_names) const +NetProc* PCallTask::elaborate_sys_task_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const { - NetESignal*sig = new NetESignal(net); - sig->set_line(*this); + NetEProperty*prop = new NetEProperty(net, property_idx, 0); + prop->set_line(*this); + unsigned nparms = parms_.size(); + + vectorargv (1 + nparms); + argv[0] = prop; + + if (method_name == "delete") { + const netclass_t*cls = dynamic_cast(net->net_type()); + ivl_assert(*this, cls); + ivl_type_t pt = cls->get_prop_type(property_idx); + bool is_queue = pt && pt->base_type() == IVL_VT_QUEUE; + if (is_queue) { + if (nparms > 1) { + cerr << get_fileline() << ": error: queue delete() " + << "method takes zero or one argument." << endl; + des->errors += 1; + } + } else if (nparms > 0) { + cerr << get_fileline() << ": error: darray delete() " + << "method takes no arguments." << endl; + des->errors += 1; + } + } else if (parm_names.size() != parms_.size()) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes " << parm_names.size() << " arguments, got " + << parms_.size() << "." << endl; + des->errors++; + } + + auto args = map_named_args(des, parm_names, parms_); + for (unsigned idx = 0 ; idx < nparms ; idx += 1) { + argv[idx + 1] = elab_sys_task_arg(des, scope, method_name, + idx, args[idx]); + } + + NetSTask*sys = new NetSTask(sys_task_name, IVL_SFUNC_AS_TASK_IGNORE, argv); + sys->set_line(*this); + return sys; +} + +/* + * Common implementation for queue push/insert methods. The queue_base + * expression is either a NetESignal or NetEProperty; use_darray + * supplies the element type. + */ +NetProc* PCallTask::elaborate_queue_method_expr_(Design*des, NetScope*scope, + NetExpr*queue_base, + const netdarray_t*use_darray, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const +{ unsigned nparms = parms_.size(); // insert() requires two arguments. if ((method_name == "insert") && (nparms != 2)) { @@ -4100,10 +4147,10 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, << "() method requires a single argument." << endl; des->errors += 1; } - ivl_type_t element_type = net->queue_type()->element_type(); + ivl_type_t element_type = use_darray->element_type(); unsigned expected_nparms = method_name == "insert" ? 2 : 1; vectorargv (expected_nparms+1); - argv[0] = sig; + argv[0] = queue_base; auto args = map_named_args(des, parm_names, parms_); if (method_name != "insert") { @@ -4144,14 +4191,55 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, return sys; } +/* + * This private method is called to elaborate queue push methods. The + * sys_task_name is the internal system-task name to use. + */ +NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, + NetNet*net, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const +{ + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + + const netdarray_t*use_darray = net->darray_type(); + ivl_assert(*this, use_darray); + + return elaborate_queue_method_expr_(des, scope, sig, use_darray, + method_name, sys_task_name, + parm_names); +} + +NetProc* PCallTask::elaborate_queue_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const +{ + const netclass_t*cls = dynamic_cast(net->net_type()); + ivl_assert(*this, cls); + ivl_type_t ptype = cls->get_prop_type(property_idx); + const netdarray_t*use_darray = dynamic_cast(ptype); + ivl_assert(*this, use_darray); + + NetEProperty*prop = new NetEProperty(net, property_idx, 0); + prop->set_line(*this); + + return elaborate_queue_method_expr_(des, scope, prop, use_darray, + method_name, sys_task_name, + parm_names); +} + /* * This is used for array/queue function methods called as tasks. */ -NetProc* PCallTask::elaborate_method_func_(NetScope*scope, - NetNet*net, - ivl_type_t type, - perm_string method_name, - const char*sys_task_name) const +NetProc* PCallTask::elaborate_method_func_expr_(NetScope*scope, + NetExpr*queue_base, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const { if (!void_cast_) { cerr << get_fileline() << ": warning: method function '" @@ -4161,9 +4249,7 @@ NetProc* PCallTask::elaborate_method_func_(NetScope*scope, // Generate the function. NetESFunc*sys_expr = new NetESFunc(sys_task_name, type, 1); sys_expr->set_line(*this); - NetESignal*arg = new NetESignal(net); - arg->set_line(*net); - sys_expr->parm(0, arg); + sys_expr->parm(0, queue_base); // Create a L-value that matches the function return type. NetNet*tmp; tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, type); @@ -4175,6 +4261,30 @@ NetProc* PCallTask::elaborate_method_func_(NetScope*scope, return cur; } +NetProc* PCallTask::elaborate_method_func_(NetScope*scope, + NetNet*net, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const +{ + NetESignal*arg = new NetESignal(net); + arg->set_line(*this); + return elaborate_method_func_expr_(scope, arg, type, + method_name, sys_task_name); +} + +NetProc* PCallTask::elaborate_method_property_func_(NetScope*scope, + NetNet*net, int property_idx, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const +{ + NetEProperty*prop = new NetEProperty(net, property_idx, 0); + prop->set_line(*this); + return elaborate_method_func_expr_(scope, prop, type, + method_name, sys_task_name); +} + NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, bool add_this_flag) const { @@ -4223,13 +4333,92 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, << net->name() << ".data_type() --> " << net->data_type() << endl; } + // Class handle with one path_tail component: queue/darray property method + // e.g. c.q.push_back(x) => sr.path_tail = {q}, method_name = push_back + if (sr.path_tail.size() == 1) { + const netclass_t*cls = dynamic_cast(sr.type); + if (!cls && net->net_type()) + cls = dynamic_cast(net->net_type()); + if (cls) { + perm_string prop_name = sr.path_tail.front().name; + int pidx = cls->property_idx_from_name(prop_name); + if (pidx >= 0) { + ivl_type_t ptype = cls->get_prop_type(pidx); + if (ptype && dynamic_cast(ptype)) { + const netdarray_t*use_darray = dynamic_cast(ptype); + ivl_assert(*this, use_darray); + + if (method_name == "push_back") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_property_method_(des, scope, net, pidx, + method_name, + "$ivl_queue_method$push_back", + parm_names); + } + if (method_name == "push_front") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_property_method_(des, scope, net, pidx, + method_name, + "$ivl_queue_method$push_front", + parm_names); + } + if (method_name == "insert") { + static const std::vector parm_names = { + perm_string::literal("index"), + perm_string::literal("item") + }; + return elaborate_queue_property_method_(des, scope, net, pidx, + method_name, + "$ivl_queue_method$insert", + parm_names); + } + if (method_name == "pop_front") { + return elaborate_method_property_func_(scope, net, pidx, + use_darray->element_type(), + method_name, + "$ivl_queue_method$pop_front"); + } + if (method_name == "pop_back") { + return elaborate_method_property_func_(scope, net, pidx, + use_darray->element_type(), + method_name, + "$ivl_queue_method$pop_back"); + } + if (method_name == "size") { + return elaborate_method_property_func_(scope, net, pidx, + &netvector_t::atom2s32, + method_name, "$size"); + } + } else if (ptype && ptype->base_type() == IVL_VT_DARRAY) { + if (method_name == "delete") { + static const std::vector parm_names = { + perm_string::literal("index") + }; + return elaborate_sys_task_property_method_(des, scope, net, pidx, + method_name, + "$ivl_darray_method$delete", + parm_names); + } + if (method_name == "size") { + return elaborate_method_property_func_(scope, net, pidx, + &netvector_t::atom2s32, + method_name, "$size"); + } + } + } + } + } + // Is this a method of a "string" type? if (dynamic_cast(net->net_type())) { if (method_name == "itoa") { static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$itoa", parm_names); @@ -4237,7 +4426,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$hextoa", parm_names); @@ -4245,7 +4433,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$octtoa", parm_names); @@ -4253,7 +4440,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$bintoa", parm_names); @@ -4261,7 +4447,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("r") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$realtoa", parm_names); @@ -4274,7 +4459,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("index") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_darray_method$delete", parm_names); @@ -4311,7 +4495,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, } if (net->queue_type()) { - const netdarray_t*use_darray = net->darray_type(); if (method_name == "push_back") { static const std::vector parm_names = { perm_string::literal("item") @@ -4338,11 +4521,13 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, "$ivl_queue_method$insert", parm_names); } else if (method_name == "pop_front") { + const netdarray_t*use_darray = net->darray_type(); return elaborate_method_func_(scope, net, use_darray->element_type(), method_name, "$ivl_queue_method$pop_front"); } else if (method_name == "pop_back") { + const netdarray_t*use_darray = net->darray_type(); return elaborate_method_func_(scope, net, use_darray->element_type(), method_name, diff --git a/eval_tree.cc b/eval_tree.cc index 7cbb49321..cab465892 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -26,6 +26,7 @@ # include # include "netlist.h" +# include "netclass.h" # include "ivl_assert.h" # include "netmisc.h" @@ -2182,6 +2183,25 @@ static bool get_array_info(const NetExpr*arg, long dim, left = range.get_msb(); right = range.get_lsb(); return false; + } + /* Class property (e.g. queue field): size is dynamic; defer to runtime + * instead of folding to all-X in evaluate_array_funcs_. */ + if (const NetEProperty*prop = dynamic_cast(arg)) { + const NetNet*obj = prop->get_sig(); + const netclass_t*cls = dynamic_cast(obj->net_type()); + if (cls == 0) return true; + ivl_type_t ptype = cls->get_prop_type(prop->property_idx()); + if (ptype == 0) return true; + switch (ptype->base_type()) { + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + case IVL_VT_STRING: + defer = true; + return true; + default: + break; + } + return true; } /* The argument must be a signal that has enough dimensions. */ const NetESignal*esig = dynamic_cast(arg); diff --git a/ivl.def b/ivl.def index 867d832f4..f95572ba6 100644 --- a/ivl.def +++ b/ivl.def @@ -337,6 +337,7 @@ ivl_type_packed_width ivl_type_prop_name ivl_type_prop_type ivl_type_properties +ivl_type_queue_max ivl_type_signed ivl_udp_init diff --git a/ivl_target.h b/ivl_target.h index b33ea9aa4..b4eec2c3b 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -2400,6 +2400,10 @@ extern int ivl_type_properties(ivl_type_t net); extern const char* ivl_type_prop_name(ivl_type_t net, int idx); extern ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx); +/* Maximum element count for a queue type (0 = unbounded). Only valid + * when ivl_type_base(net) == IVL_VT_QUEUE. */ +extern unsigned ivl_type_queue_max(ivl_type_t net); + #if defined(__MINGW32__) || defined (__CYGWIN__) # define DLLEXPORT __declspec(dllexport) diff --git a/ivtest/gold/br1005.gold b/ivtest/gold/br1005.gold index e59c1def5..c054f026b 100644 --- a/ivtest/gold/br1005.gold +++ b/ivtest/gold/br1005.gold @@ -1,18 +1,5 @@ -./ivltests/br1005.v:2: sorry: Queues inside classes are not yet supported. -./ivltests/br1005.v:15: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:16: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:17: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:18: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:19: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:19: : method path: q.pop_front -./ivltests/br1005.v:19: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:22: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:22: : method path: q.pop_front -./ivltests/br1005.v:22: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:25: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:25: : method path: q.pop_front -./ivltests/br1005.v:25: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:28: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:28: : method path: q.pop_front -./ivltests/br1005.v:28: error: Object test.a has no method "q.pop_front(...)". -9 error(s) during elaboration. +1 +2 +3 +4 +PASSED diff --git a/ivtest/ivltests/sv_class_queue_prop_methods.v b/ivtest/ivltests/sv_class_queue_prop_methods.v new file mode 100644 index 000000000..47d879bd7 --- /dev/null +++ b/ivtest/ivltests/sv_class_queue_prop_methods.v @@ -0,0 +1,39 @@ +// Regression: queue-typed class properties — push_front/push_back and +// pop_front/pop_back. (VVP asm must recognize %store/prop/qf/* and +// %qpop/prop/*; opcode_table must stay lexicographically sorted.) + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). expected %0d, got %0d", `__LINE__, exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int q[$]; + endclass + + C c; + int t; + + initial begin + c = new; + c.q.push_back(1); + c.q.push_front(0); + c.q.push_back(2); + t = c.q.pop_back(); + `check(t, 32'd2); + `check(c.q.size(), 32'd2); + t = c.q.pop_front(); + `check(t, 32'd0); + `check(c.q[0], 32'd1); + + if (!failed) begin + $display("PASSED"); + end + end +endmodule diff --git a/ivtest/regress-ivl1.list b/ivtest/regress-ivl1.list index af0a5b3e5..599805f1c 100644 --- a/ivtest/regress-ivl1.list +++ b/ivtest/regress-ivl1.list @@ -274,7 +274,6 @@ array_lval_select3a CE ivltests br605a EF ivltests br605b EF ivltests br971 EF ivltests -br1005 CE,-g2009 ivltests br1015b CE,-g2009 ivltests br_ml20150315b CE,-g2009 ivltests sv_deferred_assert1 CE,-g2009 ivltests gold=sv_deferred_assert1.gold diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 424ce1cc6..79fcdcab8 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -284,6 +284,7 @@ sv_class_prop_nest_obj1 vvp_tests/sv_class_prop_nest_obj1.json sv_class_prop_nest_real1 vvp_tests/sv_class_prop_nest_real1.json sv_class_prop_nest_str1 vvp_tests/sv_class_prop_nest_str1.json sv_class_prop_nest_vec1 vvp_tests/sv_class_prop_nest_vec1.json +sv_class_queue_prop_methods vvp_tests/sv_class_queue_prop_methods.json sv_class_task_expr_fail vvp_tests/sv_class_task_expr_fail.json sv_const1 vvp_tests/sv_const1.json sv_const2 vvp_tests/sv_const2.json @@ -375,9 +376,9 @@ sv_queue_method_push_back_too_few_arg_fail vvp_tests/sv_queue_method_push_back_t sv_queue_method_push_back_too_many_arg_fail vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json sv_queue_method_push_front_too_few_arg_fail vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json sv_queue_method_push_front_too_many_arg_fail vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json -sv_string_method_substr_too_few_arg_fail vvp_tests/sv_string_method_substr_too_few_arg_fail.json sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json +sv_string_method_substr_too_few_arg_fail vvp_tests/sv_string_method_substr_too_few_arg_fail.json sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json diff --git a/ivtest/vvp_tests/sv_class_queue_prop_methods.json b/ivtest/vvp_tests/sv_class_queue_prop_methods.json new file mode 100644 index 000000000..735de2f79 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_queue_prop_methods.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_queue_prop_methods.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/t-dll-api.cc b/t-dll-api.cc index 7a33018f8..4e586bb0d 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2025 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2026 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright (c) 2016 CERN Michele Castellana (michele.castellana@cern.ch) * @@ -25,6 +25,7 @@ # include "discipline.h" # include "netclass.h" # include "netdarray.h" +# include "netqueue.h" # include "netenum.h" # include "netvector.h" # include @@ -3308,6 +3309,17 @@ extern "C" ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx) return class_type->get_prop_type(idx); } +extern "C" unsigned ivl_type_queue_max(ivl_type_t net) +{ + if (net == 0) return 0; + if (const netqueue_t*que = dynamic_cast(net)) { + long max_idx = que->max_idx(); + if (max_idx < 0) return 0; + return (unsigned)(max_idx + 1); + } + return 0; +} + extern "C" int ivl_type_signed(ivl_type_t net) { assert(net); diff --git a/tgt-vvp/draw_class.c b/tgt-vvp/draw_class.c index 69431d7b6..f993dfc46 100644 --- a/tgt-vvp/draw_class.c +++ b/tgt-vvp/draw_class.c @@ -58,6 +58,7 @@ static void show_prop_type(ivl_type_t ptype) show_prop_type_vector(ptype); break; case IVL_VT_DARRAY: + case IVL_VT_QUEUE: case IVL_VT_CLASS: fprintf(vvp_out, "\"o\""); if (packed_dimensions > 0) { diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index a49ffb308..82587b25d 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -375,6 +375,20 @@ static void draw_vpi_taskfunc_args(const char*call_string, } break; + case IVL_EX_PROPERTY: + if (ivl_expr_oper1(expr) == 0 && + (ivl_expr_value(expr) == IVL_VT_QUEUE || + ivl_expr_value(expr) == IVL_VT_DARRAY)) { + ivl_signal_t clas = ivl_expr_signal(expr); + unsigned pidx = ivl_expr_property_idx(expr); + unsigned isq = ivl_expr_value(expr) == IVL_VT_QUEUE ? 1U : 0U; + snprintf(buffer, sizeof buffer, "&PQ", + clas, pidx, isq); + args[idx].text = strdup(buffer); + continue; + } + break; + case IVL_EX_SIGNAL: case IVL_EX_SELECT: args[idx].stack = vec4_stack_need; @@ -402,6 +416,10 @@ static void draw_vpi_taskfunc_args(const char*call_string, switch (ivl_expr_value(expr)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: + case IVL_VT_QUEUE: + case IVL_VT_DARRAY: + /* Queue/darray element selects may still carry the + * container ivl_expr_value; evaluate as vec4 like logic. */ draw_eval_vec4(expr); args[idx].vec_flag = ivl_expr_signed(expr)? 's' : 'u'; args[idx].str_flag = 0; @@ -433,6 +451,20 @@ static void draw_vpi_taskfunc_args(const char*call_string, buffer[0] = 0; break; default: + /* See eval_vec4.c:draw_eval_vec4 — indexed selects sometimes + * carry an unexpected ivl_expr_value (e.g. NO_TYPE). */ + if (ivl_expr_type(expr) == IVL_EX_SELECT && + ivl_expr_oper2(expr) != 0) { + draw_eval_vec4(expr); + args[idx].vec_flag = ivl_expr_signed(expr)? 's' : 'u'; + args[idx].str_flag = 0; + args[idx].real_flag = 0; + args[idx].stack = vec4_stack_need; + args[idx].vec_wid = ivl_expr_width(expr); + vec4_stack_need += 1; + buffer[0] = 0; + break; + } fprintf(stderr, "%s:%u: Sorry, cannot generate code for argument %u.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), idx+1); fprintf(vvp_out, "\nXXXX Unexpected argument: call_string=<%s>, arg=%u, type=%d\n", diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index 1a996b814..9712b7003 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2024 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -277,6 +277,15 @@ static void real_ex_pop(ivl_expr_t expr) fb = "f"; arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/r %u;\n", fb, pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/real v%p_0;\n", fb, ivl_expr_signal(arg)); diff --git a/tgt-vvp/eval_string.c b/tgt-vvp/eval_string.c index 2624c43f4..25ab2a180 100644 --- a/tgt-vvp/eval_string.c +++ b/tgt-vvp/eval_string.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -167,6 +167,15 @@ static void string_ex_pop(ivl_expr_t expr) fb = "f"; arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/str %u;\n", fb, pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/str v%p_0;\n", fb, ivl_expr_signal(arg)); diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 6bbbb0c0a..4a71f39bf 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -917,6 +917,28 @@ static void draw_property_vec4(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); unsigned pidx = ivl_expr_property_idx(expr); + /* Queue/dynamic-array property with index: index is oper1 (see + * dll_target::expr_property). */ + ivl_expr_t index_ex = ivl_expr_oper1(expr); + + if (index_ex) { + ivl_type_t cls_type = ivl_signal_net_type(sig); + ivl_type_t ptype = ivl_type_prop_type(cls_type, pidx); + if (ptype) { + ivl_variable_type_t pbase = ivl_type_base(ptype); + if (pbase == IVL_VT_QUEUE || pbase == IVL_VT_DARRAY) { + unsigned wid = ivl_expr_width(expr); + draw_eval_expr_into_integer(index_ex, 3); + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", + pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + if (ivl_expr_value(expr) == IVL_VT_BOOL) + fprintf(vvp_out, " %%cast2;\n"); + return; + } + } + } fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%prop/v %u;\n", pidx); @@ -948,7 +970,31 @@ static void draw_select_vec4(ivl_expr_t expr) return; } - if (ivl_expr_value(subexpr)==IVL_VT_DARRAY) { + /* Class property that is a queue or dynamic array: c.q[idx] */ + if (ivl_expr_type(subexpr) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(subexpr); + unsigned pidx = ivl_expr_property_idx(subexpr); + ivl_type_t cls_type = ivl_signal_net_type(clas); + ivl_type_t ptype = ivl_type_prop_type(cls_type, pidx); + if (ptype) { + ivl_variable_type_t pbase = ivl_type_base(ptype); + if (pbase == IVL_VT_QUEUE || pbase == IVL_VT_DARRAY) { + assert(base); + draw_eval_expr_into_integer(base, 3); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", + pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + if (ivl_expr_value(expr) == IVL_VT_BOOL) { + fprintf(vvp_out, " %%cast2;\n"); + } + return; + } + } + } + + if (ivl_expr_value(subexpr) == IVL_VT_DARRAY || + ivl_expr_value(subexpr) == IVL_VT_QUEUE) { ivl_signal_t sig = ivl_expr_signal(subexpr); assert(sig); assert( (ivl_signal_data_type(sig)==IVL_VT_DARRAY) @@ -957,8 +1003,9 @@ static void draw_select_vec4(ivl_expr_t expr) assert(base); draw_eval_expr_into_integer(base, 3); fprintf(vvp_out, " %%load/dar/vec4 v%p_0;\n", sig); - if (ivl_expr_value(expr) == IVL_VT_BOOL) + if (ivl_expr_value(expr) == IVL_VT_BOOL) { fprintf(vvp_out, " %%cast2;\n"); + } return; } @@ -1011,12 +1058,23 @@ static void draw_darray_pop(ivl_expr_t expr) { const char*fb; - if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) { fb = "b"; - else + } else { fb = "f"; + } ivl_expr_t arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/v %u, %u;\n", fb, pidx, + ivl_expr_width(expr)); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/v v%p_0, %u;\n", fb, ivl_expr_signal(arg), @@ -1049,6 +1107,18 @@ static void draw_sfunc_vec4(ivl_expr_t expr) return; } + if (strcmp(ivl_expr_name(expr), "$size")==0 && parm_count==1) { + ivl_expr_t arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t sig = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + fprintf(vvp_out, " %%prop/queue/size %u;\n", pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + } + draw_vpi_func_call(expr); } @@ -1357,7 +1427,10 @@ void draw_eval_vec4(ivl_expr_t expr) } assert(ivl_expr_value(expr) == IVL_VT_BOOL || - ivl_expr_value(expr) == IVL_VT_VECTOR); + ivl_expr_value(expr) == IVL_VT_VECTOR || + (ivl_expr_type(expr) == IVL_EX_SELECT && + ivl_expr_oper2(expr) != 0) || + ivl_expr_type(expr) == IVL_EX_PROPERTY); switch (ivl_expr_type(expr)) { case IVL_EX_BINARY: diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index d8c2aefec..9e3d3a5b2 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -1360,15 +1360,16 @@ static int show_stmt_assign_sig_cobject(ivl_statement_t net) fprintf(vvp_out, " %%store/prop/str %d;\n", prop_idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); - } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY) { + } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY || + ivl_type_base(prop_type) == IVL_VT_QUEUE) { int idx = 0; - /* The property is a darray, and there is no mux - expression to the assignment is of an entire - array object. */ + /* The property is a darray or queue, and there is no mux + expression so the assignment is of an entire + array/queue object. */ errors += draw_eval_object(rval); - fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY\n", prop_idx, idx); + fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY or QUEUE\n", prop_idx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_CLASS) { diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 8382b94ab..d01611671 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1663,6 +1663,24 @@ static int show_delete_method(ivl_statement_t net) return 1; ivl_expr_t parm = ivl_stmt_parm(net, 0); + if (ivl_expr_type(parm) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm); + unsigned pidx = ivl_expr_property_idx(parm); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t queue_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(queue_type) == IVL_VT_QUEUE); + + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + if (parm_count == 2) { + draw_eval_expr_into_integer(ivl_stmt_parm(net, 1), 3); + fprintf(vvp_out, " %%delete/prop/elem %u;\n", pidx); + } else { + fprintf(vvp_out, " %%delete/prop/obj %u;\n", pidx); + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return 0; + } + assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm); @@ -1687,6 +1705,47 @@ static int show_insert_method(ivl_statement_t net) return 1; ivl_expr_t parm0 = ivl_stmt_parm(net,0); + if (ivl_expr_type(parm0) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm0); + unsigned pidx = ivl_expr_property_idx(parm0); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t var_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", idx, + ivl_type_queue_max(var_type)); + + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t parm1 = ivl_stmt_parm(net,1); + draw_eval_expr_into_integer(parm1, 3); + ivl_expr_t parm2 = ivl_stmt_parm(net,2); + switch (ivl_type_base(element_type)) { + case IVL_VT_REAL: + draw_eval_real(parm2); + fprintf(vvp_out, " %%qinsert/prop/r %u, %d;\n", + pidx, idx); + break; + case IVL_VT_STRING: + draw_eval_string(parm2); + fprintf(vvp_out, " %%qinsert/prop/str %u, %d;\n", + pidx, idx); + break; + default: + draw_eval_vec4(parm2); + fprintf(vvp_out, " %%qinsert/prop/v %u, %d, %u;\n", + pidx, idx, + ivl_type_packed_width(element_type)); + break; + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + clr_word(idx); + return 0; + } + assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); @@ -1740,6 +1799,45 @@ static int show_push_frontback_method(ivl_statement_t net, bool is_front) return 1; ivl_expr_t parm0 = ivl_stmt_parm(net,0); + if (ivl_expr_type(parm0) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm0); + unsigned pidx = ivl_expr_property_idx(parm0); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t var_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", idx, + ivl_type_queue_max(var_type)); + + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t parm1 = ivl_stmt_parm(net,1); + switch (ivl_type_base(element_type)) { + case IVL_VT_REAL: + draw_eval_real(parm1); + fprintf(vvp_out, " %%store/prop/%s/r %u, %d;\n", + type_code, pidx, idx); + break; + case IVL_VT_STRING: + draw_eval_string(parm1); + fprintf(vvp_out, " %%store/prop/%s/str %u, %d;\n", + type_code, pidx, idx); + break; + default: + draw_eval_vec4(parm1); + fprintf(vvp_out, " %%store/prop/%s/v %u, %d, %u;\n", + type_code, pidx, idx, + ivl_type_packed_width(element_type)); + break; + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + clr_word(idx); + return 0; + } + assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); diff --git a/vpi/libvpi.c b/vpi/libvpi.c index d82422586..d88254257 100644 --- a/vpi/libvpi.c +++ b/vpi/libvpi.c @@ -286,6 +286,11 @@ void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit) assert(vpip_routines); vpip_routines->format_strength(str, value, bit); } +char* vpip_format_pretty(vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->format_pretty(ref); +} void vpip_make_systf_system_defined(vpiHandle ref) { assert(vpip_routines); diff --git a/vpi/sys_display.c b/vpi/sys_display.c index d5b3f2923..6522d20ea 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -867,6 +867,35 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, * be a binary string (can contain NULLs). */ break; + case 'p': + case 'P': + *idx += 1; + if (plus != 0 || prec != -1) { + vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } + if (*idx >= info->nitems) { + vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } else { + char *pp = vpip_format_pretty(info->items[*idx]); + if (pp == 0) { + vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } else { + char *cp = pp; + if (width == -1) width = 0; + size = strlen(cp) + 1; + if ((signed)size < (width+1)) size = width+1; + if (size > ini_size) result = realloc(result, size*sizeof(char)); + if (ljust == 0) sprintf(result, "%*s", width, cp); + else sprintf(result, "%-*s", width, cp); + free(pp); + size = strlen(result) + 1; + } + } + break; + default: vpi_printf("WARNING: %s:%d: unknown format %s%s.\n", info->filename, info->lineno, info->name, fmtb); @@ -1215,6 +1244,7 @@ static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name, #endif case vpiClassVar: case vpiSysFuncCall: + case vpiRegArray: /* dynamic arrays, queues, SV array vars */ break; default: diff --git a/vpi_user.h b/vpi_user.h index c347fe15e..9fcfd6342 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -641,6 +641,10 @@ extern DLLEXPORT void (*vlog_startup_routines[])(void); /* Format a scalar a la %v. The str points to a 4byte character buffer. The value must be a vpiStrengthVal. */ extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit); + /* Pretty-print a dynamic array or queue (including class property queues) + * for %p. Returns a malloc'd string, or NULL if the handle is not + * supported. Caller must free the returned pointer when non-NULL. */ +extern char* vpip_format_pretty(vpiHandle ref); /* Set the return value to return from the vvp run time. This is usually 0 or 1. This is the exit code that the vvp process returns, and in distinct from the finish_number that is an @@ -696,7 +700,7 @@ extern void vpip_count_drivers(vpiHandle ref, unsigned idx, */ // Increment the version number any time vpip_routines_s is changed. -static const PLI_UINT32 vpip_routines_version = 1; +static const PLI_UINT32 vpip_routines_version = 2; typedef struct { vpiHandle (*register_cb)(p_cb_data); @@ -737,6 +741,7 @@ typedef struct { s_vpi_vecval(*calc_clog2)(vpiHandle); void (*count_drivers)(vpiHandle, unsigned, unsigned [4]); void (*format_strength)(char*, s_vpi_value*, unsigned); + char* (*format_pretty)(vpiHandle); void (*make_systf_system_defined)(vpiHandle); void (*mcd_rawwrite)(PLI_UINT32, const char*, size_t); void (*set_return_value)(int); diff --git a/vvp/codes.h b/vvp/codes.h index 00594e1f0..b15f51888 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -150,6 +150,7 @@ extern bool of_LOAD_REAL(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_R(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_STR(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_LOAD_PROP_DAR_VEC4(vthread_t thr, vvp_code_t code); extern bool of_LOAD_OBJ(vthread_t thr, vvp_code_t code); extern bool of_LOAD_OBJA(vthread_t thr, vvp_code_t code); extern bool of_LOAD_STR(vthread_t thr, vvp_code_t code); @@ -233,6 +234,24 @@ extern bool of_STORE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_V(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_R(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_STR(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_REAL(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_STR(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_V(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_F_REAL(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_F_STR(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_F_V(vthread_t thr, vvp_code_t code); +extern bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t code); +extern bool of_DELETE_PROP_ELEM(vthread_t thr, vvp_code_t code); +extern bool of_DELETE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_V(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index fa5391717..4160c0868 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -48,9 +48,13 @@ unsigned compile_errors = 0; /* * The opcode table lists all the code mnemonics, along with their - * opcode and operand types. The table is written sorted by mnemonic - * so that it can be searched by binary search. The opcode_compare - * function is a helper function for that lookup. + * opcode and operand types. The table must be sorted lexicographically + * by mnemonic string: compile_code() uses bsearch() on this array. + * If the order is wrong, lookup fails and the assembler reports + * "Invalid opcode" for otherwise valid instructions (e.g. class + * property queue ops must sort among all %delete/*, %qpop/*, %store/* + * names, not grouped by feature). + * The opcode_compare function is a helper for that lookup. */ enum operand_e { @@ -151,6 +155,8 @@ static const struct opcode_table_s opcode_table[] = { { "%delayx", of_DELAYX, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%delete/elem",of_DELETE_ELEM,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%delete/obj",of_DELETE_OBJ,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%delete/prop/elem", of_DELETE_PROP_ELEM, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%delete/prop/obj", of_DELETE_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%delete/tail",of_DELETE_TAIL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%disable", of_DISABLE, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, { "%disable/flow", of_DISABLE_FLOW, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, @@ -204,6 +210,7 @@ static const struct opcode_table_s opcode_table[] = { { "%load/dar/vec4",of_LOAD_DAR_VEC4,1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%load/obj", of_LOAD_OBJ, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%load/obja", of_LOAD_OBJA, 2,{OA_ARR_PTR, OA_BIT1, OA_NONE} }, + { "%load/prop/dar/vec4", of_LOAD_PROP_DAR_VEC4, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, { "%load/real", of_LOAD_REAL, 1,{OA_VPI_PTR, OA_NONE, OA_NONE} }, { "%load/str", of_LOAD_STR, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%load/stra", of_LOAD_STRA, 2,{OA_ARR_PTR, OA_BIT1, OA_NONE} }, @@ -242,6 +249,7 @@ static const struct opcode_table_s opcode_table[] = { { "%pow/s", of_POW_S, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%pow/wr", of_POW_WR, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%prop/obj",of_PROP_OBJ,2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%prop/queue/size", of_PROP_QUEUE_SIZE, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/r", of_PROP_R, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/str",of_PROP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/v", of_PROP_V, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, @@ -250,6 +258,9 @@ static const struct opcode_table_s opcode_table[] = { { "%pushi/vec4",of_PUSHI_VEC4,3,{OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%pushv/str", of_PUSHV_STR, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%putc/str/vec4",of_PUTC_STR_VEC4,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qinsert/prop/r", of_QINSERT_PROP_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qinsert/prop/str", of_QINSERT_PROP_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qinsert/prop/v", of_QINSERT_PROP_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, { "%qinsert/real",of_QINSERT_REAL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%qinsert/str", of_QINSERT_STR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%qinsert/v", of_QINSERT_V, 3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, @@ -259,6 +270,12 @@ static const struct opcode_table_s opcode_table[] = { { "%qpop/f/real",of_QPOP_F_REAL,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%qpop/f/str", of_QPOP_F_STR, 1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%qpop/f/v", of_QPOP_F_V, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qpop/prop/b/r", of_QPOP_PROP_B_REAL, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/b/str", of_QPOP_PROP_B_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/b/v", of_QPOP_PROP_B_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qpop/prop/f/r", of_QPOP_PROP_F_REAL, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/f/str", of_QPOP_PROP_F_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/f/v", of_QPOP_PROP_F_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, { "%release/net",of_RELEASE_NET,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/reg",of_RELEASE_REG,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, @@ -283,6 +300,12 @@ static const struct opcode_table_s opcode_table[] = { { "%store/obj", of_STORE_OBJ, 1, {OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%store/obja", of_STORE_OBJA, 2, {OA_ARR_PTR, OA_BIT1, OA_NONE} }, { "%store/prop/obj",of_STORE_PROP_OBJ,2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/r", of_STORE_PROP_QB_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/str", of_STORE_PROP_QB_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/v", of_STORE_PROP_QB_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, + { "%store/prop/qf/r", of_STORE_PROP_QF_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qf/str", of_STORE_PROP_QF_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qf/v", of_STORE_PROP_QF_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, { "%store/prop/r", of_STORE_PROP_R, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/str",of_STORE_PROP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/v", of_STORE_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, diff --git a/vvp/compile.h b/vvp/compile.h index f2e949d66..a813db3b2 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -344,6 +344,10 @@ class resolv_list_s { */ extern void functor_ref_lookup(vvp_net_t**ref, char*lab); +extern vpiHandle vpip_make_prop_queue_ref(char* class_label, + unsigned prop_idx, + unsigned is_queue_flag); + /* * This function schedules a lookup of the labeled instruction. The * code points to a code structure that points to the instruction diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 1a6ce88ef..122bcaf45 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -272,6 +272,7 @@ inline uint64_t strtouint64(const char*str, char**endptr, int base) "&A" { return K_A; } "&APV" { return K_APV; } +"&PQ" { return K_PQ; } "&PV" { return K_PV; } "%"[.$_/a-zA-Z0-9]+ { diff --git a/vvp/parse.y b/vvp/parse.y index 2fc3dc93c..b8af4e50f 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -93,7 +93,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_NET K_NET_S K_NET_R K_NET_2S K_NET_2U %token K_NET8 K_NET8_2S K_NET8_2U K_NET8_S %token K_PARAM_STR K_PARAM_L K_PARAM_REAL K_PART K_PART_PV -%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR +%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PQ K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR %token K_REDUCE_NAND K_REDUCE_NOR K_REDUCE_XNOR K_REPEAT %token K_RESOLV K_RTRAN K_RTRANIF0 K_RTRANIF1 %token K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS @@ -1122,6 +1122,8 @@ symbol_access { $$ = vpip_make_PV($3, $5, $7); } | K_APV '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER ',' T_NUMBER '>' { $$ = vpip_make_vthr_APV($3, $5, $7, $9); } + | K_PQ '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER '>' + { $$ = vpip_make_prop_queue_ref($3, $5, $7); } ; /* functor operands can only be a list of symbols. */ diff --git a/vvp/vpi_darray.cc b/vvp/vpi_darray.cc index db9b902c6..2ad9cb7d7 100644 --- a/vvp/vpi_darray.cc +++ b/vvp/vpi_darray.cc @@ -22,6 +22,7 @@ # include "vpi_priv.h" # include "vvp_net_sig.h" # include "vvp_darray.h" +# include "vvp_cobject.h" # include "array_common.h" # include "schedule.h" #ifdef CHECK_WITH_VALGRIND @@ -294,38 +295,23 @@ vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net) } __vpiQueueVar::__vpiQueueVar(__vpiScope*sc, const char*na, vvp_net_t*ne) -: __vpiBaseVar(sc, na, ne) +: __vpiDarrayVar(sc, na, ne) { } -int __vpiQueueVar::get_type_code(void) const -{ return vpiArrayVar; } - - int __vpiQueueVar::vpi_get(int code) { - vvp_fun_signal_object*fun = dynamic_cast (get_net()->fun); - assert(fun); - vvp_object_t val = fun->get_object(); - const vvp_queue*aval = val.peek(); - switch (code) { case vpiArrayType: return vpiQueueArray; - case vpiSize: - if (aval == 0) - return 0; - else - return aval->get_size(); - default: - return 0; + return __vpiDarrayVar::vpi_get(code); } } void __vpiQueueVar::vpi_get_value(p_vpi_value val) { - val->format = vpiSuppressVal; + __vpiDarrayVar::vpi_get_value(val); } @@ -339,9 +325,94 @@ vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net) return obj; } +__vpiPropQueueRef::__vpiPropQueueRef(__vpiScope*scope, unsigned pidx, + bool is_queue) +: class_net_(0), prop_idx_(pidx), is_queue_(is_queue), scope_(scope) +{ +} + +int __vpiPropQueueRef::get_type_code(void) const +{ return vpiArrayVar; } + +static vvp_darray* get_live_darray_from_prop(vvp_net_t* class_net, unsigned pid) +{ + if (!class_net) return 0; + vvp_fun_signal_object* fun = dynamic_cast(class_net->fun); + if (!fun) return 0; + vvp_object_t obj = fun->get_object(); + vvp_cobject* cobj = obj.peek(); + if (!cobj) return 0; + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + return qobj.peek(); +} + +int __vpiPropQueueRef::vpi_get(int code) +{ + vvp_darray* aobj = get_live_darray_from_prop(class_net_, prop_idx_); + switch (code) { + case vpiArrayType: + return is_queue_ ? vpiQueueArray : vpiDynamicArray; + case vpiLeftRange: + return 0; + case vpiRightRange: + return aobj ? (int) (aobj->get_size() - 1) : 0; + case vpiSize: + return aobj ? (int) aobj->get_size() : 0; + default: + return 0; + } +} + +char* __vpiPropQueueRef::vpi_get_str(int code) +{ + if (code == vpiFile) { + return simple_set_rbuf_str(file_names[0]); + } + return generic_get_str(code, scope_, "prop_darray", NULL); +} + +void __vpiPropQueueRef::vpi_get_value(p_vpi_value val) +{ + val->format = vpiSuppressVal; +} + +vpiHandle vpip_make_prop_queue_ref(char* class_label, unsigned prop_idx, + unsigned is_queue_flag) +{ + __vpiPropQueueRef* obj = new __vpiPropQueueRef(vpip_peek_current_scope(), + prop_idx, + is_queue_flag != 0); + functor_ref_lookup(&obj->class_net_, class_label); + return obj; +} + +vvp_darray* vpip_vpi_darray_from_handle(vpiHandle ref) +{ + if (!ref) return 0; + + if (__vpiPropQueueRef* pr = dynamic_cast<__vpiPropQueueRef*>(ref)) { + return get_live_darray_from_prop(pr->class_net_, pr->prop_idx_); + } + + if (__vpiDarrayVar* dv = dynamic_cast<__vpiDarrayVar*>(ref)) { + vvp_fun_signal_object* fun = dynamic_cast(dv->get_net()->fun); + if (!fun) return 0; + vvp_object_t obj = fun->get_object(); + return obj.peek(); + } + + return 0; +} + #ifdef CHECK_WITH_VALGRIND void array_delete(vpiHandle item) { + if (__vpiPropQueueRef* pq = dynamic_cast<__vpiPropQueueRef*>(item)) { + delete pq; + return; + } + __vpiDarrayVar*dobj = dynamic_cast<__vpiDarrayVar*>(item); if (dobj) { if (dobj->vals_words) delete [] (dobj->vals_words-1); @@ -349,12 +420,6 @@ void array_delete(vpiHandle item) return; } - __vpiQueueVar*qobj = dynamic_cast<__vpiQueueVar*>(item); - if (qobj) { - delete qobj; - return; - } - fprintf(stderr, "Need support for deleting array type: %d\n", item->vpi_get(vpiArrayType)); assert(0); } diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index c3aabdfa9..1d84146b2 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -2098,6 +2098,7 @@ vpip_routines_s vpi_routines = { .calc_clog2 = vpip_calc_clog2, .count_drivers = vpip_count_drivers, .format_strength = vpip_format_strength, + .format_pretty = vpip_format_pretty, .make_systf_system_defined = vpip_make_systf_system_defined, .mcd_rawwrite = vpip_mcd_rawwrite, .set_return_value = vpip_set_return_value, diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 3b253c999..83fce94ce 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -870,18 +870,40 @@ class __vpiDarrayVar : public __vpiBaseVar, public __vpiArrayBase { extern vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net); -class __vpiQueueVar : public __vpiBaseVar { +class __vpiQueueVar : public __vpiDarrayVar { public: __vpiQueueVar(__vpiScope*scope, const char*name, vvp_net_t*net); - int get_type_code(void) const override; int vpi_get(int code) override; void vpi_get_value(p_vpi_value val) override; }; extern vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net); +class __vpiPropQueueRef : public __vpiHandle { + + public: + explicit __vpiPropQueueRef(__vpiScope*scope, unsigned pidx, bool is_queue); + + int get_type_code(void) const override; + int vpi_get(int code) override; + char* vpi_get_str(int code) override; + void vpi_get_value(p_vpi_value val) override; + + vvp_net_t* class_net_; + unsigned prop_idx_; + bool is_queue_; + + private: + __vpiScope* scope_; +}; + +extern vpiHandle vpip_make_prop_queue_ref(char* class_label, + unsigned prop_idx, + unsigned is_queue_flag); +extern vvp_darray* vpip_vpi_darray_from_handle(vpiHandle ref); + class __vpiCobjectVar : public __vpiBaseVar { public: diff --git a/vvp/vpip_format.cc b/vvp/vpip_format.cc index 4d0b085f8..fdc63c9dd 100644 --- a/vvp/vpip_format.cc +++ b/vvp/vpip_format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -18,7 +18,15 @@ */ # include "vpi_user.h" +# include "vpi_priv.h" +# include "vvp_darray.h" +# include "vvp_net_sig.h" # include +# include +# include +# include +# include +# include "ivl_alloc.h" static const char str_char1_table[257] = { ".HS1M222" "W3333333" /* 00 0F */ "L4444444" "44444444" /* 10 1F */ @@ -104,3 +112,60 @@ extern "C" void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit) assert(0); } } + +static char* vpip_format_pretty_string(const char*msg) +{ + return strdup(msg); +} + +static char* format_darray_pretty(vvp_darray* aobj) +{ + if (!aobj || aobj->get_size() == 0) + return strdup("{}"); + + std::string out = "{"; + + for (size_t i = 0; i < aobj->get_size(); i += 1) { + if (i > 0) + out += ", "; + if (dynamic_cast(aobj)) { + double d; + aobj->get_word((unsigned) i, d); + char buf[256]; + snprintf(buf, sizeof buf, "%g", d); + out += buf; + } else if (dynamic_cast(aobj)) { + std::string s; + aobj->get_word((unsigned) i, s); + out += "\""; + out += s; + out += "\""; + } else { + vvp_vector4_t v; + aobj->get_word((unsigned) i, v); + s_vpi_value val; + val.format = vpiDecStrVal; + vpip_vec4_get_value(v, v.size(), false, &val); + out += val.value.str; + } + } + + out += "}"; + return strdup(out.c_str()); +} + +extern "C" char* vpip_format_pretty(vpiHandle ref) +{ + if (!ref) + return vpip_format_pretty_string("Handle is NULL"); + + if (dynamic_cast<__vpiPropQueueRef*>(ref) || + dynamic_cast<__vpiDarrayVar*>(ref)) { + vvp_darray* aobj = vpip_vpi_darray_from_handle(ref); + if (!aobj) + return vpip_format_pretty_string("Object not found"); + return format_darray_pretty(aobj); + } + + return vpip_format_pretty_string("Unsupported object type"); +} diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 2b171111f..81989cb29 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -3873,6 +3873,36 @@ bool of_LOAD_DAR_VEC4(vthread_t thr, vvp_code_t cp) return load_dar(thr, cp); } +/* + * %load/prop/dar/vec4 , ; + * Indexed read of queue/dynamic-array class property; object on stack, + * index in words[3] (same as %load/dar/vec4). + */ +bool of_LOAD_PROP_DAR_VEC4(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + int64_t adr = thr->words[3].w_int; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t pobj; + cobj->get_object(pid, pobj, 0); + vvp_darray*darray = pobj.peek(); + + vvp_vector4_t word; + if (darray && (adr >= 0) && (thr->flags[4] == BIT4_0)) { + darray->get_word(adr, word); + } else { + dq_default(word, wid); + } + + thr->push_vec4(word); + return true; +} + /* * %load/obj */ @@ -5897,6 +5927,292 @@ bool of_STORE_PROP_V(vthread_t thr, vvp_code_t cp) return store_prop(thr, cp, cp->bit_idx[0]); } +template +static QTYPE* get_queue_prop(vvp_cobject*cobj, size_t pid) +{ + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + QTYPE* queue = qobj.peek(); + if (queue == 0) { + queue = new QTYPE; + vvp_object_t val(queue); + cobj->set_object(pid, val, 0); + } + return queue; +} + +template +static bool store_prop_qb(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + size_t pid = cp->number; + ELEM value; + unsigned max_size = thr->words[cp->bit_idx[0]].w_uint; + pop_value(thr, value, wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + queue->push_back(value, max_size); + return true; +} + +template +static bool store_prop_qf(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + size_t pid = cp->number; + ELEM value; + unsigned max_size = thr->words[cp->bit_idx[0]].w_uint; + pop_value(thr, value, wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + queue->push_front(value, max_size); + return true; +} + +bool of_STORE_PROP_QB_R(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qb(thr, cp, 0); +} + +bool of_STORE_PROP_QB_STR(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qb(thr, cp, 0); +} + +bool of_STORE_PROP_QB_V(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qb(thr, cp, cp->bit_idx[1]); +} + +bool of_STORE_PROP_QF_R(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qf(thr, cp, 0); +} + +bool of_STORE_PROP_QF_STR(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qf(thr, cp, 0); +} + +bool of_STORE_PROP_QF_V(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qf(thr, cp, cp->bit_idx[1]); +} + +template +static bool qinsert_prop(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + int64_t idx = thr->words[3].w_int; + ELEM value; + size_t pid = cp->number; + pop_value(thr, value, wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + if (idx < 0) { + cerr << thr->get_fileline() + << "Warning: cannot insert at a negative " + << get_queue_type(value) + << " index (" << idx << "). "; + print_queue_value(value); + cerr << " was not added." << endl; + } else if (thr->flags[4] != BIT4_0) { + cerr << thr->get_fileline() + << "Warning: cannot insert at an undefined " + << get_queue_type(value) << " index. "; + print_queue_value(value); + cerr << " was not added." << endl; + } else { + unsigned max_size = thr->words[cp->bit_idx[0]].w_int; + queue->insert(idx, value, max_size); + } + return true; +} + +bool of_QINSERT_PROP_R(vthread_t thr, vvp_code_t cp) +{ + return qinsert_prop(thr, cp, 0); +} + +bool of_QINSERT_PROP_STR(vthread_t thr, vvp_code_t cp) +{ + return qinsert_prop(thr, cp, 0); +} + +bool of_QINSERT_PROP_V(vthread_t thr, vvp_code_t cp) +{ + return qinsert_prop(thr, cp, cp->bit_idx[1]); +} + +template +static bool q_pop_prop(vthread_t thr, vvp_code_t cp, + void (*get_val_func)(vvp_queue*, ELEM&), + const char*loc, unsigned wid) +{ + size_t pid = cp->number; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + + size_t size = queue->get_size(); + + ELEM value; + if (size) { + get_val_func(queue, value); + } else { + dq_default(value, wid); + cerr << thr->get_fileline() + << "Warning: pop_" << loc << "() on empty " + << get_queue_type(value) << "." << endl; + } + + push_value(thr, value, wid); + return true; +} + +template +static bool qpop_b_prop(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + return q_pop_prop(thr, cp, get_back_value, "back", wid); +} + +template +static bool qpop_f_prop(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + return q_pop_prop(thr, cp, get_front_value, "front", wid); +} + +bool of_QPOP_PROP_B_REAL(vthread_t thr, vvp_code_t cp) +{ + return qpop_b_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_B_STR(vthread_t thr, vvp_code_t cp) +{ + return qpop_b_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_B_V(vthread_t thr, vvp_code_t cp) +{ + return qpop_b_prop(thr, cp, cp->bit_idx[0]); +} + +bool of_QPOP_PROP_F_REAL(vthread_t thr, vvp_code_t cp) +{ + return qpop_f_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_F_STR(vthread_t thr, vvp_code_t cp) +{ + return qpop_f_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_F_V(vthread_t thr, vvp_code_t cp) +{ + return qpop_f_prop(thr, cp, cp->bit_idx[0]); +} + +bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue* queue = qobj.peek(); + + size_t sz = queue ? queue->get_size() : 0; + + vvp_vector4_t val(32, BIT4_0); + unsigned long ul = sz; + for (unsigned idx = 0; idx < 32; idx++) { + if (ul & 1UL) { + val.set_bit(idx, BIT4_1); + } else { + val.set_bit(idx, BIT4_0); + } + ul >>= 1; + } + thr->push_vec4(val); + return true; +} + +bool of_DELETE_PROP_ELEM(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + + int64_t idx_val = thr->words[3].w_int; + if (thr->flags[4] == BIT4_1) { + cerr << thr->get_fileline() + << "Warning: skipping queue delete() with undefined index." + << endl; + return true; + } + if (idx_val < 0) { + cerr << thr->get_fileline() + << "Warning: skipping queue delete() with negative index." + << endl; + return true; + } + size_t idx = idx_val; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue* queue = qobj.peek(); + if (queue == 0) { + cerr << thr->get_fileline() + << "Warning: skipping delete(" << idx + << ") on empty queue." << endl; + } else { + size_t size = queue->get_size(); + if (idx >= size) { + cerr << thr->get_fileline() + << "Warning: skipping out of range delete(" << idx + << ") on queue of size " << size << "." << endl; + } else { + queue->erase(idx); + } + } + + return true; +} + +bool of_DELETE_PROP_OBJ(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + cobj->set_object(pid, vvp_object_t(), 0); + + return true; +} + template static bool store_qb(vthread_t thr, vvp_code_t cp, unsigned wid=0) { diff --git a/vvp/vvp.def b/vvp/vvp.def index 1d1737c77..702103d60 100644 --- a/vvp/vvp.def +++ b/vvp/vvp.def @@ -43,6 +43,7 @@ vpi_vprintf vpip_calc_clog2 vpip_count_drivers vpip_format_strength +vpip_format_pretty vpip_make_systf_system_defined vpip_mcd_rawwrite vpip_set_return_value From 6844ed4194b095269c478af2b4d08dabe96e1df7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 8 Jul 2026 21:27:04 -0700 Subject: [PATCH 076/101] Accept type-identifier tokens in named binding selectors A named binding selector such as `.T(expr)` names an existing formal port, parameter, task or function argument, or constructor argument. It is not a declaration of a new identifier. If a visible typedef named `T` exists at the use site the lexer returns `TYPE_IDENTIFIER`, which made the parser reject the binding selector. Modport simple port aliases use the same grammar, but are slightly different: the selector is the modport-visible port name and can shadow a visible typedef name in the interface scope. Use `identifier_name` for the selector name in `named_expression` and `named_expression_opt`. This covers named parameter overrides, named task and function arguments, named constructor arguments, and modport simple port aliases. Also use `identifier_name` in the named module port connection rules, including implicit named port connections and error recovery. Signed-off-by: Lars-Peter Clausen --- parse.y | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parse.y b/parse.y index 6f9edcf15..b7d3a2e31 100644 --- a/parse.y +++ b/parse.y @@ -6089,7 +6089,7 @@ parameter_value_opt ; named_expression - : '.' IDENTIFIER '(' expression ')' + : '.' identifier_name '(' expression ')' { named_pexpr_t*tmp = new named_pexpr_t; FILE_NAME(tmp, @$); tmp->name = lex_strings.make($2); @@ -6100,7 +6100,7 @@ named_expression named_expression_opt : named_expression - | '.' IDENTIFIER '(' ')' + | '.' identifier_name '(' ')' { named_pexpr_t*tmp = new named_pexpr_t; FILE_NAME(tmp, @$); tmp->name = lex_strings.make($2); @@ -6190,7 +6190,7 @@ port_name { delete $1; $$ = $2; } - | attribute_list_opt '.' IDENTIFIER '(' error ')' + | attribute_list_opt '.' identifier_name '(' error ')' { yyerror(@3, "error: Invalid port connection expression."); named_pexpr_t*tmp = new named_pexpr_t; FILE_NAME(tmp, @$); @@ -6200,7 +6200,7 @@ port_name delete $1; $$ = tmp; } - | attribute_list_opt '.' IDENTIFIER + | attribute_list_opt '.' identifier_name { pform_requires_sv(@3, "Implicit named port connections"); named_pexpr_t*tmp = new named_pexpr_t; FILE_NAME(tmp, @$); From 3e0c298b254edeb2e212fb2e83b78ebc0084a714 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 8 Jul 2026 21:27:57 -0700 Subject: [PATCH 077/101] Add regression tests for named selectors matching typedef names Check that a named binding selector can have the same text as a visible typedef name. Cover named module port connections, named parameter overrides, and named task, function and constructor arguments. Also check that a modport simple port selector can shadow a visible typedef name. Modport simple port aliases share the same parser rule but declare the modport-visible name rather than binding to an existing formal. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_modport_named_port.v | 18 ++++++++++ ...pe_identifier_named_constructor_argument.v | 36 +++++++++++++++++++ ..._type_identifier_named_function_argument.v | 31 ++++++++++++++++ ...sv_type_identifier_named_parameter_value.v | 33 +++++++++++++++++ ...sv_type_identifier_named_port_connection.v | 35 ++++++++++++++++++ .../sv_type_identifier_named_task_argument.v | 31 ++++++++++++++++ ivtest/regress-vvp.list | 6 ++++ ...sv_type_identifier_modport_named_port.json | 5 +++ ...identifier_named_constructor_argument.json | 9 +++++ ...pe_identifier_named_function_argument.json | 5 +++ ...type_identifier_named_parameter_value.json | 5 +++ ...type_identifier_named_port_connection.json | 5 +++ ...v_type_identifier_named_task_argument.json | 5 +++ 13 files changed, 224 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_modport_named_port.v create mode 100644 ivtest/ivltests/sv_type_identifier_named_constructor_argument.v create mode 100644 ivtest/ivltests/sv_type_identifier_named_function_argument.v create mode 100644 ivtest/ivltests/sv_type_identifier_named_parameter_value.v create mode 100644 ivtest/ivltests/sv_type_identifier_named_port_connection.v create mode 100644 ivtest/ivltests/sv_type_identifier_named_task_argument.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_modport_named_port.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_named_constructor_argument.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_named_function_argument.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_named_parameter_value.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_named_port_connection.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_named_task_argument.json diff --git a/ivtest/ivltests/sv_type_identifier_modport_named_port.v b/ivtest/ivltests/sv_type_identifier_modport_named_port.v new file mode 100644 index 000000000..53e6adbfd --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_modport_named_port.v @@ -0,0 +1,18 @@ +// Check that a modport simple port selector can shadow a visible typedef name. + +interface I; + typedef int T; + logic value; + + modport m(input .T(value)); +endinterface + +module test; + + I i(); + + initial begin + $display("PASSED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_named_constructor_argument.v b/ivtest/ivltests/sv_type_identifier_named_constructor_argument.v new file mode 100644 index 000000000..8bbc0f298 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_named_constructor_argument.v @@ -0,0 +1,36 @@ +// Check that a named constructor argument selector can match a visible typedef name. + +module test; + + class C; + integer value; + + function new(input integer T, input integer B); + value = T + B; + endfunction + endclass + + typedef int T; + + reg failed; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + C c; + + failed = 1'b0; + c = new(.T(40), .B(2)); + `check(c.value, 42); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_named_function_argument.v b/ivtest/ivltests/sv_type_identifier_named_function_argument.v new file mode 100644 index 000000000..d193a241f --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_named_function_argument.v @@ -0,0 +1,31 @@ +// Check that a named function argument selector can match a visible typedef name. + +module test; + + function integer f(input integer T, input integer B); + f = T + B; + endfunction + + typedef int T; + + reg failed; + integer value; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + value = f(.T(40), .B(2)); + `check(value, 42); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_named_parameter_value.v b/ivtest/ivltests/sv_type_identifier_named_parameter_value.v new file mode 100644 index 000000000..d448253de --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_named_parameter_value.v @@ -0,0 +1,33 @@ +// Check that a named parameter override selector can match a visible typedef name. + +module M #(parameter integer T = 0) (output [31:0] Y); + assign Y = T; +endmodule + +module test; + + typedef int T; + + reg failed; + wire [31:0] y; + + M #(.T(32'd42)) i_m(.Y(y)); + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + #1; + `check(y, 32'd42); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_named_port_connection.v b/ivtest/ivltests/sv_type_identifier_named_port_connection.v new file mode 100644 index 000000000..be9ed82ff --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_named_port_connection.v @@ -0,0 +1,35 @@ +// Check that a named port connection selector can match a visible typedef name. + +module M(input [3:0] T, output [3:0] Y); + assign Y = T; +endmodule + +module test; + + typedef int T; + + reg failed; + reg [3:0] value; + wire [3:0] y; + + M i_m(.T(value), .Y(y)); + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0h, got %0h", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + value = 4'ha; + #1; + `check(y, 4'ha); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_named_task_argument.v b/ivtest/ivltests/sv_type_identifier_named_task_argument.v new file mode 100644 index 000000000..0ee84ae4f --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_named_task_argument.v @@ -0,0 +1,31 @@ +// Check that a named task argument selector can match a visible typedef name. + +module test; + + task t(input integer T, output integer result); + result = T; + endtask + + typedef int T; + + reg failed; + integer value; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + t(.T(42), .result(value)); + `check(value, 42); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index e8d021038..10359abca 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -396,7 +396,13 @@ sv_type_identifier_function_name vvp_tests/sv_type_identifier_function_name.json sv_type_identifier_generate_label_name vvp_tests/sv_type_identifier_generate_label_name.json sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json +sv_type_identifier_modport_named_port vvp_tests/sv_type_identifier_modport_named_port.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json +sv_type_identifier_named_constructor_argument vvp_tests/sv_type_identifier_named_constructor_argument.json +sv_type_identifier_named_function_argument vvp_tests/sv_type_identifier_named_function_argument.json +sv_type_identifier_named_parameter_value vvp_tests/sv_type_identifier_named_parameter_value.json +sv_type_identifier_named_port_connection vvp_tests/sv_type_identifier_named_port_connection.json +sv_type_identifier_named_task_argument vvp_tests/sv_type_identifier_named_task_argument.json sv_type_identifier_net_name vvp_tests/sv_type_identifier_net_name.json sv_type_identifier_package_item vvp_tests/sv_type_identifier_package_item.json sv_type_identifier_package_name vvp_tests/sv_type_identifier_package_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_modport_named_port.json b/ivtest/vvp_tests/sv_type_identifier_modport_named_port.json new file mode 100644 index 000000000..b3138f57d --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_modport_named_port.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_modport_named_port.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_named_constructor_argument.json b/ivtest/vvp_tests/sv_type_identifier_named_constructor_argument.json new file mode 100644 index 000000000..e50d8a5c7 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_named_constructor_argument.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_named_constructor_argument.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Class scopes and new operators are not translated", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_named_function_argument.json b/ivtest/vvp_tests/sv_type_identifier_named_function_argument.json new file mode 100644 index 000000000..7186b6525 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_named_function_argument.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_named_function_argument.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_named_parameter_value.json b/ivtest/vvp_tests/sv_type_identifier_named_parameter_value.json new file mode 100644 index 000000000..b3bf35a92 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_named_parameter_value.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_named_parameter_value.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_named_port_connection.json b/ivtest/vvp_tests/sv_type_identifier_named_port_connection.json new file mode 100644 index 000000000..48e05b779 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_named_port_connection.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_named_port_connection.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_named_task_argument.json b/ivtest/vvp_tests/sv_type_identifier_named_task_argument.json new file mode 100644 index 000000000..0f0d45766 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_named_task_argument.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_named_task_argument.v", + "iverilog-args" : [ "-g2005-sv" ] +} From f870c755fa6b8f85c4708b087b0730d7848d2906 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Jul 2026 20:45:55 -0700 Subject: [PATCH 078/101] ivtest: Run nested enum tests through vlog95 The br_gh1385a, br_gh1385b, and br_gh1385c JSON descriptors mark the vlog95 variants as compile errors. The enum typedefs are translated correctly, so the compile error expectation causes the tests to fail when compilation succeeds. Remove the stale overrides and run the translated tests through vlog95. Fixes: 10349287a030 ("Add regression tests for enum typedefs in nested scopes") Signed-off-by: Lars-Peter Clausen --- ivtest/vvp_tests/br_gh1385a.json | 6 +----- ivtest/vvp_tests/br_gh1385b.json | 6 +----- ivtest/vvp_tests/br_gh1385c.json | 6 +----- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/ivtest/vvp_tests/br_gh1385a.json b/ivtest/vvp_tests/br_gh1385a.json index 82757362e..86da73c46 100644 --- a/ivtest/vvp_tests/br_gh1385a.json +++ b/ivtest/vvp_tests/br_gh1385a.json @@ -1,9 +1,5 @@ { "type" : "normal", "source" : "br_gh1385a.v", - "iverilog-args" : [ "-g2005-sv" ], - "vlog95" : { - "__comment" : "Enum typedefs are not translated", - "type" : "CE" - } + "iverilog-args" : [ "-g2005-sv" ] } diff --git a/ivtest/vvp_tests/br_gh1385b.json b/ivtest/vvp_tests/br_gh1385b.json index c2e17a36d..1949f3d57 100644 --- a/ivtest/vvp_tests/br_gh1385b.json +++ b/ivtest/vvp_tests/br_gh1385b.json @@ -1,9 +1,5 @@ { "type" : "normal", "source" : "br_gh1385b.v", - "iverilog-args" : [ "-g2005-sv" ], - "vlog95" : { - "__comment" : "Enum typedefs are not translated", - "type" : "CE" - } + "iverilog-args" : [ "-g2005-sv" ] } diff --git a/ivtest/vvp_tests/br_gh1385c.json b/ivtest/vvp_tests/br_gh1385c.json index 8f0c46a12..0e6955bc5 100644 --- a/ivtest/vvp_tests/br_gh1385c.json +++ b/ivtest/vvp_tests/br_gh1385c.json @@ -1,9 +1,5 @@ { "type" : "normal", "source" : "br_gh1385c.v", - "iverilog-args" : [ "-g2005-sv" ], - "vlog95" : { - "__comment" : "Enum typedefs are not translated", - "type" : "CE" - } + "iverilog-args" : [ "-g2005-sv" ] } From 20a34d33c690b51b2788feb083030aece483bd2c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Jul 2026 20:05:46 -0700 Subject: [PATCH 079/101] parser: Reject implicit parameter types without `parameter` The LRM allows omitting the `parameter` keyword in a module parameter port list, but the optional type in that form is a data_type, not an implicit data type. A parameter port list like this is therefore invalid: module M #([3:0] P = 1); The parameter declaration grammar was reusing the general value_parameter_assign_with_type rule for the omitted-keyword form. That rule also accepts implicit types so that ordinary `parameter signed P = 1` declarations work, which made the omitted-keyword form accept implicit types as well. Add a separate rule for value parameter assignments without the `parameter` keyword. The rule still accepts bare identifiers and explicit data types so a parameter name can shadow a visible typedef name, but it rejects implicit types. Fixes: e56c93a2be56 ("Support shadowing type identifiers in parameter declarations") Signed-off-by: Lars-Peter Clausen --- parse.y | 59 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/parse.y b/parse.y index 6f9edcf15..f8cc4225a 100644 --- a/parse.y +++ b/parse.y @@ -1020,6 +1020,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type data_type_or_implicit_plus_id %type data_type_or_implicit_plus_id_dim %type data_type_or_implicit_or_void_plus_id +%type data_type_or_parameter_id_base +%type data_type_or_parameter_id_dim %type partial_port_name_dim %type partial_port_type_plus_id_dim %type partial_port_typedef_plus_id_dim @@ -2787,6 +2789,33 @@ data_type_or_implicit_plus_id_dim } ; + // Parameter port lists can omit the `parameter` keyword, but the optional + // type in that form is a data_type, not data_type_or_implicit. Keep this + // separate from data_type_or_implicit_plus_id_dim so a bare typedef name can + // still be parsed as a parameter name while implicit types like `signed P` + // and `[3:0] P` are rejected. +data_type_or_parameter_id_base + : IDENTIFIER + { set_type_id_range($$, nullptr, $1, @1, nullptr); + } + | atomic_type identifier_name + { set_type_id_range($$, $1, $2, @2, nullptr); + } + | ps_type_identifier_dim identifier_name + { set_type_id_range($$, $1, $2, @2, nullptr); + } + ; + +data_type_or_parameter_id_dim + : TYPE_IDENTIFIER dimensions_opt + { set_type_id_range($$, nullptr, $1.text, @1, $2); + } + | data_type_or_parameter_id_base dimensions_opt + { $$ = $1; + $$.ranges = $2; + } + ; + // `void` is only an explicit return type. The following identifier is still a // function name and may be tokenized as TYPE_IDENTIFIER when it shadows a // visible type name. @@ -5271,7 +5300,7 @@ module_parameter module_parameter_port_list : module_parameter - | value_parameter_assign_with_type + | value_parameter_assign_without_parameter { pform_requires_sv(@1, "Omitting initial `parameter` in parameter port " "list"); } @@ -5951,10 +5980,28 @@ value_parameter_assign_with_type } ; +value_parameter_assign_without_parameter + : data_type_or_parameter_id_dim initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1.type; + YYLTYPE id_loc = @1; + id_loc.first_line = $1.first_line; + id_loc.first_column = $1.first_column; + id_loc.last_line = $1.last_line; + id_loc.last_column = $1.last_column; + id_loc.lexical_pos = $1.lexical_pos; + pform_set_parameter(id_loc, $1.id, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $1.ranges, $2, $3); + } + ; + // Parameter port lists allow an explicit type after a comma without // repeating `parameter`, e.g. `#(parameter p = 1, int q = 2)`. Keep this // narrower than value_parameter_assign_with_type so a bare typedef name - // after a comma remains a parameter name that inherits the previous type. + // after a comma remains a parameter name that inherits the previous type. The + // LRM allows data_type here, but not implicit_type. value_parameter_assign_with_explicit_type : atomic_type identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt { param_is_type = false; @@ -5964,14 +6011,6 @@ value_parameter_assign_with_explicit_type param_is_type, param_type_restrict, param_data_type, $3, $4, $5); } - | implicit_type identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt - { param_is_type = false; - param_type_restrict = {}; - param_data_type = $1; - pform_set_parameter(@2, $2, param_is_local, - param_is_type, param_type_restrict, - param_data_type, $3, $4, $5); - } | ps_type_identifier_dim identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt { param_is_type = false; param_type_restrict = {}; From c0cf842eb21a8eb000c6d89b78d63b237e32066d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Jul 2026 20:13:44 -0700 Subject: [PATCH 080/101] ivtest: Move parameter omit tests to JSON The parameter_omit tests have different expectations depending on whether the regression is run in the default Verilog mode or with force SystemVerilog. The old list files modelled this by registering the same tests in both regress-vlg.list and regress-fsv.list. Move the tests to JSON descriptors. Use the existing force-sv override for the forms that are valid SystemVerilog, and keep the implicit type cases as CE in both modes. This also runs the tests through the additional configurations supported by vvp_reg.py, providing better coverage in CI. Signed-off-by: Lars-Peter Clausen --- ivtest/regress-fsv.list | 3 --- ivtest/regress-vlg.list | 6 ------ ivtest/regress-vvp.list | 6 ++++++ ivtest/vvp_tests/parameter_omit1.json | 7 +++++++ ivtest/vvp_tests/parameter_omit2.json | 7 +++++++ ivtest/vvp_tests/parameter_omit3.json | 7 +++++++ ivtest/vvp_tests/parameter_omit_invalid1.json | 4 ++++ ivtest/vvp_tests/parameter_omit_invalid2.json | 4 ++++ ivtest/vvp_tests/parameter_omit_invalid3.json | 4 ++++ 9 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 ivtest/vvp_tests/parameter_omit1.json create mode 100644 ivtest/vvp_tests/parameter_omit2.json create mode 100644 ivtest/vvp_tests/parameter_omit3.json create mode 100644 ivtest/vvp_tests/parameter_omit_invalid1.json create mode 100644 ivtest/vvp_tests/parameter_omit_invalid2.json create mode 100644 ivtest/vvp_tests/parameter_omit_invalid3.json diff --git a/ivtest/regress-fsv.list b/ivtest/regress-fsv.list index afe656c74..462c5ded3 100644 --- a/ivtest/regress-fsv.list +++ b/ivtest/regress-fsv.list @@ -84,9 +84,6 @@ module_input_port_list_def normal ivltests module_input_port_type normal ivltests parameter_in_generate1 normal ivltests parameter_no_default normal ivltests -parameter_omit1 normal ivltests -parameter_omit2 normal ivltests -parameter_omit3 normal ivltests pr3015421 CE ivltests gold=pr3015421-fsv.gold resetall normal,-Wtimescale ivltests gold=resetall-fsv.gold scope2b normal ivltests diff --git a/ivtest/regress-vlg.list b/ivtest/regress-vlg.list index 55fbc8cc5..a2a1f86ed 100644 --- a/ivtest/regress-vlg.list +++ b/ivtest/regress-vlg.list @@ -761,12 +761,6 @@ param_times normal ivltests # param has multiplication. parameter_1bit normal ivltests parameter_in_generate1 CE ivltests parameter_no_default CE ivltests -parameter_omit1 CE ivltests -parameter_omit2 CE ivltests -parameter_omit3 CE ivltests -parameter_omit_invalid1 CE ivltests -parameter_omit_invalid2 CE ivltests -parameter_omit_invalid3 CE ivltests parameter_override_invalid1 CE ivltests parameter_override_invalid2 CE ivltests parameter_override_invalid3 CE ivltests diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index e8d021038..86489f83a 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -371,6 +371,12 @@ 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 +parameter_omit1 vvp_tests/parameter_omit1.json +parameter_omit2 vvp_tests/parameter_omit2.json +parameter_omit3 vvp_tests/parameter_omit3.json +parameter_omit_invalid1 vvp_tests/parameter_omit_invalid1.json +parameter_omit_invalid2 vvp_tests/parameter_omit_invalid2.json +parameter_omit_invalid3 vvp_tests/parameter_omit_invalid3.json sv_parameter_type vvp_tests/sv_parameter_type.json sv_partsel_var_negative_packed vvp_tests/sv_partsel_var_negative_packed.json sv_queue_ap_method vvp_tests/sv_queue_ap_method.json diff --git a/ivtest/vvp_tests/parameter_omit1.json b/ivtest/vvp_tests/parameter_omit1.json new file mode 100644 index 000000000..3dd77e1cd --- /dev/null +++ b/ivtest/vvp_tests/parameter_omit1.json @@ -0,0 +1,7 @@ +{ + "type" : "CE", + "source" : "parameter_omit1.v", + "force-sv" : { + "type" : "normal" + } +} diff --git a/ivtest/vvp_tests/parameter_omit2.json b/ivtest/vvp_tests/parameter_omit2.json new file mode 100644 index 000000000..cb09ba0c4 --- /dev/null +++ b/ivtest/vvp_tests/parameter_omit2.json @@ -0,0 +1,7 @@ +{ + "type" : "CE", + "source" : "parameter_omit2.v", + "force-sv" : { + "type" : "normal" + } +} diff --git a/ivtest/vvp_tests/parameter_omit3.json b/ivtest/vvp_tests/parameter_omit3.json new file mode 100644 index 000000000..9970cd8d1 --- /dev/null +++ b/ivtest/vvp_tests/parameter_omit3.json @@ -0,0 +1,7 @@ +{ + "type" : "CE", + "source" : "parameter_omit3.v", + "force-sv" : { + "type" : "normal" + } +} diff --git a/ivtest/vvp_tests/parameter_omit_invalid1.json b/ivtest/vvp_tests/parameter_omit_invalid1.json new file mode 100644 index 000000000..d906a0c44 --- /dev/null +++ b/ivtest/vvp_tests/parameter_omit_invalid1.json @@ -0,0 +1,4 @@ +{ + "type" : "CE", + "source" : "parameter_omit_invalid1.v" +} diff --git a/ivtest/vvp_tests/parameter_omit_invalid2.json b/ivtest/vvp_tests/parameter_omit_invalid2.json new file mode 100644 index 000000000..a3205e5ad --- /dev/null +++ b/ivtest/vvp_tests/parameter_omit_invalid2.json @@ -0,0 +1,4 @@ +{ + "type" : "CE", + "source" : "parameter_omit_invalid2.v" +} diff --git a/ivtest/vvp_tests/parameter_omit_invalid3.json b/ivtest/vvp_tests/parameter_omit_invalid3.json new file mode 100644 index 000000000..106baed2c --- /dev/null +++ b/ivtest/vvp_tests/parameter_omit_invalid3.json @@ -0,0 +1,4 @@ +{ + "type" : "CE", + "source" : "parameter_omit_invalid3.v" +} From ee14022127b046fc3323c2f2dc0a083e17b2b8cc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Jul 2026 22:31:31 -0700 Subject: [PATCH 081/101] Support type identifier names in attributes Standard attribute names and target names in both forms of the Icarus `$attribute` extension are unambiguous identifier positions. When such a name matches a visible typedef the lexer returns `TYPE_IDENTIFIER`, while the grammar only accepts `IDENTIFIER`. Use `identifier_name` for all of these positions. Signed-off-by: Lars-Peter Clausen --- parse.y | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parse.y b/parse.y index 6b514ee0c..1081bf11f 100644 --- a/parse.y +++ b/parse.y @@ -1748,7 +1748,7 @@ description /* IEEE1800-2005: A.1.2 */ | package_declaration | discipline_declaration | package_item - | KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' + | KK_attribute '(' identifier_name ',' STRING ',' STRING ')' { perm_string tmp3 = lex_strings.make($3); pform_set_type_attrib(tmp3, $5, $7); delete[] $3; @@ -3109,7 +3109,7 @@ attribute_list attribute - : IDENTIFIER initializer_opt + : identifier_name initializer_opt { named_pexpr_t*tmp = new named_pexpr_t; FILE_NAME(tmp, @$); tmp->name = lex_strings.make($1); @@ -5715,7 +5715,7 @@ module_item /* These rules are for the Icarus Verilog specific $attribute extensions. Then catch the parameters of the $attribute keyword. */ - | KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' ';' + | KK_attribute '(' identifier_name ',' STRING ',' STRING ')' ';' { perm_string tmp3 = lex_strings.make($3); perm_string tmp5 = lex_strings.make($5); pform_set_attrib(tmp3, tmp5, $7); From 36a79568b70463af1b8af75496135270fb3d38b8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Jul 2026 22:31:52 -0700 Subject: [PATCH 082/101] Add regression tests for attribute names matching type identifiers Check that a standard attribute name can match a visible typedef. Also check that the global `$attribute` extension can target a primitive whose name is visible as a type identifier. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_attribute_name.v | 12 ++++++++++ .../sv_type_identifier_attribute_target.v | 23 +++++++++++++++++++ ivtest/regress-vvp.list | 2 ++ .../sv_type_identifier_attribute_name.json | 5 ++++ .../sv_type_identifier_attribute_target.json | 5 ++++ 5 files changed, 47 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_attribute_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_attribute_target.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_attribute_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_attribute_target.json diff --git a/ivtest/ivltests/sv_type_identifier_attribute_name.v b/ivtest/ivltests/sv_type_identifier_attribute_name.v new file mode 100644 index 000000000..0e3654347 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_attribute_name.v @@ -0,0 +1,12 @@ +// Check that attribute names can match visible type identifiers. + +typedef int ATTR; + +(* ATTR = 1 *) +module test; + + initial begin + $display("PASSED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_attribute_target.v b/ivtest/ivltests/sv_type_identifier_attribute_target.v new file mode 100644 index 000000000..5b5b5bef7 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_attribute_target.v @@ -0,0 +1,23 @@ +// Check that $attribute targets can match visible type identifiers. + +primitive ATTR_TYPE (out, in); + output out; + input in; + + table + 0 : 0; + 1 : 1; + endtable +endprimitive + +typedef int ATTR_TYPE; + +$attribute(ATTR_TYPE, "test", "true") + +module test; + + initial begin + $display("PASSED"); + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index b256f7422..04be7ee50 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -392,6 +392,8 @@ sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json +sv_type_identifier_attribute_name vvp_tests/sv_type_identifier_attribute_name.json +sv_type_identifier_attribute_target vvp_tests/sv_type_identifier_attribute_target.json sv_type_identifier_block_label_name vvp_tests/sv_type_identifier_block_label_name.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json sv_type_identifier_enum_item_name vvp_tests/sv_type_identifier_enum_item_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_attribute_name.json b/ivtest/vvp_tests/sv_type_identifier_attribute_name.json new file mode 100644 index 000000000..598982a44 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_attribute_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_attribute_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_attribute_target.json b/ivtest/vvp_tests/sv_type_identifier_attribute_target.json new file mode 100644 index 000000000..bb1ecb5f6 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_attribute_target.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_attribute_target.v", + "iverilog-args" : [ "-g2005-sv" ] +} From 9ec8f8e2dd0608f0f81f0f9eda8a7f8f05080241 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 3 Jul 2026 18:43:27 -0700 Subject: [PATCH 083/101] Support for and foreach identifiers shadowing type identifiers SystemVerilog allows a declaration in an inner scope to use the same name as a type identifier from an outer scope. The lexer reports such names as `TYPE_IDENTIFIER` until the new declaration has been installed. The parser previously created the synthetic loop scope and declared the loop variable only after parsing the complete `for` header. When the variable name matches a visible typedef, this is too late: the lexer can continue returning `TYPE_IDENTIFIER` for references to the variable in the initializer, condition, and step expressions. Accept `identifier_name` for the declaration name and create the loop scope and variable in a mid-rule action immediately after it, so the declaration is visible while the rest of the header is parsed. The executable foreach grammar also used to require the array expression name before the index list to be an `IDENTIFIER`. Use `identifier_name` there as well, since this position is an expression name followed by `[` and not a type name. Signed-off-by: Lars-Peter Clausen --- parse.y | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/parse.y b/parse.y index 1081bf11f..4329c7c3f 100644 --- a/parse.y +++ b/parse.y @@ -2030,7 +2030,9 @@ loop_statement /* IEEE1800-2005: A.6.8 */ // statement in a synthetic named block. We can name the block // after the variable that we are creating, that identifier is // safe in the controlling scope. - | K_for '(' K_var_opt data_type IDENTIFIER '=' expression ';' expression_opt ';' for_step_opt ')' + | K_for '(' K_var_opt data_type identifier_name + // Make the loop variable symbol visible while parsing the rest of + // the header. { static unsigned for_counter = 0; char for_block_name [64]; snprintf(for_block_name, sizeof for_block_name, "$ivl_for_loop%u", for_counter); @@ -2044,6 +2046,7 @@ loop_statement /* IEEE1800-2005: A.6.8 */ assign_list.push_back(tmp_assign); pform_make_var(@5, &assign_list, $4); } + '=' expression ';' expression_opt ';' for_step_opt ')' statement_or_null { pform_name_t tmp_hident; tmp_hident.push_back(name_component_t(lex_strings.make($5))); @@ -2051,8 +2054,8 @@ loop_statement /* IEEE1800-2005: A.6.8 */ PEIdent*tmp_ident = pform_new_ident(@5, tmp_hident); FILE_NAME(tmp_ident, @5); - check_for_loop(@1, $7, $9, $11); - PForStatement*tmp_for = new PForStatement(tmp_ident, $7, $9, $11, $14); + check_for_loop(@1, $8, $10, $12); + PForStatement*tmp_for = new PForStatement(tmp_ident, $8, $10, $12, $14); FILE_NAME(tmp_for, @1); pform_pop_scope(); @@ -2091,7 +2094,7 @@ loop_statement /* IEEE1800-2005: A.6.8 */ // When matching a foreach loop, implicitly create a named block // to hold the definitions for the index variables. - | K_foreach '(' IDENTIFIER '[' loop_variables ']' ')' + | K_foreach '(' identifier_name '[' loop_variables ']' ')' { static unsigned foreach_counter = 0; char for_block_name[64]; snprintf(for_block_name, sizeof for_block_name, "$ivl_foreach%u", foreach_counter); @@ -2143,7 +2146,7 @@ loop_statement /* IEEE1800-2005: A.6.8 */ yyerror(@1, "error: Error in do/while loop condition."); } - | K_foreach '(' IDENTIFIER '[' error ']' ')' statement_or_null + | K_foreach '(' identifier_name '[' error ']' ')' statement_or_null { $$ = 0; yyerror(@4, "error: Errors in foreach loop variables list."); } From a540a7a163e00ade84f36df17e9faf263d000864 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 3 Jul 2026 18:54:39 -0700 Subject: [PATCH 084/101] Add regression tests for for and foreach type identifier names Check that a for loop variable declaration can use the same name as a visible typedef, including references from the loop condition and step expressions. Also check that procedural foreach can parse an array expression name that is initially seen as a type identifier. Declare the array after the loop so the parser sees the outer typedef while parsing the foreach header, then elaboration resolves the array declaration as a module item. Use unsigned variables and omit the foreach iterator because these tests do not depend on signed values or iteration behavior. This lets both tests run through the vlog95 backend as normal regressions. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_type_identifier_for_name.v | 32 +++++++++++++++++++ .../sv_type_identifier_foreach_array_name.v | 25 +++++++++++++++ ivtest/regress-vvp.list | 2 ++ .../sv_type_identifier_for_name.json | 5 +++ ...sv_type_identifier_foreach_array_name.json | 5 +++ 5 files changed, 69 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_for_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_foreach_array_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_for_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_foreach_array_name.json diff --git a/ivtest/ivltests/sv_type_identifier_for_name.v b/ivtest/ivltests/sv_type_identifier_for_name.v new file mode 100644 index 000000000..2963e6a33 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_for_name.v @@ -0,0 +1,32 @@ +// Check that for loop variables can shadow visible type identifiers. + +typedef int T; + +module test; + + reg failed; + int unsigned values [3]; + + initial begin + int unsigned total; + + failed = 1'b0; + total = 0; + + for (int unsigned T = 0; T < 3; T += 1) begin + total += T; + values[T] = 10 + T; + end + + if (total != 3 || values[0] != 10 || + values[1] != 11 || values[2] != 12) begin + $display("FAILED(%0d). for loop variable did not hide typedef", `__LINE__); + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_foreach_array_name.v b/ivtest/ivltests/sv_type_identifier_foreach_array_name.v new file mode 100644 index 000000000..8a76ebae6 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_foreach_array_name.v @@ -0,0 +1,25 @@ +// Check that foreach array expressions can shadow visible type identifiers. + +typedef int A; + +module test; + + reg failed; + + initial begin + failed = 1'b0; + + // The array declaration follows the foreach loop, so the lexer only sees + // the visible typedef when parsing the array expression name. + foreach (A[]) begin + failed = 1'b1; + end + + if (!failed) begin + $display("PASSED"); + end + end + + int unsigned A [3]; + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 04be7ee50..54997d6c5 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -398,6 +398,8 @@ sv_type_identifier_block_label_name vvp_tests/sv_type_identifier_block_label_nam sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json sv_type_identifier_enum_item_name vvp_tests/sv_type_identifier_enum_item_name.json sv_type_identifier_event_name vvp_tests/sv_type_identifier_event_name.json +sv_type_identifier_for_name vvp_tests/sv_type_identifier_for_name.json +sv_type_identifier_foreach_array_name vvp_tests/sv_type_identifier_foreach_array_name.json sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json sv_type_identifier_fork_label_name vvp_tests/sv_type_identifier_fork_label_name.json sv_type_identifier_function_name vvp_tests/sv_type_identifier_function_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_for_name.json b/ivtest/vvp_tests/sv_type_identifier_for_name.json new file mode 100644 index 000000000..0b0e7b9a6 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_for_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_for_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_foreach_array_name.json b/ivtest/vvp_tests/sv_type_identifier_foreach_array_name.json new file mode 100644 index 000000000..b64ff4aea --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_foreach_array_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_foreach_array_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} From 1d623a67d104737cb5743808dea7253ef8fbff1b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 10 Jul 2026 07:50:06 -0700 Subject: [PATCH 085/101] Support discipline and nature names shadowing type identifiers Verilog-AMS nature and discipline declarations can use names that are also visible as type identifiers. The `potential` and `flow` discipline items can likewise reference a nature whose name is returned as `TYPE_IDENTIFIER` by the lexer. These grammar positions currently only accept `IDENTIFIER`. Use `identifier_name` for nature and discipline declaration names and for the `potential` and `flow` nature references. Signed-off-by: Lars-Peter Clausen --- parse.y | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parse.y b/parse.y index 4329c7c3f..dffeb9104 100644 --- a/parse.y +++ b/parse.y @@ -3614,7 +3614,7 @@ delay_value_simple optional_semicolon : ';' | ; discipline_declaration - : K_discipline IDENTIFIER optional_semicolon + : K_discipline identifier_name optional_semicolon { pform_start_discipline($2); } discipline_items K_enddiscipline { pform_end_discipline(@1); delete[] $2; } @@ -3630,14 +3630,14 @@ discipline_item { pform_discipline_domain(@1, IVL_DIS_DISCRETE); } | K_domain K_continuous ';' { pform_discipline_domain(@1, IVL_DIS_CONTINUOUS); } - | K_potential IDENTIFIER ';' + | K_potential identifier_name ';' { pform_discipline_potential(@1, $2); delete[] $2; } - | K_flow IDENTIFIER ';' + | K_flow identifier_name ';' { pform_discipline_flow(@1, $2); delete[] $2; } ; nature_declaration - : K_nature IDENTIFIER optional_semicolon + : K_nature identifier_name optional_semicolon { pform_start_nature($2); } nature_items K_endnature From 0e3ac8685cdad49b43f8c63937bc9da64f711437 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 10 Jul 2026 07:50:26 -0700 Subject: [PATCH 086/101] Add regression tests for discipline and nature type identifier names Check that nature and discipline declaration names can match visible type identifiers. Also check `potential` and `flow` references to nature names that are visible as type identifiers. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_discipline_name.v | 25 ++++++++++++++++++ ...sv_type_identifier_discipline_nature_ref.v | 26 +++++++++++++++++++ .../ivltests/sv_type_identifier_nature_name.v | 14 ++++++++++ ivtest/regress-vvp.list | 3 +++ .../sv_type_identifier_discipline_name.json | 5 ++++ ...type_identifier_discipline_nature_ref.json | 5 ++++ .../sv_type_identifier_nature_name.json | 5 ++++ 7 files changed, 83 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_discipline_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_discipline_nature_ref.v create mode 100644 ivtest/ivltests/sv_type_identifier_nature_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_discipline_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_discipline_nature_ref.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_nature_name.json diff --git a/ivtest/ivltests/sv_type_identifier_discipline_name.v b/ivtest/ivltests/sv_type_identifier_discipline_name.v new file mode 100644 index 000000000..a0615de42 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_discipline_name.v @@ -0,0 +1,25 @@ +// Check that discipline names can match visible type identifiers. + +nature potential_nature; + units = "V"; + access = POTENTIAL_ACCESS; +endnature + +nature flow_nature; + units = "A"; + access = FLOW_ACCESS; +endnature + +typedef int DISCIPLINE_NAME; + +discipline DISCIPLINE_NAME; + potential potential_nature; + flow flow_nature; + domain continuous; +enddiscipline + +module test; + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_discipline_nature_ref.v b/ivtest/ivltests/sv_type_identifier_discipline_nature_ref.v new file mode 100644 index 000000000..24c9a6cba --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_discipline_nature_ref.v @@ -0,0 +1,26 @@ +// Check that discipline nature references can match visible type identifiers. + +nature potential_nature; + units = "V"; + access = POTENTIAL_ACCESS; +endnature + +nature flow_nature; + units = "A"; + access = FLOW_ACCESS; +endnature + +typedef int potential_nature; +typedef int flow_nature; + +discipline electrical; + potential potential_nature; + flow flow_nature; + domain continuous; +enddiscipline + +module test; + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_nature_name.v b/ivtest/ivltests/sv_type_identifier_nature_name.v new file mode 100644 index 000000000..1d2a9e174 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_nature_name.v @@ -0,0 +1,14 @@ +// Check that nature names can match visible type identifiers. + +typedef int NATURE_NAME; + +nature NATURE_NAME; + units = "V"; + access = NATURE_ACCESS; +endnature + +module test; + initial begin + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 54997d6c5..50a325091 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -396,6 +396,8 @@ sv_type_identifier_attribute_name vvp_tests/sv_type_identifier_attribute_name.js sv_type_identifier_attribute_target vvp_tests/sv_type_identifier_attribute_target.json sv_type_identifier_block_label_name vvp_tests/sv_type_identifier_block_label_name.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json +sv_type_identifier_discipline_name vvp_tests/sv_type_identifier_discipline_name.json +sv_type_identifier_discipline_nature_ref vvp_tests/sv_type_identifier_discipline_nature_ref.json sv_type_identifier_enum_item_name vvp_tests/sv_type_identifier_enum_item_name.json sv_type_identifier_event_name vvp_tests/sv_type_identifier_event_name.json sv_type_identifier_for_name vvp_tests/sv_type_identifier_for_name.json @@ -413,6 +415,7 @@ sv_type_identifier_named_function_argument vvp_tests/sv_type_identifier_named_fu sv_type_identifier_named_parameter_value vvp_tests/sv_type_identifier_named_parameter_value.json sv_type_identifier_named_port_connection vvp_tests/sv_type_identifier_named_port_connection.json sv_type_identifier_named_task_argument vvp_tests/sv_type_identifier_named_task_argument.json +sv_type_identifier_nature_name vvp_tests/sv_type_identifier_nature_name.json sv_type_identifier_net_name vvp_tests/sv_type_identifier_net_name.json sv_type_identifier_package_item vvp_tests/sv_type_identifier_package_item.json sv_type_identifier_package_name vvp_tests/sv_type_identifier_package_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_discipline_name.json b/ivtest/vvp_tests/sv_type_identifier_discipline_name.json new file mode 100644 index 000000000..1947092fb --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_discipline_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_discipline_name.v", + "iverilog-args" : [ "-g2005-sv", "-gverilog-ams" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_discipline_nature_ref.json b/ivtest/vvp_tests/sv_type_identifier_discipline_nature_ref.json new file mode 100644 index 000000000..d740f86fa --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_discipline_nature_ref.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_discipline_nature_ref.v", + "iverilog-args" : [ "-g2005-sv", "-gverilog-ams" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_nature_name.json b/ivtest/vvp_tests/sv_type_identifier_nature_name.json new file mode 100644 index 000000000..89c0d37ec --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_nature_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_nature_name.v", + "iverilog-args" : [ "-g2005-sv", "-gverilog-ams" ] +} From 82350c4866ecfe20d495d32af511e97f7c2420fd Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 07:44:20 -0700 Subject: [PATCH 087/101] Fix conditional block for boolean type handling --- tgt-vvp/eval_vec4.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 4a71f39bf..f92eb27db 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -933,8 +933,9 @@ static void draw_property_vec4(ivl_expr_t expr) fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", pidx, wid); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); - if (ivl_expr_value(expr) == IVL_VT_BOOL) + if (ivl_expr_value(expr) == IVL_VT_BOOL) { fprintf(vvp_out, " %%cast2;\n"); + } return; } } From 9a7178fef0f7ac358cd9e3aacd07e403f14fa0b0 Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 07:52:48 -0700 Subject: [PATCH 088/101] Refactor format_darray_pretty for readability --- vvp/vpip_format.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vvp/vpip_format.cc b/vvp/vpip_format.cc index fdc63c9dd..c7c09e296 100644 --- a/vvp/vpip_format.cc +++ b/vvp/vpip_format.cc @@ -120,14 +120,12 @@ static char* vpip_format_pretty_string(const char*msg) static char* format_darray_pretty(vvp_darray* aobj) { - if (!aobj || aobj->get_size() == 0) - return strdup("{}"); + if (!aobj || aobj->get_size() == 0) return strdup("{}"); std::string out = "{"; for (size_t i = 0; i < aobj->get_size(); i += 1) { - if (i > 0) - out += ", "; + if (i > 0) out += ", "; if (dynamic_cast(aobj)) { double d; aobj->get_word((unsigned) i, d); @@ -156,14 +154,16 @@ static char* format_darray_pretty(vvp_darray* aobj) extern "C" char* vpip_format_pretty(vpiHandle ref) { - if (!ref) + if (!ref) { return vpip_format_pretty_string("Handle is NULL"); + } if (dynamic_cast<__vpiPropQueueRef*>(ref) || dynamic_cast<__vpiDarrayVar*>(ref)) { vvp_darray* aobj = vpip_vpi_darray_from_handle(ref); - if (!aobj) + if (!aobj) { return vpip_format_pretty_string("Object not found"); + } return format_darray_pretty(aobj); } From e7233856c85fd342b95e3ff36e4f4d764816aea4 Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 07:57:09 -0700 Subject: [PATCH 089/101] Simplify conditional return statements --- elab_expr.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 0a03bb3f9..553d7bf21 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -3329,8 +3329,7 @@ NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*sco ivl_assert(*this, use_index.lsb == 0); NetExpr*mux = elab_and_eval(des, scope, use_index.msb, -1, false); - if (!mux) - return 0; + if (!mux) return 0; NetESelect*tmp = new NetESelect(sub_expr, mux, darray->element_width(), darray->element_type()); tmp->set_line(*this); @@ -3356,8 +3355,9 @@ NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*sco NetNet* cur_net = search_results.net; const netclass_t* cur_class = dynamic_cast(search_results.type); - if (cur_class == 0) + if (cur_class == 0) { cur_class = dynamic_cast(cur_net->net_type()); + } if (cur_class == 0) { cerr << get_fileline() << ": internal error: " << "IVL_VT_CLASS net without netclass_t type." << endl; @@ -3373,9 +3373,7 @@ NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*sco NetExpr* step = elaborate_class_method_net_(des, scope, cur_net, cur_class, method_name, &no_parms); - if (step == 0) { - return 0; - } + if (step == 0) return 0; NetEUFunc*uf = dynamic_cast (step); if (uf == 0) { From 2ff0be6bfd17e5297d600f77e7f91005a9cfd18a Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 08:00:14 -0700 Subject: [PATCH 090/101] Fix conditional check for net class type --- elaborate.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/elaborate.cc b/elaborate.cc index 4d48fa546..e2e409f7f 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4337,8 +4337,9 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, // e.g. c.q.push_back(x) => sr.path_tail = {q}, method_name = push_back if (sr.path_tail.size() == 1) { const netclass_t*cls = dynamic_cast(sr.type); - if (!cls && net->net_type()) + if (!cls && net->net_type()) { cls = dynamic_cast(net->net_type()); + } if (cls) { perm_string prop_name = sr.path_tail.front().name; int pidx = cls->property_idx_from_name(prop_name); From d376375020ac2c18dbb67ee9d983c4dd781ab2fc Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 12 Jul 2026 10:01:58 -0700 Subject: [PATCH 091/101] Fix br1005 tests for fsv and vlog95 --- ivtest/regress-fsv.list | 1 - ivtest/regress-vlog95.list | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ivtest/regress-fsv.list b/ivtest/regress-fsv.list index 462c5ded3..9f40cd7a7 100644 --- a/ivtest/regress-fsv.list +++ b/ivtest/regress-fsv.list @@ -110,7 +110,6 @@ array_lval_select3a NI ivltests br605a NI ivltests br605b NI ivltests br971 NI ivltests -br1005 NI ivltests br1015a NI ivltests br1015b NI ivltests br_ml20150315b NI ivltests diff --git a/ivtest/regress-vlog95.list b/ivtest/regress-vlog95.list index 8222197cc..647d3b2f7 100644 --- a/ivtest/regress-vlog95.list +++ b/ivtest/regress-vlog95.list @@ -360,6 +360,7 @@ unp_array_typedef CE,-g2009,-pallowsigned=1 ivltests # Also string br959 CE,-g2009 ivltests br1003a CE,-g2009 ivltests br1004 CE,-g2009 ivltests +br1005 CE,-g2009 ivltests br_gh104a CE,-g2009 ivltests br_gh167a CE,-g2009 ivltests br_gh167b CE,-g2009 ivltests From e5482b89b1ec36237e0c70bdb8e0c8f8acd87153 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 12 Jul 2026 10:03:20 -0700 Subject: [PATCH 092/101] Fix compile warning --- vvp/compile.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vvp/compile.cc b/vvp/compile.cc index 4160c0868..844367a7a 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -52,7 +52,7 @@ unsigned compile_errors = 0; * by mnemonic string: compile_code() uses bsearch() on this array. * If the order is wrong, lookup fails and the assembler reports * "Invalid opcode" for otherwise valid instructions (e.g. class - * property queue ops must sort among all %delete/*, %qpop/*, %store/* + * property queue ops must sort among all %delete*, %qpop*, %store* * names, not grouped by feature). * The opcode_compare function is a helper for that lookup. */ From 15689210c137c23107c96f63ddcdfb0d72076aae Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 12 Jul 2026 17:32:36 -0700 Subject: [PATCH 093/101] Fix the building of libvvp.pc --- vvp/Makefile.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 9b7f0013f..32a6aa0a5 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -178,7 +178,7 @@ VVP_POSTBUILD = ln -sf vvp@EXEEXT@ vvp$(suffix)@EXEEXT@ VVP_CLEAN += vvp$(suffix)@EXEEXT@ endif -all: dep vvp@EXEEXT@ vvp.man +all: dep vvp@EXEEXT@ libvvp.pc vvp.man # ENV_VVP sets LD_LIBRARY_PATH by default to run vvp from the build tree check: all @@ -221,6 +221,10 @@ vvp@EXEEXT@: $(VVP_OBJ) $(VVP_DEPS) $(CXX) $(LDFLAGS) -o $@ $(VVP_OBJ) -L. $(VVP_LDFLAGS) $(LIBS) $(VVP_POSTBUILD) +libvvp.pc: $(srcdir)/libvvp.pc.in ../config.status + cd ..; ./config.status --file=vvp/$@ + + %.o: %.cc config.h | dep $(CXX) $(CPPFLAGS) -DIVL_SUFFIX='"$(suffix)"' $(MDIR1) $(MDIR2) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d From 4a505546a1011beb842ff1a128847eecb5d8f5f7 Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Wed, 1 Jul 2026 12:45:16 +0500 Subject: [PATCH 094/101] SV: chained method calls a().b() Add parsing and elaboration for chained calls on expression results, with sv_call_chain_method1 regression. Split from steveicarus/iverilog#1330 (part 02/6). --- PExpr.cc | 25 +++- PExpr.h | 22 ++++ elab_expr.cc | 133 +++++++++++++++++++- ivtest/ivltests/sv_call_chain_method1.v | 25 ++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/sv_call_chain_method1.json | 9 ++ parse.y | 34 +++-- pform.cc | 18 +++ pform.h | 4 + pform_dump.cc | 4 + 10 files changed, 260 insertions(+), 15 deletions(-) create mode 100644 ivtest/ivltests/sv_call_chain_method1.v create mode 100644 ivtest/vvp_tests/sv_call_chain_method1.json diff --git a/PExpr.cc b/PExpr.cc index e9634511b..c9b35aa4d 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -271,12 +271,28 @@ PECallFunction::PECallFunction(perm_string n, const list &parms) { } +PECallFunction::PECallFunction(PExpr* chain_prefix, const pform_name_t &method, + const vector &parms) +: path_(method), parms_(parms), chain_prefix_(chain_prefix), is_overridden_(false) +{ +} + +PECallFunction::PECallFunction(PExpr* chain_prefix, const pform_name_t &method, + const list &parms) +: path_(method), parms_(parms.begin(), parms.end()), + chain_prefix_(chain_prefix), is_overridden_(false) +{ +} + PECallFunction::~PECallFunction() { + delete chain_prefix_; } void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { + if (chain_prefix_) + chain_prefix_->declare_implicit_nets(scope, type); for (const auto &parm : parms_) { if (parm.parm) parm.parm->declare_implicit_nets(scope, type); @@ -285,12 +301,13 @@ void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const { - bool flag = false; + if (chain_prefix_ && chain_prefix_->has_aa_term(des, scope)) + return true; for (const auto &parm : parms_) { - if (parm.parm) - flag |= parm.parm->has_aa_term(des, scope); + if (parm.parm && parm.parm->has_aa_term(des, scope)) + return true; } - return flag; + return false; } PEConcat::PEConcat(const list&p, PExpr*r) diff --git a/PExpr.h b/PExpr.h index db1f5010e..af31e0932 100644 --- a/PExpr.h +++ b/PExpr.h @@ -920,8 +920,18 @@ class PECallFunction : public PExpr { explicit PECallFunction(const pform_name_t &n, const std::list &parms); explicit PECallFunction(perm_string n, const std::list &parms); + // SystemVerilog: prefix().method(args) — prefix elaborates to a class handle. + explicit PECallFunction(PExpr* chain_prefix, const pform_name_t &method, + const std::vector &parms); + explicit PECallFunction(PExpr* chain_prefix, const pform_name_t &method, + const std::list &parms); + ~PECallFunction() override; + // For chained-call resolution (path is only the final method name). + const pform_scoped_name_t& peek_path(void) const { return path_; } + const PExpr* peek_chain_prefix(void) const { return chain_prefix_; } + virtual void dump(std::ostream &) const override; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type) override; @@ -940,6 +950,8 @@ class PECallFunction : public PExpr { private: pform_scoped_name_t path_; std::vector parms_; + // If non-null, this call is prefix().tail_name(...) (SV method chain). + PExpr* chain_prefix_ = nullptr; // For system functions. bool is_overridden_; @@ -985,8 +997,18 @@ class PECallFunction : public PExpr { perm_string method_name, const std::vector*src_parms) const; + NetExpr* elaborate_class_method_net_this_(Design*des, NetScope*scope, + NetExpr* this_expr, + const netclass_t*class_type, + perm_string method_name, + const std::vector*src_parms) const; + NetExpr* elaborate_expr_method_chained_(Design*des, NetScope*scope, symbol_search_results&search_results) const; + + NetExpr* elaborate_expr_chain_(Design*des, NetScope*scope, unsigned flags) const; + + unsigned test_width_chain_(Design*des, NetScope*scope, width_mode_t&mode); }; /* diff --git a/elab_expr.cc b/elab_expr.cc index 3cec07f3e..65d3da65c 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -55,6 +55,53 @@ bool type_is_vectorable(ivl_variable_type_t type) } } +/* + * For a().b()… call chains, find the class type of the prefix expression + * (the value before the final ".method(args)"). + */ +static const netclass_t* resolve_call_chain_prefix_class(Design*des, NetScope*scope, + PECallFunction* link) +{ + if (link == 0) + return 0; + + if (link->peek_chain_prefix() == 0) { + symbol_search_results sr; + if (!symbol_search(link, des, scope, link->peek_path(), UINT_MAX, &sr)) + return 0; + if (!sr.is_scope() || sr.scope->type() != NetScope::FUNC) + return 0; + NetFuncDef* fundef = sr.scope->func_def(); + ivl_assert(*link, fundef); + NetScope* dscope = fundef->scope(); + NetNet* res = dscope->find_signal(dscope->basename()); + if (res == 0) + return 0; + return dynamic_cast(res->net_type()); + } + + PECallFunction* inner = dynamic_cast( + const_cast(link->peek_chain_prefix())); + if (inner == 0) + return 0; + + const netclass_t* recv_class = resolve_call_chain_prefix_class(des, scope, inner); + if (recv_class == 0) + return 0; + + perm_string mname = peek_tail_name(link->peek_path()); + NetScope* mscope = recv_class->method_from_name(mname); + if (mscope == 0) + return 0; + NetFuncDef* mdef = mscope->func_def(); + if (mdef == 0) + return 0; + NetNet* mres = mscope->find_signal(mscope->basename()); + if (mres == 0) + return 0; + return dynamic_cast(mres->net_type()); +} + static ivl_nature_t find_access_function(const pform_scoped_name_t &path) { if (path.package || path.name.size() != 1) @@ -1752,9 +1799,53 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return 0; } +unsigned PECallFunction::test_width_chain_(Design*des, NetScope*scope, + width_mode_t&) +{ + PECallFunction* inner_pf = dynamic_cast(chain_prefix_); + if (inner_pf == 0) { + expr_width_ = 0; + return 0; + } + + const netclass_t* cls = resolve_call_chain_prefix_class(des, scope, inner_pf); + if (cls == 0) { + expr_width_ = 0; + return 0; + } + + perm_string mname = peek_tail_name(path_); + NetScope* mscope = cls->method_from_name(mname); + if (mscope == 0) { + expr_width_ = 0; + return 0; + } + + NetFuncDef* mdef = mscope->func_def(); + if (mdef == 0 || mdef->is_void()) { + expr_width_ = 0; + return 0; + } + + NetNet* mres = mscope->find_signal(mscope->basename()); + if (mres == 0) { + expr_width_ = 0; + return 0; + } + + expr_type_ = mres->data_type(); + expr_width_ = mres->vector_width(); + min_width_ = expr_width_; + signed_flag_ = mres->get_signed(); + return expr_width_; +} + unsigned PECallFunction::test_width(Design*des, NetScope*scope, width_mode_t&mode) { + if (chain_prefix_) + return test_width_chain_(des, scope, mode); + if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width: " << "path_: " << path_ << endl; @@ -2902,6 +2993,9 @@ NetExpr* PECallFunction::elaborate_expr_(Design*des, NetScope*scope, { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + if (chain_prefix_) + return elaborate_expr_chain_(des, scope, flags); + // Search for the symbol. This should turn up a scope. symbol_search_results search_results; bool search_flag = symbol_search(this, des, scope, path_, UINT_MAX, &search_results); @@ -3267,6 +3361,18 @@ NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope, NetNet*net, const netclass_t*class_type, perm_string method_name, const vector*src_parms) const +{ + NetESignal*ethis = new NetESignal(net); + ethis->set_line(*this); + return elaborate_class_method_net_this_(des, scope, ethis, class_type, + method_name, src_parms); +} + +NetExpr* PECallFunction::elaborate_class_method_net_this_(Design*des, NetScope*scope, + NetExpr* this_expr, + const netclass_t*class_type, + perm_string method_name, + const vector*src_parms) const { NetScope*method = class_type->method_from_name(method_name); @@ -3295,9 +3401,7 @@ NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope, vector parms(def->port_count()); ivl_assert(*this, def->port_count() >= 1); - NetESignal*ethis = new NetESignal(net); - ethis->set_line(*this); - parms[0] = ethis; + parms[0] = this_expr; elaborate_arguments_(des, scope, def, false, parms, 1, src_parms); @@ -3307,6 +3411,29 @@ NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope, return call; } +NetExpr* PECallFunction::elaborate_expr_chain_(Design*des, NetScope*scope, + unsigned flags) const +{ + ivl_assert(*this, chain_prefix_); + NetExpr* inner = chain_prefix_->elaborate_expr(des, scope, -1, flags); + if (inner == 0) + return 0; + + const netclass_t* cls = dynamic_cast(inner->net_type()); + if (cls == 0) { + cerr << get_fileline() << ": error: " + << "The prefix of a chained call (a().b()) must be an expression " + << "with class type." << endl; + des->errors += 1; + delete inner; + return 0; + } + + perm_string method_name = peek_tail_name(path_); + return elaborate_class_method_net_this_(des, scope, inner, cls, + method_name, &parms_); +} + /* * Handle obj.m1().m2(args): arguments apply only to the last call; intermediate * methods must be class methods returning class handles. diff --git a/ivtest/ivltests/sv_call_chain_method1.v b/ivtest/ivltests/sv_call_chain_method1.v new file mode 100644 index 000000000..d6ba43dee --- /dev/null +++ b/ivtest/ivltests/sv_call_chain_method1.v @@ -0,0 +1,25 @@ +// Chained call: function returns class handle, then method on result (a().b()). + +module test; + + class C; + function int f; + f = 7; + endfunction + endclass + + function C get_c; + get_c = new; + endfunction + + initial begin + int x; + x = get_c().f(); + if (x !== 7) begin + $display("FAILED"); + end else begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 3ab22372d..c24235408 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -273,6 +273,7 @@ sv_byte_array_string_fail2 vvp_tests/sv_byte_array_string_fail2.json sv_byte_array_string_fail3 vvp_tests/sv_byte_array_string_fail3.json sv_byte_array_string_fail4 vvp_tests/sv_byte_array_string_fail4.json sv_byte_array_string_fail5 vvp_tests/sv_byte_array_string_fail5.json +sv_call_chain_method1 vvp_tests/sv_call_chain_method1.json sv_chained_constructor1 vvp_tests/sv_chained_constructor1.json sv_chained_constructor2 vvp_tests/sv_chained_constructor2.json sv_chained_constructor3 vvp_tests/sv_chained_constructor3.json diff --git a/ivtest/vvp_tests/sv_call_chain_method1.json b/ivtest/vvp_tests/sv_call_chain_method1.json new file mode 100644 index 000000000..edd7b87fd --- /dev/null +++ b/ivtest/vvp_tests/sv_call_chain_method1.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_call_chain_method1.v", + "iverilog-args" : [ "-g2012" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/parse.y b/parse.y index dffeb9104..4a0265532 100644 --- a/parse.y +++ b/parse.y @@ -931,7 +931,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type timeskew_fullskew_opt_remain_active_flag %type assignment_pattern expression expression_opt expr_mintypmax -%type expr_primary_or_typename expr_primary +%type expr_primary_or_typename expr_primary call_chain_expr %type class_new dynamic_array_new %type net_decl_initializer_opt var_decl_initializer_opt initializer_opt %type inc_or_dec_expression inside_expression lpvalue @@ -4238,6 +4238,26 @@ expr_primary_or_typename ; + /* SystemVerilog: a().b() — call a function, then invoke a method on the + returned class handle. Extends with further ".id(args)" as needed. */ + +call_chain_expr + : hierarchy_identifier attribute_list_opt argument_list_parens + { PECallFunction*tmp = pform_make_call_function(@1, *$1, *$3); + delete $1; + delete $2; + delete $3; + $$ = tmp; + } + | call_chain_expr '.' hierarchy_identifier attribute_list_opt argument_list_parens + { PECallFunction*tmp = pform_make_chained_call_function(@2, $1, *$3, *$5); + delete $3; + delete $4; + delete $5; + $$ = tmp; + } + ; + expr_primary : number { assert($1); @@ -4297,6 +4317,11 @@ expr_primary /* The hierarchy_identifier rule matches simple identifiers as well as indexed arrays and part selects */ + /* SV call chains get_c1().f() — must come before bare hierarchy_identifier + so `id (` is not reduced as PEIdent + error. */ + | call_chain_expr + { $$ = $1; + } | hierarchy_identifier { PEIdent*tmp = pform_new_ident(@1, *$1); FILE_NAME(tmp, @1); @@ -4347,13 +4372,6 @@ expr_primary function call. If a system identifier, then a system function call. It can also be a call to a class method (function). */ - | hierarchy_identifier attribute_list_opt argument_list_parens - { PECallFunction*tmp = pform_make_call_function(@1, *$1, *$3); - delete $1; - delete $2; - delete $3; - $$ = tmp; - } | class_hierarchy_identifier argument_list_parens { PECallFunction*tmp = pform_make_call_function(@1, *$1, *$2); delete $1; diff --git a/pform.cc b/pform.cc index 17b8ebfdf..0f5b3578f 100644 --- a/pform.cc +++ b/pform.cc @@ -975,6 +975,24 @@ PECallFunction* pform_make_call_function(const struct vlltype&loc, return tmp; } +PECallFunction* pform_make_chained_call_function(const struct vlltype&loc, + PExpr*prefix, + const pform_name_t&method, + const list &parms) +{ + if (!gn_system_verilog()) { + pform_requires_sv(loc, "Chained calls like a().b()"); + delete prefix; + return new PECallFunction(method, parms); + } + + check_potential_imports(loc, method.front().name, true); + + PECallFunction*tmp = new PECallFunction(prefix, method, parms); + FILE_NAME(tmp, loc); + return tmp; +} + PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, const list &parms) diff --git a/pform.h b/pform.h index 610dd8390..598eb3988 100644 --- a/pform.h +++ b/pform.h @@ -333,6 +333,10 @@ extern void pform_set_type_referenced(const struct vlltype&loc, const char*name) extern PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, const std::list &parms); +extern PECallFunction* pform_make_chained_call_function(const struct vlltype&loc, + PExpr*prefix, + const pform_name_t&method, + const std::list &parms); extern PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, const std::list &parms); diff --git a/pform_dump.cc b/pform_dump.cc index 94913eca6..49de3728f 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -421,6 +421,10 @@ void PEConcat::dump(ostream&out) const void PECallFunction::dump(ostream &out) const { + if (peek_chain_prefix()) { + peek_chain_prefix()->dump(out); + out << "."; + } out << path_ << "(" << parms_ << ")"; } From 29a8a4d05223207a10b40d050f4bc814ac001107 Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Mon, 13 Jul 2026 22:48:28 -0700 Subject: [PATCH 095/101] Simplify conditional checks in elab_expr.cc --- elab_expr.cc | 65 +++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 65d3da65c..693e51abf 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -62,50 +62,44 @@ bool type_is_vectorable(ivl_variable_type_t type) static const netclass_t* resolve_call_chain_prefix_class(Design*des, NetScope*scope, PECallFunction* link) { - if (link == 0) - return 0; + if (link == 0) return 0; if (link->peek_chain_prefix() == 0) { symbol_search_results sr; - if (!symbol_search(link, des, scope, link->peek_path(), UINT_MAX, &sr)) + if (!symbol_search(link, des, scope, link->peek_path(), UINT_MAX, &sr)) { return 0; - if (!sr.is_scope() || sr.scope->type() != NetScope::FUNC) + } + if (!sr.is_scope() || sr.scope->type() != NetScope::FUNC) { return 0; + } NetFuncDef* fundef = sr.scope->func_def(); ivl_assert(*link, fundef); NetScope* dscope = fundef->scope(); NetNet* res = dscope->find_signal(dscope->basename()); - if (res == 0) - return 0; + if (res == 0) return 0; return dynamic_cast(res->net_type()); } PECallFunction* inner = dynamic_cast( const_cast(link->peek_chain_prefix())); - if (inner == 0) - return 0; + if (inner == 0) return 0; const netclass_t* recv_class = resolve_call_chain_prefix_class(des, scope, inner); - if (recv_class == 0) - return 0; + if (recv_class == 0) return 0; perm_string mname = peek_tail_name(link->peek_path()); NetScope* mscope = recv_class->method_from_name(mname); - if (mscope == 0) - return 0; + if (mscope == 0) return 0; NetFuncDef* mdef = mscope->func_def(); - if (mdef == 0) - return 0; + if (mdef == 0) return 0; NetNet* mres = mscope->find_signal(mscope->basename()); - if (mres == 0) - return 0; + if (mres == 0) return 0; return dynamic_cast(mres->net_type()); } static ivl_nature_t find_access_function(const pform_scoped_name_t &path) { - if (path.package || path.name.size() != 1) - return nullptr; + if (path.package || path.name.size() != 1) return nullptr; return access_function_nature[peek_tail_name(path)]; } @@ -1802,36 +1796,22 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, unsigned PECallFunction::test_width_chain_(Design*des, NetScope*scope, width_mode_t&) { + expr_width_ = 0; PECallFunction* inner_pf = dynamic_cast(chain_prefix_); - if (inner_pf == 0) { - expr_width_ = 0; - return 0; - } + if (inner_pf == 0) return 0; const netclass_t* cls = resolve_call_chain_prefix_class(des, scope, inner_pf); - if (cls == 0) { - expr_width_ = 0; - return 0; - } + if (cls == 0) return 0; perm_string mname = peek_tail_name(path_); NetScope* mscope = cls->method_from_name(mname); - if (mscope == 0) { - expr_width_ = 0; - return 0; - } + if (mscope == 0) return 0; NetFuncDef* mdef = mscope->func_def(); - if (mdef == 0 || mdef->is_void()) { - expr_width_ = 0; - return 0; - } + if (mdef == 0 || mdef->is_void()) return 0; NetNet* mres = mscope->find_signal(mscope->basename()); - if (mres == 0) { - expr_width_ = 0; - return 0; - } + if (mres == 0) return 0; expr_type_ = mres->data_type(); expr_width_ = mres->vector_width(); @@ -1843,8 +1823,7 @@ unsigned PECallFunction::test_width_chain_(Design*des, NetScope*scope, unsigned PECallFunction::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - if (chain_prefix_) - return test_width_chain_(des, scope, mode); + if (chain_prefix_) return test_width_chain_(des, scope, mode); if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width: " @@ -2993,8 +2972,7 @@ NetExpr* PECallFunction::elaborate_expr_(Design*des, NetScope*scope, { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag - if (chain_prefix_) - return elaborate_expr_chain_(des, scope, flags); + if (chain_prefix_) return elaborate_expr_chain_(des, scope, flags); // Search for the symbol. This should turn up a scope. symbol_search_results search_results; @@ -3416,8 +3394,7 @@ NetExpr* PECallFunction::elaborate_expr_chain_(Design*des, NetScope*scope, { ivl_assert(*this, chain_prefix_); NetExpr* inner = chain_prefix_->elaborate_expr(des, scope, -1, flags); - if (inner == 0) - return 0; + if (inner == 0) return 0; const netclass_t* cls = dynamic_cast(inner->net_type()); if (cls == 0) { From 55250457ed9d292b8e433c3e4242993e3707f896 Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Mon, 13 Jul 2026 22:50:55 -0700 Subject: [PATCH 096/101] Fix formatting in parse.y for call_chain_expr --- parse.y | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/parse.y b/parse.y index 4a0265532..43dd95a6a 100644 --- a/parse.y +++ b/parse.y @@ -4320,8 +4320,7 @@ expr_primary /* SV call chains get_c1().f() — must come before bare hierarchy_identifier so `id (` is not reduced as PEIdent + error. */ | call_chain_expr - { $$ = $1; - } + { $$ = $1; } | hierarchy_identifier { PEIdent*tmp = pform_new_ident(@1, *$1); FILE_NAME(tmp, @1); From 67d39803e69168bdd5551fa55e88170ed78d96d4 Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Mon, 13 Jul 2026 22:54:26 -0700 Subject: [PATCH 097/101] Fix formatting and add braces for clarity --- PExpr.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/PExpr.cc b/PExpr.cc index c9b35aa4d..b2151f970 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -291,21 +291,25 @@ PECallFunction::~PECallFunction() void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { - if (chain_prefix_) + if (chain_prefix_) { chain_prefix_->declare_implicit_nets(scope, type); + } for (const auto &parm : parms_) { - if (parm.parm) + if (parm.parm) { parm.parm->declare_implicit_nets(scope, type); + } } } bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const { - if (chain_prefix_ && chain_prefix_->has_aa_term(des, scope)) + if (chain_prefix_ && chain_prefix_->has_aa_term(des, scope)) { return true; + } for (const auto &parm : parms_) { - if (parm.parm && parm.parm->has_aa_term(des, scope)) + if (parm.parm && parm.parm->has_aa_term(des, scope)) { return true; + } } return false; } From e4bede3c760bb1cb7cab31f3e72a61896f83db3a Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 13 Jul 2026 23:50:33 -0700 Subject: [PATCH 098/101] Fix some compile warnings under mingw/cygwin --- tgt-vvp/eval_vec4.c | 4 +++- vpi_modules.cc | 2 ++ vvp/vpi_bit.cc | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index f92eb27db..357830ee5 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2013-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -462,6 +462,8 @@ static void draw_binary_vec4_logical(ivl_expr_t expr, char op) jmp_type = "1"; break; default: + opcode = "" + jmp_type = "" assert(0); break; } diff --git a/vpi_modules.cc b/vpi_modules.cc index 17a42c69f..4aecff2db 100644 --- a/vpi_modules.cc +++ b/vpi_modules.cc @@ -126,6 +126,7 @@ s_vpi_vecval vpip_calc_clog2(vpiHandle) } void vpip_count_drivers(vpiHandle, unsigned, unsigned [4]) { } void vpip_format_strength(char*, s_vpi_value*, unsigned) { } +void vpip_format_pretty(vpiHandle) { } void vpip_make_systf_system_defined(vpiHandle) { } void vpip_mcd_rawwrite(PLI_UINT32, const char*, size_t) { } void vpip_set_return_value(int) { } @@ -230,6 +231,7 @@ vpip_routines_s vpi_routines = { .calc_clog2 = vpip_calc_clog2, .count_drivers = vpip_count_drivers, .format_strength = vpip_format_strength, + .format_pretty = vpip_format_pretty, .make_systf_system_defined = vpip_make_systf_system_defined, .mcd_rawwrite = vpip_mcd_rawwrite, .set_return_value = vpip_set_return_value, diff --git a/vvp/vpi_bit.cc b/vvp/vpi_bit.cc index ad76f9a22..9b956861d 100644 --- a/vvp/vpi_bit.cc +++ b/vvp/vpi_bit.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2025 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2020-2026 Cary R. (cygcary@yahoo.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -57,6 +57,7 @@ static int bit_get_type(const __vpiBit*rfp) return vpiRegBit; } assert(0); + return 0; } static int bit_get(int code, vpiHandle ref) From e367080276035aa554188cd1c66ee6b190db8c62 Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 13 Jul 2026 23:59:38 -0700 Subject: [PATCH 099/101] Fix incorrect definition --- vpi_modules.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpi_modules.cc b/vpi_modules.cc index 4aecff2db..22d4b345d 100644 --- a/vpi_modules.cc +++ b/vpi_modules.cc @@ -126,7 +126,7 @@ s_vpi_vecval vpip_calc_clog2(vpiHandle) } void vpip_count_drivers(vpiHandle, unsigned, unsigned [4]) { } void vpip_format_strength(char*, s_vpi_value*, unsigned) { } -void vpip_format_pretty(vpiHandle) { } +char* vpip_format_pretty(vpiHandle) { return "invalid"; } void vpip_make_systf_system_defined(vpiHandle) { } void vpip_mcd_rawwrite(PLI_UINT32, const char*, size_t) { } void vpip_set_return_value(int) { } From dec4e939f4e01635eb70aac3ccec4490c58d9dae Mon Sep 17 00:00:00 2001 From: Cary R Date: Tue, 14 Jul 2026 00:07:34 -0700 Subject: [PATCH 100/101] Fix compile issues in previous patch --- tgt-vvp/eval_vec4.c | 4 ++-- vpi_modules.cc | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 357830ee5..adc0fabfa 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -462,8 +462,8 @@ static void draw_binary_vec4_logical(ivl_expr_t expr, char op) jmp_type = "1"; break; default: - opcode = "" - jmp_type = "" + opcode = ""; + jmp_type = ""; assert(0); break; } diff --git a/vpi_modules.cc b/vpi_modules.cc index 22d4b345d..2f978c73f 100644 --- a/vpi_modules.cc +++ b/vpi_modules.cc @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include #include "config.h" #include "compiler.h" #include "vpi_user.h" @@ -126,7 +127,7 @@ s_vpi_vecval vpip_calc_clog2(vpiHandle) } void vpip_count_drivers(vpiHandle, unsigned, unsigned [4]) { } void vpip_format_strength(char*, s_vpi_value*, unsigned) { } -char* vpip_format_pretty(vpiHandle) { return "invalid"; } +char* vpip_format_pretty(vpiHandle) { return strdup("invalid"); } void vpip_make_systf_system_defined(vpiHandle) { } void vpip_mcd_rawwrite(PLI_UINT32, const char*, size_t) { } void vpip_set_return_value(int) { } From 614607c1357c86f2f4b44d076dac6c8f2f8c3cea Mon Sep 17 00:00:00 2001 From: Cary R Date: Tue, 14 Jul 2026 01:30:05 -0700 Subject: [PATCH 101/101] Cppcheck fixes in vvp --- vvp/array.cc | 4 ++-- vvp/cppcheck.sup | 18 +++++++++--------- vvp/island_tran.cc | 4 ++-- vvp/vpi_darray.cc | 14 ++++---------- vvp/vpi_priv.cc | 2 +- vvp/vpi_priv.h | 3 +-- vvp/vthread.cc | 2 +- 7 files changed, 20 insertions(+), 27 deletions(-) diff --git a/vvp/array.cc b/vvp/array.cc index 2e9317b4d..98809adb8 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -162,7 +162,7 @@ int __vpiArray::get_word_size() const /* For a net array we need to get the width from the first element. */ if (nets) { assert(vals4 == 0 && vals == 0); - struct __vpiSignal*vsig = dynamic_cast<__vpiSignal*>(nets[0]); + const struct __vpiSignal*vsig = dynamic_cast<__vpiSignal*>(nets[0]); assert(vsig); width = vpip_size(vsig); /* For a variable array we can get the width from vals_width. */ @@ -625,7 +625,7 @@ void __vpiArray::set_word(unsigned address, unsigned part_off, const vvp_vector4 // Select the word of the array that we affect. vpiHandle word = nets[address]; - struct __vpiSignal*vsig = dynamic_cast<__vpiSignal*>(word); + const struct __vpiSignal*vsig = dynamic_cast<__vpiSignal*>(word); assert(vsig); vsig->node->send_vec4_pv(val, part_off, vpip_size(vsig), 0); diff --git a/vvp/cppcheck.sup b/vvp/cppcheck.sup index 128d0ab86..75f282368 100644 --- a/vvp/cppcheck.sup +++ b/vvp/cppcheck.sup @@ -92,11 +92,11 @@ knownConditionTrueFalse:vthread.cc:3571 duplicateExpression:vpi_signal.cc:1207 // cppcheck does not relize this is deleted[] in the called routine -leakNoVarFunctionCall:compile.cc:452 +leakNoVarFunctionCall:compile.cc:476 // Yes, these are not currently initialized in the constructor // All are added after __vpiSysTaskCall is built -uninitMemberVar:vpi_priv.h:946 +uninitMemberVar:vpi_priv.h:967 // All are added after __vpiSignal is built uninitMemberVar:vpi_priv.h:394 // run_run_ptr is added after the event is built @@ -151,13 +151,13 @@ knownConditionTrueFalse:vpi_modules.cc:118 redundantAssignment:vpi_modules.cc:117 // cppcheck does not undestand the format types match -invalidScanfArgType_int:compile.cc:597 -invalidScanfArgType_int:compile.cc:603 -invalidScanfArgType_int:compile.cc:609 -invalidScanfArgType_int:vthread.cc:4541 -invalidScanfArgType_int:vthread.cc:4544 -invalidScanfArgType_int:vthread.cc:4547 -invalidScanfArgType_int:vthread.cc:4550 +invalidScanfArgType_int:compile.cc:621 +invalidScanfArgType_int:compile.cc:627 +invalidScanfArgType_int:compile.cc:633 +invalidScanfArgType_int:vthread.cc:4580 +invalidScanfArgType_int:vthread.cc:4583 +invalidScanfArgType_int:vthread.cc:4586 +invalidScanfArgType_int:vthread.cc:4589 // The new() operator is always used to allocate space for this class and // pool is defined there. diff --git a/vvp/island_tran.cc b/vvp/island_tran.cc index 5e8c793a2..511527f7e 100644 --- a/vvp/island_tran.cc +++ b/vvp/island_tran.cc @@ -196,7 +196,7 @@ void vvp_island_tran::count_drivers(vvp_island_port*port, unsigned bit_idx, void vvp_island_branch_tran::run_test_enabled() { - vvp_island_port*ep = en? dynamic_cast (en->fun) : NULL; + const vvp_island_port*ep = en? dynamic_cast (en->fun) : NULL; // If there is no ep port (no "enabled" input) then this is a // tran branch. Assume it is always enabled. @@ -243,7 +243,7 @@ void vvp_island_branch_tran::run_test_enabled() bool vvp_island_branch_tran::rerun_test_enabled() { - vvp_island_port*ep = en? dynamic_cast (en->fun) : NULL; + const vvp_island_port*ep = en? dynamic_cast (en->fun) : NULL; if (ep == 0) return false; diff --git a/vvp/vpi_darray.cc b/vvp/vpi_darray.cc index 2ad9cb7d7..a81dcd6ba 100644 --- a/vvp/vpi_darray.cc +++ b/vvp/vpi_darray.cc @@ -309,12 +309,6 @@ int __vpiQueueVar::vpi_get(int code) } } -void __vpiQueueVar::vpi_get_value(p_vpi_value val) -{ - __vpiDarrayVar::vpi_get_value(val); -} - - vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net) { __vpiScope*scope = vpip_peek_current_scope(); @@ -337,7 +331,7 @@ int __vpiPropQueueRef::get_type_code(void) const static vvp_darray* get_live_darray_from_prop(vvp_net_t* class_net, unsigned pid) { if (!class_net) return 0; - vvp_fun_signal_object* fun = dynamic_cast(class_net->fun); + const vvp_fun_signal_object* fun = dynamic_cast(class_net->fun); if (!fun) return 0; vvp_object_t obj = fun->get_object(); vvp_cobject* cobj = obj.peek(); @@ -349,7 +343,7 @@ static vvp_darray* get_live_darray_from_prop(vvp_net_t* class_net, unsigned pid) int __vpiPropQueueRef::vpi_get(int code) { - vvp_darray* aobj = get_live_darray_from_prop(class_net_, prop_idx_); + const vvp_darray* aobj = get_live_darray_from_prop(class_net_, prop_idx_); switch (code) { case vpiArrayType: return is_queue_ ? vpiQueueArray : vpiDynamicArray; @@ -395,8 +389,8 @@ vvp_darray* vpip_vpi_darray_from_handle(vpiHandle ref) return get_live_darray_from_prop(pr->class_net_, pr->prop_idx_); } - if (__vpiDarrayVar* dv = dynamic_cast<__vpiDarrayVar*>(ref)) { - vvp_fun_signal_object* fun = dynamic_cast(dv->get_net()->fun); + if (const __vpiDarrayVar* dv = dynamic_cast<__vpiDarrayVar*>(ref)) { + const vvp_fun_signal_object* fun = dynamic_cast(dv->get_net()->fun); if (!fun) return 0; vvp_object_t obj = fun->get_object(); return obj.peek(); diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index 1d84146b2..efcebe052 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -128,7 +128,7 @@ struct vpip_string_chunk { char data[64*1024 - sizeof (struct vpip_string_chunk*)]; }; -unsigned vpip_size(__vpiSignal *sig) +unsigned vpip_size(const __vpiSignal *sig) { return abs(sig->msb.get_value() - sig->lsb.get_value()) + 1; } diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 83fce94ce..b0fc1e322 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -396,7 +396,7 @@ struct __vpiSignal : public __vpiHandle { static void*operator new[] (std::size_t size); static void operator delete[](void*); }; -extern unsigned vpip_size(__vpiSignal *sig); +extern unsigned vpip_size(const __vpiSignal *sig); extern __vpiScope* vpip_scope(__vpiSignal*sig); extern vpiHandle vpip_make_int2(const char*name, int msb, int lsb, @@ -876,7 +876,6 @@ class __vpiQueueVar : public __vpiDarrayVar { __vpiQueueVar(__vpiScope*scope, const char*name, vvp_net_t*net); int vpi_get(int code) override; - void vpi_get_value(p_vpi_value val) override; }; extern vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net); diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 81989cb29..8aa011dad 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -6138,7 +6138,7 @@ bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t cp) vvp_object_t qobj; cobj->get_object(pid, qobj, 0); - vvp_queue* queue = qobj.peek(); + const vvp_queue* queue = qobj.peek(); size_t sz = queue ? queue->get_size() : 0;