docs: record the REAL GC<->BBA EXI protocol; current header format is wrong

Read from the two drivers that will actually talk to this device: libogc
(libdb/uIP/bba.c) and Swiss (cube/patches/bba/bba.c).  The implemented 2-byte
header with a 2-bit length field does not match either.

The real protocol has two address spaces selected by bit 7 of byte 0, and no
length field at all -- the data phase runs until CS:

  COMMAND  (6-bit reg)   one 4-byte readwrite; response in byte 3
  REGISTER (16-bit reg)  [0x80|w<<6, reg>>8, reg&0xFF, 0x00] + data phase

NCRA, IR, NWAYS, RWP, RRP and PAR all live in the register space.  The
mismatch is destructive rather than merely non-functional: Swiss reading
NWAYS sends 80 00 31 00, which the current decoder reads as 'write 0x31 to
NCRA' -- and bit 0 of 0x31 is RESET.  Every register read would reset the
device.  The device-ID query is the one case where the two formats coincide,
which is exactly why enumeration worked on hardware and nothing else would
have.

Also explains the 22-bit response latency measured on console: the trailing
0x00 byte in the real header is the device's prefetch window, and the current
format spends those bits on the length field, leaving zero slack.  Adopting
the real format is required for compatibility anyway and recovers ~296 ns of
the ~815 ns deficit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 11:47:50 +02:00
parent abc61f3e66
commit f2762f613d
2 changed files with 146 additions and 3 deletions
+45 -3
View File
@@ -633,16 +633,58 @@ The full design specification lives in `docs/gc_bba_fpga_design.md`.
- Getting this wrong means the GC never enumerates the device.
- CS is active **low**, delineates each transaction.
### EXI Transaction Header (2 bytes before data)
### EXI Transaction Header
⚠️ **THE IMPLEMENTED FORMAT IS WRONG AND MUST BE REPLACED (found 2026-10-03).**
What `exi_bba/bba_register_file.py` implements today is:
```
Byte 0: [7]=write_flag [6:0]=addr[12:6]
Byte 1: [7:2]=addr[5:0] [1:0]=xfer_len-1 (0=1B … 3=4B)
```
Full address = 13 bits → 0x0000–0x1FFF.
That was designed from the spec's own guess. The design doc hedged on it
(§"ID query": *"Alternatively, read the Dolphin source for the exact byte
sequence…"*) and that verification never happened.
**The REAL protocol**, read from the two drivers that actually run — libogc
(`libdb/uIP/bba.c`) and Swiss (`cube/patches/bba/bba.c`) — has TWO address
spaces, selected by bit 7 of the first byte, and **no length field at all**:
```
COMMAND space (6-bit reg) one 4-byte READWRITE transfer
write: [0x40|reg, 00, 00, data]
read : [reg, 00, 00, xx ] <- response is the 4th byte
REGISTER space (16-bit reg) 4-byte header, then a SEPARATE data phase
read : [0x80, reg>>8, reg&0xFF, 0x00] then N bytes read (Swiss uses DMA)
write: [0xC0, reg>>8, reg&0xFF, 0x00] then N bytes written
```
NCRA, IR, NWAYS, RWP, RRP, PAR… all live in the REGISTER space
(`bba_in8`/`bba_out8`/`bba_ins`/`bba_outs`). Only the device-ID query uses a
short header, which is the single case where the two formats coincide — which
is exactly why enumeration appeared to work and nothing else would have.
**The mismatch is destructive, not merely non-functional.** A `NWAYS` read
arrives as `80 00 31 00`; the current decoder reads that as *write 0x31 to
NCRA*, and bit 0 of 0x31 is RESET. Every register read Swiss performs would
reset the device.
**The trailing `0x00` byte is the device's prefetch window**, and that is the
root of the latency problem below: in the real protocol the address is complete
one full byte before the data phase, giving ~8 bit-times (≈296 ns at 27 MHz)
to fetch the response. The format above spends those bits on the length field,
so the address is not known until the final header bit and the device gets
**zero** slack — which is why the first on-console run returned the device ID
22 bit-times late.
**Data-phase length is whatever the GC clocks** (it streams until CS), so the
register path must stream like the SPRAM path already does — the 4-byte cap
imposed by the 2-bit length field has to go. `bba_ins` reads with **EXI DMA**;
DMA *reads* are fine (the GC's DMA-*write* engine is the buggy one).
### Device ID Query
On power-on the GC writes `0x0000` (2 bytes) then reads 4 bytes.
Must return: `0x04 0x02 0x02 0x00`.
Must return: `0x04 0x02 0x02 0x00`. **Hardware-confirmed 2026-10-03** on a
real console — this is the one path that works with the current (wrong)
header format, because `00 00` decodes to address 0 either way.
---