ec82764bef
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>
146 lines
5.3 KiB
Markdown
146 lines
5.3 KiB
Markdown
# Ch291 closeout — paired syscall 0x16 HLE; verdict flips back to opcode (SYNC)
|
|
|
|
**Status:** Closed. **Verdict from re-running qbert.elf:**
|
|
`elf_first_unsupported_opcode (pc=0x00112994 instr=0x0000000F)` —
|
|
opcode 0x00 SPECIAL + funct 0x0F = **MIPS SYNC** (memory ordering
|
|
barrier).
|
|
|
|
qbert advanced 27,950 → **27,954 retires (+4)** through the paired
|
|
enable call. The runner observer captured both halves of the
|
|
paired sequence and confirmed Codex's prediction was exact:
|
|
|
|
```
|
|
syscall_0x12 = seen=1 count=1 first_pc=0x00112a54 $a0=0x05 $a1=0x00112ab0 $a2=0x00000000 $a3=0x001328c0 → $v0=0
|
|
syscall_0x16 = seen=1 count=1 first_pc=0x00112a74 $a0=0x05 $a1=0x00112ab0 $a2=0x00000000 $a3=0x001328c0 → $v0=0
|
|
```
|
|
|
|
**Args literally identical between 0x12 and 0x16.** The
|
|
"Add*Handler + Enable*Handler" hypothesis is confirmed.
|
|
|
|
## What landed
|
|
|
|
### Dispatcher case — `rtl/ee/ee_core_stub.sv`
|
|
|
|
The 7th case in the Ch273 HLE switch:
|
|
|
|
```sv
|
|
32'h0000_0016: begin
|
|
regfile[2] <= 32'd0;
|
|
gpr128[2] <= 128'd0;
|
|
pc <= pc + 32'd4;
|
|
retire_pulse <= 1'b1;
|
|
state <= S_IFETCH_REQ;
|
|
end
|
|
```
|
|
|
|
Per Codex: do NOT call the handler, do NOT synthesize DMAC
|
|
completion or interrupt yet. Just accept the enable.
|
|
|
|
### TB extension — `tb_ee_core_syscall_hle.sv`
|
|
|
|
Same mechanical pattern as Ch285/Ch289/Ch290. The TB now covers
|
|
seven known syscall numbers (3C / 3D / 40 / 64 / 78 / 12 / 16) plus
|
|
the unknown-halt path.
|
|
|
|
### Runner observer — `tb_ee_core_elf_runner.sv`
|
|
|
|
Third observer in the library (after 0x78 and 0x12). The SUMMARY
|
|
block now has three named-syscall lines, each with first PC + args
|
|
+ return.
|
|
|
|
## The paired-call signal, confirmed
|
|
|
|
| Field | Syscall 0x12 (Ch290) | Syscall 0x16 (Ch291) |
|
|
|-------|---------------------|----------------------|
|
|
| PC | 0x00112A54 | 0x00112A74 |
|
|
| $a0 | 0x00000005 | 0x00000005 |
|
|
| $a1 | 0x00112AB0 | 0x00112AB0 |
|
|
| $a2 | 0x00000000 | 0x00000000 |
|
|
| $a3 | 0x001328C0 | 0x001328C0 |
|
|
| count | 1 | 1 |
|
|
| $v0 | 0 | 0 |
|
|
|
|
PCs are 0x20 = 32 bytes = 8 instructions apart. Between them
|
|
qbert likely just loaded the same args back into $a0..$a3 from a
|
|
saved-arg block or kept them in place. The shape is unambiguous:
|
|
**register handler with `Add*Handler` then activate with
|
|
`Enable*Handler`, both for handler slot 5 with fn pointer
|
|
0x00112AB0 and context pointer 0x001328C0.**
|
|
|
|
The runner observer's "paired count=1" output is the kind of
|
|
visibility that justified the Ch289-introduced pattern. Without
|
|
it, knowing whether qbert called 0x16 with the same args as 0x12
|
|
would require re-reading the trace file or hierarchically peeking
|
|
at registers from a custom debug TB.
|
|
|
|
## qbert progression
|
|
|
|
| Chapter | Blocker | retire_count |
|
|
|---|---|---|
|
|
| Post-Ch289 (syscall 0x78) | syscall 0x12 | 27,930 |
|
|
| Post-Ch290 (syscall 0x12) | syscall 0x16 | 27,950 |
|
|
| **Post-Ch291 (syscall 0x16)** | **SYNC (instr=0x0000000F) at 0x00112994** | **27,954** |
|
|
|
|
The +4 retires are: syscall 0x16 retire + jump back into a code
|
|
block ending with SYNC. PC jumped *backward* from 0x00112A74 (the
|
|
syscall) to 0x00112994 (the SYNC). This is the typical
|
|
post-registration pattern: return from the kernel-call wrapper,
|
|
issue a memory barrier to ensure the registration is visible
|
|
globally, then proceed.
|
|
|
|
## Ch292 framing — MIPS SYNC
|
|
|
|
Instr `0x0000000F` decodes as:
|
|
- opcode 0x00 (SPECIAL)
|
|
- funct 0x0F (= 15)
|
|
- rs/rt/rd/sa all 0
|
|
|
|
MIPS SYNC: architecturally, a memory-ordering barrier. In our
|
|
model, with no out-of-order memory access and no
|
|
multiprocessor coherence to worry about, SYNC is a semantic NOP.
|
|
|
|
Concrete Ch292 scope (mirrors Ch286's narrow EI accept):
|
|
1. `localparam FUNC_SYNC = 6'h0F;` — wait, this clashes with the
|
|
already-reserved `FUNC_*` namespace. May need
|
|
`FUNC_SYNC` to be added cleanly or use a different name.
|
|
2. `is_sync = is_special && (func == FUNC_SYNC)` decode.
|
|
3. Add `!is_sync` to the SPECIAL nop-class exclusion (the funct
|
|
"anything not in {0x00..., DSLL, ADD/ADDU/SUB/SUBU, ..., MFHI/MFLO,
|
|
MULT/MULTU/DIV/DIVU, SLL, SRL, SRA, SLLV, SRLV, SRAV}" branch).
|
|
4. Accept in default execute path: PC += 4, retire fires, no
|
|
GPR/HI/LO writeback (none of the writeback predicates match
|
|
`is_sync`).
|
|
5. Focused TB: execute SYNC, verify no trap + PC advances + no
|
|
register damage.
|
|
|
|
~5 RTL edits. Should be a one-shot chapter.
|
|
|
|
## Pattern review (21 chapters)
|
|
|
|
The runner observer library now tracks three syscalls:
|
|
|
|
| $v1 | Tracked | First args observed |
|
|
|-----|---------|----------------------|
|
|
| 0x78 | Ch289 | (0, 0x00130000, 0x20000000, 0x001328C0) |
|
|
| 0x12 | Ch290 | (5, 0x00112AB0, 0, 0x001328C0) |
|
|
| 0x16 | Ch291 | (5, 0x00112AB0, 0, 0x001328C0) — identical to 0x12 |
|
|
|
|
The shared `$a3 = 0x001328C0` across syscalls 0x78, 0x12, and
|
|
0x16 is a strong hint that this is a **global context pointer** —
|
|
likely qbert's main kernel-state struct or thread control block.
|
|
|
|
## Files changed
|
|
|
|
- `rtl/ee/ee_core_stub.sv` — one new HLE case.
|
|
- `sim/tb/integration/tb_ee_core_syscall_hle.sv` — extended with
|
|
syscall 0x16 case.
|
|
- `sim/tb/integration/tb_ee_core_elf_runner.sv` — syscall_0x16
|
|
observer + SUMMARY line.
|
|
|
|
No new TB, no new Makefile target; regression count unchanged at
|
|
**175/175**.
|
|
|
|
## Regression
|
|
|
|
**175/175 PASS** (unchanged from Ch290; no new TB).
|