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>
7.0 KiB
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_validstays 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 (
$errorunderifndef 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:
- Collision — both reads on the same cycle, different addresses.
Asserts DMA gets
DMA_SENTINELnext cycle, CPU getsCPU_SENTINELthe cycle after,iop_rd_validstays low during the deferral. - Solo CPU read — no DMA contention. CPU sentinel arrives next cycle, no deferral.
- 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. .PHONYlist.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 singlego_ipulse, 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:
- 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? - 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_iport, 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? - 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.