xilinx: emit the BUFR divide the design asks for, not always BYPASS - #151
Conversation
A placed BUFR emits no configuration at all today, and the divider is
unreachable. Both follow from the same cause: BUFR configuration is
carried by pp_config, the pseudo-pip table, keyed on
HCLK_IOI_RCLK_BEFORE_DIV<i> -> HCLK_IOI_RCLK_OUT<i>
which is the route taken when a BUFR site is traversed as ROUTING. When
a BUFR is an actual placed cell the router enters and leaves the site,
no pip is crossed, and the table never fires.
Measured, not inferred. A probe instantiating BUFR -> ODDR places the
buffer (nextpnr reports 'BUFR: 1/20') and the HCLK_IOI3 tile receives
its eight routing pips:
HCLK_IOI3_X113Y78.HCLK_IOI_RCLK_BEFORE_DIV3.HCLK_IOI_RCLK0
HCLK_IOI3_X113Y78.HCLK_IOI_RCLK2RCLK3.HCLK_IOI_RCLK_OUT3
HCLK_IOI3_X113Y78.HCLK_IOI_BUFR3_CE.HCLK_RCLK_DIV_CE3
...
with no BUFR_Y*.IN_USE and no BUFR_Y*.BUFR_DIVIDE.* anywhere. The buffer
is placed, wired, clocked and left unconfigured.
The divider compounds it: pp_config hardcodes BUFR_DIVIDE.BYPASS, so
even on the pass-through path a design asking for BUFR_DIVIDE("5")
produced a bitstream bit-identical to one asking for BYPASS.
prjxray-db has documented BYPASS and D1..D8 for all four slots since it
was fuzzed, so the ladder was reachable in silicon and in the database,
and unreachable only through this flow.
write_bufr() emits from the cell, where the parameters are, and is
dispatched from the site loop next to DSP/GT/PCIE. The pp_config entries
are untouched: they cover the pass-through case, which is disjoint.
An unsupported BUFR_DIVIDE is a log_error naming the cell rather than a
FasmLookupError naming a bit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
d4695bd to
0b91457
Compare
|
Probe result — the fix works, and it confirms the larger defect rather than the one I opened this PR for. Run: https://github.com/gHashTag/trinity-fpga/actions/runs/32037150788
Complete FASM delta for the divided design, stock → patched: + HCLK_IOI3_X113Y78.BUFR_Y1.IN_USE
+ HCLK_IOI3_X113Y78.BUFR_Y1.BUFR_DIVIDE.D5Two lines added, none removed. Four assertions, all green:
Slot Two corrections to my own work, for the recordThe first version of this PR was wrong, and this probe is what showed it. It The probe's own assertions were wrong too. They tested the original Still not verifiedSilicon. The probe is pinned to xc7a35tcsg324-1 with prjxray's own arty-a7 pins, |
|
@gHashTag Should we merge this before a release? I want to do a bugfix release now. |
|
I am merging this now. @gHashTag Please supply a new PR if any issues arise. |
Part of #149 — the smallest of its three gaps, and the only one that needs no new database rows.
The defect
BUFR configuration is emitted by
pp_config, a table built once at startup, soit cannot see cell parameters. It hardcodes, for every BUFR in every design:
pp_config[{ ctx->id("HCLK_IOI3"), ... }] = { "BUFR_Y" + yy + ".IN_USE", "BUFR_Y" + yy + ".BUFR_DIVIDE.BYPASS" // <- always };prjxray-dbhas documented the whole ladder since it was fuzzed —BYPASSandD1..D8for all four slots:So the divider is reachable in silicon and in the database, and was unreachable
only through this flow.
A design written with
BUFR_DIVIDE("5")produced a bitstream bit-identical toone written with
BYPASS— no error, no warning, a silently undivided clock.That is the failure mode this project keeps finding: a parameter accepted,
ignored, and reported as success.
The change
populate_bufr_divide()records what each boundBUFR_BUFRasks for, keyed by(tile, slot_y)— the same shape as the existingbufgctrl_bound_slots. Inwrite_pip, aBUFR_Y<y>.BUFR_DIVIDE.BYPASSfeature is replaced by the boundcell's actual setting.
Strictly additive. When no BUFR is bound at that slot, or when the cell does
ask for
BYPASS, the emitted bits are unchanged — every design that builtbefore builds identically. Only a design that explicitly asks for a division
sees a different bitstream, and today that design is silently mis-built.
An unsupported
BUFR_DIVIDEvalue is now alog_errornaming the cell.Emitting an undocumented feature instead fails later in
fasm2frameswith aFasmLookupErrornaming a bit — the same information with the cell removed.Verification status, stated honestly
amd64 image under QEMU and did not finish inside a sane timeout.
BUFR_DIVIDE("5")producesD5 = 32_22 33_18 33_19 !33_20 !33_21needs a design whose router actuallycrosses
HCLK_IOI_RCLK_OUT<i> -> HCLK_IOI_RCLK_BEFORE_DIV<i>, i.e. a BUFRdriving a regional clock into ILOGIC/OLOGIC. That is the same test design the
BUFIO half of BUFIO is not packed, the BUFR divide ladder is unreachable, and the HCLK_L enables are never emitted #149 needs, so I would rather build it once and use it for both
than hand-wave it here.
the right instrument — a one-cell Vivado design with
BUFR_DIVIDE("5"), decoded,would settle the encoding independently of the database.
I am raising it now rather than holding it until that design exists, because the
change is small and the current behaviour is silent. Happy to hold the merge
until the bit-level check lands if you prefer that ordering.
What this does not do
The other two gaps in #149 are untouched: nothing packs
BUFIO(it exists onlyas the constid
BUFIO_BUFIO), and nothing emits theHCLK_LENABLE_BUFFER.HCLK_CK_BUFRCLK*features. Those need database rows that do notexist yet — @cavearr's
047bcampaign is the prerequisite, and the ordering isagreed in that thread.
🤖 Generated with Claude Code