Files
retroDE_ps2/tools/verify_runtime_fabric_fit.py
thejayman77 ba74bbd5aa Snapshot: fog implementation + fidelity tooling baseline (pre bilinear-clamp fix)
Per-vertex GS fog end-to-end (gs_stub emit incl. persp_emit5, gs_prim_list_feeder
XYZ2->XYZF2 on PRIM.FGE, gs_make_sh3_scheduler_fixture.py F/FGE packing), new fog
TBs, fidelity attribution tooling. Functional baseline before removing the dead
bilinear lerp8 clamps (Codex: 161-node comb loop -> -0.042ns setup fail).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 19:56:46 -04:00

99 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""Fail closed if a runtime-CLUT fit has timed out a required fabric branch."""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
DEFAULT_OUTPUT = Path(
"synth/de25_nano/top_psmct32_raster_demo/output_files"
)
REVISION = "de25_nano_psmct32_raster_demo_top"
def read(path: Path) -> str:
try:
return path.read_text(encoding="utf-8", errors="replace")
except OSError as exc:
raise SystemExit(f"FAIL: cannot read {path}: {exc}") from exc
def summary_value(text: str, label: str) -> int:
match = re.search(rf"^{re.escape(label)}\s*:\s*([0-9,]+)\s*/", text, re.M)
if not match:
raise SystemExit(f"FAIL: missing '{label}' in fit summary")
return int(match.group(1).replace(",", ""))
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT)
args = parser.parse_args()
fit_summary = args.output_dir / f"{REVISION}.fit.summary"
fit_report = args.output_dir / f"{REVISION}.fit.rpt"
sta_summary = args.output_dir / f"{REVISION}.sta.summary"
summary = read(fit_summary)
fit = read(fit_report)
sta = read(sta_summary)
failures: list[str] = []
if "Fitter Status : Successful" not in summary:
failures.append("fitter did not report success")
alms = summary_value(summary, "Logic utilization (in ALMs)")
rams = summary_value(summary, "Total RAM Blocks")
# A 640x480 persistent-Z runtime scene normally consumes roughly 79% ALMs
# and 74% M20Ks. These deliberately loose floors catch path pruning, not
# healthy placement variation.
if alms < 20_000:
failures.append(f"only {alms:,} ALMs; runtime raster fabric appears pruned")
if rams < 150:
failures.append(f"only {rams} RAM blocks; runtime texture/Z fabric appears pruned")
for name in ("u_demo|g_feeder", "u_texcache|tex_mem", "u_zc_emit|u_req"):
if name not in fit:
failures.append(f"required fitted hierarchy missing: {name}")
for warning in (
"feeder staging RAM dest EMPTY",
"tex_mem dst matched 0 keepers",
):
if warning in fit:
failures.append(f"runtime SDC endpoint missing: {warning}")
emif_setup = re.search(
r"Type\s*:\s*Setup 'u_emif_lpddr4b\|iopll\|iopll_0_outclk0'\s*\n"
r"Slack\s*:\s*([+-]?[0-9.]+)\s*\n"
r"TNS\s*:\s*([+-]?[0-9.]+)",
sta,
)
if not emif_setup:
failures.append("missing 310 MHz EMIF setup summary")
else:
setup_slack, setup_tns = map(float, emif_setup.groups())
if setup_slack < 0 or setup_tns < 0:
failures.append(
f"310 MHz setup fails: slack {setup_slack:+.3f} ns, TNS {setup_tns:+.3f} ns"
)
if failures:
print("RUNTIME FABRIC FIT: FAIL")
for failure in failures:
print(f"- {failure}")
return 1
print(
"RUNTIME FABRIC FIT: PASS "
f"({alms:,} ALMs, {rams} RAM blocks, feeder/texture/Z present, 310 MHz setup met)"
)
return 0
if __name__ == "__main__":
sys.exit(main())