123 Main Street, New York, NY 10001

UART Hardware Flow Control (RTS/CTS, DTR/DSR)

← Back to: I²C / SPI / UART — Serial Peripheral Buses

UART hardware flow control prevents byte loss by turning RX buffer pressure into physical backpressure (RTS/CTS), while DTR/DSR gates session readiness. The key is budgeting stop distance and validating with measurable pass criteria (overrun=0, bounded blocked time, and preserved RX margin).

What HW Flow Control really solves

Hardware flow control makes UART resilient under load by turning receiver buffer pressure into a physical backpressure signal (RTS/CTS), preventing RX overruns and protecting long-frame transfers when latency and burstiness are unavoidable.

The root cause (why bytes get lost)

  • UART RX is finite: bytes enter a shift register and then an RX FIFO with limited depth.
  • Service latency is variable: ISR arrival jitter (interrupt masking, critical sections, scheduling) and DMA-ring draining delays.
  • Overrun is irreversible: once RX FIFO overflows, bytes are dropped; higher layers only observe corrupted frames, missing logs, or stalled bootloaders.

Typical symptoms (how “lost bytes” show up)

  • CRC / frame check failures that scale with background CPU load or burst traffic.
  • Protocol retries or timeouts in long transfers (firmware update blocks, large binary payloads, log dumps).
  • Missing characters / truncated lines on console output under heavy system activity.
  • Bootloader “hangs” or bricking-like behavior when a critical byte is lost in a small command/response window.

Why software-only flow control often fails under stress

  • Backpressure arrives too late: when RX is already near-full, in-band control characters may not be processed in time.
  • Binary streams are hostile to in-band control: payloads may collide with reserved bytes unless additional escaping rules exist.
  • Hardware gating is deterministic: RTS/CTS provides a physical stop/go mechanism that does not depend on CPU availability at the worst moment.

Scenario A · Console / debug UART under CPU load

  • Symptom: missing characters, truncated logs, “random” gaps.
  • Trigger: bursts during logging + interrupts masked by critical paths.
  • Why SW is not enough: the worst timing occurs exactly when the system is busiest.

Scenario B · Modem / cellular module (AT + large payload)

  • Symptom: payload corruption or stalls during high-throughput upload/download.
  • Trigger: module bursts data while host task scheduling is jittery.
  • Why SW is not enough: in-band control relies on timely parsing; CTS gating does not.

Scenario C · Long-frame binary protocol (FW update / bulk logs)

  • Symptom: CRC fails late in the frame; retries increase dramatically.
  • Trigger: sustained stream + occasional service latency spikes.
  • Why SW is not enough: recovery happens after loss; HW backpressure prevents loss.

Scope note: this page focuses on RTS/CTS and DTR/DSR handshaking (backpressure and readiness). Topics like baud-rate accuracy, frame format, and PHY-level signaling belong to their dedicated subpages.

UART overrun prevention timeline with RTS/CTS flow control Two timelines compare continuous TX causing RX FIFO overrun versus RTS/CTS gating that pauses TX and keeps FIFO level in a safe band. No handshake vs RTS/CTS backpressure No HW flow control With RTS/CTS flow control time → TXD RX FIFO safe band overrun → bytes lost ISR arrives late RTS/CTS TX allowed TX paused resume TXD RX FIFO no bytes lost

Signal semantics: RTS/CTS and DTR/DSR (who controls whom)

The names on a connector are not a guarantee of direction or polarity. Correct handshaking comes from verifying each pin’s driver, its meaning, and its effect on the peer UART.

Practical wiring rule

Ensure that the receiver’s “pressure” signal reaches the sender’s “permission” input. Use datasheets to confirm direction and polarity; do not rely on signal names alone.

RTS · Receiver pressure (flow control source)

  • Meaning: receiver indicates “ready / not-ready” based on buffer state (implementation-defined).
  • Driven by: RX side (often automatic via RX FIFO watermarks).
  • Effect: should throttle the peer’s transmitter indirectly (by feeding its CTS / gate input).
  • MCU mapping hint: look for “auto-RTS”, “RTS on RX FIFO threshold”, “RTS deassert level”.

CTS · Transmit permission (flow control sink)

  • Meaning: indicates whether it is safe to keep sending bytes right now.
  • Driven by: peer side (often the peer’s RTS output).
  • Effect: gates TX (hardware may stop at a byte boundary; the current byte may complete).
  • MCU mapping hint: look for “auto-CTS”, “CTS gating”, “CTS sampling filter”.

DTR · Session readiness (host/terminal ready)

  • Meaning: indicates a device is present and ready to participate in the session.
  • Driven by: the device that “owns the session” (often host/terminal side).
  • Effect: used for mode entry, readiness, or sleep/wake coordination (not primary byte throttling).
  • MCU mapping hint: commonly a GPIO/modem-control pin; verify default state at reset.

