You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 0bfa871
Browse filesBrowse the repository at this point in the historyBrowse files
docs(audits): record the RTL audit's v2.8.2 verdicts and the Solder plan
The RTL ledger's eleven v2.8.2 rows now carry verdicts with evidence. Five
are FIXED on the sibling, each by a co-simulation or module gate that failed
first and catches its mutant: R-3.5b (MMC3 acknowledge vs a same-edge counter
clock, cart-mmc3-gate), R-3.5d (SNROM's CHR-A16 PRG-RAM enable,
mapper1snrom067, 3,924 cycles diverged before), R-3.2b (the $2002 data-bus
latch, ppulatch072, 2 cycles before) and R-3.3c (triangle/noise reload drop,
aputrireload070/apunoisereload071, per-channel mutants). The pulse-1 sweep
row (R-3.3b, refuted earlier) gains its promised gate, apusweepneg069.
Three are NOT A DEFECT as written. R-3.5c: the RTL implements the "normal"
Sharp revision and the report names the wrong one. R-3.2a: unreachable,
because it needs $2002 reads on dot 0 and dot 1 of one frame and CPU
accesses are three dots apart. R-3.1a/b stay PARTIAL: no gate shows an
effect, so nothing changed, and the v2.9.0 re-audit revisits them.
R-3.5a is a new verdict, INVERTED, added to docs/audits/README.md: the two
implementations disagreed as the report says, but the documentation shows
the audited side (the RTL) was right. The oracle's MMC1 was the defect and
was fixed in dae2053. Recording it as FIXED or REFUTED would each have been
false: the finding was real, and it was about the other repository.
The sibling commit ids are in the rows; the PR column fills when the
sibling PR opens. Also: to-dos/plans/v2.8.2-solder-plan.md (codename
checked unused across CHANGELOG, VERSION-PLAN, release notes and plans),
the CHANGELOG [Unreleased] RTL bullets, and two docs/agents/mister-cosim.md
notes (an audit's disagreement does not say which side is wrong; a drifting
loop reaches a dot only if its period is coprime with the frame, which is
why ppulatch072 carries a NOP).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Copy file name to clipboardExpand all lines: docs/agents/mister-cosim.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,3 +40,5 @@
40
40
- **THE ELEVEN-VARIANT SWEEP CLOSED (v2.6.23): `chrram-live` GREEN, 32,861 -> 0 PIXELS.** The axis the sweep missed (recorded above) was what a `$2007` access during rendering COMPUTES FROM, not what it computes. On silicon the access pulses the rendering pipeline's existing load rather than performing its own increment. Three changes, each exposing the next: `v_pipeline` (increment the value *this dot's* pipeline load produced, 21,941 -> 2,646 diverging fetches), `v_acc_addr` (RESOLVE the access against that same value, -> 18, which stopped 33 nametable cells being written one coarse-X cell early), and `chr_wr_addr` sourced from it too (-> 0 pixels). `chr_wr` asserts 4,389 times before and after — same stimulus. The `chr_wr_addr` step is a MAINTAINER DECISION (ADR 0037 territory: the write address the oracle now follows is a case the wiki declares undefined), taken knowingly and kept in the RTL comment marked resolved-by-decision rather than deleted, so it governs the next case like it. `chrram-fetch` stays `run_xfail` at 18 of 185,725 — all garbage sprite-window nametable fetches (dots 257-320) whose data is discarded, which is why the picture is byte-perfect while the trace is not.
41
41
- **GOLDEN RECIPES: 97 OF 110 MANIFESTS, GENERATED NOT TRANSCRIBED, AND 472 ARTIFACTS BYTE-FOR-BYTE (v2.6.23).** `tb/gen_golden_recipes.py` derives each recipe row from fields the manifest already carries (`frames_req`, `ckpt_interval`, `obs_count` sizing `--irq-trace`, `press_start`), because 110 hand-transcribed caps is 110 silent ways to under-cover a golden. `VERIFY=1 tb/fetch-goldens.sh` regenerates into scratch and diffs against `goldens/` WITHOUT writing to it — **472 artifacts byte-for-byte** across goldens recorded as rustynes 2.5.1 through 2.6.22, fifteen releases. **Every apparent difference across four diff passes was a recipe defect, never oracle drift**: a `.irq.csv` diagnostic nothing reads; a guessed `--apu-trace` cap the exporter correctly REFUSED rather than silently under-covering; eleven blargg stems producing nothing because the exporter names artifacts after the ROM FILE's stem, not the target stem; and AccuracyCoin differing in 11.4% of its framebuffer with a MATCHING rom hash — actually a missing `--press-start`, caught by `accuracycoin_status` refusing to score a vacuous run ("every one of the 144 SCORED entries is NotRun"). The ROM is located by `rom_sha256` (content), never the manifest's unusable `rom` path (relative / `../../../`-relative / absolute-under-$HOME / pointing into deleted `/tmp/blargg-apu/` depending on the stem).
42
42
-**FITTER SEED 4 PINNED ON BEST-OVERALL, NOT STRICT BINDING-MARGIN (v2.6.23).** RTL changed, so by `RustyNES.qsf`'s own rule the prior sweep described a design that no longer exists. Re-swept, 5 seeds, all closing; hold binds at every seed (0.078-0.115ns vs setup's 0.247-0.421ns), so the established rule mechanically selects seed 1 (+0.115 hold). Maintainer decision pinned seed 4 instead (+0.112 hold, +0.421 setup) on BEST OVERALL — within 3ps of seed 1 on hold while beating it by 174ps on setup — with the reasoning AND the falsifying experiment (re-fit one seed repeatedly, measure run-to-run variance; not yet done) written into the `.qsf` alongside the pinned seed, not left implicit. Marked `<- pinned`, not `<- published`, since no bitstream had been built from it yet at write time. Seed 5 — the PREVIOUS release's outright winner — is joint-worst on this RTL: a sweep result belongs to one specific RTL, never carries forward.
43
+
-**AN AUDIT'S "THE RTL DISAGREES WITH THE ORACLE" DOES NOT SAY WHICH SIDE IS WRONG (v2.8.2).** RTL audit R-3.5a said `cart.sv` checked MMC1's reset bit before the consecutive-write filter "while the oracle filters first", and called the RTL the defect. nesdev MMC1 says a reset is never ignored and only a DATA write on the cycle after a write is, so the ORACLE was wrong (`m001_mmc1.rs`, fixed red-first in v2.8.2) and the RTL was left alone. The v2.7.0 pulse-1 sweep finding had the same shape. Before fixing either side of a disagreement, read the wiki for the rule itself; the audit ledger now has an INVERTED verdict for this case (`docs/audits/README.md`).
44
+
-**A DRIFTING LOOP REACHES A DOT ONLY IF ITS PERIOD IS COPRIME WITH THE FRAME (v2.8.2).** The first `ppulatch072` draft, an 11-cycle `$2002`/`$2000` loop, is 33 dots, and 11 divides the 89,342-dot NTSC frame (2·11·31·131), so the loop reaches only 3 of 33 landing residues and could miss the VBL set dot forever. A `NOP` makes it 13 cycles (39 dots, coprime), and 39 frames cover every dot. Coverage was then read from the oracle's `irq.csv` (reads at scanline 241 dots 0-4, dot 1 twice), not assumed. The reverse case is `apureload042`'s: a walk resolves a residue class, not a cycle, so a one-cycle target inside a long period needs a PLACED write instead.
Copy file name to clipboardExpand all lines: docs/audits/README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -83,5 +83,6 @@ ADR 0037 reference firewall.
83
83
| REFUTED | The code does not do what the finding says; evidence given. Closed. |
84
84
| NOT A DEFECT | Accurate description, but the behaviour is deliberate or correct. Closed. |
85
85
| REJECTED FIX | The defect is real but the recommended fix breaks a project rule; a different fix is recorded. |
86
+
| INVERTED | The two implementations disagree as the finding says, but the documentation shows the audited side is right and the other one wrong, so the fix lands on the other side (first used for R-3.5a, v2.8.2). |
86
87
| UNTRIAGED | Not yet read against the code. Nothing is planned on an untriaged finding. |
87
88
| FIXED | Closed by the PR in the ledger row, with the red-first test named. |
| R-3.1a | Spurious `last_cycle` at `tcyc == 0` for `AM_REL` (§3.1, Fix 11) | PARTIAL |`cpu6502.sv:1285-1286` matches the description, but the only consumer not gated by `ST_EXEC` is the `sh_rdy_low` clear (`:921`), which the SH instruction re-arms. No functional effect found. Fixed only if a gate can show one | v2.8.2 ||
26
-
| R-3.1b | Second NMI edge swallowed during NMI entry (§3.1) | PARTIAL |`cpu6502.sv:1860-1862` has no `!int_is_nmi` guard, as described; losing a second edge before the vector fetch may be hardware behaviour. Not a proven defect | v2.8.2 ||
| R-3.2b |`$2002` read latches register state, not the driven bus, into open bus (§3.2) |UNTRIAGED || v2.8.2 ||
25
+
| R-3.1a | Spurious `last_cycle` at `tcyc == 0` for `AM_REL` (§3.1, Fix 11) | PARTIAL |`cpu6502.sv:1285-1286` matches the description, but the only consumer not gated by `ST_EXEC` is the `sh_rdy_low` clear (`:921`), which the SH instruction re-arms. No functional effect found, and v2.8.2 found no gate that shows one; left unchanged. Revisited at the v2.9.0 re-audit| v2.8.2 ||
26
+
| R-3.1b | Second NMI edge swallowed during NMI entry (§3.1) | PARTIAL |`cpu6502.sv:1860-1862` has no `!int_is_nmi` guard, as described; losing a second edge before the vector fetch may be hardware behaviour. Not a proven defect, and v2.8.2 found no gate that shows an effect; left unchanged. Revisited at the v2.9.0 re-audit| v2.8.2 ||
27
+
| R-3.2a |`vblank_now` ignores `suppress_vbl` (§3.2, Fix 12) |NOT A DEFECT | True as written and unreachable: the failure needs a `$2002` read on dot 0 (setting `suppress_vbl`) AND another on dot 1 of the same frame, and CPU accesses are three dots apart. Left unchanged | —||
28
+
| R-3.2b |`$2002` read latches register state, not the driven bus, into open bus (§3.2) |FIXED | On the VBL set dot bit 7 comes from the combinational `vblank_now`; the latch was rebuilt from the registers and kept 0. Stimulus `ppulatch072` (a 13-cycle `$2002`/`$2000` loop that walks every landing dot): 2 of 1,429,465 cycles diverged before, 0 after (349 checkpoints). Fix `open_bus <= cpu_dout`. Sibling `b09ee32`| v2.8.2 ||
29
29
| R-3.3a |`audio_dc_block.sv` 32-bit overflow (§3.3, Fix 1) | REFUTED |`W = 32`, `FRAC = 15`, `mix` is 16-bit unsigned. Worst case `65535<<15 = 2^31-2^15` fits; for unipolar input the high-pass output stays within ±M·2^15 and the ±(32767<<15) clamp (`:120-123`) fires first | — ||
30
-
| R-3.3b | Pulse-1 sweep with negate not muted (§3.3, Fix 7) | REFUTED |`apu2a03.sv:230-231` gates overflow behind `!neg`, which is correct: `nesdev_wiki/APU_Sweep.xhtml` says a negative target clamps and negate is the documented way to disable the sweep. The RTL is right; see core ledger T-01 for the oracle side | — ||
| R-3.3b | Pulse-1 sweep with negate not muted (§3.3, Fix 7) | REFUTED |`apu2a03.sv:230-231` gates overflow behind `!neg`, which is correct: `nesdev_wiki/APU_Sweep.xhtml` says a negative target clamps and negate is the documented way to disable the sweep. The RTL is right; see core ledger T-01 for the oracle side. v2.8.2 adds the co-simulation gate `apusweepneg069` (pulse 1 `$4001=$08` sounds, pulse 2 `$00` mutes; a mute mutant diverges on 163,968 cycles), sibling `6e343b2`| — ||
31
+
| R-3.3c | Triangle / noise length reload overrides a coinciding decrement (§3.3) |FIXED | CONFIRMED by first divergence against the oracle, which drops a reload landing on a half-frame length clock when the counter was non-zero (as the pulses already did in the RTL). Stimuli `aputrireload070` / `apunoisereload071`; each channel's mutant is caught only by its own gate. Sibling `b8610c9`| v2.8.2 ||
32
32
| R-3.4a | CKE raised on the same edge as the first PRECHARGE (§3.4, Fix 2) | CONFIRMED |`sdram.sv:481-482``sdram_cke <= 1'b1; cmd <= C_PRECHARGE;`. Off-die build only | v2.8.4 ||
33
33
| R-3.4b | DQM held high through the read wait, floating the bus at CL3 (§3.4, Fix 3) | REFUTED |`CAS_LATENCY` is 2 at `CLK_PS = 11640` (`sdram.sv:198`) and READ is issued with DQM low (`:666`); at CL2 that covers the data beat | — ||
34
34
| R-3.4c | Arbiter byte-lane select read from `*_hold[0]` at ack time (§3.4, Fix 4) | CONFIRMED |`sdram_arbiter.sv:353` slices on `chr_hold[0]` at ack; `chr_hold <= chr_addr` (`:317`) reloads on a new strobe, and a re-strobe in flight sets no overrun. Latent: the shipped cart path does not use it | v2.8.4 ||
| R-3.5a | MMC1 bit-7 reset checked before the consecutive-write filter (§3.5, Fix 5) |CONFIRMED|`cart.sv:389` tests `cpu_din[7]` before `!mmc1_wrote_last_cycle`; the oracle filters first (`m001_mmc1.rs:315-321`). The report misstates the impact: "resets twice" is harmless; the case that matters is a bit7=0 write followed by a bit7=1 write| v2.8.2 ||
37
-
| R-3.5b | MMC3 `$E000` acknowledge vs counter decrement race (§3.5, Fix 6) |UNTRIAGED|Verify against nesdev before any fix| v2.8.2 ||
| R-3.5a | MMC1 bit-7 reset checked before the consecutive-write filter (§3.5, Fix 5) |INVERTED|The RTL is right and the ORACLE was wrong: `nesdev_wiki` MMC1 says a reset write is never ignored, only a data write on the cycle after another write (the RMW idiom of *Bill & Ted*). Oracle fixed (`m001_mmc1.rs`, red test first, mutation caught); sibling gate `mapper1rmw068` pins the RTL against it. RTL unchanged. Sibling `6531830`| v2.8.2 ||
37
+
| R-3.5b | MMC3 `$E000` acknowledge vs counter decrement race (§3.5, Fix 6) |FIXED|CONFIRMED. On the same edge the counter's `pending <= 1` came after the acknowledge's `pending <= 0` and won. "Permanently stuck" is overstated: it lasts until the next `$E000`. Module gate `cart-mmc3-gate` (control / coincide / after / reload-to-zero), red first. Sibling `60a2aab`| v2.8.2 ||
38
+
| R-3.5c | MMC3 Sharp reload-to-zero (§3.5) |NOT A DEFECT | The RTL raises the IRQ when a reload yields zero, which is the "normal" (Sharp) behaviour on nesdev; the report attributes the wrong revision. Covered by the `reload-to-zero` case of `cart-mmc3-gate`| —||
39
+
| R-3.5d | SNROM PRG-RAM `/CE2` protection missing (§3.5) |FIXED | CONFIRMED. SNROM gates PRG-RAM on CHR A16 (`E` bit 4 of the selected CHR bank) as well as MMC1 `E` bit 4. Stimulus `mapper1snrom067`: 3,924 cycles diverged before, 0 after. Sibling `5461730`| v2.8.2 ||
40
40
| R-4.1 | Timing table and per-domain slack figures (§4.1) | UNTRIAGED | The binding figures (+0.421 ns setup, +0.112 ns hold) match the pinned seed 4; the per-domain Fmax values and domain attribution are unverified | v2.8.3 ||
41
41
| R-4.2 | Runtime `%` bank dividers in `cart.sv` (§4.2, Fix 10) | NOT A DEFECT | Confirmed present (`cart.sv:679,681,698`; `lpm_divide:Mod0` in the fit report) and deliberate: documented, and timing closed by registering the crossing (`:760-775`). The "~480 ALMs" has no source. Revisited only on a measured need | — ||
42
42
| R-4.3 |`dec_rom[din]` 256:1 mux in the fetch path (§4.3) | UNTRIAGED | The "978 LEs" figure is to be measured in Quartus, not quoted | v2.8.3 ||
- After the oracle merges, the sibling's `tb/ORACLE_COMMIT` moves to the
40
+
merge commit and `VERIFY=1 tb/fetch-goldens.sh` re-verifies every golden
41
+
against it.
42
+
43
+
## What was found and done
44
+
45
+
| Row | Verdict | How it was shown |
46
+
| --- | --- | --- |
47
+
| R-3.5a MMC1 reset vs the consecutive-write filter | INVERTED | nesdev MMC1: a reset is never ignored, only a data write on the cycle after a write. The RTL was right; the ORACLE ignored the reset. Oracle fixed (red unit test, mutation caught); sibling stimulus `mapper1rmw068` (an `INC` on the serial port) pins the RTL against the corrected oracle |
48
+
| R-3.5b MMC3 `$E000` vs a same-edge counter clock | FIXED |`cart-mmc3-gate`, a module gate placing both on one master clock: pending stayed set with IRQs disabled. "Permanently stuck" overstated: it lasts until the next `$E000`|
49
+
| R-3.5c MMC3 Sharp reload-to-zero | NOT A DEFECT | the RTL implements the "normal" (Sharp) revision; the report attributes the wrong one. Covered by the gate's `reload-to-zero` case |
| R-3.2a `vblank_now` vs `suppress_vbl`| NOT A DEFECT | needs `$2002` reads on dot 0 and dot 1 of one frame; CPU accesses are three dots apart |
52
+
| R-3.2b `$2002` open-bus latch | FIXED |`ppulatch072` (a 13-cycle loop, coprime with the frame, so every landing dot is reached): 2 cycles diverged before, 0 after |
53
+
| R-3.3c triangle / noise reload vs a length clock | FIXED | first-divergence read against the oracle; `aputrireload070` / `apunoisereload071`, each channel's mutant caught only by its own gate |
54
+
| pulse-1 sweep negate | GATE ADDED |`apusweepneg069` guards the v2.7.0 oracle fix; a mute mutant diverges on 163,968 cycles |
55
+
| R-3.1a / R-3.1b CPU `last_cycle`, NMI entry | PARTIAL, unchanged | no gate shows an effect; revisited at the v2.9.0 re-audit |
56
+
57
+
## Risks
58
+
59
+
- The oracle's MMC1 change is an emulation behaviour change. Its reach is
60
+
the reset-after-write case only; the test-roms suite and AccuracyCoin are
61
+
re-run on it.
62
+
- The sibling's goldens are pinned at an oracle older than every fix here.
63
+
Until the pin moves, the new stems' goldens come from the release branch;
64
+
the pin bump re-derives all of them.
65
+
66
+
## Out of scope
67
+
68
+
- RTL robustness (reset synchronisers, the `dec_rom` mux, `rmw_addr`,
69
+
tabs): v2.8.3.
70
+
- The off-die SDRAM build: v2.8.4.
71
+
- Anything on hardware: v2.9.2. **No hardware has run any bitstream.**
0 commit comments