gc_bba_test: add header->data idle gap; device replies 22 bit-times late
First on-console run: the Identity suite failed with the device ID reading 00 00 00 10 instead of 04 02 02 00. Bit analysis shows that is an EXACT match for the correct stream delayed by 22 bits -- the device answers correctly, just too late for a 32-clock window to catch more than its first set bit. Not corruption, and not a broken link: response latency. At EXI index 5 (~27 MHz) 22 bit-times is ~815 ns, which is about what the CDC chain costs (rx AsyncFIFO capture->exi, register-file decode, tx AsyncFIFO exi->capture, SPI engine) at a 24 MHz exi clock. The design expects the GC to hold the clock idle between the header write and the data read so the register file can prefetch the response; libogc's inter-call overhead alone is not enough at full speed. Adds bba_gap_us (default 16 us, busy-waited with CS asserted since we hold the EXI lock) and a Gap sweep test that reports the minimum clean gap at the current speed -- which measures the latency rather than guessing at it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -156,6 +156,29 @@ static int t_speed_sweep(char *m, size_t n)
|
||||
return best >= EXI_SPEED32MHZ ? 0 : 1;
|
||||
}
|
||||
|
||||
static int t_gap_sweep(char *m, size_t n)
|
||||
{
|
||||
static const u32 gaps[] = {0, 1, 2, 4, 8, 16, 32};
|
||||
u32 saved = bba_gap_us;
|
||||
int i, g, best = -1;
|
||||
for (g = 0; g < (int)(sizeof(gaps)/sizeof(gaps[0])); g++) {
|
||||
int bad = 0;
|
||||
bba_gap_us = gaps[g];
|
||||
for (i = 0; i < 30; i++)
|
||||
if (bba_r8(REG_NWAYS) != NWAYS_LINKUP) bad++;
|
||||
if (!bad) { best = (int)gaps[g]; break; }
|
||||
}
|
||||
bba_gap_us = saved;
|
||||
if (best < 0) {
|
||||
snprintf(m, n, "no gap up to 32us gave clean reads at speed idx %u",
|
||||
(unsigned)bba_speed);
|
||||
return 1;
|
||||
}
|
||||
snprintf(m, n, "min clean header->data gap = %d us at speed idx %u",
|
||||
best, (unsigned)bba_speed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ───────────────────────── suite 5: ethernet ───────────────────────── */
|
||||
|
||||
static u8 g_mac[6] = {0x00,0x09,0xBF,0x00,0x00,0x01};
|
||||
@@ -293,6 +316,7 @@ static test_t s_reset[] = {
|
||||
static test_t s_exi[] = {
|
||||
{"200x read", "no bit errors over 200 register reads", t_repeat, T_PENDING, {0}},
|
||||
{"Speed sweep", "which EXI clock indices are reliable (0..5)", t_speed_sweep, T_PENDING, {0}},
|
||||
{"Gap sweep", "min header->data idle gap the device needs", t_gap_sweep, T_PENDING, {0}},
|
||||
};
|
||||
static test_t s_eth[] = {
|
||||
{"MAC readback", "PAR0-5 hold a Nintendo-OUI MAC", t_mac_read, T_PENDING, {0}},
|
||||
@@ -308,7 +332,7 @@ suite_t g_suites[] = {
|
||||
{"Identity", "device enumerates and reports itself", s_identity, 3},
|
||||
{"Registers", "register file is readable and writable", s_regs, 4},
|
||||
{"Reset/control", "NCRA reset and control bits behave", s_reset, 2},
|
||||
{"EXI integrity", "the EXI link is error-free, and how fast", s_exi, 2},
|
||||
{"EXI integrity", "the EXI link is error-free, and how fast", s_exi, 3},
|
||||
{"Ethernet", "frames get to and from the W5100", s_eth, 3},
|
||||
{"SPRAM/DMA", "bulk reads stream correctly from the ring", s_spram, 2},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user