DSR · Peer readiness (module/modem ready)

  • Meaning: indicates the peer is online/ready (session-level availability).
  • Driven by: peer device (often module/modem side).
  • Effect: can gate when to start traffic; combine with RTS/CTS for robust operation.
  • MCU mapping hint: treat as status input; debounce/filter if wiring is long/noisy.

Polarity note: “asserted” is frequently active-low on RTS/CTS and modem-control signals, but not universal. Confirm idle level and assert level in both endpoints’ datasheets.

RTS/CTS flow gating and DTR/DSR session gating between UART endpoints A block diagram shows an MCU UART connected to a module UART with TXD/RXD plus RTS/CTS for flow control and DTR/DSR for session readiness, with arrows indicating typical signal direction and purpose. Who controls whom: Flow gate vs Session gate Host / MCU UART + modem-control pins Peer / Module UART + modem-control pins FLOW gate SESSION gate Wires TXD RXD RTS → (peer pressure) CTS input RTS out RTS → (host pressure) RTS out CTS input DTR → (ready) DSR ← (peer ready) Verify direction + polarity in datasheets; names may be crossed by cabling conventions.

Polarity, idle levels, and “active-low” traps

A single polarity or idle-level mismatch can nullify handshaking: the link may appear configured, yet transmission is permanently gated, or overruns still occur because the “asserted” state is interpreted in reverse across endpoints.

What must be consistent across both endpoints

  • Meaning of “asserted”: commonly active-low (assert = LOW), but not universal. Confirm idle and assert levels.
  • Which state allows TX: some devices treat asserted CTS as “allowed,” others as “busy.” Use datasheet logic definitions, not names.
  • Reset / boot defaults: tri-stated pins or wrong pull-up/down can hold CTS in “deny” during initialization, or leave RTS permissive before RX is ready.
  • Controller options: RTS polarity, CTS polarity, and auto-RTS thresholds often exist and must match the peer’s expectations.

Fast fault tree: 3 symptoms → 1 root cause class (polarity / idle / default state)

Symptom A · Peer never transmits (or sends briefly then stops)

  • Likely cause: CTS interpreted as “deny” at idle (polarity reversed or pulled into deny at boot).
  • Quick check: capture CTS + TXD; verify that TXD only appears when CTS is in the documented “allow” state.
  • Immediate fix: correct CTS polarity or add/adjust pull-up/down so the reset default becomes “allow” until configured.

Symptom B · Occasional lost bytes; worse under higher load

  • Likely cause: RTS/CTS toggles exist, but the “pause” state is inverted or arrives too late due to incorrect threshold assumptions.
  • Quick check: correlate overrun flags with CTS/RTS edges; if overruns happen while CTS indicates “allow,” polarity/meaning is suspect.
  • Immediate fix: verify asserted meaning and adjust polarity first; threshold budgeting belongs to the next section.

Symptom C · Logic analyzer shows CTS “never allows”

  • Likely cause: floating input, wrong pull, or crossed wiring makes CTS stuck in the deny state.
  • Quick check: force CTS to a known “allow” level briefly (per datasheet logic) and confirm TX resumes.
  • Immediate fix: correct wiring and define a deterministic default with pull-ups/downs; avoid tri-state during reset if the peer is active.

Bring-up checklist (polarity & defaults)

  • Record CTS/RTS idle levels at reset; compare with datasheet “allow/deny” definitions.
  • Verify TXD activity occurs only in the “allow” state; avoid interpreting names as direction.
  • Ensure safe defaults during boot (pulls + pinmux order) before enabling DMA/traffic.
Correct vs wrong CTS polarity: gating relationship with TXD Two waveform panels compare correct polarity where TXD activity occurs only when CTS is asserted-allow, versus wrong polarity where gating is reversed and TXD appears in the deny window. CTS polarity trap: TX allowed vs TX denied Correct polarity Wrong polarity CTS TXD assert = ALLOW idle idle TX only when allowed CTS TXD assert = DENY TX occurs in deny window

Timing model: latency, thresholds, and safe stop distance

Handshaking is not instantaneous. After RTS/CTS changes state, the peer may still transmit a tail of bytes due to shift-register completion, TX FIFO backlog, or DMA bursts. Preventing overruns requires budgeting the stop distance against available RX margin.

Key variables (concept model)

Treact

Reaction time from RTS/CTS state change to actual TX stop at the peer (includes shift-register completion, TX FIFO, and DMA push behavior).

Bstop

Tail bytes that still arrive after a “pause” request. This is the practical stop distance that must fit into remaining RX capacity.

RX_margin

Available receive headroom at the instant flow control asserts: remaining RX FIFO space plus DMA ring slack that is truly free.

Safety requirement

RX_margin > Bstop

If the remaining RX headroom is smaller than the tail bytes that arrive during stop latency, overruns can still occur even when RTS/CTS appears to work.

What to measure

  • Bstop: count how many bytes appear on TXD after RTS/CTS requests “pause” (logic analyzer edge-to-byte counting).
  • Worst-case behavior: repeat under maximum burst and maximum system load to capture the largest tail.
  • RX headroom: log peak RX FIFO occupancy and DMA ring slack during stress windows.

