This commit is contained in:
david rice
2026-05-06 15:57:48 +01:00
parent 395e9d6a43
commit 0edb95d7e1
30 changed files with 2493 additions and 0 deletions

132
analysis/registers.py Normal file
View File

@@ -0,0 +1,132 @@
"""Parse SN65DSI83 and DSIM register dumps into structured flags.
DSIM PHY_TIMING bit-field layout is undocumented in the i.MX 8M Mini RM.
We log raw hex AND decoded cycle counts so they can be cross-checked
against kernel dmesg output that prints the cycle counts explicitly.
"""
from __future__ import annotations
from typing import Optional
from config import (
BYTE_CLK_HZ,
SN65_ERR_SOT,
SN65_ERR_SYNCH,
SN65_ERR_UNC,
SN65_FLICKER_MASK,
)
def _to_int(v) -> Optional[int]:
if v is None:
return None
if isinstance(v, int):
return v
s = str(v).strip().lower()
try:
if s.startswith("0x"):
return int(s, 16)
return int(s, 16)
except ValueError:
return None
# ---------------------------------------------------------------------------
# SN65DSI83
# ---------------------------------------------------------------------------
def parse_sn65(reg_json: dict) -> dict:
"""Extract structured flicker flags from /sn65_registers response.
Accepts either the server's pre-parsed shape (with explicit bool keys)
or a raw {register: hex} mapping; falls back to bit-decoding in either case.
"""
irq_raw = _to_int(reg_json.get("irq_stat_raw"))
if irq_raw is None:
regs = reg_json.get("registers", {})
irq_raw = _to_int(regs.get("e5") or regs.get("E5") or regs.get("0xE5"))
irq_raw = irq_raw or 0
pll_raw = _to_int(reg_json.get("registers", {}).get("0a")) if reg_json.get("registers") else None
clk_raw = _to_int(reg_json.get("registers", {}).get("0b")) if reg_json.get("registers") else None
pll_locked = reg_json.get("pll_locked")
if pll_locked is None and pll_raw is not None:
pll_locked = bool(pll_raw & 0x80)
clk_detected = reg_json.get("clk_detected")
if clk_detected is None and clk_raw is not None:
clk_detected = bool(clk_raw & 0x01)
sot_err = bool(irq_raw & SN65_ERR_SOT)
synch_err = bool(irq_raw & SN65_ERR_SYNCH)
unc_ecc_err = bool(irq_raw & SN65_ERR_UNC)
flicker_detected = bool(irq_raw & SN65_FLICKER_MASK)
return {
"irq_stat_raw": f"0x{irq_raw:02X}",
"irq_stat_int": irq_raw,
"pll_locked": bool(pll_locked) if pll_locked is not None else None,
"clk_detected": bool(clk_detected) if clk_detected is not None else None,
"sot_err": sot_err,
"synch_err": synch_err,
"unc_ecc_err": unc_ecc_err,
"flicker_detected": flicker_detected,
"registers": reg_json.get("registers", {}),
}
# ---------------------------------------------------------------------------
# DSIM PHY_TIMING / PHY_TIMING1 / PHY_TIMING2
# ---------------------------------------------------------------------------
def _cycles_to_ns(cycles: int) -> float:
return cycles / BYTE_CLK_HZ * 1e9
def parse_dsim(reg_json: dict) -> dict:
pt = _to_int(reg_json.get("PHY_TIMING"))
pt1 = _to_int(reg_json.get("PHY_TIMING1"))
pt2 = _to_int(reg_json.get("PHY_TIMING2"))
out: dict = {
"PHY_TIMING_raw": f"0x{pt:08X}" if pt is not None else None,
"PHY_TIMING1_raw": f"0x{pt1:08X}" if pt1 is not None else None,
"PHY_TIMING2_raw": f"0x{pt2:08X}" if pt2 is not None else None,
}
if pt is not None:
hs_exit = (pt >> 4) & 0xF
lpx = pt & 0xF
out["hs_exit_cycles"] = hs_exit
out["hs_exit_ns"] = _cycles_to_ns(hs_exit)
out["lpx_cycles"] = lpx
out["lpx_ns"] = _cycles_to_ns(lpx)
if pt1 is not None:
clk_zero = (pt1 >> 24) & 0xFF
clk_post = (pt1 >> 16) & 0xFF
clk_trail = (pt1 >> 8) & 0xFF
clk_prepare = pt1 & 0xFF
out["clk_zero_cycles"] = clk_zero
out["clk_zero_ns"] = _cycles_to_ns(clk_zero)
out["clk_post_cycles"] = clk_post
out["clk_post_ns"] = _cycles_to_ns(clk_post)
out["clk_trail_cycles"] = clk_trail
out["clk_trail_ns"] = _cycles_to_ns(clk_trail)
out["clk_prepare_cycles"] = clk_prepare
out["clk_prepare_ns"] = _cycles_to_ns(clk_prepare)
if pt2 is not None:
hs_prepare = (pt2 >> 16) & 0xFF
hs_zero = (pt2 >> 8) & 0xFF
hs_trail = pt2 & 0xFF
out["hs_prepare_cycles"] = hs_prepare
out["hs_prepare_ns"] = _cycles_to_ns(hs_prepare)
out["hs_zero_cycles"] = hs_zero
out["hs_zero_ns"] = _cycles_to_ns(hs_zero)
out["hs_trail_cycles"] = hs_trail
out["hs_trail_ns"] = _cycles_to_ns(hs_trail)
return out