Skip to content

Worst Case Methodology

Lukas edited this page Jul 20, 2026 · 1 revision

Worst-Case Methodology

The core idea

BIOS applies exactly one value for each timing parameter, system-wide, across every populated slot on a channel — there's no per-DIMM tRCD setting in consumer BIOS. So for a mixed kit to run stably, every installed module's own minimum requirement for a given timing has to be satisfied simultaneously by whatever single value BIOS actually applies. The safe value for any one parameter is therefore:

system_value = max(module.value for module in installed_modules)

...taken independently, per parameter, across every module. This is correct and sufficient for almost everything on this page: CL, tRCD, tRP, tRAS, tRFC1/2/4, tFAW, tRRD_S/L, tCCD_L, tWR, tWTR_S/L. Whichever module declares the tightest (highest-in-ns) requirement for a given parameter sets the floor for everyone.

The one place independent-per-parameter maxima isn't enough: tRC

As covered in Primary Timings CL tRCD tRP tRAS tRC, tRC is not an independent physical quantity — it's the full ACT-to-ACT cycle, and by definition:

tRC >= tRAS + tRP

Taking max(tRC) across modules independently of max(tRAS) and max(tRP) can produce a result that's internally inconsistent with the protocol: if the module with the loosest tRC isn't the same module with the loosest tRAS+tRP combination, the naive per-parameter maximum under-reports what's actually required.

This happened for real, not hypothetically, on this project's own reference kit: one module's SPD declares tRC = 44.5ns, which is itself already less than its own tRAS + tRP = 32.375 + 14.0 = 46.375ns — real vendor data, not corrupted or misread. When another module in the kit separately holds the record for worst-case tRC at 45.75ns, a naive max(tRC) across all modules reports 45.75ns as "the" governing tRC — which is less than 46.375ns, the actual floor implied by that first module's own tRAS+tRP. The table would have printed a real, provably insufficient tRC value.

The fix

worst["tRC"] = max(worst["tRC"], worst["tRAS"] + worst["tRP"])

Using the system-wide worst-case tRAS and worst-case tRP (already independently computed as maxima) as the floor, rather than trying to track which specific module's own tRAS+tRP combination is largest, is a safe over-approximation: since system-wide worst-tRAS is, by construction, >= any individual module's own tRAS (same for tRP), their sum is guaranteed >= any single module's own tRAS+tRP requirement too. It may occasionally be very slightly more conservative than the theoretical minimum, but never less than what's actually required — exactly the direction a "safe" calculator should round its uncertainty.

The source annotation for a floor-corrected tRC (e.g. 1-0053 (tRAS+tRP floor)) exists specifically so the number doesn't look like it silently appeared from nowhere — every other value's source line just names the one module that set it; this is the one line that explains why the value is what it is.

Why this bug is now permanently guarded against

spd-matchtable's --selftest includes a synthetic two-module scenario reproducing this exact case byte-for-byte (module A: tRAS=32.0, tRP=13.75, tRC=45.75; module B: tRAS=32.375, tRP=14.0, tRC=44.5 — the real reference-kit numbers), asserting the corrected tRC comes out to 46.375ns with an annotated source, specifically so a future change to the worst-case logic can't silently regress this without a test failing.

Clone this wiki locally