What to configure

  • RTS assertion threshold: assert earlier to increase RX_margin (choose a watermark that leaves room for the measured tail).
  • TX FIFO / DMA burst behavior: reduce tail growth by limiting how much data can be “in flight” past the gate.
  • CTS gating granularity: verify whether gating stops only at byte boundaries (common) and budget accordingly.

What is safe (pass criteria template)

  • No overruns: overrun count = 0 during stress test for X minutes.
  • FIFO never hits full: peak occupancy remains below “full” with ≥ X bytes margin.
  • Tail is bounded: worst-case Bstop ≤ X bytes and is covered by the configured RX_margin.
Stop distance budget: RTS change to TX stop, tail bytes and RX margin A timing diagram shows RTS/CTS pause request, the peer reaction time Treact, tail bytes Bstop on TXD, and RX FIFO level rising above a threshold while consuming RX margin. Stop distance budget: tail bytes vs RX margin time → RTS/CTS TXD RX FIFO allowed pause resume pause edge TX stop Treact Bstop RTS threshold RX_margin must cover tail

UART controller behaviors: auto-RTS, auto-CTS, and DMA interplay

“Hardware flow control” differs across UART IP. Correct operation depends on where CTS gating occurs, how RTS is generated from RX occupancy, and how DMA bursts change tail bytes (Bstop) and consume RX headroom (RX_margin).

Peripheral capability checklist (datasheet keywords to search)

Auto-CTS (TX gating)

  • CTS enable / CTS polarity (idle, asserted, allow/deny mapping)
  • Gating granularity (byte-boundary stop is common)
  • Gating point (gating at TX FIFO write vs at shift-register launch)
  • CTS change event (interrupt/status latch, optional filter)

Auto-RTS (RX pressure generation)

  • RX watermark (assert threshold) and deassert level (hysteresis)
  • RTS polarity and reset/boot default state
  • Source of “pressure” (RX FIFO only vs FIFO + additional buffering signals)
  • RTS automation mode (fully automatic vs assisted by software)

FIFO and DMA (in-flight bytes)

  • RX/TX FIFO depth and trigger levels
  • DMA request threshold and burst size (burst affects tail bytes Bstop)
  • Circular/ring DMA support and buffer watermark hooks
  • Overrun flags and “FIFO full” status visibility

Two common “it still loses bytes” mechanisms (even with flow control enabled)

Failure A · RX DMA ring is near full, but RTS asserts too late

  • Reason: auto-RTS often tracks RX FIFO, not the remaining DMA ring slack.
  • Impact: RX_margin is overestimated; ring fills while FIFO watermark is not yet crossed.
  • Control knob: assert RTS earlier (lower FIFO watermark) and ensure software ring watermarks keep slack.

Failure B · TX DMA bursts enlarge tail bytes (Bstop)

  • Reason: DMA may push multiple bytes into TX FIFO after CTS requests pause.
  • Impact: Bstop increases, consuming RX_margin and violating the safety requirement.
  • Control knob: bound burst size and reduce “in-flight” bytes past the gate.

Configuration priority (order matters)

  1. Enable FIFO and choose conservative RX/TX trigger levels (leave headroom first).
  2. Enable auto-RTS/auto-CTS with verified polarity and documented allow/deny meaning.
  3. Bring up RX path before heavy traffic (avoid ring starvation during boot).
  4. Enable DMA with bounded burst (limit in-flight bytes that can escape after a pause).
  5. Stress and measure worst-case tail bytes (Bstop) and ensure RX_margin covers it.
FIFO, DMA, and RTS/CTS interplay state machine A block-style state machine shows RX FIFO watermarks driving RTS, CTS gating TX, and DMA ring/burst behavior affecting RX margin and tail bytes. UART IP behavior map: FIFO + DMA + RTS/CTS Receiver side RX FIFO watermark: LOW / HIGH RX DMA ring slack: FREE / LOW risk: ring full while FIFO not HIGH auto-RTS: assert / release RTS CTS Transmitter side auto-CTS gate allow / deny TX FIFO queued bytes TX DMA burst size: bounded risk: burst extends tail bytes (Bstop) threshold crossing gating affects queued bytes

Long-frame transfers: preventing overruns and keeping framing intact

Long frames amplify rare latency spikes: sustained RX occupancy leaves little slack, so brief service delays can still trigger overruns. Reliable long-frame delivery requires handshaking, sufficient buffering, and pause-friendly transfer structure.

Why long frames fail more often (handshake-relevant causes)

  • Sustained occupancy: RX stays near high watermark for long periods; slack is continuously consumed.
  • Latency spikes: scheduling jitter, cache effects, and bursty background work temporarily reduces draining rate.
  • Burst sources: peer devices often transmit in bursts; tail bytes (Bstop) must still fit in RX_margin.

