Files
retroDE_ps2/docs/ch269_closeout.md
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

191 lines
9.2 KiB
Markdown

# Ch269 closeout — HARD STOP: the BEQ treadmill is an artifact of our Ch215 shim
**Status:** Closed. Codex's hypothesis confirmed in one run.
**Verdict:** `v0_set_by_ch215_restore`.
> Every steady-state BEQ retire at PC=0xBFC52350 saw `$v0=1` set
> by `CH215_WAIT` — count=7 of 7. The treadmill BEQ is an
> artifact of our Ch215 jmp_buf restore shim, NOT a hidden BIOS
> load. **The post-Ch215 thunk-chain search Ch264..Ch268 is
> closed as a shim artifact.**
## The data, end to end
```
[ch269] V0_LINEAGE counters:
total $v0 changes since reset = 535323
$v0 changes in passes >= 1 = 462644
latch armed = 1
BEQ@0xBFC52350 retire_count = 9 (cap=16)
[ch269] LATCH_AT_BEQ:
[0] pass=0 $v0_at_BEQ=0x00000000 last_writer: cyc=293833 state_d1=EXECUTE pc=0xbfc4db80 v0=0x00000000 source=normal_retire
[1] pass=0 $v0_at_BEQ=0x00000001 last_writer: cyc=10194393 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[2] pass=1 $v0_at_BEQ=0x00000001 last_writer: cyc=20095043 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[3] pass=2 $v0_at_BEQ=0x00000001 last_writer: cyc=29995693 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[4] pass=3 $v0_at_BEQ=0x00000001 last_writer: cyc=39896343 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[5] pass=4 $v0_at_BEQ=0x00000001 last_writer: cyc=49796993 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[6] pass=5 $v0_at_BEQ=0x00000001 last_writer: cyc=59697643 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[7] pass=6 $v0_at_BEQ=0x00000001 last_writer: cyc=69598293 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[8] pass=7 $v0_at_BEQ=0x00000001 last_writer: cyc=79498943 state_d1=CH215_WAIT pc=0x8003eec4 v0=0x00000001 source=CH215_RESTORE
[ch269] SUMMARY (steady-state, pass>=1):
BEQ retires with $v0=1 = 7
... last writer from CH215 = 7
... last writer from normal = 0
```
Pass=0 retire [0] caught the **real BIOS setjmp() return**:
`$v0=0` from `pc=0xBFC4DB80` (EXECUTE state, normal retire).
That's the FIRST setjmp return — the path where the BEQ
takes. Then pass=0 retire [1] and all subsequent passes show
`$v0=1` from CH215_WAIT — our shim's longjmp simulation, every
10.0 M cycles like clockwork.
The cyc=10194393 → 20095043 → 29995693 → ... cadence is the
Ch215 restore firing once per Ch217 pass. The producer is
literally `regfile[2] <= 32'd1;` at
[ee_core_stub.sv:1280](rtl/ee/ee_core_stub.sv#L1280).
## What this closes
Chapters **Ch264..Ch268** were autopsying the longjmp-return
chain (callee → helper → dispatcher → kernel global) looking
for the "real polled gate." The premise was that BIOS was
gated on something the chain returned, and finding that
something would let us perturb it to break the treadmill.
That premise is now disproven:
- The BEQ at 0xBFC52350 is the post-setjmp/longjmp split.
- The reason it falls through every pass is OUR shim sets
`$v0=1`.
- The chain that runs after the BEQ does **internal
bookkeeping** (clearing 0xA000A8C8, doing INTC W1Cs) — its
output is incidental, never consumed as a gate value.
- The treadmill loops not because BIOS is waiting for a gate
to change, but because **our shim re-installs the same
longjmp context on every SYSCALL #8**.
The Ch264..Ch268 autopsies were genuinely informative (we
learned the chain's structure: three thunk-layers leading to
a leaf "clear and return"; we mapped 0xA000A8C8 as the cleared
buffer; we found I_STAT/I_MASK clears post-chain). But the
**search target was misplaced**: there is no hidden BIOS gate
in this chain because the chain itself is a no-op as far as
BIOS escape is concerned.
## What this leaves open
The **real** question, restated in light of Ch269:
> What would convince BIOS not to call SYSCALL #8 again?
The longjmp shim fires because SYSCALL #8 is invoked. If BIOS
stopped invoking it, the treadmill would break. Whatever
state SYSCALL #8 dispatches on (an exception table, a kernel
flag, an exception cause register) is what should change
between passes — and isn't, in our model.
This is **outside the scope of the BIOS-instruction-flow
autopsies**. It's a question about:
- The exception entry path that lands at SYSCALL #8
- The kernel handler that decides to re-issue SYSCALL #8 or
not
- The IOP/SBUS state that primes that handler
Codex's framing for what to do next:
1. **Stop the BIOS thunk recursion.** Done — Ch269 hard-stops
it.
2. **Treat Ch215 restore as an EXPERIMENT, not foundation.**
Future conclusions after Ch215 should be labeled "under
jmp_buf fallback semantics."
3. **Prefer subsystem modeling over hardcoded BIOS pokes.**
Ch261..Ch263 (IOP responder + INTC pulse + RAM mutation)
were the right pivot direction. Continue that line —
model a recurring IOP/SBUS responder with explicit state.
4. **Shorten chapter loops.** Ch269 itself is the model: one
question, one hard stop, one run.
## What Ch269 v2 fixed about Ch269 v1
Ch269 v1 used a 256-entry fill-from-boot array. The first
~256 `$v0` writes happen in pre-treadmill init (cycles
~6580+), so the array was full by the time the first Ch215
commit landed at cycle 10,194,393. Result: v1 reported
`v0_unchanged_in_steady_state` — a false negative caused by
instrumentation overflow, not by the underlying question.
Ch269 v2 uses a **live latch + print-at-trigger**: one
register holding the last-known `$v0` writer, refreshed every
cycle it changes, snapshotted at each PC=0xBFC52350 retire.
No depth, no overflow, no rerun. Plus pre-print
"V0_LINEAGE counters" (total changes / pass>=1 changes /
latch_armed / retire count) so a misarmed observer surfaces
immediately instead of after a 5-minute sim.
The lesson is saved as
[feedback_observer_design_for_lineage.md](file:///home/ubuntu/.claude/projects/-home-ubuntu-FPGA-Projects-retroDE-ps2/memory/feedback_observer_design_for_lineage.md):
**for "last X before event Y" questions, use a live latch +
print-at-trigger, not a fixed-depth fill-from-boot array.**
## Codex Ch269 acceptance — line-by-line
| Codex requirement | Status | Where |
|--------------------------------------------------------------------------------|--------|-------|
| Add $v0 write/commit observer around each pass | ✅ | live latch updates every cycle $v0 changes |
| Capture last $v0 writer before PC=0xBFC52350 | ✅ | latch snapshot at each BEQ retire |
| Classify as ch215_restore / normal retire / etc. | ✅ | state-lag by 1 cycle attributes the write to the FSM state that drove it |
| Print $v0 at: ch215 commit, first retire at 0xBFC52350, branch decision | ✅ | per-pass last_writer row shows cyc/state/pc/v0 at the writing instant; BEQ retire row shows $v0_at_BEQ |
| Expected verdict v0_set_by_ch215_restore | ✅ | confirmed: 7 of 7 steady-state retires |
| Hard stop on thunk-chain | ✅ | verdict explicitly states "post-Ch215 thunk-chain search Ch264..Ch268 is closed as a shim artifact" |
| Routine regression unaffected | ✅ | 157 / 157 with target off-by-default |
| One question, one run | ✅ | one build, one sim run, one verdict |
## Files changed
- `sim/tb/integration/tb_ee_core_bios_smoke.sv` — added
`\`ifdef CH269_V0_LINEAGE` block (v2: live latch + trigger
print, NOT v1's fill array). Two call sites
(`ch269_print_observer()`) in halt + timeout exits.
- `sim/Makefile` — new `tb_ee_core_bios_long_v0_lineage`
target.
## Regression
Full regression: 157 / 157 with `CH269_V0_LINEAGE` off by
default.
## Recommendation for Codex's next call
Per Codex's broader steering:
> Next substantive work should be either:
> - model a minimal recurring IOP/SBUS responder with
> explicit state, OR
> - step back to hardware-facing deliverables where progress
> is more directly testable.
**My read on Ch270 direction:** the Ch261..Ch263 work
already established that we can compose IOP-side state into
the EE map (the IOP responder + bridge + EE-visible
mutation chain). What's missing is the *recurring* part —
state that advances between Ch217 passes. A first try:
ramp the IOP responder's behavior so that each invocation
posts a slightly different value into a kernel-readable
location, and observe whether BIOS's SYSCALL #8 dispatch
behavior changes when that value progresses past some
threshold. That's harder to scope cleanly than Ch269 (it's
not a single-question chapter), but it's the path to a
genuine BIOS-state advance.
Alternatively the hardware-facing path: pivot to bringing
up something testable on real silicon (e.g., the OSD,
input → behavior, or VRAM read-back integrity on the
DE25-Nano) and treat the BIOS bringup as on-hold until the
IOP-side modeling matures. The user can pick which suits
their immediate priorities better.
**Standing by — and not recursing further down the
post-Ch215 BIOS thunk chain.**