Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
102 lines
4.1 KiB
Python
102 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Gate on the RKNN compiler's register-field diagnostics.
|
|
|
|
`rknn.build` reports an op encoding whose field exceeds the DPU's 13-bit register width by
|
|
printing
|
|
|
|
REGTASK: The bit width of field value exceeds the limit, ... limit: 0x1fff, value: 0x7800
|
|
|
|
from the C++ compiler on file descriptors 1 and 2, not through Python logging, so a gate on
|
|
those lines has to redirect the descriptors rather than `sys.stdout`.
|
|
|
|
The lines are not by themselves a failure. The RK3566 builds of the three shipping engines log 352
|
|
(Kokoro w128 generator), 355 (Piper w93 vocoder) and 6570 (Qwen3 B=64 vocoder) of them and measure
|
|
clean on the board; the RK3588 build of the Piper w93 vocoder logs none. What correlates with a
|
|
dead engine is the magnitude of the count: the Kokoro W=512 build, whose engine voices clipped
|
|
noise (measured rms 32764 of 32767, SNR -27 dB against the CPU decode), logs 403 587 of them, with
|
|
the peak value 0x7800 = 30720, nearly four times past the 0x1fff field. Hence a threshold between
|
|
the two regimes, not a zero tolerance.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import os
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REGTASK_FATAL_LINES = 100_000
|
|
|
|
_VALUE = re.compile(r"REGTASK.*?value: 0x([0-9a-fA-F]+)")
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def rknn_build_gate(label: str, log_path: Path, fatal: int = REGTASK_FATAL_LINES):
|
|
"""Run the block (the `rknn.build` call) with fds 1/2 captured to `log_path`, then count
|
|
the register-field overflow lines and abort past `fatal` of them. The whole build log is
|
|
kept at `log_path` for inspection."""
|
|
log_path = Path(log_path)
|
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
with log_path.open("w+") as log:
|
|
saved = (os.dup(1), os.dup(2))
|
|
try:
|
|
os.dup2(log.fileno(), 1)
|
|
os.dup2(log.fileno(), 2)
|
|
yield log
|
|
finally:
|
|
sys.stdout.flush()
|
|
sys.stderr.flush()
|
|
os.dup2(saved[0], 1)
|
|
os.dup2(saved[1], 2)
|
|
os.close(saved[0])
|
|
os.close(saved[1])
|
|
log.flush()
|
|
log.seek(0)
|
|
text = log.read()
|
|
|
|
values = [int(v, 16) for v in _VALUE.findall(text)]
|
|
if not values:
|
|
bare = text.count("REGTASK")
|
|
if bare:
|
|
print(f"{label}: {bare} register-field overflow lines (no value field parsed) "
|
|
f"-> {log_path}")
|
|
return
|
|
print(f"{label}: {len(values)} register-field overflow lines "
|
|
f"(peak value 0x{max(values):x}, limit 0x1fff) -> {log_path}")
|
|
if len(values) >= fatal:
|
|
raise SystemExit(
|
|
f"{label}: {len(values)} register-field overflow lines is the signature of a build "
|
|
f"whose engine voices saturated output (measured: 403 587 lines at the Kokoro W=512 "
|
|
f"window, rms 32764 of 32767, SNR -27 dB against the CPU decode; the shipping builds "
|
|
f"log 352 to 6570). Shrink the window, or split the axis that carries the long "
|
|
f"dimension so no single op sees more than 8191 of it."
|
|
)
|
|
|
|
|
|
def _self_test() -> int:
|
|
"""Both regimes, and that the capture really sees C-level writes to fds 1/2."""
|
|
import tempfile
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
line = b"REGTASK: The bit width of field value exceeds the limit, limit: 0x1fff, value: 0x7800\n"
|
|
benign = Path(tmp) / "benign.log"
|
|
with rknn_build_gate("benign", benign):
|
|
os.write(1, line * 352)
|
|
os.write(2, b"plain build noise\n")
|
|
text = benign.read_text()
|
|
assert text.count("REGTASK") == 352 and "plain build noise" in text, text[:120]
|
|
fatal = Path(tmp) / "fatal.log"
|
|
try:
|
|
with rknn_build_gate("fatal", fatal):
|
|
os.write(1, line * REGTASK_FATAL_LINES)
|
|
except SystemExit as exit_:
|
|
assert "100000 register-field" in str(exit_), str(exit_)[:120]
|
|
else:
|
|
raise AssertionError("a fatal build did not abort the gate")
|
|
os.write(1, b"fds restored\n")
|
|
print("rknn_build_log self-test ok")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(_self_test())
|