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

🐛 fix FPU's float-to-signed-integer corner case #943

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions rtl/core/neorv32_cpu_cp_fpu.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -2267,8 +2267,8 @@ begin
-- the mantissa is not 0 (without hidden 1) then we have a true overflow.
-- Otherwise we have a "real" 1 in the result MSB which should result in -MAX as the correct value.
-- This captures the corner case where the number is exactly 2^-31
elsif ((ctrl.sign = '1') and (ctrl.over = '1') and
(ctrl.result_tmp /= x"80000000") and (mantissa_i /= "00000000000000000000000")) then -- negative out-of-range
elsif (ctrl.sign = '1') and (ctrl.over = '1') then -- negative out-of-range
-- and (ctrl.result_tmp /= x"80000000") and (mantissa_i /= "00000000000000000000000") then -- negative out-of-range
ctrl.result <= x"80000000";
-- if we had a negative out of range we are not valid but never inexact
ctrl.flags(fp_exc_nv_c) <= '1';
Expand Down
15 changes: 12 additions & 3 deletions sw/example/floating_point_test/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ int main() {
// ----------------------------------------------------------------------------
// Initialize FPU hardware
// ----------------------------------------------------------------------------
neorv32_cpu_csr_write(CSR_FCSR, 0); // clear exception flags and set "round to nearest"
neorv32_cpu_csr_write(CSR_FFLAGS, 0); // clear exception flags
neorv32_cpu_csr_write(CSR_FRM, 0b000); // set dynamic rounding mode "round to nearest, ties to even"


// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -264,6 +265,14 @@ int main() {

neorv32_uart0_printf("\n#%u: FCVT.W.S (float to signed integer)...\n", test_cnt);
err_cnt = 0;
// corner case tests (#942)
for (i=0;i<256;i++) {
opa.binary_value = i << 24;
res_hw.binary_value = (uint32_t)riscv_intrinsic_fcvt_ws(opa.float_value);
res_sw.binary_value = (uint32_t)riscv_emulate_fcvt_ws(opa.float_value);
err_cnt += verify_result(i, opa.binary_value, 0, res_sw.binary_value, res_hw.binary_value);
}
// regular tests
for (i=0;i<(uint32_t)NUM_TEST_CASES; i++) {
opa.binary_value = get_test_vector();
res_hw.binary_value = (uint32_t)riscv_intrinsic_fcvt_ws(opa.float_value);
Expand Down Expand Up @@ -902,14 +911,14 @@ uint32_t get_test_vector(void) {
// generate special value "every" ~256th time this function is called
if ((neorv32_aux_xorshift32() & 0xff) == 0xff) {

switch((neorv32_aux_xorshift32() >> 10) & 0x3) { // random decision which special value we are taking
switch((neorv32_aux_xorshift32() >> 20) & 0x7) { // random decision which special value we are taking
case 0: tmp.float_value = +INFINITY; break;
case 1: tmp.float_value = -INFINITY; break;
case 2: tmp.float_value = +0.0f; break;
case 3: tmp.float_value = -0.0f; break;
case 4: tmp.binary_value = 0x7fffffff; break;
case 5: tmp.binary_value = 0xffffffff; break;
case 6: tmp.float_value = NAN; break;
case 6: tmp.binary_value = 0xff000000; break;
case 7: tmp.float_value = NAN; break; // FIXME signaling_NAN?
default: tmp.float_value = NAN; break;
}
Expand Down
26 changes: 16 additions & 10 deletions sw/example/floating_point_test/neorv32_zfinx_extension_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@
* @brief Also provides emulation functions for all intrinsics (functionality re-built in pure software). The functionality of the emulation
* @brief functions is based on the RISC-V floating-point spec.
*
* @note All operations from this library use the default GCC "round to nearest, ties to even" rounding mode.
* @note All intrinsics/instruction use the DYNAMIC rounding mode (actual rounding mode is defined by the FCSR).
*
* @warning This library is just a temporary fall-back until the Zfinx extensions are supported by the upstream RISC-V GCC port.
**************************************************************************/

#ifndef neorv32_zfinx_extension_intrinsics_h
#define neorv32_zfinx_extension_intrinsics_h

Expand Down Expand Up @@ -112,6 +112,7 @@ float subnormal_flush(float tmp) {

/**********************************************************************//**
* Single-precision floating-point addition
* @note Instruction uses DYNAMIC rounding mode.
*
* @param[in] rs1 Source operand 1.
* @param[in] rs2 Source operand 2.
Expand All @@ -123,13 +124,14 @@ inline float __attribute__ ((always_inline)) riscv_intrinsic_fadds(float rs1, fl
opa.float_value = rs1;
opb.float_value = rs2;

res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0000000, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0000000, opb.binary_value, opa.binary_value, 0b111, 0b1010011);
return res.float_value;
}


/**********************************************************************//**
* Single-precision floating-point subtraction
* @note Instruction uses DYNAMIC rounding mode.
*
* @param[in] rs1 Source operand 1.
* @param[in] rs2 Source operand 2.
Expand All @@ -141,13 +143,14 @@ inline float __attribute__ ((always_inline)) riscv_intrinsic_fsubs(float rs1, fl
opa.float_value = rs1;
opb.float_value = rs2;

res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0000100, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0000100, opb.binary_value, opa.binary_value, 0b111, 0b1010011);
return res.float_value;
}


/**********************************************************************//**
* Single-precision floating-point multiplication
* @note Instruction uses DYNAMIC rounding mode.
*
* @param[in] rs1 Source operand 1.
* @param[in] rs2 Source operand 2.
Expand All @@ -159,7 +162,7 @@ inline float __attribute__ ((always_inline)) riscv_intrinsic_fmuls(float rs1, fl
opa.float_value = rs1;
opb.float_value = rs2;

res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0001000, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0001000, opb.binary_value, opa.binary_value, 0b111, 0b1010011);
return res.float_value;
}

Expand Down Expand Up @@ -202,6 +205,7 @@ inline float __attribute__ ((always_inline)) riscv_intrinsic_fmaxs(float rs1, fl

/**********************************************************************//**
* Single-precision floating-point convert float to unsigned integer
* @note Instruction uses DYNAMIC rounding mode.
*
* @param[in] rs1 Source operand 1.
* @return Result.
Expand All @@ -211,12 +215,13 @@ inline uint32_t __attribute__ ((always_inline)) riscv_intrinsic_fcvt_wus(float r
float_conv_t opa;
opa.float_value = rs1;

return CUSTOM_INSTR_R2_TYPE(0b1100000, 0b00001, opa.binary_value, 0b000, 0b1010011);
return CUSTOM_INSTR_R2_TYPE(0b1100000, 0b00001, opa.binary_value, 0b111, 0b1010011);
}


/**********************************************************************//**
* Single-precision floating-point convert float to signed integer
* @note Instruction uses DYNAMIC rounding mode.
*
* @param[in] rs1 Source operand 1.
* @return Result.
Expand All @@ -226,12 +231,13 @@ inline int32_t __attribute__ ((always_inline)) riscv_intrinsic_fcvt_ws(float rs1
float_conv_t opa;
opa.float_value = rs1;

return (int32_t)CUSTOM_INSTR_R2_TYPE(0b1100000, 0b00000, opa.binary_value, 0b000, 0b1010011);
return (int32_t)CUSTOM_INSTR_R2_TYPE(0b1100000, 0b00000, opa.binary_value, 0b111, 0b1010011);
}


/**********************************************************************//**
* Single-precision floating-point convert unsigned integer to float
* @note Instruction uses DYNAMIC rounding mode.
*
* @param[in] rs1 Source operand 1.
* @return Result.
Expand All @@ -240,13 +246,14 @@ inline float __attribute__ ((always_inline)) riscv_intrinsic_fcvt_swu(uint32_t r

float_conv_t res;

res.binary_value = CUSTOM_INSTR_R2_TYPE(0b1101000, 0b00001, rs1, 0b000, 0b1010011);
res.binary_value = CUSTOM_INSTR_R2_TYPE(0b1101000, 0b00001, rs1, 0b111, 0b1010011);
return res.float_value;
}


/**********************************************************************//**
* Single-precision floating-point convert signed integer to float
* @note Instruction uses DYNAMIC rounding mode.
*
* @param[in] rs1 Source operand 1.
* @return Result.
Expand All @@ -255,7 +262,7 @@ inline float __attribute__ ((always_inline)) riscv_intrinsic_fcvt_sw(int32_t rs1

float_conv_t res;

res.binary_value = CUSTOM_INSTR_R2_TYPE(0b1101000, 0b00000, rs1, 0b000, 0b1010011);
res.binary_value = CUSTOM_INSTR_R2_TYPE(0b1101000, 0b00000, rs1, 0b111, 0b1010011);
return res.float_value;
}

Expand Down Expand Up @@ -1083,4 +1090,3 @@ float __attribute__ ((noinline)) riscv_emulate_fnmadds(float rs1, float rs2, flo


#endif // neorv32_zfinx_extension_intrinsics_h