b3685237b2
Reading Swiss's own driver (cube/patches/bba/bba.c) shows the previous commit
fixed libogc and BROKE Swiss. The two disagree, and both are real:
Swiss bba_cmd_out8: exi_imm_read_write(0x40<<24 | reg<<24 | val, 4)
-> ONE 4-byte transfer [0x40|reg, 00, 00, val], and bba_cmd_in8
returns the LOW byte, so a read response must be in BYTE 3.
libogc2 bba_cmd_outsnosel: req = (reg<<8)|0x4000;
EXI_ImmEx(&req, 2, WRITE); EXI_ImmEx(val, len, WRITE)
-> THREE bytes, value in byte 2, and a read takes byte 2.
stock libogc: identical to libogc2 (read out of libbba.a).
They share bytes 0 and 1 and differ only in how many bytes follow, so one
decoder serves both: take the LAST data byte before CS rise as the write value
(Swiss sends [00, val], libogc sends [val] -- last is val either way), and push
a read response TWICE so bytes 2 and 3 both carry it. Swiss clocks 4 bytes and
pops both; libogc clocks 3 and leaves one, which the next transaction's header
pops drain -- a 2-byte header gives exactly one drain slot, so the leftover is
bounded at one and cannot accumulate.
Critically this means NO auto-increment of cmd_reg across the data phase: that
applied Swiss's padding 00 to the register and val to reg+1, which is precisely
how the libogc-only version broke Swiss.
T12 covers the Swiss form and asserts bytes 2 AND 3; T10 keeps the libogc form.
Both mutation-tested: re-adding the auto-increment fails T12 with 0x00 in both
slots, and pushing the response once fails T12's byte 3. gc_bba_test gains
"cmd-space Swiss" alongside "cmd-space write", with bba_cmd_write4/read4.
Also corrects two things in CLAUDE.md now checkable against three drivers:
- NO driver writes TWD -- not Swiss, not libogc, not libogc2. The "GC sets
TWD before streaming" note was self-confirming: t_tx writes TWD because we
wrote t_tx. The counted TXDATA length is the primary path, not a fallback.
- Swiss's exi_dma_write rounds the length UP to 32 bytes (OSRoundUp32B), so
TXDATA gets up to 31 trailing bytes past the frame and a counted length sends
that much extra. Real hardware must do the same since Swiss works on it;
trailing padding past the IP total length is ignored. A 1514-byte frame
rounds to 1536 though, over the 1518 maximum.
Both drivers trigger TX with NCRA ST1, which the existing any-ST test accepts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
105 lines
3.8 KiB
C
105 lines
3.8 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);
|
|
|
|
/* Swiss's command-space form: ONE 4-byte transfer, value/response in byte 3.
|
|
* Differs from bba_cmd_write/read above (libogc's 2-byte-header form); the
|
|
* device must serve both. */
|
|
int bba_cmd_write4(u8 creg, u8 val);
|
|
int bba_cmd_read4(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
|