Remove the device-ID one-shot: it made Swiss see no adapter at all

id_phase was set at reset and cleared permanently by the GC's first NCRA reset,
after which a command-space read of register 0 returned 0x00000000.  Swiss
calls EXI_GetID from bba_exists() AND again from bba_requires_init(), long
after init, and matches the result against EXI_MX98730EC_ID -- so with the
one-shot spent no case matched and Swiss reported NO ADAPTER, not a broken one.
Running gc_bba_test was enough to spend it, so the adapter would "disappear"
after any test session.

The justification in the comment was wrong.  It claimed the fast path would
otherwise hijack address 0 so the GC could never read NCRA back -- but NCRA is
REGISTER space (0x80/0xC0 header) while the fast path requires byte 0 == 0x00,
i.e. COMMAND space.  They were never ambiguous, and no driver reads command
register 0 for anything else (libogc2 reads 0x01/0x03/0x0B/0x0F, Swiss 0x03).
Real hardware answers EXI_GetID whenever asked; that is how EXI enumerates
devices.

id_arm is now tied high, and the id_phase output, the FFSynchronizer that
crossed it into the pin domain, and the BBATop wiring are all gone.  The port
stays only so the PHASE_METER build can disarm the fast path, which would
otherwise send ID bytes instead of its constant 0x80.

bba_top T1b reads the device ID AFTER an NCRA reset, which T1 (before any
reset) structurally cannot catch.  Mutation-tested: disarming the fast path
returns 00 00 00 00 for both, exactly what Swiss saw.

gc_bba_test: the ID tests no longer skip.  A wrong ID is a FAILURE again, with
no iface_alive() discrimination and no ordering requirement between suites --
the ID is readable at any point in a run.  g_ncra_reset_done and iface_alive()
are removed outright rather than left as a dead flag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 14:33:12 +02:00
parent 991342c0b5
commit 90a583dd46
6 changed files with 104 additions and 99 deletions
+40 -21
View File
@@ -88,18 +88,9 @@ Reset/control, EXI integrity (speed sweep `ok=[1,1,1,1,1,1]`, per-speed dump
clean at all six indices, raw reg dump `0x17` in slot 0), Ethernet, and
SPRAM/DMA. The EXI front end runs at the real 27 MHz hardware rate.
(The ID tests report SKIP, not FAIL, once `id_phase` has been consumed —
reflash the FPGA to restore it. They decide that from the hardware, via
`iface_alive()`: HIPR and NWAYS are hardwired and independent of `id_phase`, so
if both read correctly the EXI path is fine and only a spent `id_phase` can
explain a wrong answer at address 0.
⚠️ The `g_ncra_reset_done` flag alone is NOT sufficient, and this bites every
time a build is delivered over the wire: netboot replaces the DOL and restarts
the app with the flag clear, while the FPGA keeps running with `id_phase`
already spent by the previous run. The app then queries address 0, gets NCRA
back — correct behaviour — and called it a FAILURE. Software state cannot stand
in for hardware state across a self-update.)
(The ID tests simply PASS or FAIL now, in any order, at any point in a run —
see "The device ID is answerable at any time" below. The old SKIP machinery,
`iface_alive()` and `g_ncra_reset_done` are all gone.)
**Timing closes on EVERY seed (re-measured 2026-10-10).** `python -m
exi_bba.synth --seeds 8`:
@@ -563,9 +554,11 @@ until byte 0 has been decoded — which is the latency in question.
constant and belongs to the physical interface, not the register file, and
`byte 0 == 0x00` identifies the query exactly (bit 7 clear = command space,
bit 6 clear = read, reg 0). `SPIMode3Slave` recognises it and answers
`04 02 02 00` from the EXI clock domain with **no CDC at all**. The only thing
it needs from `sync` is `id_phase`, a single bit that changes once, crossed
with an `FFSynchronizer`.
`04 02 02 00` from the EXI clock domain with **no CDC at all** — and since
2026-10-10 it needs nothing from `sync` either: `id_arm` is tied high (the
`id_phase` bit and its `FFSynchronizer` are gone). The only remaining use of
the port is the `PHASE_METER` build, which disarms the fast path so the
constant `0x80` is not replaced by ID bytes.
The register file's ID path was **removed**, not kept as a fallback: left in,
it pushes bytes a 2-byte header has too few pops to drain — exactly how an
@@ -745,12 +738,38 @@ not clear it (RST_N floats to its pull-up while the FPGA is unconfigured). A
12 V power cycle is required. Check ETH_3V3 and GC_ON (U12 gates the W5100 rail
off GC_ON) before suspecting gateware.
**`id_phase` is a ONE-SHOT.** The device ID is returned for reads of address 0
only until the first NCRA write with the reset bit, which clears `id_phase`
permanently (`bba_register_file.py:330`); after that address 0 reads back NCRA.
This is correct — it matches the GC's init order — but it means a re-run of any
probe that already issued an NCRA reset will read `00 00 00 00` for the device
ID and look broken. Reconfigure the FPGA (`iceprog -t` is enough) to restore it.
### ✅ The device ID is answerable AT ANY TIME (one-shot removed 2026-10-10)
`id_phase` **used to be a one-shot**: set at reset and cleared permanently by
the GC's first NCRA write with the reset bit, after which a command-space read
of register 0 returned `0x00000000`. That is now gone — `id_arm` is tied high
and the register file has no `id_phase` output.
⚠️ **The one-shot broke Swiss outright, which is how it was caught.** Swiss
calls `EXI_GetID` from `bba_exists()` *and* again from `bba_requires_init()`,
long after init has run:
```c
if ((location & LOC_SERIAL_PORT_1) && EXI_GetID(EXI_CHANNEL_0, EXI_DEVICE_2, &id)) {
switch (id) { case EXI_MX98730EC_ID: ...
```
With the one-shot spent, that read returns `0x00000000`, no case matches, and
Swiss reports **no adapter at all** — not a broken one, absent. Running
`gc_bba_test` first was enough to spend it, so the adapter would "disappear"
after any test session.
**The old justification was wrong.** The comment claimed the fast path would
otherwise "hijack address 0 forever so the GC could never read NCRA back".
NCRA is **register space** (`0x80`/`0xC0` header); the fast path requires
byte 0 == `0x00`, i.e. **command space**. They were never ambiguous, and no
driver reads command register 0 for anything else (libogc2 reads
`0x01`/`0x03`/`0x0B`/`0x0F`, Swiss reads `0x03`). Real hardware answers
`EXI_GetID` whenever asked — that is how EXI enumerates devices.
Test: `bba_top` **T1b** reads the device ID *after* an NCRA reset, which T1
(before any reset) structurally cannot catch. Mutation-tested: disarming the
fast path returns `00 00 00 00` for both, exactly what Swiss saw.
Two bring-up gotchas, both cost real debugging time:
- **The master must sample MISO during the CLK-LOW phase.** The FPGA drives MISO