This commit is contained in:
david rice
2026-04-13 15:03:47 +01:00
parent 17d393cbd1
commit fa96fef9ea
11 changed files with 724 additions and 37 deletions

View File

@@ -425,6 +425,106 @@ def analyze_1v8_file(path: Path) -> "V1V8Metrics":
)
# ---------------------------------------------------------------------------
# DSIM PHY timing register decoder (D-PHY v1.1 Table 14 @ 432 Mbit/s, 54 MHz byte clock)
# ---------------------------------------------------------------------------
# Byte-clock period used to convert register fields (in byte-clock units) to nanoseconds.
# 54 MHz byte clock → 18.518 ns per byte clock.
_DSIM_BYTE_PERIOD_NS = 18.518
# Per-field decode table. Key = lowest 2 hex digits of register address.
# Each entry: (field_name, bit_shift, byte_mask, spec)
# spec = ("min", ns) — field_ns must be ≥ ns
# ("range", lo, hi) — field_ns must be lo ≤ x ≤ hi
# None — not individually checked (part of a combined check only)
_DSIM_PHY_FIELDS: dict[str, list] = {
"b4": [ # PHYTIMING 0x32e100b4
("TLPX", 8, 0xFF, ("min", 50.0)),
("THS_EXIT", 0, 0xFF, ("min", 100.0)),
],
"b8": [ # PHYTIMING1 0x32e100b8
("TCLK_PREPARE", 24, 0xFF, ("range", 38.0, 95.0)),
("TCLK_ZERO", 16, 0xFF, None), # combined with TCLK_PREPARE ≥ 300 ns
("TCLK_POST", 8, 0xFF, ("min", 180.4)),
("TCLK_TRAIL", 0, 0xFF, ("min", 60.0)),
],
"bc": [ # PHYTIMING2 0x32e100bc
# Field order verified against kernel logs (samsung_dsim_set_phy_ctrl):
# [23:16]=THS_PREPARE, [15:8]=THS_ZERO, [7:0]=THS_TRAIL
("THS_PREPARE", 16, 0xFF, ("range", 49.3, 98.9)),
("THS_ZERO", 8, 0xFF, None), # combined with THS_PREPARE ≥ 168.2 ns
("THS_TRAIL", 0, 0xFF, ("min", 69.3)),
],
}
# Combined (sum) checks applied after individual field decoding.
# (field_a, field_b, min_ns, label)
_DSIM_COMBINED_CHECKS = [
("TCLK_PREPARE", "TCLK_ZERO", 300.0, "TCLK_PREPARE+TCLK_ZERO"),
("THS_PREPARE", "THS_ZERO", 168.2, "THS_PREPARE+THS_ZERO"),
]
def _decode_dsim_registers(registers: list) -> list[str]:
"""
Decode DSIM PHY timing registers and return a list of annotated strings,
one per field, with D-PHY v1.1 spec compliance check results.
"""
ok = lambda c: "" if c else "✗ VIOLATION"
lines = []
field_ns: dict[str, float] = {}
for reg in registers:
addr_str = reg.get("address", "").lower().lstrip("0x")
val_str = reg.get("value", "0x0").lower()
suffix = addr_str[-2:] if len(addr_str) >= 2 else ""
fields = _DSIM_PHY_FIELDS.get(suffix)
if fields is None:
continue # register not in our decode table
try:
val = int(val_str, 16)
except ValueError:
lines.append(f" {reg.get('address')} : {reg.get('value')} (parse error)")
continue
reg_name = reg.get("name") or f"0x{addr_str}"
lines.append(f" {reg.get('address')} ({reg_name}) = {val_str}")
for (fname, shift, mask, spec) in fields:
raw = (val >> shift) & mask
ns = raw * _DSIM_BYTE_PERIOD_NS
field_ns[fname] = ns
if spec is None:
# shown in combined check only
lines.append(f" {fname:<16s} = {raw:3d} bc → {ns:6.1f} ns (combined check below)")
elif spec[0] == "min":
pass_check = ns >= spec[1]
lines.append(
f" {fname:<16s} = {raw:3d} bc → {ns:6.1f} ns "
f"(spec ≥ {spec[1]:.1f} ns) {ok(pass_check)}"
)
elif spec[0] == "range":
pass_check = spec[1] <= ns <= spec[2]
lines.append(
f" {fname:<16s} = {raw:3d} bc → {ns:6.1f} ns "
f"(spec {spec[1]:.1f}{spec[2]:.1f} ns) {ok(pass_check)}"
)
# Combined sum checks
for (fa, fb, min_ns, label) in _DSIM_COMBINED_CHECKS:
if fa in field_ns and fb in field_ns:
total = field_ns[fa] + field_ns[fb]
pass_check = total >= min_ns
lines.append(
f" {label:<28s} = {total:6.1f} ns (spec ≥ {min_ns:.1f} ns) {ok(pass_check)}"
)
return lines
@dataclass
class RegDump:
"""DSI controller register snapshot read from device via memtool."""
@@ -443,9 +543,14 @@ class RegDump:
lines.append(" No registers captured")
return "\n".join(lines)
lines.append(f" Commands : {'; '.join(self.commands)}")
for r in self.registers:
name = f" ({r['name']})" if r.get("name") else ""
lines.append(f" {r['address']} : {r['value']}{name}")
decoded = _decode_dsim_registers(self.registers)
if decoded:
lines.extend(decoded)
else:
# Fallback: raw hex dump if no addresses matched decode table
for r in self.registers:
name = f" ({r['name']})" if r.get("name") else ""
lines.append(f" {r['address']} : {r['value']}{name}")
return "\n".join(lines)