diff --git a/accuracy/load_readfudge_ldabsorb.md b/accuracy/load_readfudge_ldabsorb.md index 8b80adc0..fbc760e4 100644 --- a/accuracy/load_readfudge_ldabsorb.md +++ b/accuracy/load_readfudge_ldabsorb.md @@ -14,10 +14,27 @@ unchanged + matching); ruler #1 [0x80001C5C→0x80001CA4] 54 → **56** == Beetl intro FMV (no regression). The `(ReadFudge>>4)&2` fudge, the region(RAM +3)+completion(+2) LDAbsorb give-back, and scratchpad +0 are all modeled per below. -FOLLOW-UP (not in this commit; ACCURACY axis-2): GTE-read (MFC2/CFC2) + MFC0 LDAbsorb +FOLLOW-UP (CONFIRMED divergences — ACCURACY axis-2): GTE-read (MFC2/CFC2) + MFC0 LDAbsorb give-back arming, and the muldiv-stall give-back consumption / off-by-one (Beetle -cpu.cpp:1332-1338 / 1723-1736). These don't affect the rulers (no CPU-load give-back in the -div/mult/gte loops) but are needed for full faithfulness in mixed game/FMV code. +cpu.cpp:1332-1338 / 1723-1736). **NOW Δ-GATED** by new ruler #2 probe loops, measured at +STEADY STATE (Beetle must run long enough — its warm-up window reports the no-give-back +value, which is why the handoff mis-recorded gte_rtps as "+15 EXACT"; the true steady +Beetle value is +11). Steady-state native(no give-back) vs Beetle(give-back): + +| loop | native | Beetle | gap | divergence source | +|---------------|--------|--------|-----|------------------------------------------| +| gte_rtps | 18 | **14** | +4 | MFC2 give-back (LDAbsorb=gte_ts_done-ts) | +| gte_nclip | 11 | **7** | +4 | MFC2 give-back | +| gte_read_use | 19 | **14** | +5 | MFC2 give-back (then dependent use) | +| ld_div | 45 | **49** | −4 | muldiv stall must CONSUME the lw give-back| + +Implementation: MFC2/CFC2 — after §1+DO_LDS, stall to gte_ts_done AND set +ld_absorb = (gte_ts_done - now), ld_which_t = rt (replace psx_gte_stall on READS only; +MTC2/CTC2 stay stall-only). MFC0 — arm ld_which_t=rt, ld_absorb=0. muldiv stall (MFLO/MFHI) +— while stalling to muldiv_ts_done, decrement read_absorb[read_absorb_which] per cycle, and +the muldiv_ts_done-1 off-by-one decrement (Beetle cpu.cpp:1723-1736). Re-validate the FULL +12-loop suite at steady state (the 8 passing components must stay matched). +All-CPU-load + alu + div + mult components MATCH at steady state — COMMIT 1 is solid. --- ORIGINAL SPEC (model derivation) follows. diff --git a/tools/cycle_testrom/gen_testrom.py b/tools/cycle_testrom/gen_testrom.py index 83f4c197..25fe793f 100644 --- a/tools/cycle_testrom/gen_testrom.py +++ b/tools/cycle_testrom/gen_testrom.py @@ -153,6 +153,11 @@ def build(): inner_loop("load", [lw("$t4", 0, "$t5")]) # load2: two lw (linearity check: should be ~2x the load delta) inner_loop("load2", [lw("$t4", 0, "$t5"), lw("$t7", 0, "$t5")]) + # load_use: lw then an addu that DEPs the loaded reg ($t4) — the dependent + # use ends the LDAbsorb give-back (GPR_DEP), so the following instrs do NOT + # absorb it. Isolates the give-back-TERMINATION path (load/load2 load into + # dead regs and never exercise it). Δ vs Beetle pins psx_cyc_deps. + inner_loop("load_use", [lw("$t4", 0, "$t5"), addu("$t6", "$t4", "$t3")]) # div: divu + mflo (isolates div latency + stall-on-read, worst case) inner_loop("div", [divu("$t2", "$t3"), mflo("$t4")]) # div_spaced: divu, 2 filler addu, then mflo (stall partly absorbed) @@ -168,6 +173,16 @@ def build(): inner_loop("gte_rtps", [cop2cmd(0x01), mfc2("$t4", 14)]) # gte_nclip: NCLIP command (0x06, cost 8) + mfc2 MAC0 (reg 24) read. inner_loop("gte_nclip", [cop2cmd(0x06), mfc2("$t4", 24)]) + # gte_read_use: RTPS + mfc2 result read + an addu that DEPs the mfc2 dest + # ($t4). PROBE for the GTE-read LDAbsorb give-back (Beetle MFC2 sets + # LDAbsorb=gte_ts_done-ts). If our stall-only model diverges from Beetle this + # loop reveals it; if native==Beetle there is no give-back divergence to fix. + inner_loop("gte_read_use", [cop2cmd(0x01), mfc2("$t4", 14), addu("$t6", "$t4", "$t3")]) + # ld_div: lw arms a load give-back, then divu+mflo. PROBE for the muldiv-stall + # give-back consumption (Beetle decrements ReadAbsorb while stalling MFLO to + # muldiv_ts_done). Divergence here would mean the muldiv stall must consume the + # pending load give-back; native==Beetle means no divergence to fix. + inner_loop("ld_div", [lw("$t4", 0, "$t5"), divu("$t2", "$t3"), mflo("$t7")]) # loop outer forever o = a.labels["outer"]