Skip to content
VLSI Mentor

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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 frame

Any 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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 × OVERSAMPLE

Machine-computed for the standard configurations at OVERSAMPLE = 16:

configdataparitystopframe bitsBREAK_TICKScounter widthminimum low time @115,200
5N15017112760.76 µs
8N180110160886.81 µs
7E171110160886.81 µs
8E181111176895.49 µs
8N280211176895.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:

OutputShapeMeans
break_active_olevelthe line is low now, past the threshold
break_evt_oone-cycle pulsethe moment the break was declared
break_released_oone-cycle pulsethe 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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------------
//  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
endmodule

Four 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:

configMBREAK_TICKS$clog2(BT)holds BT?$clog2(BT+1)
8N1161608yes8
8E1161768yes8
6N18646no — 0…637
6N1161287no — 0…1278

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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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:

StimulusExpectedResult
159 low ticks (threshold − 1)no break, no eventpass
160 low ticks (threshold)break declared, sticky setpass
— event countexactly 1pass
3 × threshold of continued lowstill active, counter held at 160pass
— event countstill exactly 1pass
line returns to markreleased, exactly one release pulsepass

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 cycles
A comparison of two low-line scenarios measured in oversample ticks. The upper trace is a valid frame carrying the byte zero in an eight-data-bit, no-parity, one-stop-bit configuration: the line is low for the start interval and all eight zero data bits, which is nine consecutive low bit intervals or one hundred and forty-four oversample ticks, and then returns to the mark level for the stop interval, resetting the break measurement. The lower trace is a genuine break in which the line never returns to mark; the low-tick counter continues past one hundred and forty-four and reaches the threshold of one hundred and sixty ticks, at which point the break is declared and the counter saturates rather than wrapping.indistinguishable from a valid all-zero frameindistinguishable from a valid all-zero framebreak active, heldbreak active, heldvalid 0x00 ends here — 144 ticksvalid 0x00 ends here — 144ticksthreshold 160 — break declaredthreshold 160 — breakdeclaredcounter SATURATES, no re-declarecounter SATURATES, nore-declareos ticks1632...112128144152160176192valid 0x00breaklow_cnt_q01632..112128144152160160160break_active_ot0t1t2t3t4t5t6t7t8t9t10
Figure 1 — the threshold, at both sides. Columns are OVERSAMPLE TICKS, not bit intervals and not fabric clocks, with the count compressed at the ellipsis. The upper trace is a valid 0x00 frame in 8N1 — nine consecutive low intervals, which is why any threshold at or below nine intervals would fire on legitimate traffic. The lower trace is a genuine break: the line never returns to mark and the count reaches the threshold at tick 160.

Read 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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.

A comparison showing that a deliberate break and a wiring fault are indistinguishable to a UART receiver. In the first case a peer device deliberately holds its transmit line at the space level to signal out of band. In the second case a cable fault or an unpowered transmitter leaves the line at the space level. Both present the receiver's synchroniser with a line continuously at space. The break detector counts oversample ticks in both cases, reaches its threshold in both cases, and declares a break in both cases with identical outputs. The only difference appears later: a deliberate break is released and normal traffic resumes, whereas a fault persists. That information is not available at the moment the receiver must report, so the hardware reports the observation and leaves the interpretation to a layer with more context.Peer (break)FaultDetectorSystemline held at space —deliberateline held at space —cable / unpoweredcounts os_ticks —SAME input eitherwaybreak_active_o —IDENTICAL outputreleased, trafficresumespersistsbreak_released_o —the ONLYdiscriminator
Figure 2 — why the receiver cannot tell the two apart. Both paths deliver the identical observation to the detector, so the detector's output is identical; the only thing that ever differs is what happens afterwards, which is information the receiver does not have at the time it must report.
Deliberate breakStuck wire / fault
On the wireline at space, indefinitelyline at space, indefinitely
Receiver's viewidenticalidentical
Ends whenthe transmitter releases itthe fault is repaired
Typical durationbounded — the sender has a planunbounded
Distinguished byit ends, and traffic resumesit 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
BREAK_TICKS = FRAME_BITS × OVERSAMPLE      160 at 8N1 with 16x

There 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

Where this fits

Part of the UART curriculum.