This commit is contained in:
david rice
2026-04-16 11:23:25 +01:00
parent 534ae81aba
commit 6dd4bb8aeb
14 changed files with 2060 additions and 10 deletions

View File

@@ -48,10 +48,16 @@ LP11_SPEC_MAX_V = 1.45 # V — LP-11 maximum voltage spec
LP_LOW_DUR_MIN_NS = 50.0 # ns — minimum LP-low duration per D-PHY spec (LP-01 + LP-00 combined)
HS_OSC_STD_V = 0.045 # V — rolling-std threshold above which a region is classified as HS
# Flicker detection threshold
# Flicker detection thresholds
# LP-low plateau below this → SoT sequence too brief for receiver to detect → flicker risk
FLICKER_LP_LOW_MAX_NS = 50.0 # ns
# HS burst amplitude below this (single-ended p-p / 2, mV) → HS burst absent after LP transition.
# On this hardware normal HS = 105122 mV; confirmed flicker = 1432 mV (DC / LP-11 recovery).
# Captures where LP-01/LP-00 completed normally but the bridge never entered HS mode show
# essentially zero amplitude (the burst window is DC LP-11), so lp_low alone cannot detect this.
HS_BURST_AMPLITUDE_MIN_MV = 50.0 # mV — below this, no real HS burst is present
@dataclass
class ChannelMetrics:
@@ -670,7 +676,18 @@ class LPMetrics:
if self.hs_amplitude_mv is not None:
lines.append(f" HS amplitude : {self.hs_amplitude_mv:.0f} mV (single-ended p-p/2)")
if self.flicker_suspect:
lines.append(f" *** FLICKER SUSPECT: LP-low plateau absent or < {FLICKER_LP_LOW_MAX_NS:.0f} ns ***")
if (self.hs_amplitude_mv is not None
and self.hs_amplitude_mv < HS_BURST_AMPLITUDE_MIN_MV
and (self.lp_low_duration_ns is None
or self.lp_low_duration_ns >= FLICKER_LP_LOW_MAX_NS)):
lines.append(
f" *** FLICKER SUSPECT: HS burst absent "
f"(amplitude {self.hs_amplitude_mv:.0f} mV < {HS_BURST_AMPLITUDE_MIN_MV:.0f} mV) ***"
)
else:
lines.append(
f" *** FLICKER SUSPECT: LP-low plateau absent or < {FLICKER_LP_LOW_MAX_NS:.0f} ns ***"
)
for w in self.warnings:
lines.append(f" WARNING: {w}")
return "\n".join(lines)
@@ -732,7 +749,8 @@ def analyze_lp_file(path: Path) -> "LPMetrics":
lp11_voltage_v = round(float(np.concatenate(
[volts[s:e] for s, e in lp11_regions]).mean()), 3)
lp11_duration_us = round(
sum((times[e] - times[s]) for s, e in lp11_regions) * 1e6, 3)
sum((times[min(e, len(times) - 1)] - times[s])
for s, e in lp11_regions) * 1e6, 3)
# ── HS burst detection ────────────────────────────────────────────────
# On DAT0+ with a uniform-colour display, HS data can look DC (no bit
@@ -758,8 +776,9 @@ def analyze_lp_file(path: Path) -> "LPMetrics":
for i, (lp11_s, lp11_e) in enumerate(lp11_regions):
# Burst ends at start of next LP-11, or at window end
burst_end = lp11_regions[i + 1][0] if i + 1 < len(lp11_regions) else len(times) - 1
burst_dur_ns = round((times[burst_end] - times[lp11_e]) * 1e9, 1)
hs_bursts.append((lp11_e, burst_end, burst_dur_ns))
lp11_e_idx = min(lp11_e, len(times) - 1) # guard: region end can == len(times)
burst_dur_ns = round((times[burst_end] - times[lp11_e_idx]) * 1e9, 1)
hs_bursts.append((lp11_e_idx, burst_end, burst_dur_ns))
if hs_bursts:
n_hs_bursts = len(hs_bursts)
@@ -776,12 +795,15 @@ def analyze_lp_file(path: Path) -> "LPMetrics":
# LP-low plateau: look for a contiguous region in the exit window
# where voltage < LP_LOW_V and std is low (true LP-01/LP-00 plateau)
lp_low_mask = (volts < LP_LOW_V) & (rstd < HS_OSC_STD_V)
lp_low_regions = _find_contiguous_regions(lp_low_mask, min_samples=5)
# Time-based minimum: reject glitches shorter than 5 ns.
# At ~40 GSa/s (25 ps/sample) the old min_samples=5 admitted 125 ps noise spikes.
_min_lp_low = max(5, int(5e-9 / dt))
lp_low_regions = _find_contiguous_regions(lp_low_mask, min_samples=_min_lp_low)
exit_window = int(1e-6 / dt)
for lplow_s, lplow_e in lp_low_regions:
if s_end <= lplow_s <= s_end + exit_window:
lp_low_duration_ns = round(
(times[lplow_e] - times[lplow_s]) * 1e9, 1)
(times[min(lplow_e, len(times) - 1)] - times[lplow_s]) * 1e9, 1)
break
# HS single-ended amplitude from the first burst (where data may vary)
@@ -818,13 +840,31 @@ def analyze_lp_file(path: Path) -> "LPMetrics":
if n_hs_bursts == 0:
warnings.append("No HS bursts detected after LP transition")
# Flicker suspect: LP→HS sequence detected but LP-low plateau is absent or too short.
# Normal captures show ~340 ns; the confirmed flicker capture showed 0 ns.
# Flicker suspect: either the LP-low plateau is absent/short, OR the HS burst
# amplitude is too low (indicating the HS burst never actually started).
#
# The second condition catches the confirmed failure mode on this hardware:
# LP-11 → LP-01/LP-00 preamble (normal ~342 ns) → bridge misses SoT
# → driver returns to LP-11 without entering HS mode
# → burst window is DC LP-11, hs_amplitude ≈ 1530 mV (vs normal 105122 mV).
# In this case lp_low_duration_ns = ~342 ns (above threshold), so the LP-low
# check alone produces a false negative.
#
# Only flag DAT lane (CLK is continuous HS — LP states not expected).
# NOTE: lp11_to_hs_ns is NOT used here — on this hardware a consistent noise spike
# at LP-11 exit causes the rolling-std gate to fire at ~3 ns for every capture,
# making it indistinguishable from a genuine flicker (2.8 ns confirmed flicker).
hs_burst_absent = (
hs_amplitude_mv is not None
and hs_amplitude_mv < HS_BURST_AMPLITUDE_MIN_MV
)
flicker_suspect = (
channel == "dat"
and lp_transition_valid
and (lp_low_duration_ns is None or lp_low_duration_ns < FLICKER_LP_LOW_MAX_NS)
and (
(lp_low_duration_ns is None or lp_low_duration_ns < FLICKER_LP_LOW_MAX_NS)
or hs_burst_absent
)
)
return LPMetrics(