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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user