Fix the RX ring descriptor to the format libogc actually parses
The assembler wrote 00 00 len[11:8] len[7:0] with bytes 0 and 1 zero. The real format, read out of libogc's own BBA driver, is a PACKED 32-BIT LITTLE-ENDIAN word: next_packet_ptr in bits 0-11, packet_len in bits 12-23, status in bits 24-31. Under that driver our descriptor for a 342-byte frame decoded as a 12-byte frame with a next-pointer of 0 into the reserved page, so Swiss and anything else built on libogc would have initialised perfectly and then misparsed every received frame. Found by disassembling /opt/devkitpro/libogc/lib/cube/libbba.a (gcif.o, bba_interrupt) rather than by guessing: lwbrx r9,0,r9 # 4 bytes as a little-endian word rlwinm r4,r9,20,20,31 # packet_len = (word >> 12) & 0xFFF addi r4,r4,-4 # payload = packet_len - 4 cmpwi r4,1540 srwi r9,r9,24 # status = word >> 24 and. r9,r9,r18 # r18 = 0x..84 -> errors are 0x80 | 0x04 and the low 12 bits are written straight back to RRP (0x0018/0x0019), which is why next_packet_ptr has to be there at all -- it is the driver's read pointer. The length semantics and addressing were already correct (descriptor at page<<8, body at +4, length including the descriptor); only the packing was wrong and next_packet_ptr was never written. RXFrameAssembler now computes the next page BEFORE writing the descriptor (CALC_NEXT, formerly ADVANCE_RWP, moved ahead of the backfill) and writes bytes 0-3 as WRITE_D0..WRITE_D3. WRITE_D3 fixes a latent bug this exposed: WRITE_PLACEHOLDER issues four writes but the first lands twice, so desc_base+3 was NEVER written. That was harmless while the length write covered byte 3. As the status byte it would have been whatever the previous frame left in that cell, and a stale 0x80 or 0x04 makes the driver discard the frame. Readers updated to decode identically: netboot (which also now takes RRP from the descriptor's own next_packet_ptr, as libogc does, instead of recomputing the page span) and t_dma, including its page map. netboot's +0/+1 offset probe is removed -- the underrun that shifted descriptors is fixed and confirmed on console, and under a packed format a +1 read is not a shifted descriptor but garbage. The assembler's testbench now decodes with libogc_decode(), reproducing the instruction sequence above, so the tests check the DRIVER'S algorithm rather than our own packing. Every module testbench and bba_top at EXI_HALF=1/2/8 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+20
-10
@@ -394,7 +394,7 @@ static int t_rx(char *m, size_t n)
|
||||
static int t_dma(char *m, size_t n)
|
||||
{
|
||||
u8 buf[64], rp[2], wp[2];
|
||||
u32 dlen, page, addr;
|
||||
u32 dlen, page, addr, desc, nextp;
|
||||
if (bba_read(REG_RRP_LO, rp, 2) < 0) { snprintf(m, n, "RRP read failed"); return 1; }
|
||||
if (bba_read(REG_RWP_LO, wp, 2) < 0) { snprintf(m, n, "RWP read failed"); return 1; }
|
||||
page = ((u32)rp[1] << 8) | rp[0];
|
||||
@@ -417,11 +417,18 @@ static int t_dma(char *m, size_t n)
|
||||
snprintf(m, n, "stream read at 0x%04X failed", (unsigned)addr);
|
||||
return 1;
|
||||
}
|
||||
/* 4-byte ring descriptor; its length field INCLUDES the descriptor. */
|
||||
dlen = ((u32)buf[2] << 8) | buf[3];
|
||||
if (dlen > 4 && dlen <= 1518) {
|
||||
snprintf(m, n, "p%u len 0x%04X = %u bytes, etype %02X%02X",
|
||||
(unsigned)page, (unsigned)dlen, (unsigned)(dlen - 4),
|
||||
/* The 4-byte ring descriptor is a PACKED 32-BIT LITTLE-ENDIAN WORD, the
|
||||
* format libogc's own driver reads:
|
||||
* bits 0-11 next_packet_ptr (page), 12-23 packet_len (INCLUDING these
|
||||
* 4 bytes), 24-31 status (0x80 and 0x04 are errors).
|
||||
* Decoded here exactly as bba_interrupt does. */
|
||||
desc = (u32)buf[0] | ((u32)buf[1] << 8) |
|
||||
((u32)buf[2] << 16) | ((u32)buf[3] << 24);
|
||||
dlen = (desc >> 12) & 0xFFF;
|
||||
nextp = desc & 0xFFF;
|
||||
if (((desc >> 24) & 0x84) == 0 && dlen > 4 && dlen <= 1518 + 4) {
|
||||
snprintf(m, n, "p%u len %u B, next p%u, etype %02X%02X",
|
||||
(unsigned)page, (unsigned)(dlen - 4), (unsigned)nextp,
|
||||
buf[16], buf[17]);
|
||||
return 0;
|
||||
}
|
||||
@@ -442,10 +449,13 @@ static int t_dma(char *m, size_t n)
|
||||
map[pg - 1] = '?';
|
||||
continue;
|
||||
}
|
||||
dl = ((u32)d[2] << 8) | d[3];
|
||||
if (d[0] == 0 && d[1] == 0 && dl == 0) map[pg - 1] = '-';
|
||||
else if (dl > 4 && dl <= 1518) map[pg - 1] = 'o';
|
||||
else map[pg - 1] = 'x';
|
||||
u32 w = (u32)d[0] | ((u32)d[1] << 8) |
|
||||
((u32)d[2] << 16) | ((u32)d[3] << 24);
|
||||
dl = (w >> 12) & 0xFFF;
|
||||
if (w == 0) map[pg - 1] = '-';
|
||||
else if (((w >> 24) & 0x84) == 0 &&
|
||||
dl > 4 && dl <= 1518 + 4) map[pg - 1] = 'o';
|
||||
else map[pg - 1] = 'x';
|
||||
}
|
||||
map[15] = 0;
|
||||
snprintf(m, n, "p%u len 0x%04X raw %02X %02X %02X %02X RWP=%u map %s",
|
||||
|
||||
Reference in New Issue
Block a user