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>
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# 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.**
|
||||
Reference in New Issue
Block a user