Handshake-focused reliability rules

  1. Enable RTS/CTS for backpressure (do not rely on in-band control for primary throttling).
  2. Ensure RX headroom covers worst-case tail (RX_margin > Bstop under stress).
  3. Use pause-friendly framing so the stream can stop/resume without corrupting state.

Frame integrity (acceptance definition)

  • No loss: no missing bytes (no gaps within the frame).
  • No unintended duplication: repeats should be protocol-visible and recoverable if retransmission exists.
  • Order preserved: UART preserves ordering; integrity failures are typically loss-related.

Scenario A · Firmware update frames

  • Risk point: sustained streaming leaves no slack; a brief stall can overrun.
  • Handshake strategy: RTS asserts early; CTS gating must be effective at limiting in-flight bytes.
  • Buffer guidance: maintain ring slack above the measured worst-case tail bytes (Bstop).

Scenario B · Binary log dump / telemetry bursts

  • Risk point: logs can burst while the receiver is busy with other work.
  • Handshake strategy: treat backpressure as mandatory; allow pauses anywhere in the byte stream.
  • Buffer guidance: set RTS watermark to keep a fixed margin even at peak log rate.

Scenario C · Modem high-volume uplink/downlink

  • Risk point: bursty peer transmission and deep internal queues create long tails.
  • Handshake strategy: verify CTS gating behavior and measure worst-case tail bytes under load.
  • Buffer guidance: keep RX ring slack conservative; avoid consuming slack with unrelated high-priority tasks during transfers.
Long-frame chunking with RTS backpressure and resumed TX A long frame is split into chunks. RTS asserts during transfer to pause TX, RX drains, then RTS releases to resume. The diagram highlights no overrun and integrity kept. Long-frame delivery: chunk + backpressure + resume Frame (Long) Chunk 1 Chunk 2 Chunk 3 time → RTS allow pause resume TXD RX drain then resume No overrun Integrity kept

DTR/DSR usage patterns: readiness, sleep/wake, and session gating

DTR/DSR are best treated as session/readiness signals (online, ready, enter/exit data mode). They are not a primary byte-level throttle. Use DTR/DSR to gate whether a session may start or continue, and use RTS/CTS to regulate byte flow inside an active session.

Two-layer gating model (keep roles separated)

  • Session gate: DTR/DSR decides whether the link is “ready/online” for a session.
  • Flow gate: RTS/CTS controls byte-level backpressure during the session.
  • Practical rule: avoid using DTR/DSR as a byte throttle; avoid using RTS/CTS as a “ready” indicator.

Bring-up readiness pattern (power-on to “safe to talk”)

  • When to use: modules that ignore or mis-handle data before internal init completes.
  • Failure mode: early traffic causes dropped first commands, partial responses, or a stuck state-machine.
  • Quick check: align DSR (or equivalent “ready”) transition with the first reliable response window.
  • Safe default: keep “session disabled” until readiness is confirmed; then allow the session and rely on RTS/CTS for flow.

Sleep/wake session gating (enter/exit data mode)

  • When to use: peers that expose “data mode” vs “sleep/idle mode” behavior.
  • Failure mode: traffic starts before wake completes; early bytes are lost or mis-framed.
  • Quick check: measure DTR edge → DSR change latency across min/typ/max conditions.
  • Safe default: after wake request, allow a short guard time and confirm “ready” before high-volume transfer.

Note: DTR/DSR semantics originate from RS-232-style “readiness/session” conventions. Electrical levels, transceivers, and protection belong to the Voltage Levels & PHY subpage.

Two-layer gating: DTR/DSR session gate plus RTS/CTS flow gate A two-layer diagram shows the session gate controlled by DTR/DSR and the byte flow gate controlled by RTS/CTS. Data passes only when both gates allow. Dual gating: Session (DTR/DSR) + Flow (RTS/CTS) Host / MCU Peer / Module Session gate Flow gate DTR req DSR ready RTS press CTS allow DTR DSR RTS CTS Data stream session OK bytes OK

Wiring & grounding pitfalls: swapped lines, missing reference, ghost gating

Many “flow-control problems” are wiring problems: swapped handshake lines, connector definition mismatches, floating inputs at reset, or unstable ground reference that creates spurious edges. A fast acceptance checklist prevents chasing firmware for a physical-layer fault.

Common pitfalls (board, cable, adapter)

  • Swapped semantics: DTE/DCE conventions can invert expectations; avoid trusting names alone.
  • Connector confusion: board-to-board vs cable vs adapter may apply crossing twice (or not at all).
  • Missing reference: handshake lines still require a stable reference ground; common-mode motion can create false gating.
  • Floating inputs: CTS/DSR left floating at boot can become “always deny” or “random allow/deny”.

Wiring acceptance checklist (fast pass/fail)

1) Continuity

  • Verify end-to-end connectivity for TXD/RXD/RTS/CTS (and DTR/DSR if used).
  • Confirm whether crossing is present exactly once where intended.

