diff --git a/exi_bba/bba_register_file.py b/exi_bba/bba_register_file.py index 33f90a2..8e16ac2 100644 --- a/exi_bba/bba_register_file.py +++ b/exi_bba/bba_register_file.py @@ -27,6 +27,10 @@ _RWP_LO = 0x16 _RWP_HI = 0x17 _RRP_LO = 0x18 _RRP_HI = 0x19 + +# First page of the SPRAM RX ring (pages 1-15; page 0 is the register region). +# Must match rx_frame_assembler._RX_PAGE_FIRST. +_RX_PAGE_FIRST = 0x01 _PAR0 = 0x20 _PAR1 = 0x21 _PAR2 = 0x22 @@ -183,8 +187,19 @@ class BBARegisterFile(Elaboratable): r_ncra = Signal(8) r_imr = Signal(8) r_ir = Signal(8) - r_rwp_lo = Signal(8) - r_rrp_lo = Signal(8) + # Both ring pointers reset to the ring's FIRST PAGE, not to 0. + # The SPRAM ring is pages 1-15 (page 0 holds the register region), and + # RXFrameAssembler's own write pointer resets to page 1 -- so a 0 here + # disagreed with the assembler and, worse, told the GC driver to read + # its first frame from page 0. The frame is at page 1; page 0 is the + # register page. Hardware symptom: "RRP page 0 outside ring pages + # 1-15" from the test app's DMA descriptor check, on a board whose RX + # path was otherwise working. + # 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, and pointing at the start of the ring. + r_rwp_lo = Signal(8, init=_RX_PAGE_FIRST) + r_rrp_lo = Signal(8, init=_RX_PAGE_FIRST) # PAR0–5 reset to a valid Nintendo OUI MAC (00:09:BF:00:00:01) so the # device has a sane source MAC even before the GC driver programs its # own. PAR0 is the first MAC octet. @@ -720,6 +735,28 @@ if __name__ == "__main__": if result != []: errors.append(f"T1 device-ID query must push nothing, got {result}") print(f"T1 device-ID query pushes nothing: {result == []}") + + # T1b: both ring pointers must come out of reset at the ring's FIRST + # PAGE, not 0. RRP == RWP == 1 is "empty, pointing at the start of + # the ring"; 0 points at the register page, and a driver that read its + # first frame from RRP=0 would get the register region instead of the + # frame -- which is what the DMA descriptor check caught on hardware. + for name, a in (("RWP", _RWP_LO), ("RRP", _RRP_LO)): + got = await exi_read(ctx, a, 1) + if got != [_RX_PAGE_FIRST]: + errors.append(f"T1b {name} reset: expected " + f"[{_RX_PAGE_FIRST}], got {got}") + print(f"T1b {name} at reset: {got} (want [{_RX_PAGE_FIRST}])") + + # ...and the constant must not drift from the assembler's, which owns + # the write pointer. Two copies of a page number is exactly how the + # off-by-one got in. + from exi_bba.rx_frame_assembler import _RX_PAGE_FIRST as _ASM_FIRST + if _RX_PAGE_FIRST != _ASM_FIRST: + errors.append(f"T1b ring-first-page mismatch: register file " + f"{_RX_PAGE_FIRST} vs assembler {_ASM_FIRST}") + print(f"T1b ring first page agrees with assembler: " + f"{_RX_PAGE_FIRST == _ASM_FIRST}") await ctx.tick("sync").repeat(4) # T2: Write and read back PAR0-PAR3