docs(fpga): rewrite the HDL-backend design against the generator as built - #777
Merged
Merged
Conversation
…uilt The FPGA backend doc was written before the software generator existed (#769). Now that it does, its central claim — that the hardware backend reuses the codegen front-end wholesale — could be checked rather than assumed, and it did not survive intact. New §2 audits what the generator actually hands the hardware backend. Most of it carries; the part the document led with does not. Closure quotation was advertised as the bridge to rhdl's no-closures kernel subset, but every op closure in the catalog is bound `Fn(&A) -> B` and rhdl forbids references, so recorded bodies need a signature rewrite and deref elision rather than a verbatim splice — and eligibility cannot be delegated to rhdl's frontend, because what we hand it is not what the user wrote. The clean carryovers are the unglamorous ones: `NodeInfo` traversal, the refusal machinery with `#[track_caller]` call sites, capture detection, and `check_artifact`. Also records the asymmetry that there is no `nitro!` equivalent for RTL, so the parity burden the software generator sidestepped lands on the op twins. New §5.4: pipeline skew, absent from the original, whose worked example is a single path and so cannot exhibit it. Fan-in across paths of unequal pipeline depth combines values from different logical ticks, and `NodeInfo` carries no depth information to balance against — the one part of the backend the generator gives no head start on. §5.1 corrected: fixed-point is not something to introduce. `Px`/`Qty` already ship, and their `i128` width — deliberate, so large-notional venues stay representable — is the actual obstacle. The work is narrowing to a venue-scaled `Fixed<I, F>`, not adding fixed-point. New §5.5 (`Burst` has no strobe; backpressure is mandatory in fabric and absent in software) and §6, the host↔card interface: PCIe discipline, the DMA ring as an `Activation::ALWAYS` poll source, torn-parameter avoidance, wire timestamping, and the decision tap. §6 applies whether the gateware is generated, hand-written, or bought, which is why `trading-roadmap.md` item 8 already sequences it first. §1 widens the survey beyond rust-hdl/rhdl and splits the backend: rhdl for leaf kernels only, wingfoil owning structural emission. That bounds the pre-release dependency to the half with a fallback. §7's spike is amended so that it can fail — the original was all single-path and scalar, so it could only succeed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P9wTP8Gyd9jFWpi6awakVP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
docs/fpga-hdl-backend-decision.mdwas written before the software generator existed. Now that #769 has built it, the document's central claim — that a hardware backend reuses the codegen front-end wholesale — could be checked rather than assumed. It did not survive intact. This rewrites the doc around what actually carries over, and adds the two walls it was missing.Docs only; no code changes.
Why
Refs #727 (this doc's tracking issue). Follows #769.
The question that prompted it: now that lightning-codegen has landed, is it actually useful for the Verilog work? The answer is yes, substantially — but not through the part the document led with.
The headline claim was wrong. §3 asserted that
func!quotation is "exactly the bridge RHDL's no-closures kernel subset needs" and that tier-1 closures "translate nearly verbatim". One line ofops.rssettles it:Every op closure in the catalog takes references; rhdl forbids them. So a recorded body —
|n: &u64| (n * size) as f64— needs a signature rewrite and deref elision to become a#[kernel]fn, not a splice.fold's&mutaccumulator isn't a rewrite at all: it's a state-update kernel, a different shape. The knock-on matters more than the rewrite — §3 hoped rhdl's frontend would double as the eligibility checker, and it can't, because what we hand rhdl isn't what the user wrote.The clean carryovers turn out to be the unglamorous ones:
NodeInfotraversal (active/passive edges are valid-gating vs plain sampling), the refusal machinery with#[track_caller]call sites, capture detection, andcheck_artifact.The changes
New §2 — the carryover ledger. Per-asset audit of what the generator hands the hardware backend, including §2b (why quotation doesn't splice) and §2c: there is no
nitro!equivalent for RTL, so the parity burden the software generator sidestepped lands squarely on the op twins.New §5.4 — pipeline skew, the load-bearing wall. Absent from the original, whose worked example is a single path and therefore cannot exhibit it. Fan-in across paths of unequal pipeline depth combines values from different logical ticks, and
NodeInfocarries no depth information to balance against. The one part of the backend the generator gives no head start on.§5.1 corrected. Fixed-point is not something to introduce —
adapters/marketalready shipsPx/Qty. Theiri128width is the actual obstacle, and it's deliberate (ani64at nine decimals caps at ±9.22 × 10⁹, making large-notional venues unrepresentable). The work is narrowing to a venue-scaledFixed<I, F>, which has a software payoff of its own.New §5.5 —
Bursthas no strobe (it serialises, which breaks the one-clock claim), and backpressure is mandatory in fabric while absent from software wingfoil.New §6 — the host↔card interface. PCIe posted-vs-non-posted discipline (never read across it on a hot path), the DMA ring as an
Activation::ALWAYSpoll source, torn-parameter avoidance, wire timestamping, and the decision tap. Deliberately independent of everything else: it applies whether the gateware is generated, hand-written, or bought, which is whytrading-roadmap.mditem 8 already sequences it first.§1 widened, and the backend split. Beyond rust-hdl/rhdl: CIRCT/FIRRTL, Spade, XLS, Veryl, Amaranth. Recommendation is rhdl for leaf kernels only, with wingfoil owning structural emission — which bounds the pre-release one-person dependency to the half that has a fallback. Under the original framing, an rhdl stall took the whole emitter with it.
§7's spike amended so it can fail. As written it was all single-path and scalar, so it could only succeed. Now includes doing the §2b rewrite by hand and wiring an unequal-depth
join, with a note that the latter runs first if time is short.How it was verified
Docs only — no Rust changed, so the code checklist doesn't apply. What was checked:
Fn(&A)bounds inops.rs/fluent.rs,Px/Qtyasi128atSCALE = 1e9inadapters/market.rs,poll'sOption<T>return, and theNodeInfofields the ledger cites§references consistent after renumberingNotes for the reviewer
Read §2 first — it is the section that answers "was the codegen work useful here", and the rest follows from it.
The doc keeps a revision note at the top recording what changed and why, rather than silently rewriting. §2b in particular contradicts the original §3, and the audit trail seemed worth more than a clean read.
#727's checklist is now stale — it cites "doc §5" and "doc §4" for the spike and the walls, both of which have moved (§7 and §5), and its three-item spike is the version that could only succeed. Not updated here; happy to do it in a follow-up.
Not folded in, deliberately: the review findings on #769 itself. The one I'd call a should-fix-before-merge is that
#[wiring]and theProbeexpansion hardcode::wingfoil, which breaks for anyone renaming the dependency (wf = { package = "wingfoil" }) —proc-macro-crateis the usual fix. That belongs on #769, not here.Time-sensitive item raised in §6c: capture detection already produces, per node, the exact set of values a closure depends on — which for hardware splits into frozen (baked into LUTs) and live (a control register).
wired-graph-codegen-decision.md§3 names that fork as "a follow-up, not v1"; hardware makes it a requirement, since nobody resynthesises to change a size. Cheap to decide while #769's capture machinery is fresh, expensive once artifacts are checked in across the tree.🤖 Generated with Claude Code
https://claude.ai/code/session_01P9wTP8Gyd9jFWpi6awakVP
Generated by Claude Code