How we test & how the performance numbers are computed測試方式與效能計算方法
Harness. Every ROM runs in its own headless engine process, one test per physical
core (affinity-pinned, 7 lanes), launched from a catalog of all tests with per-test budgets. The power-on CPU–PPU
clock alignment is pinned to one reproducible phase (the same trade-off a real console makes at power-on — see the
dossiers), so verdicts are deterministic run-to-run. Each result JSON records start/finish timestamps, simulated
half-cycles and engine wall time.測試框架。每個 ROM 都在獨立的 headless 引擎行程中執行,
一測一實體核心(affinity 綁定,7 lanes),由完整測試目錄排程,各測有自己的幀數/時間預算。上電 CPU–PPU 時脈對齊固定在單一可重現相位
(與真機上電時的取捨相同 —— 見卷宗),同一輪跑幾次判定都一致。每筆結果 JSON 記錄開始/結束時間戳、模擬半週期數與引擎牆鐘時間。
Verdicts. Three detection channels, matching each test's design:
the blargg $6000 protocol (signature DE B0 61; status < $80 = done, 0 = pass; $81 = the ROM requests a soft reset, honored
automatically), screen CRC32 against the author-documented accept sets (alignment-dependent tests list every real-hardware
pattern), and on-screen pass-marker text. Tests that need controller input use a scripted behavioral joypad
(button:frame schedules).判定。依各測試的設計走三種偵測通道:blargg $6000 協定
(簽章 DE B0 61;狀態 < $80 = 結束、0 = 通過;$81 = ROM 要求軟重置,自動照辦)、畫面 CRC32 對照作者記載的合法集合
(對齊相關的測試會列出每一種真機圖樣)、以及畫面通過字樣掃描。需要手把的測試用腳本化行為層手把(按鍵:幀數排程)。
Clean campaigns. A full regression starts from an empty results
directory, and the aggregator only uses the newest contiguous run (results separated by >30 min of idle are treated as a
different campaign) — numbers on this page never mix runs.乾淨戰役。全量回歸從清空的
結果目錄開始,統計也只取最新的連續一段(間隔超過 30 分鐘視為另一輪)—— 本頁數字不會混到不同輪的紀錄。
Performance metrics. One half-cycle (hc) is half a period
of the NES 21.477 MHz master clock — the engine's atomic settle step; a real console advances ≈42.95 M hc/s.
Per-test speed = half-cycles ÷ engine wall seconds (khc/s). Weighted mean = total hc ÷ total core-seconds
(per-core efficiency). Campaign aggregate = total hc ÷ wall-clock span from first start to last finish
(includes lane idle: staggered starts, inter-test gaps, tail drain). Steady-state = per-busy-second rate × peak
concurrent lanes, from an interval sweep over every test's start/finish events — what the machine sustains while all
lanes are busy, with idle excluded. All three are estimates derived from the recorded timestamps.
效能計算。一個半週期(hc)= NES 21.477 MHz 主時脈的半個週期,是引擎的最小穩定步;真機速度 ≈42.95M hc/s。
單測速度 = 半週期數 ÷ 引擎牆鐘秒數(khc/s)。加權平均 = 總 hc ÷ 總核心秒數(單核效率)。
戰役聚合 = 總 hc ÷ 從第一測開跑到最後一測完成的牆鐘跨度(含 lane 閒置:錯開起跑、測試間空檔、尾段收工)。
穩態吞吐 = 忙碌秒速率 × 峰值並行 lane 數,由所有測試的開始/結束事件做區間掃描求得 —— 即「所有 lane 都在忙」時
機器實際維持的速度,已排除閒置。三者皆由結果檔時間戳推估。
Integrity. The engine's default (benchmark) path is never touched by
test instrumentation — its state checksum is bit-identical with and without the test harness. Every deviation handling is a
documented test-mode shim (see the knowledge base and the in-depth Q&A linked above).誠信。
引擎預設(基準測試)路徑不受任何測試儀器影響 —— 掛不掛測試框架,狀態 checksum 逐位元相同。所有偏差處理都是文件化的測試模式 shim
(見上方知識庫與深入 Q&A)。
Why do test ROMs still fail when we run the real chip's netlist?為什麼拿「真晶片的 netlist」來跑,測試 ROM 還是會錯?
A fair question — if the transistors are the real chip's transistors, shouldn't every test pass?
No, and the reasons are instructive:很合理的疑問 —— 電晶體都是真晶片的電晶體了,不是每個測試都該過嗎?並不是,而且原因本身就很有教育價值:
1. The two dies are not the whole console.1. 兩顆晶粒不等於一整台主機。
The netlists cover exactly two silicon dies — the CPU (RP2A03) and the PPU (RP2C02). A working NES also
contains work RAM, video RAM, the cartridge (ROM chips plus mapper circuitry), the clock crystal, controllers, and
all the board wiring between them. Everything outside the two dies has to be re-created as a
behavioral layer: ordinary program code that must answer the bus exactly the way the real part would.
That is where errors have room to live — usually not in what value is returned, but in which
fraction of a cycle it is returned, whether the bus floats afterwards, and how long a stale value lingers.
Several bugs we fixed during this campaign lived precisely on that seam (power-up palette state, PPU open-bus
decay), and the investigations still open are probing the same seam.netlist 只涵蓋兩顆矽晶粒 —— CPU(RP2A03)與 PPU(RP2C02)。一台能動的 NES 還有工作 RAM、顯示 RAM、卡帶(ROM 晶片加 mapper 電路)、石英振盪器、手把,以及把這些全部接起來的電路板走線。晶粒以外的一切都得用行為層重建:用普通程式碼扮演那顆零件,對匯流排做出跟實品一模一樣的回應。錯誤的空間就在這裡 —— 通常不是「回傳什麼值」錯了,而是「在一個 cycle 的哪個瞬間回傳」、「回完之後匯流排有沒有浮接」、「殘值會殘留多久」這種細節。這次戰役修掉的幾個 bug 正好都住在這條接縫上(上電 palette 狀態、PPU open-bus 衰減),還在追查的問題也都在探同一條縫。
2. "The NES" is not one machine.2. 「NES」不是一台機器,是一個家族。
Nintendo shipped multiple CPU revisions (RP2A03E / G / H …), multiple PPU revisions,
and multiple board designs (front-loader NES-001, top-loader NES-101, the Famicom, licensed clones) — and the
revisions differ in measurable, test-visible ways. A test ROM is calibrated against the author's own
console; a different console can honestly produce a different result. That is why this project pins a single
reference machine — NES-001 with RP2A03G + RP2C02G, the exact revisions the Visual2A03/2C02
dies were photographed from — and implements the behavioral layer to that machine's personality.
Matching some other console's quirk would be wrong for ours.任天堂出過多個 CPU 版次(RP2A03E / G / H …)、多個 PPU 版次、多種電路板(前插式 NES-001、上插式 NES-101、Famicom、授權相容機)—— 而且版次之間的差異是量得到、測試看得到的。測試 ROM 是拿作者自己那台主機校準的;換一台主機,誠實地跑出不同結果是完全可能的。所以本專案釘死一台參考機 —— NES-001 配 RP2A03G + RP2C02G,正是 Visual2A03/2C02 晶粒照片的來源版次 —— 行為層就照這台的個性實作。去遷就別台主機的怪癖,對我們這台反而是錯的。
3. Part of the chip is analog, and a switch-level model is digital.3. 晶片有一部分是類比電路,而開關級模型是數位的。
The netlist abstraction treats each transistor as an on/off switch and each node as 0/1 with a few
strength classes. The real chip is analog NMOS silicon: OAM is dynamic RAM whose charge leaks away, floating
buses hold their last value for a temperature-dependent fraction of a second, power-on drops every latch into a
random analog equilibrium, and the APU's sound output is literally an analog mixing network. Tests that measure
these phenomena (oam_read's decay patterns, ppu_open_bus's ~600 ms decay time) are measuring
physics, not logic — a digital model can only approximate them with explicitly documented shims, or
honestly fail.netlist 抽象把每顆電晶體當成通/斷的開關、每個節點當成 0/1 加上幾級強度。真晶片卻是類比的 NMOS 矽:OAM 是會漏電的動態記憶體、浮接的匯流排會把上一個值保持零點幾秒(時間還隨溫度變)、上電瞬間每個閂鎖掉進隨機的類比平衡點、APU 的聲音輸出根本就是一張類比混音網路。量測這些現象的測試(oam_read 的衰減圖樣、ppu_open_bus 約 600 ms 的衰減時間)量的是物理,不是邏輯 —— 數位模型只能用明文記載的 shim 去近似,或者誠實地失敗。
4. The netlist itself is a hand-made transcription of die photographs.4. netlist 本身是人工從晶粒照片描繪出來的。
Visual2A03/2C02 were produced by tracing polygons off die micrographs by hand (Quietust's work — heroic
and remarkably accurate, as our own zero-diff verification against the upstream data confirms). But it is a
lumped model: no parasitic capacitance network, no analog transistor sizing, no continuous propagation
delays. Behavior that hinges on a race between two signals inside a single clock phase can legitimately resolve
differently in the model than in silicon — the model is faithful to the connectivity, not to every
picosecond of the electrodynamics.Visual2A03/2C02 是人工對著晶粒顯微照片一筆一筆描出多邊形做成的(Quietust 的工作 —— 壯舉級的準確,我們對上游資料做過雙向零差異驗證)。但它是一個集總模型:沒有寄生電容網路、沒有類比的電晶體尺寸、沒有連續的傳播延遲。凡是取決於「同一個時脈相位內兩條信號誰先到」的行為,模型和矽晶就有可能合法地解出不同答案 —— 模型忠實的是連接關係,不是電磁動力學的每一皮秒。
5. Real power-up state is undefined — a simulator must pick one.5. 真機的上電狀態是未定義的 —— 模擬器卻必須挑一個。
Real silicon wakes into random latch states: blargg's own readmes document the same console
giving different oam_read patterns on different power-ons, and the CPU÷12 / PPU÷4 clock
alignment lottery (four possible fine alignments, some tests mutually exclusive across them — see the dossier
below). A deterministic simulator has to choose one reproducible power-up; whichever it chooses, some test
somewhere is calibrated to a different roll of the dice.真矽晶醒來時每個閂鎖都是隨機的:blargg 自己的 readme 就記載同一台主機不同次開機會給出不同的 oam_read 圖樣,還有 CPU÷12 / PPU÷4 時脈對齊的抽籤(四種細對齊,某些測試在不同對齊間互斥 —— 見下方卷宗)。一個確定性的模擬器必須挑一種可重現的上電狀態;不管挑哪種,總有某個測試是照別的骰子點數校準的。
So a FAIL on this page means one of three things: a behavioral-layer
integration bug (we find it and fix it — the score's climb is exactly that process), a machine-profile difference
(documented against our pinned NES-001 / G-revision target), or physics beyond a digital model, or a gap the behavioral layer must fill
(both documented with evidence in the two sections below). The netlist gives us something no behavioral
emulator has: when a test fails, we can put a probe on the actual named transistor nodes and watch the failure
happen cycle by cycle.所以本頁的一個 FAIL,只會是三種情況之一:行為層整合 bug(找到就修 —— 分數一路爬升就是這個過程)、機型差異(對照我們釘死的 NES-001 / G 版次目標據實記載),或是數位模型構不到的物理、又或是行為層必須補上的缺口(兩者都在下方兩節據實記載並附證據)。而 netlist 給了我們行為層模擬器都沒有的東西:測試失敗時,可以把探針直接搭上有名字的電晶體節點,逐 cycle 看著失敗發生。