Findings on Rev 4 hardware
Board errata
What bring-up on Rev 4 hardware proved broken, and what remains observed but not yet attributed to the board.
Updated 2026-09-01
Status from on-hardware bring-up — serial, JTAG and network debug of Ethernet, boot, eMMC and PCIe against a Pi 5 host. Items are split two ways: proven wrong, and observed but not yet attributed to the board.
They have not been re-verified on later boards.
Confirmed errata
Fault E1 — PCIe TX pairs are not AC-coupled on the card
The SOM TX pairs (GTR_DPn_M2C → J4 PET pins B14/15, B19/20, B23/24, B27/28) run
direct, with no series capacitors. Per UG1091, “no transceiver capacitors are
added to the data lanes on the PS-GTR transceivers” on the K26 SOM, and PCIe CEM
places TX coupling on the add-in card — so these pairs must be AC-coupled here, and
are not.
Meanwhile C15–C22 sit on the RX pairs (GTR_DPn_C2M, J4 PER pins), where the host
already provides coupling; those extra series caps are redundant but harmless.
Impact: violates CEM for the upstream direction, and is the leading explanation for the link failure in O1.
The mechanism, per the LTSSM evidence in O1: receiver detection succeeds — the card’s uncoupled TX sees the host’s 50 Ω termination directly, so it advances Detect → Polling.Active and begins sending TS1 ordered sets — but the host never answers, Polling times out, and the card falls back to Detect. A DC-coupled transmit pair drives the card’s TX common-mode voltage straight into the host’s receiver, which has no coupling capacitor of its own (CEM assigns that to the add-in card) and therefore sits outside its required common-mode range and cannot recover the bit stream. Net effect: the card talks, the host cannot hear it, and the link cycles forever.
Confidence: consistent with every measurement taken, but not proven — the rework below is the experiment that settles it. Two earlier mechanisms for this erratum were wrong, and are recorded in O1 so the reasoning isn’t repeated.
Minimal rework for x1 bring-up: cut and fit 100 nF (0402) in series on the lane-0 pair at J4 B14/B15 only. Full x4 needs the same on all four pairs.
Open observations
Not yet attributable to the board.
Open O1 — PCIe link training never starts — attributed to E1
Every firmware precondition has now been verified on the running board with the PCIe-endpoint FSBL:
| check | register | value | verdict |
|---|---|---|---|
| PCIe core clock | PCIE_REF_CTRL 0xFD1A00B4 | 0x01000200 | matches psu_init intent |
| Lane 0 PLL lock | L0_PLL_STATUS_READ_1 0xFD4023E4 | 0x38 (bit 4 set) | locked |
| Lane protocol mux | ICM_CFG0/1 0xFD410010/14 | 0x11 / 0x11 | all 4 lanes = PCIe |
| Resets released | RST_FPD_TOP 0xFD1A0100 | bits 17, 18, 19 clear | released |
| Endpoint attributes | ATTR_* 0xFD4800xx | programmed, port type 0 (EP) | correct |
| ECO_0 (LTSSM hold) | 0xFD480314 | 0x0 | not holding |
| LTSSM | 0xFD480228 bits[8:3] | 0 = Detect.Quiet | stuck |
| Link status | 0xFD480238 | 0x2 (0x3 = up) | down |
Nothing in software remains to enable: the controller is clocked, out of reset,
configured as an endpoint, and its LTSSM is not held. Clearing ECO_0 by hand changes
nothing. zynqmp-pspcie-epdma is a host-side DMA/PIO datapath driver and does not
perform link bring-up, so it isn’t the missing piece either. psu_init’s PCIe section
demonstrably ran — the ID register reads 0x10EED011 as written.
Link training can be forced, and it partially works. Toggling pcie_ctrl_reset
(RST_FPD_TOP bit 17) is a PERST equivalent and restarts the LTSSM. Doing that while
the host simultaneously retrains — unbind/bind the Pi’s brcm-pcie root port —
advances the card past Detect. A 3000-sample histogram of 0xFD480228 taken across a
host retrain:
2916 samples 0x04 state 0 = Detect.Quiet
84 samples 0x14 state 2 = Polling.Active
So the card does detect a receiver and does enter Polling.Active, where it transmits TS1 ordered sets — then times out with no reply and drops back to Detect, roughly a 3% duty cycle. It is cycling, not hung.
That measurement kills two earlier explanations: the link is not blocked by anything in firmware, and receiver detection is not failing — an uncoupled TX sees the far-end termination more readily, not less. What remains is the transmit path: the card talks and the host never answers. See E1.
Open O2 — QSPI left in a non-POR state after boot
Two symptoms, one likely mechanism.
- U-Boot
sf probereads garbage JEDEC IDs —e0/40/20,43/49,c0patterns, varying per cold boot, unaffected by SPI clock. - Warm reset hangs the board, 100% reproducibly. Verified with continuous serial
capture:
resetfrom U-Boot, with the PL never loaded and Linux never run, printsresetting ...and dies before any BootROM, FSBL or U-Boot output. Only a power cycle recovers. Cold boots are 100% reliable.
The mechanism consistent with both: the boot-time QSPI driver (FSBL and/or the U-Boot
env load) leaves the mt25qu512 in a volatile non-power-on state — 4-byte addressing
and/or quad protocol. Later accessors that assume POR state (U-Boot’s sf layer, and
critically the BootROM after a warm reset) then read garbage. Power cycling resets the
flash and everything works.
The earlier suspicion that PCIe cable attach caused the sf garbage was likely
coincidence with a boot-image change made the same hour.
Operational impact: warm reset is unsupported — use POR, the bench PSU switch.
Candidate fix: patch the FSBL to issue a flash software reset after loading
(exit-4-byte 0xE9, reset-enable 0x66 + reset 0x99), and audit U-Boot’s env-load
path. Until then, treat warm reset as broken by design.
Open O3 — Power-on serial noise aborts U-Boot autoboot
Bytes land in the UART FIFO at power-on; the likely source is board-powered USB-serial adapter enumeration rather than the board itself. Any pending byte aborts the autoboot countdown.
Mitigated in the BOOT-emmc.BIN lineage by patching the default env to bootdelay=-2,
which skips the abort check. Current images boot through reliably regardless of noise.