2) Idle levels

  • Measure idle levels at reset and after pinmux; avoid floating CTS/DSR.
  • Ensure idle maps to a defined safety policy (allow vs deny).

3) Assert behavior

  • Force RTS/CTS asserted/deasserted and confirm TX gating changes as expected.
  • Check that the “allow” state matches documented polarity/semantics.

4) Unplug behavior

  • Unplug the cable and verify the system enters the intended safe state.
  • Reject designs where unplug creates random CTS/DSR toggling (“ghost gating”).
Handshake wiring verification: correct crossing vs wrong straight-through conflict A side-by-side diagram shows correct RTS to CTS crossing and an incorrect RTS to RTS straight connection causing output contention. A floating CTS input example highlights ghost gating risk. Wiring check: correct crossing vs wrong straight-through Correct Wrong Host Peer Host Peer RTS CTS CTS RTS cross RTS CTS RTS CTS output-to-output conflict Ghost gating risk floating CTS input noise edges can toggle allow/deny unexpectedly

Electrical integrity of handshake lines: edge control, filtering, debounce

Handshake lines are “slow” signals, but they control gating. On long cables or in noisy environments, a short CTS glitch can create unintended TX pauses (or releases). This section focuses on making handshake lines electrically stable without expanding into a full EMC system discussion.

Problem: handshake glitches become false gating

  • Symptom: TX shows unexpected gaps; throughput drops; protocol layers hit timeouts.
  • Typical triggers: long harness, strong common-mode disturbance, adjacent high-activity lines, weak/default-floating inputs.
  • Root mechanism: a narrow CTS (or RTS) glitch flips the gate state for long enough to interrupt TX scheduling.

Quick verification: prove “glitch → TX gap” correlation

  • Capture CTS and TXD simultaneously; check whether each TX gap aligns with a CTS edge/glitch.
  • Test reset/boot and unplug states: verify CTS is not floating and maps to a defined “allow/deny” policy.
  • Run a controlled A/B: enable a CTS filter (if available) or add a temporary debounce network and confirm the gaps disappear.

Design fixes: stable defaults + controlled edges + deglitching

1) Define a safe default state

  • Use pull-up/pull-down so CTS/DSR never floats during reset, boot, or unplug.
  • Ensure the default maps to the intended behavior (safe deny or safe allow).

2) Filter / debounce the gating input

  • Prefer a UART CTS filter/deglitch feature if present (closest to the gating point).
  • If not available, apply a simple debounce method so short glitches do not change the gate state.

3) Reduce coupled noise

  • Route handshake lines away from aggressive edges; avoid long parallel coupling with TXD or other high-activity nets.
  • Keep a stable reference return; prevent common-mode motion that creates false transitions at the receiver.

Pass criteria (placeholder): CTS noise must not create unintended TX gating. Example acceptance form: in a window T, false TX gaps caused by CTS ≤ X, and single-gap duration ≤ Y.

CTS glitch causing TX gaps without filtering, and stable TX with filtering Two stacked wave-style panels compare no filtering versus filtering. A CTS glitch flips gate state and creates a TX gap in the top panel; the bottom panel shows the glitch ignored and TX remains continuous. Handshake integrity: CTS glitch vs TX continuity No filter CTS Gate TXD time → glitch ALLOW DENY TX gap With filter CTS Gate TXD glitch ALLOW no false stop continuous

Firmware & driver design: buffering strategy + backpressure aware APIs

Hardware flow control reduces risk, but software must still manage buffering, avoid CPU-heavy busy-waiting, and expose observability. A robust design combines RX ring watermarks, CTS-aware TX queuing, and measurable “blocked” and “occupancy” metrics.

Engineering checklist (Design → Bring-up → Production)

Design: APIs and architecture

  • RX: ring buffer + watermark; ISR/DMA does transport only (avoid heavy work in ISR).
  • TX: queue-based transmit; CTS-aware non-blocking sends (no CPU busy-wait).
  • Backpressure: gate awareness is centralized in the driver (avoid bypass paths that ignore CTS).

Bring-up: observability fields to log

cts_state · blocked_ms_total · blocked_ms_max · txq_depth_max

overrun_count · rx_fifo_max · rx_slack_min · rx_ring_level_max

Emit a snapshot at first block, at block-over-threshold, and at every overrun to make gating behavior measurable.

Production: alarm placeholders (calibrate per product)

  • CTS blocked time in window T > X ms → raise a “blocked-too-long” event.
  • overrun_count > Y → raise a “rx-overrun” event.
  • rx_slack_min < Z bytes → raise a “low-slack” event.

Timeout policy (interface-level hook): if CTS denies for “too long”, the driver should surface a state/event. Session reset or escalation is handled by the higher-level owner (details belong in a dedicated recovery section or an FAQ item).

Backpressure-aware software queues and hardware CTS gating data path A block diagram shows TX: App to TX queue to UART to CTS gate to line, and RX: line to UART RX FIFO to DMA/ISR to RX ring to parser. Metric boxes show blocked time and occupancy. Software + hardware path: queues, gating, and observability TX path App TX queue depth / max UART driver CTS gate allow/deny line blocked time RX path line RX FIFO max level DMA/ISR RX ring min slack parser Backpressure-aware

Validation & instrumentation: logic analyzer triggers and pass/fail criteria

Validation should close the loop: signal evidence (RTS/CTS vs TXD/RXD) plus counters (overrun/blocked/occupancy) to prove flow control is effective under pressure and under background jitter.

What to capture (minimum set)

Logic analyzer signals

  • Always: TXD, RXD, CTS, RTS
  • If used: DTR, DSR (session gating)
  • Optional marker: a GPIO “load marker” to align background-load windows

Counters / metrics (bring-up + production)

  • Required: overrun_count (or overrun flag events)
  • Recommended: blocked_time_total, blocked_time_max
  • Recommended: rx_fifo_max (or rx_slack_min), txq_depth_max

Capture tips (avoid false conclusions)

  • Expect tail bytes: gating cannot interrupt a character mid-flight; FIFO/shift-register tail is normal.
  • Align “gating edges” (RTS/CTS transitions) with TXD activity to confirm cause-and-effect.
  • If results are inconsistent, validate polarity/default state before tuning thresholds.

Logic analyzer trigger recipes (high signal-to-noise)

Trigger A · RTS asserts, but TXD keeps running

  • Signal condition: RTS changes to “busy” (asserted), while TXD remains continuously active well beyond the expected tail window.
  • Expected: TXD activity stops after tail bytes; a clear TX gap appears while RTS remains asserted.
  • Interpretation: gating not effective (polarity/mapping/wiring) or the peer ignores backpressure.

Trigger B · CTS denies, but TXD keeps running

  • Signal condition: CTS indicates “deny”, but TXD remains active beyond the tail window.
  • Expected: TXD becomes gated (stops after tail); software queues grow, not the line.
  • Interpretation: auto-CTS not enabled, or a driver path bypasses gating (direct FIFO writes).

Trigger C · Overrun flag (or overrun_count increments)

  • Signal condition: overrun occurs during a pressure window (burst or background jitter).
  • Expected: no overrun; if gating is active, the trace should show RTS/CTS asserting before overflow.
  • Interpretation: insufficient stop-distance budget, thresholds too late, DMA burst too large, or handshake glitching.
Logic analyzer trigger cards for UART hardware flow control validation Three stacked trigger cards A, B, and C show a signal condition, expected behavior, and interpretation. The diagram acts as a quick reference for pass/fail gating validation. Validation quick reference: triggers A/B/C A Condition: RTS asserted → TXD keeps running Expected: TXD stops after tail bytes (clear TX gap) Meaning: peer ignores gating or polarity/mapping/wiring is wrong B Condition: CTS deny → TXD keeps running Expected: TXD gated; bytes stay in TX queue Meaning: auto-CTS disabled or driver bypasses gating C Condition: Overrun flag / overrun_count increments Expected: overrun_count = 0 in pressure window Meaning: budget/threshold/DMA burst/glitch issue

Two high-value test scripts (steps only)

Script 1 · Burst TX pressure test

  1. Setup: capture TXD/RXD/CTS/RTS (+ optional DTR/DSR) and enable overrun/blocked/occupancy counters.
  2. Stress: send bursts (chunked payload) while the receiver periodically slows consumption.
  3. Observe: RTS/CTS must assert before overflow; TXD must show a gated gap after tail bytes.
  4. Decide: overrun_count must remain 0 for N minutes / N cycles (placeholder).
  5. If fail: A/B triggers indicate gating not effective; C indicates budget/threshold/burst or glitching.

Script 2 · Background load jitter test

  1. Setup: log cts_state, blocked_time_total/max, rx_slack_min (or rx_fifo_max), overrun_count.
  2. Stress: introduce periodic CPU/IRQ jitter windows (implementation is platform-specific).
  3. Observe: blocked duty cycle may rise, but overrun_count should stay 0.
  4. Correlate: if TX gaps align with brief CTS glitches, treat as a handshake integrity issue (filter/default state).
  5. Decide: pass/fail uses the same template below, with placeholders calibrated per product.

Pass/Fail template (drop into a validation report)

  • Overrun: overrun_count = 0 during pressure test for N minutes (placeholder).
  • Blocked duty: CTS blocked duty cycle ≤ X% in window T (placeholder).
  • Margin: rx_fifo_max < threshold (or rx_slack_min > Z bytes) (placeholder).
  • Evidence: at least one capture shows RTS/CTS transition → TXD gating gap after tail bytes.

A failure should be tagged by trigger type: A/B = gating not effective; C = budget/threshold/burst or glitch-related.

Concrete instrumentation BOM (example material numbers)

Logic analyzer (choose one)

  • Saleae Logic Pro 8 (8-ch USB logic analyzer)
  • Saleae Logic Pro 16 (16-ch USB logic analyzer)
  • DreamSourceLab DSLogic Plus (USB logic analyzer family)

Board access (headers / pogo / debug cables)

  • Tag-Connect TC2030-IDC (6-pin Plug-of-Nails cable, IDC ribbon connector)
  • Tag-Connect TC2030-IDC-10 (10″ / 254mm variant, if longer reach is needed)
  • Samtec FTSH-105-01-L-DV-K (2×5, 1.27mm SMD header; compact logic analyzer / debug header)
  • Samtec TSW-106-07-G-S (1×6, 2.54mm TH header; simple UART breakout)

Select one standardized footprint per board family to prevent “ad-hoc flying-wire” validation.

Probing accessories (examples)

  • Pomona 5250 (mini grabber clips for fast attachment)
  • Keystone 5000 (turret-style test point for repeated clip-on)
  • Panasonic ERJ-3GEY0R00V (0603 0Ω jumper; convenient “insert/remove” points for inline capture)

Note: instrumentation part numbers above are examples. Confirm footprint, plating, pitch, and assembly constraints per board standard.

Request a Quote

Accepted Formats

pdf, csv, xls, xlsx, zip

Attachment

Drag & drop files here or use the button below.

FAQs

Troubleshooting is kept strictly within flow-control boundaries (polarity, stop-distance, default states, CTS glitches, FIFO/DMA interactions). Pass criteria use placeholders for calibration.

Metric placeholders (use consistently across validation)

N_min=test duration (minutes) · T_win=measurement window · X_pct=blocked duty limit · Z_bytes=min RX slack · M_bytes=FIFO margin · Bstop_max=max allowed tail bytes · G_gaps=max false TX gaps per window

