From 5311c0cd385e1a1c7f25579ef092482a68c335d9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 19 Jun 2026 12:33:31 -0700 Subject: [PATCH 1/2] 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 2/2] 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" +}