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

5.1 KiB

Ch292 closeout — narrow SYNC accept; next blocker is syscall 0x7A (cache-sync sibling?)

Status: Closed. Verdict from re-running qbert.elf: elf_first_unhandled_syscall (pc=0x00110994 $v1=0x7A (=122)) — qbert hit another syscall, this time with $a0 = 0x80000000 (kseg0 base / uncached-pointer base) and the same $a1 = 0x00112AB0 fn_ptr that's been threaded through syscalls 0x12/0x16. PS2 syscall 122 is plausibly SyncDCache / iSyncDCache — a semantic neighbor to the MIPS SYNC barrier we just accepted.

qbert advanced 27,954 → 27,968 retires (+14) past the SYNC and through ~13 instructions into a different code region (PC 0x00110994).

What landed

RTL — 3 surgical edits in ee_core_stub.sv

  1. localparam FUNC_SYNC = 6'h0F; next to other SPECIAL func localparams.
  2. is_sync = is_special && (func == FUNC_SYNC) decode flag.
  3. !is_sync added to the SPECIAL nop-class exclusion:
    (is_special && !is_syscall && !is_jr && !is_jalr
                 && !is_rtype_alu && !is_hilo_op
                 && !is_sync)  // Ch292
    

No execute-path arm needed. SYNC falls through every recognized predicate (is_lw/lq/sw/sq/sd/branch/etc.) and lands in the default else begin block. None of the writeback predicates match SYNC (is_rtype_alu / is_lui-family / is_jal / etc. all false), so:

  • No GPR write
  • No HI/LO write
  • No memory side effect
  • retire_advance() → PC += 4
  • Retire pulse fires

Net: side-effect-free retire, exactly what Codex specified.

TB — tb_ee_core_sync.sv

Mirrors tb_ee_core_ei from Ch286 with three correctness assertions:

  1. Retire happens — latch keyed on retired_pc == SYNC slot captures seen_sync_retire = 1.
  2. No GPR / HI / LO mutation at retire — $v0/$t0 sentinels + HI/LO snapshot all sampled at the SYNC retire cycle and verified unchanged.
  3. Decode is narrow — neighbor SPECIAL funct 0x0E (currently unallocated, encoded as instr 0x0000000E) MUST trap under strict mode. Asserts trap_pc / trap_instr at the 0x0E slot.

Plus the standard "post-SYNC LUI+ORI ran" check ($t1 = SENTINEL_C end-of-sim).

Result: retired=10 halt=0 trap=1 errors=0 PASS. Sentinels intact; HI/LO both 0; neighbor 0x0E trapped cleanly.

Makefile + regression

  • tb_ee_core_sync target.
  • Added to both PHONY list and run: master list.
  • Regression: 175 → 176.

qbert progression

Chapter Blocker retire_count
Post-Ch290 (syscall 0x12) syscall 0x16 (identical args) 27,950
Post-Ch291 (syscall 0x16) SYNC (0x0000000F) at 0x00112994 27,954
Post-Ch292 (SYNC) syscall $v1=0x7A at 0x00110994 27,968

PC jumped from 0x00112994 to 0x00110994 — qbert returned to an earlier code region (likely the "main init" function that called the handler-installation helper). The +14 retires include the SYNC retire + the function-return chain + setup for the next syscall.

Ch293 framing — syscall 0x7A

Args at halt:

  • $v1 = 0x7A (= 122)
  • $a0 = 0x80000000 — kseg0 base / uncached pointer. First syscall arg that's a kseg0 address (not heap-ish or fn-ptr).
  • $a1 = 0x00112AB0same fn_ptr seen in syscalls 0x12 and 0x16
  • $a2 = 0x00000000
  • $a3 = 0x001328C0 — same global context pointer

$a0 = 0x80000000 is suggestive: in PS2 SDK code, SyncDCache / iSyncDCache(start, end) takes a kseg0 address range. The "semantic neighbor" pattern is striking — Ch292 accepted MIPS SYNC (memory barrier), and Ch293's syscall might be the cache-management companion.

Per Codex's established precedent: first-pass return $v0 = 0 ("cache synced OK"), PC += 4, add a runner-side observer with args + count. If the next blocker is a poll for the cache-sync to complete, that's Ch294's problem.

Alternative names for PS2 syscall 122 in various sources:

  • SyncDCache(start, end) — common name
  • iSyncDCache — interruptible variant
  • Could also be a thread or signal-handling call

Empirically: $v0 = 0 and continue. The runner observer pattern makes "wrong return value" easy to detect on the next run.

Pattern review (22 chapters)

Ch Blocker Edits Pattern
286 EI 3 NEW narrow exact-32 decode
287 DMAC ctrl MMIO ~30 NEW MMIO stub
288 DMAC passive ~30 REUSE Ch287 internal-stub
289 syscall 0x78 ~20 REUSE Ch273 + NEW observer
290 syscall 0x12 ~20 REUSE Ch289 pattern
291 syscall 0x16 ~20 REUSE Ch289 pattern (paired-call)
292 SYNC 3 REUSE Ch286 narrow-NOP-class

Two narrow NOP-class accepts (Ch286 EI + Ch292 SYNC) and four syscall HLE extensions (Ch285/289/290/291) since the verdict era flipped at Ch286. The dispatcher accumulates one case per chapter; the runner observer library accumulates one entry per chapter; the TB pattern is mechanical.

Files changed

  • rtl/ee/ee_core_stub.sv — 3 edits (localparam, decode flag, nop-class exclusion).
  • sim/tb/integration/tb_ee_core_sync.sv — new focused TB.
  • sim/Makefile — target + both regression lists.

Regression

176/176 PASS (was 175/175 in Ch291; +1 for the new tb_ee_core_sync).