Files
rebbarb/gc_bba_test/source/bba.h
T
Roflin 0aa1fa884a Fix command-space writes, and replay libogc's real init sequence
Second protocol mismatch found the same way as the descriptor: by
disassembling libogc's own driver instead of trusting our notes.

COMMAND SPACE IS A 2-BYTE HEADER + DATA PHASE, not a single 4-byte transfer.
CLAUDE.md said [0x40|reg, 00, 00, data] and the decoder followed it, waiting
for a fourth byte and consuming the DATA byte as header byte 2 -- so every
command-space WRITE was silently dropped.  libogc's __bba_init writes command
register 2 (its interrupt mask) at both the start and the end of init, so none
of that took effect.  What it actually sends:

  EXI_Imm  (len=2, write) -> 42 00    the whole header, 0x4000 | (reg << 8)
  EXI_Sync
  EXI_ImmEx(len=1, write) -> 00       the data

Three bytes between CS fall and CS rise.  The device-ID read survived the wrong
decode only because SPIMode3Slave answers it on a fast path independent of this
FSM -- which is precisely why no test caught it: the only command-space traffic
under test bypassed the decoder being tested.

CHDR_2/CHDR_3 are replaced by CMD_WRITE, which takes the data byte straight
after header byte 1 and auto-increments the command register so a multi-byte
command write behaves like the register-space path.  cmd_wreg latches the
target because the write applies a cycle later, by which time cmd_reg has
moved on.  bba_register_file T10 writes command register 2 and reads it back,
and is mutation-tested: restoring the 4-byte decode fails it.

Also replaces the GUESSED init sequence in CLAUDE.md with the real transaction
list.  The old one was wrong in three ways that matter: libogc READS the MAC
out of PAR0-5 rather than writing one (so our reset value is what the console
uses), IMR is 0xDF not 0x86, and it writes RWP (0x0016/17) and RHBP (0x001A/1B)
which the register file does not accept.  The latter is harmless today -- RHBP
is 0x000F, matching our hardcoded 15-page ring -- but a driver configuring a
smaller ring would be ignored while the assembler kept writing outside it.

New gc_bba_test suite 7 "libogc init" replays the sequence on hardware:
cmd-space write (2-byte header, read back), MAC readable (one 6-byte PAR read,
Nintendo OUI), and init replay (all 30 writes in order, then NWAYS/HIPR/NCRA.SR
must still read correctly -- the old guessed header format turned a NWAYS read
into an NCRA reset, so a misdecoded write is a real risk).

bba.c gains bba_cmd_write/bba_cmd_read in libogc's shape.

Every module testbench and bba_top at EXI_HALF=1/2/8 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 10:42:17 +02:00

99 lines
3.5 KiB
C

/* bba.h — raw EXI transport to the re-bba-rb adapter in Serial Port 1.
*
* Deliberately NOT libogc's bba network driver: this talks to the register
* file directly so every check gets its own pass/fail and a failure points at
* a specific register rather than at "networking".
*/
#ifndef BBA_H
#define BBA_H
#include <gccore.h>
/* Serial Port 1 == EXI channel 0, device 2. */
#define BBA_EXI_CHAN EXI_CHANNEL_0
#define BBA_EXI_DEV EXI_DEVICE_2
/* Register map (see CLAUDE.md "Key Registers"). */
#define REG_NCRA 0x00
#define REG_IMR 0x08
#define REG_IR 0x09
#define REG_RWP_LO 0x16
#define REG_RWP_HI 0x17
#define REG_RRP_LO 0x18
#define REG_PAR0 0x20
#define REG_PAR4 0x24
#define REG_NWAYS 0x31
#define REG_TWD_LO 0x34
#define REG_TWD_HI 0x35
#define REG_HIPR 0x3A
#define REG_TXDATA 0x48
#define SPRAM_BASE 0x0100
/* NCRA bits */
#define NCRA_RESET 0x01
#define NCRA_ST0 0x02
#define NCRA_SR 0x08
/* IR bits */
#define IR_RI 0x02
#define IR_TI 0x04
/* Expected constants */
#define NWAYS_LINKUP 0x17
#define HIPR_PRESENT 0x01
/* EXI clock index, EXI_SPEED1MHZ(0) .. EXI_SPEED32MHZ(5). The nominal 32 MHz
* index is really ~27 MHz on hardware (see CLAUDE.md); the capture domain
* oversamples it 2x at 54 MHz. Settable so the suite can sweep it. */
extern u32 bba_speed;
/* Microseconds to hold the clock idle between the header write and the data
* phase, with CS still asserted. The register file decodes the header and
* PROACTIVELY pushes the response into its TX FIFO during this gap; until the
* first byte reaches the FIFO head the device drives MISO low. Measured on
* hardware 2026-10-03: at EXI index 5 the reply arrived 22 bit-times (~815 ns)
* late with no explicit gap, so a 4-byte read returned 00 00 00 10 -- the
* correct stream, just shifted out of the window. 0 = whatever gap libogc's
* inter-call overhead happens to give. */
extern u32 bba_gap_us;
/* All return 0 on success, negative on an EXI-layer failure. */
/* REGISTER-space read: 4-byte header [0x80, ah, al, 0x00] then `len` bytes.
* The real protocol has NO length field -- the data phase runs until CS, so
* `len` is unbounded. The trailing 0x00 header byte is the device's prefetch
* window. See docs/exi_protocol_real.md. */
int bba_read(u16 addr, void *buf, u32 len);
/* REGISTER-space write: 4-byte header [0xC0, ah, al, 0x00] then `len` bytes. */
int bba_write(u16 addr, const void *buf, u32 len);
/* EXI_GetID: write 0x0000 (two bytes) then read 4. Returns the device ID,
* which is a ONE-SHOT -- the first NCRA reset clears it until power cycle. */
int bba_devid(u8 id[4]);
/* Raw transaction framing, for probes that build their own byte sequence. */
int begin_raw(void);
void end_raw(void);
/* COMMAND-space access: 2-byte header + data phase (3 bytes total for a
* single-byte write), the shape libogc uses. bba_cmd_read returns the byte
* or <0 on error. */
int bba_cmd_write(u8 creg, u8 val);
int bba_cmd_read(u8 creg);
/* Convenience single-byte helpers. bba_r8 returns the byte, or <0 on error. */
int bba_r8(u16 addr);
int bba_w8(u16 addr, u8 val);
/* SPRAM / DMA-style read: any address >= 0x0100 streams until CS deasserts,
* so `len` may exceed 4 and the header's 2-bit length field is ignored. */
int bba_stream(u16 addr, void *buf, u32 len);
/* Stream `len` bytes into the TXDATA port. One byte per EXI transaction:
* the register file's effective address auto-increments per data byte, so a
* multi-byte write to 0x48 would spill into 0x49+ instead of the FIFO. */
int bba_tx_bytes(const u8 *buf, u32 len);
#endif