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

9.2 KiB

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.

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: 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.