Files
retroDE_ps2/docs/ch261_closeout.md
thejayman77 ec82764bef Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
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>
2026-06-29 20:10:50 -04:00

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.