Files
retroDE_ps2/docs/ch287_closeout.md
T
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.0 KiB

Ch287 closeout — EE DMAC global control stub; qbert advances by 5 to channel-4 base

Status: Closed. Verdict from re-running qbert.elf: elf_first_unmapped_mmio (ea=0x1000C000 pc=0x001123CC). qbert advanced 27,907 → 27,912 retires (+5) — a small but meaningful step: the D_STAT poll completed (read returned 0 → "no pending DMAC interrupts" → poll exits) and qbert moved on to the next DMAC-touch in its init sweep, the per-channel base of DMAC channel 4 (toIPU).

What landed

Per Codex's narrow framing ("not a silent region-wide accept; implement at least D_CTRL and D_STAT"), Ch287 delivers the EE DMAC global control/status surface as a dedicated stub:

New module — rtl/dmac/ee_dmac_ctrl_stub.sv

Hosts six registers in the 0x1000_E000-0x1000_E0FF window:

Offset Reg Semantics
0x00 D_CTRL Latch (write last, read back). Reset = 0.
0x10 D_STAT Low half (CIS) is W1C on writes (a 1 clears that bit); high half (CIM) is unconditional write. Reset = 0 (no pending interrupts).
0x20 D_PCR Latch.
0x30 D_SQWC Latch.
0x40 D_RBSR Latch.
0x50 D_RBOR Latch.
others Reads return 0; writes traced + dropped.

Standard reg_wr_en / reg_offset / reg_wr_data / reg_rd_en / reg_rd_data / reg_rd_valid + trace_pkg::* port interface (mirrors dmac_reg_stub and intc_stub).

Memory-map integration — rtl/memory/ee_memory_map_stub.sv

  • New REGION_EE_DMAC_CTRL = 64'd13 localparam.
  • New EE_DMAC_CTRL_BASE = 29'h1000_E000 localparam.
  • New ee_rd_is_dmac_ctrl / ee_wr_is_dmac_ctrl predicates (phys[28:12] == EE_DMAC_CTRL_BASE[28:12]).
  • Internal instantiation of ee_dmac_ctrl_stub inside ee_memory_map_stub so the 88 existing TBs don't need new port routing. Precedent: the useg_shadow_mem backing also lives inside the map.
  • Response mux arm for ee_rd_was_dmac_ctrl.
  • Read+write trace branches emit EV_READ/EV_WRITE with arg3=REGION_EE_DMAC_CTRL (instead of EV_UNMAPPED).

This last point matters — the first qbert rerun after wiring the stub still reported elf_first_unmapped_mmio because the trace branches weren't updated to recognize the new region. The runner watches for the EV_UNMAPPED event; until the trace arm is added, even a fully-routed region still surfaces as "unmapped" to the verdict. Easy mistake to make twice; the trace-emission update is mandatory for every new region.

TB — tb_ee_dmac_ctrl_stub.sv

Direct DUT instantiation (no memory map intermediate; matches the isolated-stub TB pattern used by tb_ee_biu_mmio / tb_intc_stub). 18 named assertions covering:

  1. Reset-init: all six named offsets read 0.
  2. D_CTRL latch round-trip.
  3. D_STAT W1C semantics: hierarchically poke d_stat to known values, then issue W1C writes and verify the low half clears selectively while the high half (CIM) is unconditionally written.
  4. D_PCR / D_SQWC / D_RBSR / D_RBOR latch round-trips.
  5. Distinct-register independence (D_CTRL untouched by D_PCR writes).
  6. Unknown offset (0x80): reads return 0; writes don't damage anything; the next valid read still works.

Result: errors=0 PASS (18/18 sub-checks).

The W1C assertion is the key correctness check — if a future ELF needs the bit-set side (via a real DMAC channel completion), the W1C semantics here must be preserved. The negative-half test (CIM = high 16 bits, unconditional write) ensures we don't accidentally W1C the mask.

Makefile + regression

  • tb_ee_dmac_ctrl_stub target.
  • rtl/dmac/ee_dmac_ctrl_stub.sv added to RTL_SRCS.
  • TB added to both PHONY list and run: master list.
  • Regression: 173 → 174.

qbert progression

Chapter Blocker retire_count
Post-Ch286 (EI) unmapped 0x1000E010 (D_STAT) at 0x001123A8 27,907
Post-Ch287 (DMAC ctrl stub) unmapped 0x1000C000 (DMAC ch4 toIPU) at 0x001123CC 27,912

+5 retires. The D_STAT poll completed (one read returning 0 = "no pending interrupts" → branch exits) and qbert progressed immediately to the next DMAC register touch in its init sweep. The new blocker EA 0x1000C000 is the channel-4 (toIPU) base. The hot_pc 0x00112364 (count=29 / 256) suggests a loop iterating across all DMAC channels — clearing or zeroing their per-channel registers.

Ch288 framing

0x1000C000 = D4 toIPU per-channel base. The PS2 DMAC has 10 channels:

Ch Base Endpoint Modeled?
0 0x10008000 VIF0 No
1 0x10009000 VIF1 No
2 0x1000A000 GIF Yes (dmac_reg_stub CHANNEL=2)
3 0x1000B000 IPU_FROM No
4 0x1000C000 IPU_TO No ← Ch288 blocker
5 0x1000D000 SIF0 No
8 0x1000F000 SIF1 No
9 0x1000F400 SPR_FROM No
0x1000F800 SPR_TO No

qbert is touching the per-channel surfaces. The simplest path: extend dmac_reg_stub (which is already channel-agnostic, has a CHANNEL parameter) to instantiate stubs for the missing channels inside ee_memory_map_stubOR introduce a single "unused-channel" stub that just latches CHCR/MADR/QWC/TADR for the clear-loop pattern and doesn't try to do any real transfer.

The right call (for Codex to weigh):

  • (a) Multi-instance dmac_reg_stub with CHANNEL=0/1/3/4/5 in the map. Heavier; each instance includes the full transfer FSM, but for unused channels the FSM never starts.
  • (b) Lightweight ee_dmac_unused_channel_stub.sv per-channel with just the 4 latched registers (CHCR/MADR/QWC/TADR) and no FSM. Cheaper.
  • (c) Widen dmac_reg_stub to host all channels in one module (channel-multiplexed register file).

I lean (b) for the next chapter — qbert's init sweep wants the register surface, not the transfer machinery. A real-transfer channel like GIF (ch2) keeps its full dmac_reg_stub; everyone else gets a minimal latched-register stub.

Files changed

  • rtl/dmac/ee_dmac_ctrl_stub.sv — new module (~150 LOC).
  • rtl/memory/ee_memory_map_stub.sv — localparam, predicates, internal instantiation, response mux arm, trace branches.
  • sim/tb/dmac/tb_ee_dmac_ctrl_stub.sv — new focused TB.
  • sim/Makefile — RTL_SRCS entry, new tb target, both regression lists.

Pattern review (17 chapters)

Ch Blocker Edits Pattern
271..286 opcodes various opcode-era
286 EI (last opcode chapter) 3 NEW narrow exact-32 decode
287 DMAC ctrl MMIO ~30 NEW MMIO stub + map routing

First MMIO chapter. The chapter cost is higher than recent opcode chapters because adding a new memory region requires touching multiple coordinated points in the map (predicate, internal instance, mux arm, two trace branches, RTL_SRCS, PHONY+run lists). One missing piece (the trace branches) cost a diagnostic re-run.

Regression

174/174 PASS (was 173/173 in Ch286; +1 for the new tb_ee_dmac_ctrl_stub).