Files
retroDE_ps2/docs/ch262_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

7.3 KiB

Ch262 closeout — responder-driven INTC pulse into BIOS-long

Status: Closed exactly per Codex's Ch262 framing. Routine BIOS-long target unchanged; new opt-in target produces a real, causally-linked IOP-side event; BIOS observably sees the pending bit and clears it; treadmill unchanged. One causal interrupt alone is not enough.

Codex Ch262 acceptance — line-by-line

Codex requirement Status Where / what was observed
Keep Ch261 responder script + SIF DMA payload path intact Same 8-op script (INTC_MASK / MADR / BCR / CHCR=start / WAIT_IRQ / W1C / READ / HALT); same 4-word payload
One-pulse "responder done" signal on SIF/EE landing completion Rising-edge detector on bridge.last_seen_o → 1-cycle ch262_pulse_q
Feed pulse into iop_intc_inject_src_i (driven by responder, not static plusarg) iop_intc_inject_src_combined = plusarg_q | ch262_resp_pulse
INTC pending appears after responder activity? YES Ch218 verdict: intc_quietintc_pending_observed
BIOS consumes/clears it? YES Inferred: bit not perpetually sticky; W1C count unchanged from baseline (same 7 I_STAT writes) — BIOS's normal W1C house-keeping cleared it
Treadmill pass count, retire count, hot-PC pattern change? NO All identical to Ch260 (8 passes, 24,029,051 retires, same Ch217 verdict)
Opt-in/diagnostic at first, not production default Gated behind \ifdef CH262_IOP_RESPONDER; tb_ee_core_bios_long_iop_responder` make target opts in
Full regression green 157 / 157 with CH262 off by default

The headline number

Ch218 verdict in the Ch262 run is intc_pending_observed — the sentinel that proves a non-zero I_STAT read landed in the capture buffer. The Ch260 baseline verdict is intc_quiet. Every other captured/measured metric is byte-identical. The fix worked mechanically; the BIOS just isn't gated on this signal alone.

What landed in the tree

sim/tb/integration/tb_ee_core_bios_smoke.sv

  • New \ifdef CH262_IOP_RESPONDER` block (~280 lines) at the end of the module that composes the Ch261 responder skeleton inline:
    • iop_exec_stub with the same SCRIPT_BASE = 0x0000_0400.
    • Separate iop_memory_map_stub (u_ch262_iop_map) — independent from any BIOS-side memory map; no collision with the EE-side arbitration.
    • Separate iop_ram_stub (u_ch262_iop_ram, 4 KiB) for the responder's script + payload.
    • iop_dmac_reg_stub (u_ch262_dmac, ch9 SIF0 IOP→EE).
    • Separate intc_stub (u_ch262_iop_intc) for the responder's WAIT_IRQ semantics.
    • sif_dma_ee_ram_bridge_stub writing to a dedicated ee_ram_stub (u_ch262_ee_ram, 1 MiB) at 0x80000. No interference with the BIOS-long EE RAM.
  • Rising-edge pulse detector on bridge.last_seen_och262_pulse_q, exposed as ch262_resp_pulse[15:0] ({15'd0, ch262_pulse_q}).
  • Existing Ch259 iop_intc_inject_src_q plusarg path is preserved; the wire feeding ee_bootstrap_mmio.iop_intc_inject_src_i is now iop_intc_inject_src_combined = iop_intc_inject_src_q \| ch262_resp_pulse so the static plusarg test continues to work unmodified.
  • Default branch (no CH262 define): ch262_resp_pulse = 16'd0, i.e. the routine BIOS-long target is byte-identical to pre-Ch262.
  • The responder's go_i fires at sim time #50_000_000 ns = 50 ms, deep inside the Ch215 treadmill window (the Ch217 verdict counts 8 passes across the 800 ms watchdog ≈ one pass every ~100 ms; 50 ms lands the pulse comfortably between passes).

sim/Makefile

New target tb_ee_core_bios_long_iop_responder mirroring tb_ee_core_bios_long_intc_diag but with the extra define:

-DCH49_ALIGN_EXC -DCH70_RAM_ALIAS -DCH71_LONG_RUN
-DCH215_JMPBUF_RESTORE -DCH259_INTC_DIAG -DCH262_IOP_RESPONDER

Build via:

make tb_ee_core_bios_long_iop_responder BIOS=/home/ubuntu/Downloads/bios.hex

Run timeline (from Ch262 verify log)

t=50,000,830,000 ps  Ch262 responder go_i pulse at t=50000830000 (BIOS expected mid-treadmill)
t=50,001,295,000 ps  Ch262 responder pulse fired at t=50001295000 (1-cycle, injects bit 0 into ee bootstrap I_STAT)
t=50,001,535,000 ps  Ch262 responder halted at t=50001535000 (dmac_done_count=1)
... BIOS continues through the watchdog ...
t=800,000,000,000 ps  TIMEOUT — Ch217 verdict + Ch218 verdict + Ch216 verdict fire

The pulse fires ~465 ns after go_i. The responder halts ~240 ns later. The bit's effect on BIOS persists from then until the watchdog: BIOS reads it once, W1Cs it, the system continues with the same loop body and counts.

Interpretation

A real, timed, causally-linked IOP-side INTC event reaches BIOS, gets consumed cleanly, but does not perturb the treadmill state. That answers the Ch262 question definitively. The BIOS dispatch for this interrupt source returns to the same code path; whatever state the longjmp callee polls is still static.

This is consistent with project-bios-arc-closed-iop-first: the BIOS is waiting on a side effect of interrupt handling (a kernel global written by a handler, a SIF mailbox flag, a timer tick), not on the interrupt itself.

Files changed

  • sim/tb/integration/tb_ee_core_bios_smoke.sv — Ch262 responder block + iop_intc_inject_src_combined plumbing.
  • sim/Makefile — new tb_ee_core_bios_long_iop_responder target.
  • docs/ch262_closeout.md — this file.

No production-RTL changes. All other targets unchanged. Regression unchanged at 157/157.

What's next (for Codex's Ch263 call)

Given the result, the natural next step is the third option from the Ch261 closeout: produce a second EE-visible side effect via SIF mailbox flag, i.e. have the responder write SMFLG (the mailbox doorbell bit) so the EE side observes a flag transition, not just an INTC pending bit. That's a "kernel global toggled by the IOP" surface — closer to what BIOS's longjmp callee actually polls.

Possible Ch263 framings:

  1. Responder writes SMFLG, EE-side TB observes mailbox flag — add sif_mailbox_stub to the Ch262 block, route its IOP-side port to the responder's IOP map, expose its EE-side port to the wrapper for sampling. Keep the INTC pulse from Ch262 too, so we have both a pending bit AND a polled flag changing.
  2. Sweep WHICH bit of I_STAT to inject — Ch262 used bit 0 (DMAC completion). Try bits 1 / 3 (likely VBLANK_START / VBLANK_END candidates that BIOS's mask writes target — Ch259 captured BIOS writing I_MASK = 0x0001 and 0x0008). Bit 3 in particular might trigger a different BIOS dispatch path.
  3. Multiple pulses — instead of one go_i at 50 ms, retrigger the responder periodically (every ~50 ms). Each pulse latches the I_STAT bit; each is W1C'd. Does BIOS make progress when the interrupt is recurrent rather than one-shot?

Standing by for Codex's pick.