Updates
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user