The DMA descriptor test reported "RRP page 0 outside ring pages 1-15" on a
board whose RX path was working (t_rx passed: RWP advanced and RI fired), so
the pointer itself was wrong.
RXFrameAssembler's write pointer resets to _RX_PAGE_FIRST = page 1 -- the ring
is pages 1-15 and page 0 holds the register region -- but the register file's
r_rwp_lo and r_rrp_lo were plain Signal(8) and reset to 0. Two consequences:
the register file and the assembler disagreed about where the ring starts, and
a driver reading its first frame from RRP=0 gets the REGISTER page instead of
the frame, which is at page 1.
It hid because RRP == RWP == 0 looks self-consistently empty, so nothing
complained until something actually read the pointer. RRP is driver-owned
(the GC writes it after consuming frames) but its RESET value is ours, and
RRP == RWP == 1 is the correct initial state: empty, pointing at the ring
start.
T1b locks down both reset values AND imports the assembler's constant to
assert it matches, because two copies of a page number in two modules is
exactly how the off-by-one got in.
--seeds 8: clk 28.x-32.2 MHz, EXI 56-60 MHz, 8/8 PASS. Best seed 8, flashed.
NOT fixed here, and worth separate attention: nothing in the gateware consumes
RRP, so RXFrameAssembler can overwrite pages the GC has not read yet -- there
is no ring-overflow protection (the real BBA has RBFI for this).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>