From ab95d1d9035b5488ca89ce9320bbb0aad9958c5c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jun 2022 12:41:21 +0200 Subject: [PATCH 1/4] tgt-vvp: Handle dynamic part select on array element non-blocking event control VVP array assignment operations expect the array element index to be in index register 3. For array element assignments with a dynamic part select the array index gets moved into a temporary index register and has to be moved into register 3 after evaluating the dynamic part select. This is currently not done non-blocking event control assignments. This causes the write to go to the wrong array element. It will go to whatever value is in the register 3 from previous operations. ``` reg [3:0] a[1:0]; integer i = 0; event e; a[1][i+:2] <= @e 2'b10; // Will write to the wrong array element ->e; ``` Make sure to move the temporary register to register 3. Signed-off-by: Lars-Peter Clausen --- tgt-vvp/vvp_process.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 9c42bcae6..c93de0d44 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -179,6 +179,8 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, /* If needed use the global error state. */ if (part_off_ex) { fprintf(vvp_out, " %%flag_mov 4, %d;\n", error_flag); + fprintf(vvp_out, " %%ix/mov 3, %d;\n", word_ix_reg); + clr_word(word_ix_reg); } fprintf(vvp_out, " %%assign/vec4/a/e v%p, %d;\n", lsig, part_off_reg); From ec75c1aa74081d81e43c2a954a0f3c0db9e1fdf9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 3 Jun 2022 16:47:52 +0200 Subject: [PATCH 2/4] Handle non-blocking event control to lvalue concatenation A non-blocking event controlled assignment consists of 3 steps. * Setup event * Perform assignment * Clear event This works fine if the lvalue is a singular value. If the lvalue is a concatenation multiple assignments are generated and the event is cleared after each assignment. As a result only the first assignment is event controlled. All other assignments will be regular non-blocking assignments. E.g. ``` reg x, y; event e; {x,y} <= @e 2'b11; $display(x, y); // x will be 1'b1, y will be 1'bx ``` To resolve this the event needs to be cleared after all assignments have been done. This requires changes to both tgt-vvp and the vvp runtime. tgt-vvp is updated to only insert a single `%evctl/c` instruction for each event controlled non-blocking assignment. The vvp runtime is not updated to implicitly clear the event in the `%assign/vec4/e` instruction and instead rely on the explicit `%evctl/c`. Signed-off-by: Lars-Peter Clausen --- tgt-vvp/vvp_process.c | 9 +++------ vvp/vthread.cc | 2 -- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index c93de0d44..db5e06fdd 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -205,8 +205,6 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, clr_word(delay_index); } - if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n"); - clr_flag(error_flag); if (part_off_reg) clr_word(part_off_reg); @@ -281,7 +279,6 @@ static void assign_to_lvector(ivl_lval_t lval, draw_eval_expr_into_integer(part_off_ex, offset_index); fprintf(vvp_out, " %s/vec4/off/e v%p_%lu, %d;\n", assign_op, sig, use_word, offset_index); - fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); @@ -314,7 +311,6 @@ static void assign_to_lvector(ivl_lval_t lval, fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %s/vec4/off/e v%p_%lu, %d;\n", assign_op, sig, use_word, offset_index); - fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); } else { @@ -348,8 +344,6 @@ static void assign_to_lvector(ivl_lval_t lval, /* Event control delay... */ fprintf(vvp_out, " %s/vec4/e v%p_%lu;\n", assign_op, sig, use_word); - fprintf(vvp_out, " %%evctl/c;\n"); - } else { /* * The %assign can only take a 32 bit delay. For a larger @@ -595,6 +589,9 @@ static int show_stmt_assign_nb(ivl_statement_t net) } } + if (nevents) + fprintf(vvp_out, " %%evctl/c;\n"); + return 0; } diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 7cb68568a..a1963b5cd 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -1349,8 +1349,6 @@ bool of_ASSIGN_VEC4E(vthread_t thr, vvp_code_t cp) schedule_evctl(ptr, value, 0, sig->value_size(), thr->event, thr->ecount); } - thr->event = 0; - thr->ecount = 0; return true; } From 34876c88548041361850e2434fad5561921bc727 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jun 2022 13:00:55 +0200 Subject: [PATCH 3/4] Add additional regression test for non-blocking event control on array partsel Check that non-blocking event control assignments works on an array part select if the part select index is not an immediate value. This is a copy of the nb_ec_array_pv test, but using variable indices instead of immediate values. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/nb_ec_array_pv.v | 3 ++ ivtest/ivltests/nb_ec_array_pv2.v | 90 +++++++++++++++++++++++++++++++ ivtest/regress-ivl1.list | 1 + 3 files changed, 94 insertions(+) create mode 100644 ivtest/ivltests/nb_ec_array_pv2.v diff --git a/ivtest/ivltests/nb_ec_array_pv.v b/ivtest/ivltests/nb_ec_array_pv.v index ad0b5ab8c..a23ce06bc 100644 --- a/ivtest/ivltests/nb_ec_array_pv.v +++ b/ivtest/ivltests/nb_ec_array_pv.v @@ -1,3 +1,6 @@ +// Check that non-blocking event control assignment to a part select on a vector +// array works when using an immediate index. + module top; reg pass = 1'b1; diff --git a/ivtest/ivltests/nb_ec_array_pv2.v b/ivtest/ivltests/nb_ec_array_pv2.v new file mode 100644 index 000000000..b8434b5cb --- /dev/null +++ b/ivtest/ivltests/nb_ec_array_pv2.v @@ -0,0 +1,90 @@ +// Check that non-blocking event control assignment to a part select on a vector +// array works when using a variable index. + +module top; + reg pass = 1'b1; + + integer i = 0; + integer j = 1; + integer count; + reg [2:0] icount; + reg clk = 0; + reg [3:0] in = 4'h0; + reg [7:0] result [1:0]; + + always #10 clk = ~clk; + always #20 in = in + 4'h1; + + initial begin + count = 3; + i = 0; + result[j][i+:4] <= repeat(count) @(posedge clk) in; + if ($simtime != 0 || result[j] !== 8'bx) begin + $display("Failed repeat(3) blocked at %0t, expected 8'hxx, got %h", + $simtime, result[j]); + pass = 1'b0; + end + @(result[j]); + if ($simtime != 50 || result[j] !== 8'hx0) begin + $display("Failed repeat(3) at %0t, expected 8'hx0, got %h", + $simtime, result[j]); + pass = 1'b0; + end + + #15; + count = 0; + result[j][i+4+:4] <= repeat(count) @(posedge clk) in; + @(result[j]); // Reals happen faster so they can use an #0, vectors are slower. + if ($simtime != 65 || result[j] !== 8'h30) begin + $display("Failed repeat(0) at %0t, expected 8'h30, got %h", + $simtime, result[j]); + pass = 1'b0; + end + + #20; + count = -1; + result[j][i+5+:4] <= repeat(count) @(posedge clk) in; + @(result[j]); // Reals happen faster so they can use an #0, vectors are slower. + if ($simtime != 85 || result[j] !== 8'h90) begin + $display("Failed repeat(-1) at %0t, expected 8'h80, got %h", + $simtime, result[j]); + pass = 1'b0; + end + + #20; + result[j][i+4+:4] <= @(posedge clk) 4'h0; + result[j][i+4+:4] <= @(posedge clk) in; // This one sets the final value. + @(result[j]); + if ($simtime != 110 || result[j] !== 8'h50) begin + $display("Failed @ at %0t, expected 8'h50, got %h", + $simtime, result[j]); + pass = 1'b0; + end + + icount = 3'd2; + result[j][i+:4] <= @(posedge clk) 4'h1; + result[j][i+4+:4] <= repeat(icount) @(posedge clk) 4'h2; + result[j][i+1-:4] <= repeat(3) @(posedge clk) 4'h3; + @(result[j]); + if ($simtime != 130 || result[j] !== 8'h51) begin + $display("Failed first @ at %0t, expected 8'h51, got %h", + $simtime, result[j]); + pass = 1'b0; + end + @(result[j]); + if ($simtime != 150 || result[j] !== 8'h21) begin + $display("Failed second @ at %0t, expected 8'h21, got %h", + $simtime, result[j]); + pass = 1'b0; + end + @(result[j]); + if ($simtime != 170 || result[j] !== 8'h20) begin + $display("Failed third @ at %0t, expected 8'h20, got %h", + $simtime, result[j]); + pass = 1'b0; + end + + if (pass) $display("PASSED"); + $finish; + end +endmodule diff --git a/ivtest/regress-ivl1.list b/ivtest/regress-ivl1.list index 649d38043..effab3aac 100644 --- a/ivtest/regress-ivl1.list +++ b/ivtest/regress-ivl1.list @@ -185,6 +185,7 @@ ca_time_smtm normal ivltests gold=ca_time_smtm.gold nb_array_pv normal ivltests nb_ec_array normal ivltests nb_ec_array_pv normal ivltests +nb_ec_array_pv2 normal ivltests nb_ec_pv normal ivltests nb_ec_pv2 normal ivltests nb_ec_real normal ivltests From ebd574474c838d054defea691a5fbc8589a14beb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Jun 2022 10:26:38 +0200 Subject: [PATCH 4/4] Add regression test for non-blocking event control to concatenation Check that a non-blocking event control assignment works as expected to a lvalue concatenation. All values that are part of the concatenation should only be assigned after the event triggers. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/nb_ec_concat.v | 104 +++++++++++++++++++++++++++++++++ ivtest/regress-vlg.list | 1 + 2 files changed, 105 insertions(+) create mode 100644 ivtest/ivltests/nb_ec_concat.v diff --git a/ivtest/ivltests/nb_ec_concat.v b/ivtest/ivltests/nb_ec_concat.v new file mode 100644 index 000000000..a27ae3a99 --- /dev/null +++ b/ivtest/ivltests/nb_ec_concat.v @@ -0,0 +1,104 @@ +// Check that non-blocking event control assignments to concatanations work as +// expected. + +module test; + reg failed = 1'b0; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED. %s: expected %b, got %b.", `"val`", exp, val); \ + failed = 1'b1; \ + end + + reg [3:0] x; + reg [3:0] y; + reg [3:0] z[1:0]; + + integer i = 0; + event e; + + initial begin + // Test all of + // * vector + // * vector part select + // * array element + // * array element part + + // Immediate index + {z[1][1:0],z[0],y[1:0],x} <= @e 12'h5a5; + #1 + // Assignment must not occur until the event is triggered + `check(z[1], 4'bxxxx) + `check(z[0], 4'bxxxx) + `check(y, 4'bxxxx) + `check(x, 4'bxxxx) + + ->e; + `check(z[1], 4'bxx01); + `check(z[0], 4'b0110); + `check(y, 4'bxx10); + `check(x, 4'b0101); + + x = 4'hx; + y = 4'hx; + z[0] = 4'hx; + z[1] = 4'hx; + + // Immediate index reverse order + {x,y[1:0],z[0],z[1][1:0]} <= @e 12'ha5a; + #1 + `check(z[1], 4'bxxxx) + `check(z[0], 4'bxxxx) + `check(y, 4'bxxxx) + `check(x, 4'bxxxx) + + ->e; + `check(z[1], 4'bxx10); + `check(z[0], 4'b0110); + `check(y, 4'bxx01); + `check(x, 4'b1010); + + x = 4'hx; + y = 4'hx; + z[0] = 4'hx; + z[1] = 4'hx; + + // Variable index + {z[i+1][i+:2],z[i],y[i+:2],x} <= @e 12'h5a5; + #1 + `check(z[1], 4'bxxxx) + `check(z[0], 4'bxxxx) + `check(y, 4'bxxxx) + `check(x, 4'bxxxx) + + ->e; + `check(z[1], 4'bxx01); + `check(z[0], 4'b0110); + `check(y, 4'bxx10); + `check(x, 4'b0101); + + x = 4'hx; + y = 4'hx; + z[0] = 4'hx; + z[1] = 4'hx; + + // Variable index reverse order + {x,y[i+:2],z[i],z[i+1][i+:2]} <= @e 12'ha5a; + #1 + `check(z[1], 4'bxxxx) + `check(z[0], 4'bxxxx) + `check(y, 4'bxxxx) + `check(x, 4'bxxxx) + + ->e; + `check(z[1], 4'bxx10); + `check(z[0], 4'b0110); + `check(y, 4'bxx01); + `check(x, 4'b1010); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vlg.list b/ivtest/regress-vlg.list index 9a15545ac..74b1c5ed1 100644 --- a/ivtest/regress-vlg.list +++ b/ivtest/regress-vlg.list @@ -676,6 +676,7 @@ muxtest normal ivltests # Validates that X sel and inputs same, output not X named_event_no_edges CE ivltests nb_assign normal ivltests nb_delay normal ivltests +nb_ec_concat normal ivltests nblkorder normal ivltests # Validates Non-blocking order determinism negative_genvar normal ivltests negvalue normal ivltests gold=negvalue.gold