Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect FCVT.W.D Signed Integer Conversion for Negative Double-Precision Input #145

Open
Sufyan-Ahmed1 opened this issue Nov 26, 2024 · 9 comments

Comments

@Sufyan-Ahmed1
Copy link

Sufyan-Ahmed1 commented Nov 26, 2024

  • Operand: 0xC1E0000000000000.
  • Instruction: fcvt.w.d
  • rounded_abs: 0xffffffff80000000 (correct signed integer result for the given input) (verified with Spike)
  • rounded_int_res: 0x0000000080000000
  • rounded_sign: 1 (straight from the input operand)

What is the purpose of this statement? if this is inverting the obtained answer...
assign rounded_int_res = rounded_sign ? unsigned'(-rounded_abs) : rounded_abs;

@stmach
Copy link
Collaborator

stmach commented Dec 3, 2024

I would expect that rounded_abs actually were 0x0000000080000000 (which is the absolute value of the FP number stored), which then should be negated to obtain the two's complement result.
@michael-platzer, do you think there could be some unintended sign-extension happening such that rounded_abs is full of 1-MSBs?

@stmach
Copy link
Collaborator

stmach commented Dec 3, 2024

Also @Sufyan-Ahmed1, please provide some more information, what is your setup, simulator in use, etc?

@michael-platzer
Copy link
Contributor

The input operand in question (0xC1E0000000000000) is a double precision FP number with sign bit set, an exponent (with bias) of 31, and a 0 mantissa, which corresponds to the real number $-2^{31}$. This value is converted to a signed 32-bit integer by the RISC-V instruction fcvt.w.d, which corresponds to the operation F2I, modifier 0, src_fmt_i of FP64, and int_fmt_i of INT32. The input value just happens to fit the destination format (it is in fact the smallest possible value that can be represented by a 32-bit signed integer), yielding a result value of 0x80000000. That is precisely the value held in the lower 32 bits of rounded_int_res, so the result is correct AFAICT.

@michael-platzer, do you think there could be some unintended sign-extension happening such that rounded_abs is full of 1-MSBs?

I assume that 0xffffffff80000000 is also the value of pre_round_abs that is passed through unmodified by fpnew_rounding. The 1-MSBs most likely are produced here (since the integer format is 32 bits, the upper 32 bits are all filled with the sign bit):

// sign-extend value only if it's signed
ifmt_input_val[ifmt] = '{default: operands_q[INT_WIDTH-1] & ~op_mod_q};
ifmt_input_val[ifmt][INT_WIDTH-1:0] = operands_q[INT_WIDTH-1:0];

However, AFAICT these do no harm, since these upper 32 bits will be disregarded anyways.

@michael-platzer
Copy link
Contributor

What is the purpose of this statement?

@Sufyan-Ahmed1 The purpose of this statement, as indicated by the comment just above it, is to convert an absolute integer result to a signed integer result in two's complement notation:

// Negative integer result needs to be brought into two's complement
assign rounded_int_res = rounded_sign ? unsigned'(-rounded_abs) : rounded_abs;
assign rounded_int_res_zero = (rounded_int_res == '0);

For this particular value, the statement has no effect, because the lower 32 bits (the relevant portion) of rounded_int_res are equal to the lower 32 bits of rounded_abs.

Spike is probably sign-extending the 32-bit result to 64-bit when printing it, but only the lower 32 bits are relevant here, so the result is correct.

@stmach
Copy link
Collaborator

stmach commented Dec 3, 2024

Spike is probably sign-extending the 32-bit result to 64-bit when printing it, but only the lower 32 bits are relevant here, so the result is correct.

According to the RV64I spec, 32-bit results are to be sign-extended on RV64:

5.2 Integer Computational Instructions
[....] These “*W” instructions ignore the upper 32 bits of their inputs and always produce 32-bit signed
values, i.e. bits XLEN-1 through 31 are equal.

Even unsigned 32-bit values are sign-extended in RV64I. I believe there is an issue somewhere as the rounded_abs shouldn't be sign-extended itself, maybe we just hit a corner case with the largest magnitude negative integer (which doesn't have a positive unsigned counterpart)

@Sufyan-Ahmed1
Copy link
Author

Hi @michael-platzer
I have two perspectives regarding this issue:

RTL Behavior: It is evident that the RTL performs a 2's complement operation on the absolute result. However, I am concerned about how this would impact scenarios involving a series of conversion instructions. Specifically, if the subsequent instruction processes the 64-bit register produced by the current instruction (e.g., 0x000000080000000), could this potentially propagate a bug?

Spike Discrepancy: While the RTL’s output seems mathematically correct, spike generates the result with sign negation. I am not entirely sure about their implementation, but this behavior appears inconsistent with the RTL.

@Sufyan-Ahmed1
Copy link
Author

Spike is probably sign-extending the 32-bit result to 64-bit when printing it, but only the lower 32 bits are relevant here, so the result is correct.

According to the RV64I spec, 32-bit results are to be sign-extended on RV64:

5.2 Integer Computational Instructions
[....] These “*W” instructions ignore the upper 32 bits of their inputs and always produce 32-bit signed
values, i.e. bits XLEN-1 through 31 are equal.

Even unsigned 32-bit values are sign-extended in RV64I. I believe there is an issue somewhere as the rounded_abs shouldn't be sign-extended itself, maybe we just hit a corner case with the largest magnitude negative integer (which doesn't have a positive unsigned counterpart)

Yes, this is where I stuck, because I was running regressions against spike as standard on Vivado and thus, when these mismatches are leading to test failure.

@Sufyan-Ahmed1
Copy link
Author

Also @Sufyan-Ahmed1, please provide some more information, what is your setup, simulator in use, etc?

I am running some torture_tests regressions on Vivado against spike as standard, and thus, when this corner case hit, test lead to failure.

@michael-platzer
Copy link
Contributor

Even unsigned 32-bit values are sign-extended in RV64I. I believe there is an issue somewhere as the rounded_abs shouldn't be sign-extended itself, maybe we just hit a corner case with the largest magnitude negative integer (which doesn't have a positive unsigned counterpart)

Ok, I see. In that case, the sign extension should probably happen after the conversion from absolute to signed result. I agree that it is a corner case. However, the largest magnitude negative signed integer does actually have a positive unsigned counterpart: 0x80000000, when interpreted as an unsigned integer, is that positive counterpart.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants