Support BOTH command-space forms: Swiss's 4-byte and libogc's 2-byte
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>
This commit is contained in:
@@ -421,6 +421,30 @@ static int t_cmd_write(char *m, size_t n)
|
||||
return rb == 0x00 ? 0 : 1;
|
||||
}
|
||||
|
||||
static int t_cmd_write_swiss(char *m, size_t n)
|
||||
{
|
||||
int rc, rb;
|
||||
/* Swiss sends ONE 4-byte transfer with the value as byte 3 and reads the
|
||||
* response from byte 3; libogc (and libogc2) send a 2-byte header plus a
|
||||
* data phase, value in byte 2. Both are real drivers. The device takes
|
||||
* the LAST data byte as the value and pushes a read response twice, so
|
||||
* bytes 2 and 3 both carry it -- this test is the Swiss half. */
|
||||
if ((rc = bba_cmd_write4(0x02, 0x5A)) < 0) {
|
||||
snprintf(m, n, "Swiss cmd write failed: EXI error %d", rc); return 1;
|
||||
}
|
||||
if ((rb = bba_cmd_read4(0x02)) < 0) {
|
||||
snprintf(m, n, "Swiss cmd read failed: EXI error %d", rb); return 1;
|
||||
}
|
||||
if (rb != 0x5A) {
|
||||
snprintf(m, n, "Swiss 4-byte form: wrote 0x5A, byte 3 = 0x%02X", rb);
|
||||
bba_cmd_write4(0x02, 0x00);
|
||||
return 1;
|
||||
}
|
||||
bba_cmd_write4(0x02, 0x00);
|
||||
snprintf(m, n, "4-byte form ok (byte 3 = 0x%02X)", rb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int t_mac_is_readable(char *m, size_t n)
|
||||
{
|
||||
u8 mac[6];
|
||||
@@ -654,6 +678,7 @@ static test_t s_spram[] = {
|
||||
|
||||
static test_t s_init[] = {
|
||||
{"cmd-space write","2-byte header + data phase, as libogc sends it", t_cmd_write, T_PENDING, {0}},
|
||||
{"cmd-space Swiss","4-byte transfer, value in byte 3, as Swiss sends", t_cmd_write_swiss, T_PENDING, {0}},
|
||||
{"MAC readable", "libogc READS the MAC from PAR0-5; it must be sane", t_mac_is_readable, T_PENDING, {0}},
|
||||
{"init replay", "libogc's 30 init writes, in order, do no harm", t_init_sequence, T_PENDING, {0}},
|
||||
{"TX without TWD","libogc never writes TWD; length must be counted", t_tx_no_twd, T_PENDING, {0}},
|
||||
@@ -666,7 +691,7 @@ suite_t g_suites[] = {
|
||||
{"EXI integrity", "the EXI link is error-free, and how fast", s_exi, 6},
|
||||
{"Ethernet", "frames get to and from the W5100", s_eth, 3},
|
||||
{"SPRAM/DMA", "bulk reads stream correctly from the ring", s_spram, 2},
|
||||
{"libogc init", "the real driver's init sequence and shapes", s_init, 4},
|
||||
{"driver shapes", "Swiss + libogc init sequences and transfer shapes", s_init, 5},
|
||||
};
|
||||
const int g_nsuites = sizeof(g_suites) / sizeof(g_suites[0]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user