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>
6.4 KiB
Ch288 closeout — DMAC passive per-channel surface; MMIO clear, syscall 0x78 surfaces
Status: Closed. Verdict from re-running qbert.elf:
elf_first_unhandled_syscall (pc=0x00112AA4 $v1=0x78 (=120)).
qbert advanced 27,912 → 27,920 retires (+8) — past the
per-channel clear loop and on to another kernel syscall.
The standout signal: saw_unmapped_mmio = 0 for the first time
since Ch286. The Ch287 + Ch288 combination now covers every
EE DMAC MMIO surface qbert touches during its init sweep — the
verdict shape flipped back to "unhandled syscall," which means
qbert is back in normal control flow and the MMIO era closes (for
now).
What landed
Per Codex's "lightweight per-channel register surface, no transfer FSM" framing, Ch288 delivers:
New module — rtl/dmac/ee_dmac_passive_chan_stub.sv
A single channel-multiplexed register stub covering five DMAC channels (the unmodeled ones):
| Channel | Base | Endpoint | Internal idx |
|---|---|---|---|
| ch0 | 0x10008000 | VIF0 | 0 |
| ch1 | 0x10009000 | VIF1 | 1 |
| (ch2) | (0x1000A000) | (GIF) | (skip — dedicated dmac_reg_stub on ee_dmac_ch2_* ports) |
| ch3 | 0x1000B000 | IPU_FROM | 2 |
| ch4 | 0x1000C000 | IPU_TO | 3 ← qbert blocker |
| ch5 | 0x1000D000 | SIF0 | 4 |
Per channel: CHCR / MADR / QWC / TADR (4 latched 32-bit registers at offsets 0x00/0x10/0x20/0x30). Writes latch. Reads return last latched value. Reset = 0. No transfer FSM. No start-bit side effects. No D_STAT interaction.
The module decodes the channel index from chan_addr[15:12]:
- 0x8 → idx 0 (ch0)
- 0x9 → idx 1 (ch1)
- 0xB → idx 2 (ch3)
- 0xC → idx 3 (ch4)
- 0xD → idx 4 (ch5)
- 0xA (= ch2) → silently dropped (chan_valid=0): the real GIF stub lives elsewhere; this module must not shadow it.
Unknown register offsets within a valid channel: write dropped, read returns 0.
Memory-map integration — rtl/memory/ee_memory_map_stub.sv
Five mechanical edits (the now-standard new-region recipe):
REGION_EE_DMAC_PASSIVE = 64'd14localparam.ee_rd_is_dmac_passive/ee_wr_is_dmac_passivepredicates:The(phys[28:16] == 13'h1000) && ((phys[15:12] == 8) || (== 9) || (== B) || (== C) || (== D))!= 0xAexclusion keeps ch2 GIF on its dedicated port.- Internal instantiation of
ee_dmac_passive_chan_stub. - New
ee_rd_was_dmac_passivelatch + response-mux arm. - New trace branches (read AND write) emitting
REGION_EE_DMAC_PASSIVE. The Ch287 footgun avoided — trace branches added at the same time as the predicate, not as a follow-up.
TB — tb_ee_dmac_passive_chan_stub.sv
18 named assertions covering:
- ch4 reset reads zero for all four registers.
- ch4 round-trip writes/readbacks of CHCR/MADR/QWC/TADR with distinct values.
- Channel independence: write to ch5; verify ch4 values unchanged; verify ch5 readback.
- ch2 filter: write to chan_nibble=0xA returns 0 on read (this
stub does NOT shadow ch2 — that's
dmac_reg_stub's territory). - ch0 / ch1 / ch3 reset verifies multi-channel initialization.
- Unknown register offset on a valid channel: read returns 0, write doesn't damage the channel; the next valid read still works.
Result: errors=0 PASS (18/18 sub-checks).
Makefile + regression
tb_ee_dmac_passive_chan_stubtarget.rtl/dmac/ee_dmac_passive_chan_stub.svadded to RTL_SRCS.- TB added to both PHONY list and
run:master list. - Regression: 174 → 175.
qbert progression
| Chapter | Blocker | retire_count | unmapped_mmio? |
|---|---|---|---|
| Post-Ch286 (EI) | 0x1000E010 D_STAT (unmapped MMIO) | 27,907 | YES |
| Post-Ch287 (DMAC ctrl stub) | 0x1000C000 ch4 base (unmapped MMIO) | 27,912 | YES |
| Post-Ch288 (DMAC passive) | syscall $v1=0x78 at PC 0x00112AA4 | 27,920 | NO |
The MMIO era (Ch287..Ch288) ran for just two chapters and added ~+13 retires worth of init-sweep coverage. With the per-channel clear loop satisfied, qbert advanced to a SECOND kernel syscall beyond the Ch285 $v1=0x40 — namely $v1=0x78 (120). Argument snapshot at halt:
$a0 = 0x00000000(zero / null)$a1 = 0x00130000(heap-ish or code-ish)$a2 = 0x20000000(high bit set; kseg0+0 = "uncached pointer" base in PS2 convention)$a3 = 0x001328c0(code/data pointer-looking)
Per the Ch285 framing principle ("don't over-trust the SDK name;
model the observed behavior"), the right Ch289 move is probably
another narrow case: $v0 = 0; PC += 4 and see what happens. If
qbert misbranches, return the $a2 arg pattern instead. PS2
syscall 120 in the standard table is commonly cited as one of the
GS-control or threading-related calls; Codex can pick the right
stub-return semantics.
Ch289 framing
Two narrow options for Codex:
- (a) Add
$v1 == 0x78to the existing HLE dispatcher with$v0 = 0, PC += 4. Trivial; one switch case. - (b) Identify the exact PS2 kernel service for syscall 120 and pick a context-aware return value (e.g. echo $a2 if it's a "register and return previous" pattern).
I lean (a) for the first pass — matches the Ch285 precedent. If qbert misbranches downstream, revisit and try $a2 or $a1 as the return.
Files changed
rtl/dmac/ee_dmac_passive_chan_stub.sv— new module (~160 LOC).rtl/memory/ee_memory_map_stub.sv— predicate, internal instance, mux arm, trace branches.sim/tb/dmac/tb_ee_dmac_passive_chan_stub.sv— new focused TB.sim/Makefile— RTL_SRCS entry, new tb target, both regression lists.
Pattern review (18 chapters)
| Ch | Blocker | Edits | Pattern |
|---|---|---|---|
| 271..286 | opcodes | various | opcode-era |
| 287 | DMAC ctrl MMIO | ~30 | NEW MMIO stub + map routing |
| 288 | DMAC passive per-channel | ~30 | REUSE Ch287 internal-stub pattern |
The internal-stub pattern from Ch287 paid off in Ch288: the same predicate-instance-mux-trace mechanical sequence dropped a second MMIO region into place without disturbing the 88 TBs that use ee_memory_map_stub. The chapter cost stayed flat at ~30 edits across one new RTL file + one map extension + one TB.
The trace-branch addition was done correctly at the same time as the predicate (the Ch287 footgun avoided).
Regression
175/175 PASS (was 174/174 in Ch287; +1 for the new tb_ee_dmac_passive_chan_stub).