UART · Module 9
False Starts, Noise and Malformed Frames
A glitch, a hot-plug or a reset released onto live traffic can put a receiver into a frame nobody transmitted. Idle detection suppresses it — at a cost worth pricing before adopting.
Every failure so far involved a frame that was really sent. This chapter covers the opposite: a frame the receiver assembled that nobody transmitted.
Chapter 5.2 built start qualification and established its exact guarantee — it rejects disturbances shorter than the validation delay, and nothing more. Chapter 6.2 §7 then demonstrated the case qualification cannot help with: a receiver released from reset onto a live line produced 0xFA from a frame carrying 0xA6, with clean status.
Both chapters stated the limit and deferred the remedy here. So this chapter does three things: it maps the space of start-side failures precisely, it builds the idle detection that closes the mid-frame case, and it prices that remedy honestly — because it is not free and it is not always wanted.
1. Four Start-Side Failures, by What Rejects Them
They are usually lumped together as "noise", and they are rejected — or not — by completely different mechanisms.
| Failure | Shape on the wire | Rejected by | Cost when it gets through |
|---|---|---|---|
| Narrow glitch | low for less than the validation delay | qualification — 5.2 | none; it never becomes a frame |
| Wide disturbance | low past the validation point | nothing | a fabricated frame, timed from the glitch |
| Mid-sample noise | transient at one sampling instant | majority voting, if fitted — 5.4 | one wrong bit, possibly caught by parity |
| Mid-frame attach | the line is mid-frame when we start looking | idle detection — §4 | a fabricated frame from someone else's data |
Only the first is handled by the receiver as built in Module 6. The second and fourth produce complete, well-formed-looking frames that the receiver has no basis to reject, and the third is Chapter 5.4's trade.
2. The Glitch That Is Rejected
The case qualification handles, for completeness and as the positive control every other case is measured against.
A disturbance shorter than M/2 oversample ticks: the edge detector produces a candidate, the qualifier starts counting, and at the validation point the line has returned to mark. The candidate is abandoned, no frame begins, and nothing is reported — there is no error, because nothing was lost.
That silence is correct and worth defending. A receiver that reported every rejected candidate would produce a stream of "errors" on any electrically busy board, and the events it reported would carry no information: a rejected candidate is the mechanism working.
Where a counter is genuinely useful is bring-up. A rejected-candidate count that is zero on a healthy board and climbing on a marginal one is a sensitive early indicator — and it belongs as a debug output rather than an error flag, for the reason above.
3. The Glitch That Is Not
Widen the same disturbance past the validation point and everything changes:
candidate at t=0 edge detector fires
line still at space at M/2 qualification PASSES
-> start accepted, timing origin set to a disturbance
-> eight data samples taken at 1.5, 2.5, ... UI from nothing in particular
-> a stop sample taken at 9.5 UI
-> a byte is deliveredThe receiver has no basis to reject this. Every check it performs passes or fails on its own terms: the start qualified, the data intervals were sampled at their scheduled positions, and the stop sample lands wherever the real traffic happens to be. If it happens to find mark there, the frame is delivered with completely clean status.
The observable consequences:
A framing error is likely but not guaranteed. The stop sample lands at an arbitrary point relative to real traffic, so it is mark roughly half the time on a busy line and always mark on an idle one.
The payload is meaningless, and there is no flag for that. Chapter 9.2 applies: parity over a fabricated payload is satisfied about half the time.
The receiver's timing origin is now wrong for subsequent frames too, until it returns to idle and re-anchors on a real start edge — which is Chapter 9.6's subject.
Narrow glitch rejected, wider glitch accepted
16 cycles4. Mid-Frame Attach, and the Block That Fixes It
Chapter 6.2 §7 demonstrated this and left it open. A receiver begins observing a line that is already carrying traffic — after reset, after hot-plug, after a connector is inserted — and the first mark-to-space transition it sees is not a start bit. It is a data edge inside someone else's frame.
The receiver qualifies it correctly, samples eight intervals from it correctly, and delivers a byte assembled from the tail of one frame and the head of the next. Simulation of the Module 6 receiver produced 0xFA from a frame carrying 0xA6, with the framing check passing because the line happened to be at mark where the stop sample landed.
Nothing in a UART frame identifies itself as a continuation. That is the direct consequence of Chapter 3.1's observation that framing is re-established from scratch at every start bit — there is no frame counter, no sequence number and no header. A receiver joining mid-stream cannot know it has.
The remedy is to refuse to trust a start until the line has been quiet long enough to prove no frame is in progress:
Require the line to have been continuously at MARK for one full frame time
before any start candidate may be qualified.A valid frame cannot hold the line at mark for a full frame time — the start interval is space — so observing that condition proves the line is genuinely idle rather than mid-frame.
A receiver joining live traffic
13 cyclesThe RTL
// ---------------------------------------------------------------------------
// 9.5 — idle qualification. The technique Chapter 6.2 §7 deferred: refuse to
// accept a start until the line has been at mark for a full frame time, so a
// receiver joining a live line cannot mis-frame on the remainder.
// ---------------------------------------------------------------------------
module uart_idle_qualify #(
parameter int unsigned OVERSAMPLE = 16,
parameter int unsigned FRAME_BITS = 10
) (
input logic clk,
input logic rst_n,
input logic os_tick_i,
input logic rx_sync_i,
output logic line_idle_o // LEVEL: mark held for a full frame time
);
localparam int unsigned IDLE_TICKS = FRAME_BITS * OVERSAMPLE;
localparam int unsigned CNT_W = (IDLE_TICKS <= 1) ? 1 : $clog2(IDLE_TICKS + 1);
logic [CNT_W-1:0] mark_cnt_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
mark_cnt_q <= '0;
line_idle_o <= 1'b0; // NOT idle out of reset — must be earned
end else if (rx_sync_i == 1'b0) begin
mark_cnt_q <= '0;
line_idle_o <= 1'b0;
end else if (os_tick_i) begin
if (mark_cnt_q == CNT_W'(IDLE_TICKS)) begin
mark_cnt_q <= mark_cnt_q; // saturate
end else begin
mark_cnt_q <= mark_cnt_q + 1'b1;
if (mark_cnt_q == CNT_W'(IDLE_TICKS - 1)) line_idle_o <= 1'b1;
end
end
end
endmoduleIt is the break detector's structure with the polarity inverted, which is not a coincidence: both answer "has the line held one level long enough to prove something?" — one about space, one about mark. Sharing the shape means sharing the reasoning, including the saturation and the +1 width derivation of Chapter 9.4 §4.
line_idle_o resets to 0, not 1. Idle must be earned, not assumed. A receiver out of reset has observed nothing and has no basis to claim the line is quiet — which is precisely the mid-frame case this block exists to catch. Resetting it high would defeat the entire mechanism at the one moment it matters most.
A single low sample clears it immediately, without waiting for a tick, for the same reason the break detector resets on a single mark: the condition is continuous, and deferring the clear would let a narrow disturbance be absorbed.
Integration is one extra term at the qualifier:
// Conceptual SystemVerilog — gating start acceptance on proven idle.
// The qualifier itself is Chapter 5.2's, unmodified; this adds a
// precondition rather than changing the mechanism.
if (start_cand_i && line_idle_o) begin
// ... Chapter 5.2's qualification proceeds from here
endThe measured behaviour
| Stimulus | Expected | Result |
|---|---|---|
| line low | not idle | pass |
| 159 mark ticks (one short of a frame) | not idle | pass |
| 160 mark ticks (a full frame) | idle | pass |
| counter after threshold | saturates at 160 | pass |
| a single low sample | idle cleared immediately | pass |
5. What Idle Detection Costs
It is a real remedy with a real price, and adopting it without the price is how a fix becomes a new problem.
Reception latency after every quiet period. The first frame after any idle gap shorter than a full frame time is rejected, because the line has not yet proven itself idle. On a link that sends isolated bytes with short gaps, this can reject a substantial fraction of traffic — and the rejection is silent, which is worse than a reported error.
It does not help a line that is never idle. A continuously saturated link — back-to-back frames with no gaps, which Chapter 7.4 showed a transmitter producing deliberately — never presents a full frame time of mark. A receiver attaching to such a link with idle detection enabled receives nothing at all, indefinitely, which is a far worse failure than one fabricated byte.
It cannot distinguish a genuinely idle line from a disconnected one. Chapter 1.3 noted that idle and several faults are indistinguishable; this block declares idle for both.
| Without idle detection | With idle detection | |
|---|---|---|
| Mid-frame attach | one fabricated byte, then correct | correct from the first frame |
| Isolated bytes, short gaps | all received | some silently rejected |
| Saturated link, attach mid-stream | one fabricated byte, then correct | nothing received, ever |
| Hardware | none | a counter and a comparator |
So it is the right default for a link with natural gaps and the wrong default for a saturated one, which is why Module 6 left it out of the base receiver and named it here rather than adding it silently. A design should choose deliberately, and a design that cannot predict its traffic pattern should probably accept the one bad byte and let the layer above discard it.
6. Malformed Frames: What Is Observable
A frame that starts legitimately can still go wrong in several ways, and the useful question for each is not "what happened" — the receiver cannot know — but what can hardware observe, and what status is justified?
| Shape | What hardware observes | Justified status | Payload delivered? |
|---|---|---|---|
| Valid start, corrupted data | nothing unusual at all | none — unless parity is enabled and the corruption is odd-weight | yes, wrong |
| Valid start, parity fails | the parity relation does not hold | parity_error | yes, with the flag |
| Valid start, stop is space | the stop sample is not mark | framing_error | yes, with the flag |
| Line goes low and stays | the stop sample fails, then the break threshold is reached | framing_error then break | yes for the frame, then nothing |
| Wide glitch accepted as start | nothing distinguishable from a real frame | none guaranteed | yes, meaningless |
The first and last rows are the important ones, and they say the same thing from opposite directions: the receiver has no mechanism that reports "this frame is meaningless". It reports specific relation failures, and a frame can be entirely fabricated while satisfying every one of them.
The fourth row shows why two detectors are better than one. A line that goes low mid-frame and stays produces a framing error immediately and a break declaration one frame time later. The framing error alone is ambiguous; followed by a break, the interpretation is unambiguous — and this is why Chapter 9.4's detector runs as an observer rather than inside the FSM, so it keeps counting through a frame that is already failing.
7. Verification
Sweep glitch width across the qualification boundary. Narrower than M/2 must be rejected silently; wider must be accepted. The boundary is where a defect in the qualifier shows, and testing only "very short" and "a real frame" leaves it untouched.
Sweep glitch phase as well as width. A disturbance of fixed width lands differently depending on where it falls relative to the tick grid, so sweeping both finds the region rather than a single point — the same argument Chapter 5.4 §7 made about the voting window.
Test mid-frame attach explicitly. Release the receiver from reset while a frame is in flight and assert what actually happens — which, without idle detection, is a fabricated byte. Chapter 6.2 §7's simulation did exactly this and the expected result is not "no byte":
reset released mid-frame, no idle detection
-> one fabricated byte (0xFA from a frame carrying 0xA6), status clean
-> subsequent frames correctA testbench that asserts no byte appears will fail on a correct receiver. That is worth knowing before spending a day on it.
Then test the same scenario with idle detection enabled and assert the byte is suppressed — and, separately, that a saturated link with idle detection receives nothing, because that is the cost and it should be visible in the suite rather than discovered in the field.
// Assertion — idle is never claimed while the line is low.
property p_idle_implies_mark;
@(posedge clk) disable iff (!rst_n)
line_idle_o |-> rx_sync_i;
endproperty
assert property (p_idle_implies_mark);
// Assertion — idle cannot be claimed before a full frame time of mark.
property p_no_early_idle;
@(posedge clk) disable iff (!rst_n)
$rose(line_idle_o) |-> ($past(mark_cnt_q) == CNT_W'(IDLE_TICKS - 1));
endproperty
assert property (p_no_early_idle);
// Assertion — the counter saturates, matching Chapter 9.4's policy.
property p_idle_counter_saturates;
@(posedge clk) disable iff (!rst_n)
mark_cnt_q <= CNT_W'(IDLE_TICKS);
endproperty
assert property (p_idle_counter_saturates);
// Assertion — with idle gating fitted, no start is accepted from a line
// that has not proven itself idle. This is the block's entire purpose.
property p_no_start_without_idle;
@(posedge clk) disable iff (!rst_n)
start_accept_o |-> $past(line_idle_o);
endproperty
assert property (p_no_start_without_idle);8. Debugging
9. What This Means on an FPGA
A floating receive input is the most common source of fabricated frames, and it is a board-level fix rather than an RTL one. An unconnected pin with no pull-up will produce edges, some of them wide enough to qualify, and the receiver will faithfully deliver bytes assembled from noise. A pull-up to the idle level costs one resistor and removes the entire failure class.
Probe start_cand alongside start_accept. The ratio between them is the qualifier's rejection rate, and it is a direct measure of line quality that no error flag provides. A board where candidates vastly outnumber acceptances is telling you something before any data is corrupted.
Idle detection is one counter, the same size as the break detector's, and the two can share nothing because they count opposite levels — but they can share the derivation and the review.
If both are fitted, expect them to be mutually exclusive. line_idle_o and break_active_o can never be true together, since one requires a full frame of mark and the other a full frame of space. An assertion to that effect is cheap and catches a polarity error in either block.
10. Understanding Check
11. Summary
Four start-side failures, rejected by four different mechanisms — or by none. A narrow glitch is rejected by qualification. A wide disturbance is rejected by nothing and produces a fabricated frame timed from noise. Mid-sample noise is Chapter 5.4's trade. Mid-frame attach is rejected only by idle detection.
Oversampling narrows the damaging class; it does not eliminate it. Qualification rejects what is narrower than the validation delay — 4.34 µs at 115,200 baud with M/2. Voting rejects what is narrower than one tick — 542.5 ns at 16×. A wide, well-placed disturbance is untouched by both.
A rejected candidate is not an error and should not be reported as one; as a debug counter it is a sensitive indicator of line quality.
Mid-frame attach produces a complete, clean-looking, entirely fabricated byte — 0xFA from a frame carrying 0xA6 in Module 6's simulation. Nothing in a UART frame identifies itself as a continuation, so the receiver cannot know.
Idle detection closes it: require a full frame time of continuous mark before qualifying any start, since a valid frame cannot hold mark that long. line_idle_o resets to 0 — idle is earned, not assumed.
And it costs. The first frame after any short gap is silently rejected, and on a saturated link the receiver gets nothing at all, indefinitely — a worse failure than the byte it was fitted to prevent. Right default for links with gaps; wrong for links without.
No status means "this frame is meaningless." The receiver reports relation failures, and a fabricated frame can satisfy every relation — which is the argument for framing above the byte layer.
12. What Comes Next
Five chapters have produced events: framing, parity, overrun, break, and the start-side failures that produce no event at all. None of them has said what the receiver should do.
Chapter 9.6 closes the module with that. It separates transient events from persistent state, builds the sticky flags with an explicit collision policy — what happens when software clears on the same cycle a new error arrives — distinguishes per-character status from historical status, and builds the re-synchronisation that returns the receiver to a trustworthy frame boundary without turning one bad frame into many.
Browse the full path on the UART tutorials index. For the qualification mechanism this chapter attacks, read back to Chapter 5.2; for the fabricated byte it explains, Chapter 6.2.
Continue learning
Related tutorials
- Related topic
Framing Errors
A framing error is one sampled bit at one instant. It proves the line was not at mark where the receiver expected mark — and nothing about why, which is what makes the diagnosis interesting.
- Related topic
Parity Errors and the Limits of Detection
Parity detects every odd-weight corruption and provably misses every even-weight one — shown by exhaustive enumeration. A check that passes says a relation holds, not that the data is correct.
- Related topic
Overrun, Underrun and Data Loss
Overrun is a protocol-level consequence of a full holding register; transmit starvation is an architecture-dependent system event. Both lose information an error flag records but cannot recover.
- Related topic
Break Conditions: Generation and Detection
A start bit is also low, so a break cannot be detected by looking at the line. It is the one UART condition defined by duration — which means a counter, a derived threshold, and a saturation policy.
Where this fits
Part of the UART curriculum.
