ec82764bef
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression (272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps, and all dump-derived textures/traces) is excluded via .gitignore and stays local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
149 lines
7.0 KiB
Markdown
149 lines
7.0 KiB
Markdown
# Ch261 closeout — synthetic IOP responder skeleton + arbitration fix
|
|
|
|
**Status:** Closed. All Codex Ch261 acceptance criteria met. Regression
|
|
green at **157 / 157** (was 155 pre-Ch261, +1 for the collision TB,
|
|
+1 for the SIF-landing TB).
|
|
|
|
## Codex Ch261 acceptance — line-by-line
|
|
|
|
| Codex requirement | Status | Where |
|
|
|--------------------------------------------------------------|--------|------------------------------------------------|
|
|
| Focused collision check: CPU + DMA different addresses same cycle; DMA gets its word first, CPU later gets its own word | ✅ | `sim/tb/iop/tb_iop_memory_map_collision.sv` |
|
|
| Ch261 SIF landing TB passes with intended payload | ✅ | `sim/tb/integration/tb_iop_responder_ee_ram_landing.sv` |
|
|
| Full regression green | ✅ | `make run` → 157 PASS |
|
|
| Noisy per-beat diagnostics stripped after collision test exists | ✅ | `tb_iop_responder_ee_ram_landing.sv` (removed `[diag-beat]`, `[diag] iop_ram`, `[diag] DMAC regs`) |
|
|
|
|
## What landed
|
|
|
|
### RTL fix — `rtl/iop/iop_memory_map_stub.sv`
|
|
|
|
Replaced the silent-corruption arbitration with a **one-entry
|
|
deferred-CPU-RAM-read slot** exactly per Codex's spec:
|
|
|
|
- **DMA wins** the RAM port on any CPU+DMA collision (immediate).
|
|
- **CPU's read address latches** into `cpu_pend_addr` / `cpu_pend_valid`.
|
|
- On the next non-DMA cycle, the deferred read services from the
|
|
pending slot.
|
|
- `iop_rd_valid` stays LOW for the deferred CPU read until the
|
|
slot actually fires; then pulses normally — CPU sees its own
|
|
data on the right cycle, just one cycle later than it would
|
|
without contention.
|
|
- **Single-entry safe** because every existing CPU client of the
|
|
map (`iop_exec_stub`, `iop_core_stub`, `iop_fetch_stub`) is
|
|
request-then-wait-for-valid; no second outstanding read can be
|
|
in flight from the same client.
|
|
- **Sim-only overflow detector** (`$error` under
|
|
``ifndef SYNTHESIS``) catches any future client that breaks the
|
|
single-outstanding-read assumption.
|
|
- The pre-Ch261 comment that called the collision "documented, not
|
|
guarded" was removed.
|
|
|
|
### New focused TB — `sim/tb/iop/tb_iop_memory_map_collision.sv`
|
|
|
|
Directly drives the map's CPU- and DMA-read ports (no exec stub, no
|
|
DMAC core), so no future change to clients can mask this regression.
|
|
Three scenarios:
|
|
|
|
1. **Collision** — both reads on the same cycle, different addresses.
|
|
Asserts DMA gets `DMA_SENTINEL` next cycle, CPU gets `CPU_SENTINEL`
|
|
the cycle after, `iop_rd_valid` stays low during the deferral.
|
|
2. **Solo CPU read** — no DMA contention. CPU sentinel arrives next
|
|
cycle, no deferral.
|
|
3. **Solo DMA read** — no CPU contention. DMA sentinel arrives next
|
|
cycle, no spurious CPU activity.
|
|
|
|
### Ch261 SIF landing TB — `sim/tb/integration/tb_iop_responder_ee_ram_landing.sv`
|
|
|
|
Restored to its natural shape — full `WRITE INTC_MASK / MADR / BCR /
|
|
CHCR=start / WAIT_IRQ / W1C INTC_STAT / READ DONE_COUNT / HALT`
|
|
script. The arbitration fix makes the previously-fatal CPU/DMA
|
|
collision (exec stub fetching WAIT_IRQ at the same cycle as DMA's
|
|
beat 0) resolve correctly: DMA gets its real first-beat data, CPU's
|
|
fetch services one cycle later.
|
|
|
|
Result: landed qword = `0xCAFEF00D12345678C0FFEE00DEADBEEF` —
|
|
exactly the expected pattern, all four payload sentinels in place,
|
|
1 DMA_DONE event, 1 halt event, `eebr_last_seen` latched. Clean
|
|
PASS in ~1.5 ms sim time, well under the 5 ms watchdog.
|
|
|
|
Diagnostic prints (`[diag-beat]`, `[diag] iop_ram words`,
|
|
`[diag] DMAC regs`) all stripped per Codex's framing — the
|
|
collision TB is now the standing arbitration regression, this TB
|
|
is the standing IOP-responder-architecture regression.
|
|
|
|
### Makefile
|
|
|
|
Both new TBs added to:
|
|
- Per-target rules: `tb_iop_memory_map_collision`,
|
|
`tb_iop_responder_ee_ram_landing`.
|
|
- `.PHONY` list.
|
|
- `run:` master list.
|
|
|
|
(Matches `[feedback-makefile-two-lists]` — the run-list addition
|
|
that's easy to miss otherwise.)
|
|
|
|
## What we proved (Codex's Ch261 goal in one paragraph)
|
|
|
|
The existing IOP-side stubs (`iop_exec_stub` + `iop_memory_map_stub`
|
|
+ `iop_ram_stub` + `iop_dmac_reg_stub` + `intc_stub`) can be
|
|
composed with the SIF egress chain (`sif_dma_ee_ram_bridge_stub` +
|
|
`ee_ram_stub`) to produce ONE explicit EE-visible side effect — a
|
|
known 128-bit qword landing in EE RAM at a fixed offset —
|
|
autonomously from a single `go_i` pulse, with no BIOS image, no
|
|
long watchdog, deterministic ~1.5 ms runtime. The IOP responder
|
|
architecture is real and works.
|
|
|
|
## Unexpected bonus: a real bug, found and fixed
|
|
|
|
The Ch261 SIF-landing TB surfaced what the previous TBs in the IOP
|
|
chain (`tb_iop_self_driven`, `tb_iop_autonomous_two_xfers`) never
|
|
could because they only verified event counts, not DMA payload
|
|
data. The map's pre-Ch261 arbitration silently routed CPU's data
|
|
to the DMA path on collision — a latent silent-corruption bug.
|
|
Ch261 ends with that bug fixed, locked down by the focused
|
|
collision TB, and the comment in the map updated so the next
|
|
reader knows the path is now guarded.
|
|
|
|
## Files changed
|
|
|
|
- `rtl/iop/iop_memory_map_stub.sv` — deferred-CPU-slot arbitration.
|
|
- `sim/tb/iop/tb_iop_memory_map_collision.sv` — NEW focused TB.
|
|
- `sim/tb/integration/tb_iop_responder_ee_ram_landing.sv` — NEW
|
|
composition TB (restored to natural script + diagnostics
|
|
stripped).
|
|
- `sim/Makefile` — new per-target rules + `.PHONY` + `run:`
|
|
entries for both TBs.
|
|
- `docs/ch261_arbitration_bug_brief.md` — finding writeup (kept for
|
|
archaeology; Codex's pick from it became the implementation).
|
|
- `docs/ch261_closeout.md` — this file.
|
|
|
|
## What's next (for Codex's Ch262 call)
|
|
|
|
Per Codex's Ch261 framing, Ch262 should "wire that responder into
|
|
the BIOS-long setup and ask one question." Candidates that fall
|
|
out of the Ch261 result:
|
|
|
|
1. **Plug the synthetic IOP responder into the BIOS-long TB** as
|
|
a peer that writes a sentinel into a kernel-data region BIOS
|
|
polls (`0x80030000`+ per Ch218 v5 capture). Question: does
|
|
BIOS escape the Ch215 treadmill when the polled region
|
|
actually mutates between syscall #8 cycles?
|
|
2. **Asserted-source-from-the-responder INTC**: hook the
|
|
responder's DMA-done pulse into the EE-side INTC view (via
|
|
the Ch259 `iop_intc_inject_src_i` port, now actually driven by
|
|
a real responder rather than a constant plusarg). Question: is
|
|
the BIOS dispatch path satisfied by a real source pulse + a
|
|
responder that ack-clears, vs Ch259's static-bit experiment?
|
|
3. **Keep responder isolated, add the second side effect (SIF
|
|
mailbox flag)** — proves the responder can produce *two*
|
|
different EE-visible side effects on its own. Lighter than
|
|
wiring into BIOS-long.
|
|
|
|
I think (1) is the natural Ch262 — the BIOS-long arc is paused
|
|
waiting for exactly this kind of producer. (2) is the chapter
|
|
after, layering the INTC signaling on top of the RAM-write
|
|
producer. (3) is a smaller hold-pattern if Codex wants more
|
|
isolated proof before opening BIOS-long again.
|
|
|
|
Standing by for Codex's call.
|