PUT /video stop path on the i.MX, not by signal-integrity
on the MIPI bus, not by the SN65DSI83 itself, and not by the panel.
Each video stop tears down the DSI HS-clock; the SN65's PLL loses lock
for 15–45 ms (1–3 display frames); on the subsequent start, the
PLL must re-acquire and the LVDS output is briefly garbled — the
visible flicker. Fix: change "stop" semantics so the
HS-clock keeps running (e.g. GStreamer PAUSED instead of
NULL), or simply avoid stopping video in production.
An i.MX 8M Mini SoC drives a SN65DSI83 MIPI-DSI → LVDS bridge into an LCD panel. Under steady-state operation the display sometimes flickers. Initial hypothesis: a MIPI bit-error or signal-integrity issue on the CLK or data lanes.
| Phase | Approach | Result |
|---|---|---|
| 1 – proto decoder | Decode raw MIPI bursts from differential captures, look for byte-level anomalies | Inconclusive. Capture coverage was ~0.0004% of time (20 µs windows on a 10 s cycle). Flicker events not captured. |
| 2 – segmented memory | Use scope's segmented-memory mode to capture 100 LP-trigger events per acquisition (~50× higher coverage), analyse per-segment | Negative result. Flicker and non-flicker captures statistically indistinguishable across all LP-state metrics. |
| 3 – SN65 register monitor | High-rate HTTP polling of SN65DSI83's status registers
(csr_0a, csr_e5) to catch transient PLL events
the post-event snapshot missed |
Smoking gun found. PLL unlocks during flicker sessions, never during good sessions. |
| 4 – Pulse-width characterisation | 100 Hz polling (median 20 ms) to measure actual unlock pulse width | Pulse width 15–45 ms (1–3 display frames). Too short for a software-driven clock pause; too short for a brownout-and-restart. Consistent with a brief clock-lane disturbance. |
| 5 – Cycling vs hold | Compare PLL behaviour under continuous video to behaviour under on/off cycling | Definitive. 0 unlocks in 10 min 51 s of continuous video. 30 unlocks in 9 min of cycling. The cycling is the trigger. |
| 6 – Transition isolation | Time-correlate every unlock against each PUT /video start
and PUT /video stop event |
Conclusive. 100% of unlocks happen 225–259 ms
after stop. 0% after start. |
| Run | Duration | PLL unlocks | Rate | I²C read errors |
|---|---|---|---|---|
| Hold (no cycling) | 650.9 s | 0 | 0.0/min | 0.0% |
| Run | Duration | Cycles | PLL unlocks | Unlocks / cycle |
|---|---|---|---|---|
| May 11 — 30 unlock-recovery pairs | ~9 min | ~54 | 30 | 0.56 |
| May 11 — controlled (17 cycles) | 177 s | 17 | 8 | 0.47 |
| Metric | Value | Notes |
|---|---|---|
| min | 14.5 ms | under 1 frame at 60 Hz |
| median | 21.3 ms | ~1.3 frames |
| p90 | 40.0 ms | ~2.4 frames |
| max | 44.5 ms | ~2.7 frames |
PUT /video stop arrives
│
│ ~5 ms HTTP / Flask processing
│ ~50-150ms App + GStreamer pipeline tears down
│ (state goes to NULL)
▼
DSIM driver disables HS_CLK_EN ──────► ~230 ms after stop
│
▼
MIPI CLK lane goes to LP-11
│
▼
SN65DSI83 sees no reference clock
│
▼
PLL drops lock ◄── csr_e5.pll_unlock = 1 caught here
(pulse width 15-45 ms)
│
▼
PLL re-acquires to "no-signal" idle state
│
(~500 ms OFF window)
│
PUT /video start
▼
DSIM re-enables HS_CLK_EN; MIPI traffic resumes
│
▼
SN65DSI83 PLL has to re-acquire to the new clock
│ (~10-30 ms, LVDS output is garbage during this)
▼
──── visible flicker on the panel ────
The bridge is behaving correctly: a PLL is expected to lose lock when its reference clock disappears. The defect is upstream — the act of stopping the video drops the MIPI HS-clock, which puts the bridge through an unlock-relock cycle every time, and the next start has to re-acquire from cold. That re-acquisition window is the visible flicker.
Two orthogonal options. Either should eliminate the flicker.
In the device-side video stack, change the "stop" path from a full teardown to a soft pause that keeps the DSI HS-clock running. For GStreamer:
// Today (causes flicker): gst_element_set_state(pipeline, GST_STATE_NULL); // Proposed: gst_element_set_state(pipeline, GST_STATE_PAUSED);
PAUSED retains the pipeline graph and buffers — and, importantly,
doesn't trigger the bridge-disable path in the i.MX DSIM driver, so HS-CLK
stays on and the SN65 PLL stays locked through the transition. Resume is
near-instant and visually clean.
If the only reason /video stop is called in real deployments
is the flicker test harness itself, the flicker mode is purely an artefact
of the test. Production code that starts the stream once at boot and
leaves it running will see zero PLL unlocks (confirmed
empirically — 0 unlocks in 10 min 51 s of continuous video).
Once the device server gains a soft-stop action (e.g.
{"action": "pause"}), compare_stops.py in this
repo runs an A/B test automatically:
STYLES = [
("stop_full", {"action": "start", "mode": "static-pink"}, {"action": "stop"}),
("stop_pause", {"action": "start", "mode": "static-pink"}, {"action": "pause"}),
]
$ python3 compare_stops.py --cycles 30
A successful fix will show ~0.5 unlocks/cycle for
stop_full and 0.00 for stop_pause.
| Script | Purpose |
|---|---|
sn65_monitor.py |
Polls SN65 status registers at 50–100 Hz, logs every PLL transition with
millisecond timestamps. Rolling 30 s buffer dumped to JSON on
f/g keypress. |
video_cycler.py |
Toggles /video start/stop on the device on a configurable
cadence. Logs every transition to a CSV. Has a --hold
mode for the no-cycling baseline. |
analyze_session.py |
Cross-references the latest SN65 log with the latest cycle log, classifies each unlock as "after_START / after_STOP / neither", prints a verdict. |
compare_stops.py |
Runs a controlled A/B/… test across multiple stop-style payloads. Polls SN65 inline, attributes unlocks to the active style, prints a comparison table. Use this to verify the eventual fix. |
f press on 11 May (08:33:28) had zero PLL activity in
the preceding minute, suggesting either an observation false-positive
or a separate, sub-poll-rate fault path. Worth keeping the
sn65_monitor running in any future regression testing
to catch.POST /video support for action=pause
(GStreamer GST_STATE_PAUSED)/registers to expose the runtime DSIM registers
DSIM_STATUS, DSIM_CLKCTRL,
DSIM_INTSRC, DSIM_FIFOCTRL alongside the
existing PHY-timing config registersGET /video/state exposing the GStreamer pipeline
state (NULL / READY / PAUSED / PLAYING), which would let us
time-correlate the actual pipeline state transitions with the
PLL unlock windowdata/sn65_log/,
data/cycle_logs/, and data/compare_logs/.
Each timestamped run is independently reproducible.