Skip to content

GF-T16 multiply: correct RTL, correct netlist, wrong bitstream through openXC7's DSP path #381

Description

@gHashTag

Summary

gft16_mul is correct — in simulation, in synthesis, and when mapped to LUTs. But a bitstream built from the DSP-mapped netlist through the openXC7 flow (yosys → nextpnr-xilinx → prjxray) computes the wrong answer on silicon.

Your RTL is not implicated and your published areas reproduce exactly. This is a warning for anyone building GF-T through openXC7 rather than your own flow.

What reproduces exactly

fpga/gft/SYNTH_RESULTS.md was re-run on a different host with yosys 0.63 (yours: 0.65), using your .ys scripts unmodified:

unit DSP claim/meas LUT claim/meas CARRY4 claim/meas
gft16_mul 1 / 1 47 / 47 18 / 18
gft_add 0 / 0 483 / 483 45 / 45
gft_alu 3 / 3 634 / 634 114 / 114
gft_dot4_tile 4 / 4 705 / 708 124 / 124
gft_dot4 12 / 12 1673 / 1673 303 / 303

Four exact, one off by 3 LUT (0.4%, yosys version). No discrepancy of any kind in your numbers.

What fails

gft16_mul + gft_mul verbatim, in a port-less BSCANE2 harness, checked by your own KAT vectors from gft16_mul_kat_tb.v — no expected value re-derived, so no second implementation in the loop.

build φ¹·φ¹ → (42,0) 1.5×1.5 → (43,64) cells
iverilog, behavioural RTL pass pass
gate-level sim of the synthesised netlist vs xilinx/cells_sim.v pass pass 1 DSP48E1
bitstream, on silicon pass FAIL 1 DSP48E1, 56 LUT
bitstream, -nodsp, on silicon pass pass 0 DSP, 53 LUT

Hardware: 3× QMTech Wukong XC7A200T-FGG676, idcode 0x3636093. Five stable reads per build per board, each bracketed with a wrong-part bitstream so Done went 0 → 1 before every read. Reply nibble is {v0_ok, v1_ok, done, sig}:

DSP build       a5a5a5aa   v0 ok, v1 FAIL     boards 1:4, 1:6, 1:8
LUT-only build  a5a5a5af   v0 ok, v1 ok       boards 1:4, 1:6, 1:8

Deterministic, three dice, both directions.

Where the fault is — and is not

Cleared: the RTL (passes behavioural sim) and yosys (its DSP-mapped netlist passes gate-level sim against yosys's own DSP48E1 model).

Remaining: nextpnr-xilinx FASM emission, or prjxray's DSP48E1 frame model.

The natural hypothesis — that the DSP's operating mode never reaches the bitstream, which would explain why only the nonzero-product vector fails — is refuted: the FASM carries OPMODE, ALUMODE, INMODE and the register controls (21 non-GND DSP lines), and prjxray models the tile with 436 segbits.

Separating nextpnr from prjxray needs a reference bitstream for the same netlist (Vivado, or a DSP48E1 unit test with known frames). Not attempted here.

Practical consequence

On the openXC7 flow, -nodsp costs 3 LUT (56 → 53) and both vectors pass. The DSP is not a saving on this flow.

Your on-chip claims were made through your own flow on an AX7203, which this does not test — it tests openXC7 on a QMTech board.

Reproduce

yosys -p "read_verilog -sv gft_mul.v gft16_mul.v <harness>.v; \
          synth_xilinx -family xc7 -top <harness> -flatten; write_json k.json"
nextpnr-xilinx --chipdb xc7a200t*.bin --json k.json --fasm k.fasm
# fasm2frames, xc7frames2bit, openFPGALoader
# expect a5a5a5aa ; then add -nodsp and expect a5a5a5af

Full write-up and harness: docs/reports/TRINET-DSP-DEFECT-W723.md and fpga/verilog/gft_kat_jtag.v in gHashTag/t27, branch claude/igla-fpga-improvements-3f5e1a.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions