"Zfinx" & Friends: Floating-Point in Integer Registers

Part III Linux boot: optional Vol. I (Unprivileged) pp. 149–151 · ~2 min read

For a microcontroller, F’s real cost isn’t the FPU — it’s the second 32-entry register file and the ports to feed it. The Zfinx family runs the same floating-point instructions on the x-registers: less area, half the context-switch state, and one hard consequence — Zfinx software and F software are mutually incompatible binary ecosystems.

The family (all depend on Zicsr; each = its f-register twin minus the transfer instructions)
Contents
ZfinxAll of F except FLW/FSW, FMV.W.X/FMV.X.W, C.FLW[SP]/C.FSW[SP] — integer loads/stores move the bits, FSGNJ or integer moves shuffle registers.
Zdinx (dep Zfinx)All of D except its transfers. RV32Zdinx holds doubles in EVEN-aligned x-register pairs.
Zhinx (dep Zfinx)All of Zfh except its transfers.
ZhinxminJust FCVT.S.H / FCVT.H.S (+ the D pair with Zdinx) — the storage-format subset.
Dotted-underlined cells have explanations — click one.

The two representation rules

Narrow values sign-extend, not NaN-box: a w-bit result fills bits XLEN−1:w with copies of the sign bit, and reads ignore those bits. NaN-boxing was designed for dedicated f-registers holding recoded formats; with integers sharing the file, recoding dies and the RV64 calling convention’s treatment of floats-in-x-registers becomes the natural (and now mandatory) pattern.

Wide values pair up (RV32Zdinx): even register numbers only (odd reserved), lower register = low-order bits regardless of endianness, and x0 behaves as a full-width zero source / write-void destination — writing a double to x0 does not touch x1. Memory transfers cost two loads/stores; register moves are one FSGNJ.D.

Privileged fallout

mstatus.FS hardwires to 0 and stops gating anything — there is no separate FP state to dirty-track or lazy-switch. misa.F/D/Q read 0, so F-compiled binaries correctly refuse to run. fcsr (rounding mode, flags) still exists — hence the Zicsr dependency.

Hardware Designer Notes

Not for the Linux-boot application core: the RVA profiles and the entire Linux userspace ABI assume F/D with f-registers. Zfinx’s home is the little cores around it — and there it deletes ~1.5 KiB of flops plus two register-file ports.

Minimal Linux-boot hart MUST

  • Feed the FPU datapath from the integer register file’s read ports; enforce the even-pair decode rule and the x0 pair semantics on RV32Zdinx
  • Sign-extend narrow FP writebacks and ignore (don’t check) upper bits on reads
  • Hardwire mstatus.FS=0 and misa.F/D/Q=0

MAY simplify / trap-and-emulate

  • Share the integer register file’s existing 2R1W ports — accepting structural hazards between integer and FP ops is the intended trade
  • Choose Zfinx for an M-mode-only companion core while the Linux cores use real F/D — they never share binaries anyway

Check yourself — Zfinx family

1.A 32-bit float result lands in an RV64 x-register under Zfinx. What fills bits 63:32, and why NOT NaN-boxing?

2.In RV32Zdinx, what are the register-pair rules for a double-precision operand?

3.What happens to mstatus.FS and misa under Zfinx, and why?

3 questions