Document that the command decoder is one mechanism, and its real limit

Records why supporting both command-space forms is a single decoder rather
than two paths, and where that abstraction genuinely stops.

The unification: Swiss's "4-byte transfer" is not a second protocol.  From the
device's side it is the same 2-byte header followed by a data phase that
happens to be 2 bytes (00, val) instead of 1 (val).  Command space was the last
place still assuming a fixed transfer length, which register space already
avoids.  Both rules are length-agnostic -- last data byte wins, fill the first
two data slots -- so there is no driver detection, no mode flag, and with
comments stripped the words "Swiss" and "libogc" do not appear in the RTL.

The limit, which is real but inert: a 2-byte data phase means "pad + value"
(Swiss) or "two consecutive registers" (libogc2), and those cannot be told
apart on the wire.  libogc2 does do multi-byte command writes --
bba_cmd_outs(0x04, &priv->devid, 2) runs unconditionally in its init -- so last
byte wins is wrong for those and auto-increment is wrong for Swiss.  It does
not matter today because the only command registers implemented are 0x02 (IMR)
and 0x03 (IR), which both drivers access one byte at a time, while libogc2's
multi-byte accesses target 0x04/0x05 and 0x08/0x09, none of which are decoded.
If 0x04/0x05 are ever implemented the fix is per-register width knowledge, not
a driver mode -- noted so nobody resolves it by sniffing transfer length.

Also records that the challenge/response handshake must NOT be implemented:
libogc2 reads a 4-byte challenge from command reg 0x08 and writes the response
to 0x09, but only when the DEVICE raises status bit 0x10 in command reg 0x03.
It is device-initiated, we never set that bit, so the path cannot be entered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 11:04:02 +02:00
parent b3685237b2
commit 88d05988b5
+42
View File
@@ -1110,6 +1110,48 @@ a fast path independent of this FSM.
Tests: `bba_register_file` **T10** (libogc form) and **T12** (Swiss form, Tests: `bba_register_file` **T10** (libogc form) and **T12** (Swiss form,
asserting bytes 2 AND 3), both mutation-tested — re-adding the auto-increment asserting bytes 2 AND 3), both mutation-tested — re-adding the auto-increment
fails T12, and pushing the response once fails T12's byte 3. fails T12, and pushing the response once fails T12's byte 3.
**This is ONE decoder, not two.** There is no driver detection, no mode flag
and no conditional on which driver is talking — grep the RTL with comments
stripped and the words "Swiss" and "libogc" do not appear. The unification is
that **Swiss's "4-byte transfer" is not a second protocol**: from the device's
side it is the same 2-byte header followed by a data phase that happens to be
2 bytes (`00, val`) rather than 1 (`val`). Command space was simply the last
place that still assumed a fixed transfer length — the same mistake register
space already avoids ("no length field, the data phase runs until CS"). Both
rules are therefore length-agnostic: last data byte wins, fill the first two
data slots.
⚠️ **The residual ambiguity, which is real but currently inert.** A 2-byte
data phase has two meanings that cannot be told apart on the wire:
```
[0x42, 0x00, 0x00, val] Swiss -> cmd reg 2 = val (pad + value)
[0x42, 0x00, b0, b1 ] libogc2 -> cmd reg 2 = b0, 3 = b1 (two registers)
```
libogc2 really does multi-byte command writes — `bba_cmd_outs(0x04,
&priv->devid, 2)` runs unconditionally in its init. "Last byte wins" is wrong
for those, and auto-increment is wrong for Swiss. It does not matter today
because:
- the only command registers implemented are **0x02** (IMR) and **0x03** (IR),
and BOTH drivers access those a single byte at a time; and
- libogc2's multi-byte command accesses target `0x04`/`0x05` and
`0x08`/`0x09`, none of which are decoded — they fall through the `Switch`
and are ignored.
So the decoder is correct for every register it implements. **If `0x04`/`0x05`
are ever implemented, the fix is per-register width knowledge (single-byte
registers take the last byte; multi-byte registers auto-increment), NOT a
driver mode.** Do not "resolve" the ambiguity by sniffing the transfer length.
⚠️ **Do NOT implement the challenge/response handshake.** libogc2 reads a
4-byte challenge from command reg `0x08`, computes `bba_calc_response()` and
writes it to `0x09` — but only when the DEVICE raises status bit `0x10` in
command reg `0x03`. It is device-initiated, we never set that bit, so the path
never runs. Likewise `bba_cmd_out8(0x05, priv->acstart)` and the `0x0B` status
read only appear inside those handlers. Emulating any of it would be work for
a path that cannot be entered.
NCRA, IR, NWAYS, RWP, RRP, PAR… all live in the REGISTER space NCRA, IR, NWAYS, RWP, RRP, PAR… all live in the REGISTER space
(`bba_in8`/`bba_out8`/`bba_ins`/`bba_outs`). Only the device-ID query uses a (`bba_in8`/`bba_out8`/`bba_ins`/`bba_outs`). Only the device-ID query uses a
short header — the single case where the two formats coincide. short header — the single case where the two formats coincide.