voice_audio_multi/tools/rknn/rknn_build_log.py
Fabio c36510c21e
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
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

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())