133 lines
4.4 KiB
Python
133 lines
4.4 KiB
Python
"""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
|