Skip to content

math/fxp: add lt, ge and gt beside le - #20

Draft
cecetaca wants to merge 1 commit into
asyncvlsi:mainfrom
cecetaca:fxp-signed-comparisons
Draft

cecetaca wants to merge 1 commit into
asyncvlsi:mainfrom
cecetaca:fxp-signed-comparisons

Conversation

@cecetaca

@cecetaca cecetaca commented Sep 21, 2026 •

Copy link
Copy Markdown

The gap

math::fxp exports le, and no lt, ge or gt. The fixpoint deftype has all four as methods. So working with the type gives you every comparison; working with a raw int<A+B> gives you one.

That split shows up at a channel boundary, where values arrive as int<W> and the port type is fixed. Porting C to ACT, every signed comparison then has to go through le with the operands swapped and an (x != y) term bolted on — which is precisely what the deftype methods already do internally:

function lt(...) { self := (x != rhs.x) & fxp::le<A,B>(x, rhs.x) }
function ge(...) { self := fxp::le<A,B>(rhs.x, x) }
function gt(...) { self := (x != rhs.x) & fxp::le<A,B>(rhs.x, x) }

The logic exists; it just isn't reachable from the function API, so callers write it a third time.

The change

Adds lt, ge and gt to the fxp namespace in le's style, and then has the three deftype methods call them instead of keeping a second copy of the expression. le, mults, divs and everything else are untouched, and the methods compute exactly what they did before. Header comment updated to list the four.

Net +34 / −4 in one file.

Verification

No test harness in this repo, so this was checked under actsim.

Exhaustive. All 256 ordered pairs of Q(4,0) — every 4-bit two's-complement value against every other — with each of the four comparisons checked against an oracle that flips the sign bit and compares unsigned. That is deliberately a different method from the one under test, which compares sign bits and falls through to an unsigned compare:

PASS 1024 comparisons, 0 wrong

Negative control. Dropping the (x != y) strictness term from gt makes the same bench report FAIL 16 of 1024 wrong, so it can fail.

The refactor. The three fixpoint methods were separately checked to agree with the functions they now delegate to, across a spread of Q(8,8) values.

🤖 Generated with Claude Code

The fxp function API has le, and no lt, ge or gt. The fixpoint deftype has
all four as methods -- so working with the type gives you every comparison,
and working with a raw int<A+B> gives you one.

That split matters because the raw-integer form is what a design reaches for
at a channel boundary, where values arrive as int<W> and the port type is
fixed. Porting C to ACT, every signed comparison has to go through le with
the operands swapped and an (x != y) term bolted on -- which is exactly what
the deftype methods already did internally, written out a second time.

So this adds the three functions and then has the methods call them, rather
than keeping two copies of the same expression. No behaviour change to le,
mults, divs or anything else, and the methods compute what they always did.

Verified with actsim: all 256 ordered pairs of Q(4,0) against an oracle that
flips the sign bit and compares unsigned -- a different method from the one
under test, which compares sign bits and falls through -- giving 1024
comparisons, 0 wrong. Breaking gt's strictness term makes the bench report 16
failures, so it can fail. The refactored fixpoint methods were separately
checked to agree with the functions they now call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant