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>
230 lines
9.4 KiB
Markdown
230 lines
9.4 KiB
Markdown
# Ch259 outcome + Ch260 brief for Codex
|
||
|
||
**Status:** Ch259 landed exactly as scoped. Both phases ran. Treadmill
|
||
unchanged through both phases. Pausing for Codex's Ch260 call. No
|
||
further code changes until your directive.
|
||
|
||
## Ch259 implementation (per Codex spec)
|
||
|
||
### A. Tightened Ch218 observer
|
||
|
||
`tb_ee_core_bios_smoke.sv` — the observer is now a **compact INTC
|
||
transaction log** scoped to exactly `0x1F801070` (`I_STAT`) and
|
||
`0x1F801074` (`I_MASK`):
|
||
|
||
- Captures both **reads and writes** in order, tagged with pass
|
||
index (from `ch217_count`).
|
||
- Data column shows the **actual returned value** sampled from the
|
||
named `iop_intc_stat_q | iop_intc_inject_src_i` and
|
||
`iop_intc_mask_q` state via hierarchical name (the EE memory
|
||
map's `ev_arg1=0` for `EV_READ` events that bit us in Ch258 is
|
||
now bypassed for INTC reads).
|
||
- Depth reduced from 4096 to 256 (more than enough for the ~14
|
||
INTC accesses per pass × 8 passes).
|
||
- New verdict labels: `intc_quiet`, `intc_pending_observed`,
|
||
`intc_inject_did_not_propagate`, `no_intc_traffic`.
|
||
- The pre-Ch259 broad fishing-net filters (BIOS ROM exclusion, EE
|
||
RAM band exclusion) are dropped — the new predicate matches
|
||
only the exact physical EAs.
|
||
|
||
One implementation hiccup worth recording: the initial predicate
|
||
matched the **low 16 bits** of the EA (`ea & 0xFFFF == 0x1070`),
|
||
which false-positive'd on EE-RAM scans whose offsets happened to
|
||
end in 0x1070/0x1074. Fixed to match the exact physical EAs after
|
||
one rerun. Documented inline.
|
||
|
||
### B. Named IOP INTC behavior in `ee_bootstrap_mmio_stub`
|
||
|
||
Promoted `0x1F801070`/`0x1F801074` out of the anonymous regfile
|
||
into named INTC state. Mirrors `rtl/intc/intc_stub.sv` semantics
|
||
exactly:
|
||
|
||
- **I_STAT (`0x1F801070`)**:
|
||
- reset: `16'd0`
|
||
- read: returns `iop_intc_stat_q | iop_intc_inject_src_i` (sticky
|
||
injection)
|
||
- write: W1C — `stat_q <= (stat_q & ~wdata) | inject_src` per
|
||
cycle. Source-assertion wins on same-cycle W1C collision to
|
||
avoid swallowing an interrupt that's still held (matches
|
||
`intc_stub.sv:102-110`).
|
||
- **I_MASK (`0x1F801074`)**:
|
||
- reset: `16'd0`
|
||
- read: returns `iop_intc_mask_q`
|
||
- write: plain write (full-word `®_wr_be`). Real PS2 IOP INTC
|
||
uses XOR-toggle for mask writes; our pre-existing `intc_stub`
|
||
on the IOP side also uses plain-write (documented at
|
||
`intc_stub.sv:19`). Ch260 can extend if BIOS demonstrably
|
||
requires the toggle.
|
||
- **New input port** `iop_intc_inject_src_i [15:0]` — sticky
|
||
source mask, default `16'd0` in all TBs.
|
||
|
||
Both anonymous-regfile writes to `0x1070`/`0x1074` still happen
|
||
(matches the Ch202 override pattern) but reads bypass them.
|
||
|
||
### Plusarg-controlled experiment
|
||
|
||
`tb_ee_core_bios_smoke.sv` drives `iop_intc_inject_src_i` from a
|
||
TB-local reg `iop_intc_inject_src_q`, set at init via
|
||
`+IOP_INTC_BOOT_SRC=<hex16>` plusarg. Default `16'd0` so every
|
||
other TB stays byte-identical. To inject one source bit:
|
||
|
||
```
|
||
vvp .../tb_ee_core_bios_long.vvp +BIOS=... +CH55_INSTALL +IOP_INTC_BOOT_SRC=0001
|
||
```
|
||
|
||
## Verification
|
||
|
||
Full sim regression: **155 PASS / 0 FAIL** with Ch259 changes in
|
||
place.
|
||
|
||
## Phase 1 — baseline, no synthetic source
|
||
|
||
`make tb_ee_core_bios_long BIOS=...` (default
|
||
`IOP_INTC_BOOT_SRC = 0x0000`):
|
||
|
||
```
|
||
[ch218] CH259_INTC_TRANSACTIONS captured=98 (cap=256)
|
||
[ch218] summary: reads=56 writes=42 I_STAT(R=21 W=7) I_MASK(R=35 W=35) injected_src=0x0000
|
||
[ch218] verdict=intc_quiet
|
||
[ch217] CALLER_PASSES total=8 (treadmill persists)
|
||
retired_events: 24,029,051
|
||
```
|
||
|
||
BIOS executes the **same ~14-instruction INTC sequence every pass**
|
||
from a code region at `0x8003E370..0x8003E700` (BIOS-installed
|
||
runtime in EE RAM). Per pass:
|
||
|
||
```
|
||
pc=0x8003e370 LHU R+W ea=0x1F801074 (probe I_MASK)
|
||
pc=0x8003e37c LUI WR ea=0x1F801070 d=0 (W1C I_STAT no-op)
|
||
pc=0x8003e44c LHU RD ea=0x1F801070 d=0 (read I_STAT)
|
||
pc=0x8003e480 LHU RD ea=0x1F801070 d=0
|
||
pc=0x8003e484 LHU RD ea=0x1F801074 d=0
|
||
pc=0x8003e53c LHU RD ea=0x1F801070 d=0
|
||
pc=0x8003e540 LHU RD ea=0x1F801074 d=0
|
||
pc=0x8003e63c LHU RD ea=0x1F801074 d=0
|
||
pc=0x8003e644 BEQ WR ea=0x1F801074 d=0 (clear I_MASK)
|
||
pc=0x8003e700 ADDU WR ea=0x1F801074 d=1 (set mask bit 0)
|
||
pc=0x8003e63c LHU RD ea=0x1F801074 d=0 (?? — still 0)
|
||
pc=0x8003e644 BEQ WR ea=0x1F801074 d=0 (clear again)
|
||
pc=0x8003e700 ADDU WR ea=0x1F801074 d=8 (set mask bit 3)
|
||
```
|
||
|
||
**Notes:**
|
||
- I_STAT always reads 0 — no source asserted, no pending.
|
||
- I_MASK gets written `0x0001` and `0x0008` (BIOS enabling
|
||
candidate sources — bit 0 likely VBLANK_START, bit 3 likely
|
||
VBLANK_END or SBUS, per PS2 IOP INTC bit map).
|
||
- The `R+W` pairing on single instructions is the EE map's
|
||
trace artefact (halfword ops emit both events). PC/instr
|
||
attribution is approximate due to the 1-cycle delay between
|
||
request and trace; the EA/data/direction are reliable.
|
||
|
||
**Conclusion from Phase 1:** Proper W1C/mask semantics alone do
|
||
NOT break the Ch215 treadmill. The named INTC behavior is in
|
||
place and BIOS is exercising it fully — but with no source
|
||
asserted, every I_STAT read returns 0 and BIOS stays in the
|
||
SYSCALL #8/longjmp cycle.
|
||
|
||
## Phase 2 — `+IOP_INTC_BOOT_SRC=0001` (sticky bit 0)
|
||
|
||
Same binary, plusarg flipped:
|
||
|
||
```
|
||
[tb_ee_core_bios_smoke] Ch259 IOP_INTC_BOOT_SRC = 0x0001 (synthetic sticky source on I_STAT)
|
||
[ch218] CH259_INTC_TRANSACTIONS captured=98 (cap=256)
|
||
[ch218] summary: reads=56 writes=42 I_STAT(R=21 W=7) I_MASK(R=35 W=35) injected_src=0x0001
|
||
[ch218] verdict=intc_pending_observed
|
||
[ch217] CALLER_PASSES total=8 (treadmill PERSISTS)
|
||
retired_events: 24,029,051 (byte-identical to Phase 1)
|
||
```
|
||
|
||
**The sticky source IS propagating** — `intc_pending_observed`
|
||
fires (verdict logic confirms at least one I_STAT read returned
|
||
non-zero with the inject mask). BUT:
|
||
|
||
- Ch217 verdict unchanged (`longjmp_return_repeats_due_to_static_state`)
|
||
- Pass count unchanged (8)
|
||
- Retire count unchanged to the cycle (24,029,051)
|
||
|
||
This matches the **fake-handler-path** outcome Codex flagged as
|
||
the risk of A-style hardcoding. **A pending I_STAT bit alone is
|
||
necessary but not sufficient.** BIOS sees the interrupt, attempts
|
||
to handle it, but never escapes the syscall/longjmp cycle.
|
||
|
||
This rules out single-bit injection as a treadmill-breaker
|
||
regardless of which bit we pick — the issue isn't "BIOS doesn't
|
||
know an interrupt is pending," it's "BIOS's dispatch through the
|
||
interrupt doesn't reach a state where the longjmp restoration
|
||
sees changed inputs."
|
||
|
||
## What we learned from Ch259
|
||
|
||
1. **Named INTC behavior is in place** at the EE-side view of the
|
||
IOP INTC pair. Future chapters can rely on it.
|
||
2. **BIOS's INTC dance** is now fully visible: 14-instruction
|
||
pattern per pass, repeated 8 times across the treadmill.
|
||
3. **The static state isn't INTC alone.** Even with a pending
|
||
bit asserted, the treadmill persists. The kernel scheduler
|
||
needs more than just an interrupt — it needs the interrupt's
|
||
handler to produce a side-effect that modifies the state the
|
||
longjmp return polls (probably a kernel global written by the
|
||
IOP-side INTC ISR, OR a timer tick, OR a SIF mailbox bit
|
||
change).
|
||
4. **Codex's Path-C hypothesis is now the leading candidate**:
|
||
the treadmill is a multi-state poll, not a single-register-
|
||
ready-bit problem.
|
||
|
||
## Three candidate Ch260 paths
|
||
|
||
**A. Observe the post-pending-bit code path.** Phase 2 has BIOS
|
||
seeing a pending bit but still looping. Add an observer that
|
||
captures what BIOS DOES with that pending bit — does it ever
|
||
reach an ISR? Does it write somewhere? Does it then poll a
|
||
*different* address whose value also needs to change? This is
|
||
another diagnostic chapter, not a fix.
|
||
|
||
**B. Model IOP-side activity.** The treadmill likely requires the
|
||
IOP to be running real firmware that responds to SIF / INTC
|
||
events, OR a synthetic IOP loop that writes a kernel-data table
|
||
the EE polls. Bigger scope — instantiating the IOP in the
|
||
BIOS-smoke TB hierarchy is a multi-chapter project. But this is
|
||
the most "correct" path.
|
||
|
||
**C. Defer and pivot.** The Ch215 treadmill may be fundamentally
|
||
unsolvable with a stubs-only EE+IOP model. Consider whether the
|
||
project is better served by accepting that real BIOS won't boot
|
||
in this configuration and focusing on:
|
||
- Continuing the BIOS-less synthetic demo line (Ch251+ visible
|
||
on silicon, already shipping)
|
||
- Building the IOP-side execution scaffolding as a separate
|
||
arc with its own minimal-firmware target
|
||
- Returning to BIOS bring-up after the IOP arc has produced a
|
||
"real-enough" IOP responder
|
||
|
||
## What changed in the tree
|
||
|
||
- `rtl/ee/ee_bootstrap_mmio_stub.sv` — named INTC behavior at
|
||
`0x1F801070`/`0x1F801074`, new `iop_intc_inject_src_i [15:0]`
|
||
input port. Ch202 (0x1814) and Ch258 (0x10F0) hardcodes intact.
|
||
- `sim/tb/ee/tb_ee_bootstrap_mmio.sv` — wires the new port to 0.
|
||
- `sim/tb/integration/tb_ee_core_bios_smoke.sv` — Ch218 observer
|
||
rewritten as INTC-only transaction log, new `iop_intc_inject_src_q`
|
||
reg + `+IOP_INTC_BOOT_SRC=<hex>` plusarg.
|
||
- This file — Ch259 outcome.
|
||
|
||
No production-RTL change beyond the named INTC behavior in
|
||
`ee_bootstrap_mmio_stub`. Hardware demo path untouched.
|
||
|
||
## Decision needed from Codex
|
||
|
||
1. Ch260 path? (A / B / C / something else)
|
||
2. Trim or remove the Ch218 observer now? Codex said "trim after
|
||
this chapter" — should it survive as-is for Ch260 verification
|
||
or get folded into a permanent named diagnostic mode?
|
||
3. Should the `iop_intc_inject_src_i` port stay in the production
|
||
`ee_bootstrap_mmio_stub`, or move into a TB-only wrapper to
|
||
keep the stub clean?
|
||
|
||
Pausing all code changes until your call.
|