Skip to content

flexfloat: fix fcvt.w/wu saturation and RMM rounding - #160

Open
marpac3 wants to merge 2 commits into
gvsoc:masterfrom
FondazioneChipsIT:fix/fcvt-flexfloat
Open

marpac3 wants to merge 2 commits into
gvsoc:masterfrom
FondazioneChipsIT:fix/fcvt-flexfloat

Conversation

@marpac3

@marpac3 marpac3 commented Aug 21, 2026

Copy link
Copy Markdown

Problem. Three IEEE-754 conformance gaps in the shared flexfloat FP
backend:

  1. Rounding mode 4 (RMM, round-to-nearest-ties-to-max-magnitude) aborted the
    simulation: host fenv has no such mode.
  2. On overflow, directed rounding modes always returned infinity. IEEE 754
    requires a clamp to the largest finite value: toward-zero always, downward
    for positive results, upward for negative results.
  3. fcvt.wu.s/fcvt.w.s on the RMM path dropped the sign of the input:
    converting a negative value to unsigned returned |x| instead of saturating
    to 0, and signed negative overflow saturated to INT32_MIN+1 instead of
    INT32_MIN. One path also failed to restore the saved rounding mode.

Fix. RMM becomes a scoped flag resolved in software by
flexfloat_sanitize, which already re-rounds from the round/sticky bits (a
tie rounds away from zero under FE_TONEAREST). Overflow honours the rounding
direction. The conversions pass the signed value to
double_to_uint/double_to_int, which saturate with the correct bounds.

Validation. Lockstep RVVI co-simulation of the GVSOC ISS against the
CV32E40P RTL (F and Zfinx configurations), with corev-dv random tests and
directed FP tests; bit-exact against the CV32E40P FPnew conversions on
directed boundary vectors. pulp-open and snitch build clean and give
bit-identical instruction traces on an FP workload before/after.

The second commit preserves its original author (@nikgiu).

marpac3 and others added 2 commits August 7, 2026 10:48
Rounding mode 4 (round to nearest, ties to max magnitude) used to abort
the simulation because fenv has no such mode. flexfloat_sanitize already
resolves rounding in software from the round and sticky bits, so RMM
only needs a flag: with FE_TONEAREST set and flexfloat_rmm active, a
tie rounds away from zero instead of to even. The flag is scoped to the
per-instruction set/restore pair in setFFRoundingMode.

Overflow now honours the direction of the rounding mode as IEEE 754
requires: toward-zero (always), downward (positive results) and upward
(negative results) clamp to the largest finite value instead of
returning infinity.

(cherry picked from commit aaedd2d)
The RMM path of the flexfloat float-to-int conversions negated the input to
round on its magnitude, then dropped the sign:

- lib_flexfloat_cvt_wu_ff_round returned |x| for a negative x instead of 0.
  Unsigned conversion of a negative value saturates to 0; the negation hid
  the sign from double_to_uint, which already saturates correctly, so the
  magnitude leaked through (0xffffffff / |x| instead of 0).
- lib_flexfloat_cvt_w_ff_round saturated a negative overflow to INT32_MIN+1
  (0x80000001) instead of INT32_MIN (0x80000000): |INT32_MIN| = 2^31 is not
  representable as a positive int32, so the magnitude saturated to INT32_MAX
  and negating it was off by one.

Fix: do not hide the sign from double_to_uint/double_to_int, which saturate
with the correct signed/unsigned bounds; restore the saved rounding mode
(was passing new_round instead of old, unlike the unsigned sibling).

Validated bit-exact against the CV32E40P FPnew conversions (F and Zfinx
configurations) on directed boundary vectors.

(cherry picked from commit 8aab68b)
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

Successfully merging this pull request may close these issues.

2 participants