From a5bf5e145ff9b2226872a2403c9310ebbb7bb05f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 15:29:54 -0700 Subject: [PATCH 1/2] 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 2/2] 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" ] +}