UART · Module 9
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.
Every condition so far has been decided by one sample at one instant. A framing error is the stop sample's level; a parity error is one comparison; an overrun is a state test on one clock edge.
A break is not like that, and the reason is a single observation that rules out the obvious implementation:
A start bit is also low.
So rx_sync_q == 0 carries no information on its own — it is the normal appearance of the first interval of every frame ever sent. A break is distinguishable from a start bit only by how long it lasts, which makes it the one condition in this module that requires a counter, a threshold and a policy for what happens when the counter reaches the top.
1. Why a Level Test Cannot Work
// WRONG — this is true during the start bit of every frame.
assign break_detected = ~rx_sync_q;The line is at space for the whole start interval and for any data bit that happens to be zero. A byte of 0x00 in 8N1 holds the line low for nine consecutive intervals — start plus eight zero data bits — and only the stop interval returns it to mark.
That last number is worth pausing on, because it sets the floor for any threshold:
8N1 transmitting 0x00:
start 0
d0..d7 0 0 0 0 0 0 0 0 <- eight zero data bits
stop 1
-> nine consecutive low intervals in a PERFECTLY VALID frameAny threshold at or below nine intervals would declare a break on legitimate traffic. A threshold of one full frame — ten intervals at 8N1 — is the smallest that cannot, because the stop interval must be mark and a valid frame therefore cannot hold the line low for its entire duration.
2. The Threshold, Derived
The model this chapter implements, stated once:
Declare a break when the synchronised line has been continuously at space
for at least one full frame time, measured in oversample ticks.
BREAK_TICKS = FRAME_BITS × OVERSAMPLEMachine-computed for the standard configurations at OVERSAMPLE = 16:
| config | data | parity | stop | frame bits | BREAK_TICKS | counter width | minimum low time @115,200 |
|---|---|---|---|---|---|---|---|
| 5N1 | 5 | 0 | 1 | 7 | 112 | 7 | 60.76 µs |
| 8N1 | 8 | 0 | 1 | 10 | 160 | 8 | 86.81 µs |
| 7E1 | 7 | 1 | 1 | 10 | 160 | 8 | 86.81 µs |
| 8E1 | 8 | 1 | 1 | 11 | 176 | 8 | 95.49 µs |
| 8N2 | 8 | 0 | 2 | 11 | 176 | 8 | 95.49 µs |
The threshold depends on the configuration, not on the protocol. 7E1 and 8N1 share a threshold because they share a frame length — which is the same coincidence Chapter 8.5 noted about frame duration, and for the same reason.
3. Three Outputs, Not One
A break produces three architecturally distinct pieces of information, and collapsing them loses something each time:
| Output | Shape | Means |
|---|---|---|
break_active_o | level | the line is low now, past the threshold |
break_evt_o | one-cycle pulse | the moment the break was declared |
break_released_o | one-cycle pulse | the line has returned to mark |
break_active_o is a level because a break is a condition, not an event. It is the only status in this module that is a level rather than a latched verdict, because it describes the line's present state rather than a judgement about a past frame. Software polling it learns whether the link is currently broken.
break_evt_o is what the sticky aggregator consumes, matching the shape every other error source in this module produces (Chapter 9.1 §2). It fires once per break, not once per tick while the line stays low — §4 shows the structure that guarantees that.
break_released_o is the one most often omitted, and it is the one recovery needs. Chapter 9.6 uses it to decide when the receiver may look for a start bit again; without it, the recovery logic has to poll the level and rediscover the transition itself.
4. The RTL
// ---------------------------------------------------------------------------
// 9.4 — break detector. Duration-based, counted in oversample ticks, with a
// SATURATING counter so a long break cannot wrap and un-declare itself.
// ---------------------------------------------------------------------------
module uart_break_detect #(
parameter int unsigned OVERSAMPLE = 16,
parameter int unsigned FRAME_BITS = 10 // 1 start + 8 data + 1 stop
) (
input logic clk,
input logic rst_n,
input logic os_tick_i,
input logic rx_sync_i, // synchronised line — Chapter 5.1
output logic break_active_o, // LEVEL: held low past the threshold
output logic break_evt_o, // ONE cycle: the moment it is declared
output logic break_released_o // ONE cycle: line returned to mark
);
localparam int unsigned BREAK_TICKS = FRAME_BITS * OVERSAMPLE;
// The counter must reach AND HOLD the threshold, so it needs to represent
// BREAK_TICKS itself — hence +1 before the log.
localparam int unsigned CNT_W = (BREAK_TICKS <= 1) ? 1 : $clog2(BREAK_TICKS + 1);
initial begin
if (OVERSAMPLE < 1) $fatal(1, "uart_break_detect: OVERSAMPLE must be >= 1");
if (FRAME_BITS < 2) $fatal(1, "uart_break_detect: FRAME_BITS = %0d is not a frame", FRAME_BITS);
end
logic [CNT_W-1:0] low_cnt_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
low_cnt_q <= '0;
break_active_o <= 1'b0;
break_evt_o <= 1'b0;
break_released_o <= 1'b0;
end else begin
break_evt_o <= 1'b0;
break_released_o <= 1'b0;
if (rx_sync_i == 1'b1) begin
// Any mark resets the measurement immediately — a break is a
// CONTINUOUS low, so one high tick ends it.
low_cnt_q <= '0;
if (break_active_o) begin
break_active_o <= 1'b0;
break_released_o <= 1'b1;
end
end else if (os_tick_i) begin
if (low_cnt_q == CNT_W'(BREAK_TICKS)) begin
low_cnt_q <= low_cnt_q; // SATURATE, do not wrap
end else begin
low_cnt_q <= low_cnt_q + 1'b1;
if (low_cnt_q == CNT_W'(BREAK_TICKS - 1)) begin
// This tick carries the count to the threshold.
break_active_o <= 1'b1;
break_evt_o <= ~break_active_o; // declare once
end
end
end
end
end
endmoduleFour details that decide whether this works
The counter must represent BREAK_TICKS itself, so the width is $clog2(BREAK_TICKS + 1). This is the one place in the curriculum where the familiar $clog2(N) is wrong. Elsewhere a counter walks 0 … N−1 and wraps, so $clog2(N) suffices; here it must reach the threshold and hold it, so the threshold must be representable.
The two expressions agree at most values and diverge exactly when BREAK_TICKS is a power of two — and that is not a hypothetical corner:
| config | M | BREAK_TICKS | $clog2(BT) | holds BT? | $clog2(BT+1) |
|---|---|---|---|---|---|
| 8N1 | 16 | 160 | 8 | yes | 8 |
| 8E1 | 16 | 176 | 8 | yes | 8 |
| 6N1 | 8 | 64 | 6 | no — 0…63 | 7 |
| 6N1 | 16 | 128 | 7 | no — 0…127 | 8 |
6N1 is a perfectly ordinary configuration, and under the naive width its counter cannot represent its own threshold. The comparison against BREAK_TICKS would then never be satisfied and the break would never be declared — a detector that is correct for every configuration anyone tested and silently dead for one. Elaborating the case confirms the +1 form gives 8 bits holding 0–255 for BREAK_TICKS = 128.
The counter saturates rather than wrapping, and this is not defensive style — it is the difference between a detector that works and one that fails on exactly the case it exists for:
if (low_cnt_q == CNT_W'(BREAK_TICKS)) low_cnt_q <= low_cnt_q; // hold
else low_cnt_q <= low_cnt_q + 1'b1;A wrapping counter on a line that stays low returns to zero and climbs again, so break_active_o would be declared, dropped, and re-declared forever at a period of BREAK_TICKS ticks. The symptom is a break indication that flickers on a line that is solidly stuck — which reads as a noisy connection rather than a dead one, and sends the investigation in exactly the wrong direction. Simulation over three times the threshold confirms the counter holds at 160 with a single event.
Any mark resets the measurement immediately, without waiting for a tick. A break is defined as continuously low, so one high sample ends it — and deferring that to the next oversample tick would let a disturbance up to one tick wide be absorbed into the count.
break_evt_o <= ~break_active_o declares exactly once. The event fires on the tick that carries the count to the threshold, and only if the break was not already active. Combined with saturation, that guarantees one event per break regardless of how long the line stays low.
The measured behaviour
From simulation, with a positive control on both sides of the threshold:
| Stimulus | Expected | Result |
|---|---|---|
| 159 low ticks (threshold − 1) | no break, no event | pass |
| 160 low ticks (threshold) | break declared, sticky set | pass |
| — event count | exactly 1 | pass |
| 3 × threshold of continued low | still active, counter held at 160 | pass |
| — event count | still exactly 1 | pass |
| line returns to mark | released, exactly one release pulse | pass |
The first row is the off-by-one test and it is the one that matters: a detector firing at 159 would also fire on some legitimate traffic patterns as the configuration changes.
A valid all-zero frame versus a break
11 cyclesRead the first phase band. For the first 144 ticks a break and a valid 0x00 frame are identical on the wire. That is not a detector weakness — it is the protocol, and it is why a break cannot be recognised any earlier than a full frame time without risking false positives on real traffic.
5. Generating a Break
The transmit side is simpler and is worth stating because it explains what a break is for.
A break is generated by holding the transmit line at space for longer than a frame, which means suspending the frame sequencer rather than sending a special character:
// Conceptual SystemVerilog — break generation, as an override on the line.
// The transmitter of Module 7 registers tx_o inside its baud-tick branch;
// break generation overrides that source while the request is asserted.
// Sequencing the request for a measured duration belongs to a layer above.
if (break_req_i) tx_o <= 1'b0; // hold at space
else tx_o <= <normal frame output>;A break is not a character. There is no byte value that produces one, because every frame ends with a mandatory mark stop interval — which is exactly the property §1 relied on. 0x00 is the closest a frame can come and it is still nine intervals rather than ten.
What breaks are used for: signalling out of band. Because a break cannot be confused with any data value, it carries one bit of information that is guaranteed distinguishable from the byte stream — commonly "reset your protocol state" or "I am starting over", and in some bus protocols a deliberate synchronisation marker. The transmitter must hold it for longer than the receiver's threshold, which means the two ends must agree on a duration; a break shorter than the far end's threshold is received as a framing error and nothing more.
Module 7's transmitter does not implement this, and its assumptions block says so explicitly. Adding it is the override above plus a duration counter, and the duration policy — how long, and who decides — belongs with the layer that has a reason to send one.
6. A Deliberate Break and a Stuck Wire Are Identical
To the receiver, they are the same observation: the line is at space and stays there. The detector cannot distinguish them, and no detector can, because there is no difference in the signal.
| Deliberate break | Stuck wire / fault | |
|---|---|---|
| On the wire | line at space, indefinitely | line at space, indefinitely |
| Receiver's view | identical | identical |
| Ends when | the transmitter releases it | the fault is repaired |
| Typical duration | bounded — the sender has a plan | unbounded |
| Distinguished by | it ends, and traffic resumes | it does not |
The only discriminator available to the receiver is time, and it is a weak one: a break that is released after a few frame times and followed by valid traffic was almost certainly deliberate; one that persists for seconds is almost certainly a fault. Neither is certain, and a receiver should not pretend to know.
The practical consequences:
Report both through the same status and let the system decide. The hardware's job is to say "the line has been low past the threshold" and "it has been released"; deciding whether that was a peer signalling or a cable falling out needs context the receiver does not have.
break_released_o is what makes the distinction possible at all, because it is the event that says the condition ended. A design exposing only break_active_o forces software to poll to discover the release, and a design exposing only the sticky bit cannot distinguish one long break from many short ones.
A line that is low at power-up is ambiguous in a third way. It may be a break, a fault, or simply a far end that has not been powered yet — Chapter 1.3 noted that an idle line and several faults are indistinguishable, and this is the same ambiguity at the opposite level.
7. Verification
The threshold needs testing from both sides, one tick apart. Threshold − 1 must be silent and threshold must declare. A test that only drives "a long low" passes on a detector with any off-by-one and on one whose threshold is entirely wrong.
Saturation needs a run far past the threshold. Three times the threshold is enough to expose a wrapping counter, which would have re-declared by then. The check is both that break_active_o is still high and that the event count is still one — the second catches a counter that wraps and re-declares while the level happens to stay high.
Release needs its own check. A detector that declares correctly and never releases leaves the receiver permanently disabled, which is worse than not detecting at all.
The positive control is a valid all-zero frame. 0x00 in 8N1 holds the line low for 144 ticks; a detector that fires on it is unusable on real traffic, and no negative test catches that.
// Assertion — a break can never be declared before the threshold.
// The counter is the evidence; this states the contract over it.
property p_no_early_break;
@(posedge clk) disable iff (!rst_n)
break_evt_o |-> ($past(low_cnt_q) == CNT_W'(BREAK_TICKS - 1));
endproperty
assert property (p_no_early_break);
// Assertion — the counter never exceeds the threshold. This is the
// saturation policy stated as a property, and it fails on a wrapping counter.
property p_counter_saturates;
@(posedge clk) disable iff (!rst_n)
low_cnt_q <= CNT_W'(BREAK_TICKS);
endproperty
assert property (p_counter_saturates);
// Assertion — any mark clears the measurement immediately.
property p_mark_resets_count;
@(posedge clk) disable iff (!rst_n)
rx_sync_i |=> (low_cnt_q == '0);
endproperty
assert property (p_mark_resets_count);
// Assertion — the break is declared at most once per low episode:
// a second event requires an intervening release.
property p_one_event_per_break;
@(posedge clk) disable iff (!rst_n)
break_evt_o |=> !break_evt_o until_with break_released_o;
endproperty
assert property (p_one_event_per_break);The last is the one that catches a wrapping counter even if the level happens to look right, and it is the property the three-times-threshold test exercises.
8. Debugging
9. What This Means on an FPGA
The detector is a counter, a comparator and three flip-flops. Eight bits of counter at 8N1 with 16× oversampling. It runs off the same os_tick_i the receiver already has, so it adds no timing requirement of its own.
It is an observer, not part of the receive FSM. Chapter 6.1 §3 gave the reason: a break observes the line continuously, including while a frame is in progress, and making it an FSM transition would make the frame's state graph depend on the line's history. Running it alongside keeps both halves reviewable — and it is why a break beginning mid-frame is detected at all.
Probe break_active_o with rx_sync_q. The pair immediately separates "the line is low and we noticed" from "the line is fine and something else is wrong". Adding the counter's top bit shows how close a marginal low excursion came to the threshold.
Size the threshold from the configuration that will actually be used. A threshold derived from 8N1 and deployed at 8E2 declares breaks one interval early relative to that configuration's frame — harmless in this direction, but the reverse (deriving from the longest and running the shortest) delays detection, and a design supporting several configurations should derive from the one in use rather than a compile-time default.
10. Understanding Check
11. Summary
A start bit is also low, so a break cannot be detected from the line's level. It is the one UART condition defined by duration.
A valid 0x00 frame in 8N1 holds the line low for nine consecutive intervals, which sets the floor: any threshold at or below 1 + DATA_W intervals fires on legitimate traffic. One full frame time is the smallest safe threshold, because a valid frame's stop interval must be mark.
BREAK_TICKS = FRAME_BITS × OVERSAMPLE 160 at 8N1 with 16xThere is no universal threshold. Implementations differ in the value and in the mechanism; this chapter states one model and derives everything from it.
Three outputs, not one: break_active_o is a level because a break is a condition; break_evt_o is a pulse for the sticky aggregator; break_released_o is the event recovery needs and the one most often omitted.
The counter must saturate. A wrapping counter re-declares forever on a stuck line, producing a flicker with period BREAK_TICKS that reads as an intermittent connection. And its width is $clog2(BREAK_TICKS + 1) — the one place the usual form is wrong, diverging exactly at powers of two.
A break is not a character, because every frame ends with a mandatory mark. That is what makes it usable as out-of-band signalling — and it means the two ends must agree on a duration.
A deliberate break and a stuck wire are identical to the receiver. The only discriminator is that one ends; reporting both identically and exposing the release is the honest architecture.
12. What Comes Next
A break is a line that stays low too long. The opposite failure is a line that goes low for too short a time — or goes low at exactly the wrong moment.
Chapter 9.5 takes the start-side failures: the glitch that survives edge detection but fails qualification, the disturbance wide enough to be accepted as a start, and the case Chapter 6.2 §7 demonstrated but did not solve — a receiver attaching to a live line and fabricating a byte from the middle of someone else's frame. It also builds the idle detection that suppresses it, and prices what that costs.
Browse the full path on the UART tutorials index. For the frame-length arithmetic the threshold derives from, read back to Chapter 3.5.
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
Parity Generation, Checking and Error Detection
One interval, one XOR reduction, and a detection guarantee with a sharp edge: parity catches every corruption that flips an odd number of protected bits and provably misses every even-numbered one — demonstrated, not asserted.
Where this fits
Part of the UART curriculum.