Enabled RTS/CTS but still see overruns under burst — why?
Likely cause: Stop-distance budget is violated (tail bytes exceed remaining RX margin) or thresholds assert too late under burst/DMA behavior.
Quick check: Capture RTS/CTS + TXD + RXD; confirm gating occurs and measure tail bytes after assert; log rx_fifo_max / rx_slack_min during bursts.
Fix: Assert RTS earlier (lower RX FIFO threshold / higher watermark), reduce TX DMA burst size, and increase RX slack (FIFO trigger + ring watermarks) so RX_margin > Bstop.
Pass criteria: overrun_count=0 for N_min minutes; measured tail bytes ≤ Bstop_max; rx_slack_min ≥ Z_bytes.
CTS toggles but TX doesn’t pause — what’s the first register check?
Likely cause: Auto-CTS gating is not enabled, CTS polarity is inverted, or TX path bypasses hardware gating (direct FIFO writes / alternative UART instance).
Quick check: Verify UART flow-control enable (CTS gating) + CTS polarity + pin mux mapping to the correct UART pad; test by forcing CTS to the deny level and observing whether TXD stops after tail bytes.
Fix: Enable auto-CTS in the UART controller, correct polarity/active-low settings, and ensure the driver uses the gated TX path (no bypass writes).
Pass criteria: For CTS=deny, TXD becomes idle after tail bytes within T_win; blocked_time_total increases while overrun_count remains 0.
TX pauses forever after boot — what default pin state usually caused this?
Likely cause: CTS is floating or pulled to the deny level during reset/boot (common with active-low semantics), so the gate never opens.
Quick check: Measure CTS idle level at power-up and during boot; confirm internal pull configuration and whether the pin is tri-stated before UART takes ownership.
Fix: Add/enable a defined pull-up/down to enforce a known allow/deny default, and align UART CTS polarity to that default; ensure pin mux switches early enough.
Pass criteria: After boot, CTS idle is stable (no floating) and TX begins when expected; blocked_time_max ≤ X_pct of T_win (or per system policy).
After enabling DMA, handshake became worse — what burst/threshold interaction is common?
Likely cause: TX DMA bursts enlarge tail bytes (Bstop), and RX side asserts RTS too late relative to the new burst/latency profile.
Quick check: Compare tail bytes (no-DMA vs DMA) and observe whether RTS/CTS edges occur earlier/later; log rx_fifo_max and rx_slack_min under the same burst pattern.
Fix: Reduce DMA burst size, raise DMA service priority, and move RTS assert threshold earlier to preserve RX margin; keep RX ring watermarks conservative for bursty sources.
Pass criteria: With DMA enabled, overrun_count=0 for N_min minutes; tail bytes ≤ Bstop_max; rx_fifo_max stays at least M_bytes below overflow.
Short cable is OK, long cable causes random pauses — CTS glitch or ground reference?
Likely cause: CTS sees spurious transitions (glitches) due to noise/ground reference motion; the gate toggles even when the peer is ready.
Quick check: Capture CTS and TXD; verify whether each unexpected TX gap aligns with a short CTS glitch; test with a temporary stronger pull and/or CTS filter (if supported).
Fix: Define CTS default with pull-up/down, enable CTS deglitch/filter or add simple debounce, and improve reference integrity (avoid floating/shield-only returns for handshake lines).
Pass criteria: False TX gaps caused by CTS glitches ≤ G_gaps per T_win; blocked_duty ≤ X_pct; overrun_count=0.
Only one direction loses data — does RTS actually reflect RX buffer state?
Likely cause: Auto-RTS is disabled or threshold is misconfigured, so RTS does not assert based on RX FIFO pressure; only the opposite direction is protected.
Quick check: Stress RX only; watch RTS state vs rx_fifo_level (or rx_slack); confirm RTS changes when FIFO crosses the configured threshold.
Fix: Enable auto-RTS, set a conservative assert threshold (earlier), and ensure RX ring watermarks and ISR/DMA service keep pace with expected bursts.
Pass criteria: Under RX-only stress, RTS asserts before high-water and prevents overflow; rx_slack_min ≥ Z_bytes; overrun_count=0.
DTR/DSR wired, but it never enters data mode — what’s the first session-gating sanity check?
Likely cause: DTR/DSR polarity/idle meaning is inverted, or the module expects a specific “ready” sequence before accepting data.
Quick check: Observe DTR and DSR at boot; confirm whether DSR ever indicates ready; manually force DTR to the expected “ready” level and see if DSR changes state.
Fix: Correct polarity/default pulls for DTR/DSR and follow the required session gating sequence; keep RTS/CTS as the in-session byte flow gate.
Pass criteria: DSR indicates ready within T_win after DTR is asserted; data mode is entered without overrun; blocked_duty remains within X_pct.
Logic analyzer shows RTS asserts “too late” — how to pick a safer FIFO threshold?
Likely cause: RTS asserts near overflow, leaving insufficient RX margin for tail bytes and service latency.
Quick check: Measure tail bytes (Bstop) after RTS assert and compare with remaining RX space; log rx_fifo_max and rx_slack_min during the same stress profile.
Fix: Set RTS assert threshold earlier: enforce RX_margin > Bstop + latency cushion; adjust FIFO trigger levels and ring watermarks accordingly.
Pass criteria: rx_fifo_max stays at least M_bytes below overflow; tail bytes ≤ Bstop_max; overrun_count=0 for N_min minutes.
CTS polarity is correct, but pauses occur during high EMI events — what filtering fix is fastest?
Likely cause: EMI introduces narrow CTS glitches; gating logic treats them as valid transitions, creating unintended TX gaps.
Quick check: Correlate TX gaps with CTS glitch width; if the glitch is shorter than a character time yet still gates TX, filtering is missing or too weak.
Fix: Enable UART CTS deglitch/filter if available; otherwise add a defined pull plus simple debounce so short glitches do not toggle the gate.
Pass criteria: False TX gaps ≤ G_gaps per T_win; blocked_duty ≤ X_pct; overrun_count=0 during EMI-stress run.
Why does software flow control (XON/XOFF) fail but RTS/CTS works?
Likely cause: Software flow control depends on timely parsing of in-band control bytes; under burst/jitter it can arrive too late or be indistinguishable from binary payload.
Quick check: Under the same pressure test, compare whether “deny” intent appears only after FIFO is already near full; check for binary payload collisions with control bytes.
Fix: Use RTS/CTS for physical backpressure (out-of-band) and keep software flow control only where framing and parsing are guaranteed safe.
Pass criteria: Under burst, overrun_count=0 for N_min minutes with RTS/CTS; blocked_duty and rx_slack_min meet targets (X_pct, Z_bytes).
Can RTS be used for wake-up and flow control together — what conflict appears?
Likely cause: Wake signaling requires deterministic toggles, while flow control toggles based on buffer pressure; combining them can create ambiguous states or unintended wake storms.
Quick check: During sleep/wake tests, log RTS transitions and correlate them with RX occupancy; if RTS toggles during normal buffering, it is not a clean wake indicator.
Fix: Use DTR/DSR (or a dedicated wake GPIO) for session/wake gating, and reserve RTS/CTS exclusively for in-session flow control.
Pass criteria: Wake transition count ≤ G_gaps per T_win (placeholder); during session, overrun_count=0 and blocked_duty ≤ X_pct.
Handshake works, but throughput drops too much — what’s the first optimization knob?
Likely cause: Thresholds are overly conservative (RTS asserts too early), or CTS glitches inflate blocked time; the link is protected but over-throttled.
Quick check: Inspect blocked_duty and blocked_time_max; verify whether blocks are driven by real buffer pressure or by narrow CTS toggles/glitches.
Fix: First tune FIFO/watermark thresholds to reduce unnecessary blocking while maintaining RX margin; then tighten CTS filtering and adjust DMA burst to reduce tail and jitter-driven blocks.
Pass criteria: overrun_count=0 for N_min minutes; blocked_duty ≤ X_pct with stable CTS (false gaps ≤ G_gaps per T_win).