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

141 lines
5.1 KiB
Markdown

# 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:
```sv
(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 = 0x00112AB0` — **same 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).