Skip to content
VLSI Mentor

UART · Module 5

Start-Edge Detection and Start-Bit Validation

Detecting a departure from idle is a comparison on the synchronised input. Deciding it was a start bit rather than a disturbance is a judgement with a cost — and the receiver's entire frame timing hangs on which event it treats as the origin.

Chapter 5.1 enumerated four decisions a receiver must make. This chapter takes the first two, because in practice they are inseparable.

Detection is easy. On a synchronised input, a departure from idle is a comparison between this cycle's value and last cycle's — four lines of RTL, no judgement involved.

The second decision is where the engineering is:

A falling edge is evidence that a frame may have started. It is not proof.

A disturbance on the conductor produces an identical observation. A receiver that commits on every edge will construct frames nobody sent, and — worse than delivering garbage — it will have adopted a timing origin derived from a meaningless instant, which corrupts every position in the frame it thinks it is receiving.

So real receivers wait and check. That costs latency, spends timing budget, and raises a question that catches designs out: once you have waited, which event is the frame's phase reference — the edge you detected, or the moment you decided to believe it? Getting that wrong shifts every sample by a fixed amount, and the failure looks like a baud-rate error.

1. Detecting the Departure

On the synchronised signal, detection is a one-cycle history and a comparison.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Synthesizable SystemVerilog — edge detection on the SYNCHRONISED input.
// rx_sync_q comes from the two-stage boundary of Chapter 5.1 §3. Detecting
// on the raw pin is the error that chapter rejected; everything here
// derives from one sampled version of the input.
logic rx_sync_d_q;

always_ff @(posedge clk or negedge rst_n) begin
    // Idle level at reset, for the reason Chapter 5.1 gave: a receiver
    // released from reset must not appear to have just seen a departure.
    if (!rst_n) rx_sync_d_q <= 1'b1;
    else        rx_sync_d_q <= rx_sync_q;
end

// High for exactly one clk cycle, on the cycle the synchronised input is
// first observed at the space level having been at mark.
assign start_candidate = rx_sync_d_q && !rx_sync_q;

start_candidate, not start_detected. The name is the chapter. This signal means a departure from idle was observed and nothing more.

It is one cycle wide by construction: the next cycle rx_sync_d_q has caught up and the expression is false. A pulse rather than a level is what downstream logic wants, because the event is instantaneous and the state it triggers is separate.

The observation is quantised to the clock, and there is fixed latency in front of it.

Physical edge to candidate pulse — fabric-clock scale

10 cycles
Six signals over ten fabric clock cycles. The clock alternates each column. The raw receive input is at the mark level for cycles zero and one and falls to space from cycle two onward. The first synchroniser stage registers that value one cycle later, going low at cycle three. The second synchroniser stage goes low at cycle four. A delayed copy of the second stage goes low at cycle five. The start candidate signal is the delayed copy ANDed with the inverse of the second stage, producing a single one-cycle pulse at cycle four. The physical transition and the candidate pulse are separated by fixed synchroniser latency, and the position of the transition within cycle two cannot be recovered.synchroniser latencysynchroniser latencyphysical transition, somewhere in herephysical transition,somewhere in herecandidate pulse — one cyclecandidate pulse — one cycleclkrx_irx_meta_qrx_sync_qrx_sync_d_qstart_candt0t1t2t3t4t5t6t7t8t9
Figure 1 — detection at fabric-clock scale. Each column is one clk cycle, not a UART bit interval. The line falls during cycle 2; the first synchroniser stage registers it at cycle 3, the second at cycle 4, and the comparison against the delayed copy produces a one-cycle candidate pulse at cycle 4. The physical transition and its observation are three cycles apart, and where within cycle 2 the transition actually fell is unknowable from here.

Two quantities come out of this figure, and Chapter 4.5 already budgeted both.

A fixed delay, from the synchroniser stages. It is the same on every frame, so it shifts the receiver's whole view of the frame by a constant — which matters only if the design forgets to account for it.

A variable part, because the transition fell somewhere inside a clock period and the receiver cannot tell where. That is δ_origin, bounded by one clock period, and it is the irreducible part.

2. Why the Edge Is Not Enough

A departure from idle is produced by a start bit. It is also produced by:

  • a disturbance coupled onto the conductor from something switching nearby;
  • ringing or reflection on a long or unterminated line;
  • a far end being powered on, reset, or hot-plugged mid-way;
  • a floating input drifting across the receiver's threshold (Chapter 5.1 §6);
  • the receiver being released from reset while a frame is already in progress, so a data transition is the first thing it sees.

The receiver cannot distinguish these from a real start at the moment of the edge, because at that moment the evidence is identical in every case.

3. Validation: Check That It Stayed

The standard answer is to wait and look again. If the departure was a real start bit, the line will still be at the space level partway through the interval; if it was a brief disturbance, it will not.

That requires a way to measure "partway through the interval", which is the sub-bit timing resolution Chapter 5.3 builds. This chapter takes it as given: a periodic oversample tick at M ticks per bit interval, produced as a clock enable in the clk domain. The natural validation point is the middle of the start interval, at M/2 ticks after the candidate — the same position a data sample would use, and the furthest point from both boundaries (Chapter 2.5).

False start — the line did not stay

10 cycles
A receive line is shown over ten oversample ticks at a factor of sixteen. The line is at the space level for ticks zero through two, representing a brief disturbance, then returns to the mark level from tick three onward. A candidate start was raised at tick zero when the departure was observed. At tick eight, the middle of the start interval and the validation point, the receiver samples the line and finds it at mark rather than space, so the candidate is rejected and the receiver returns to watching for a new departure without having committed to a frame.disturbancedisturbancequalifying — counting to M/2qualifying — counting to M/2rejected, back to idlerejected, back toidlecandidate raisedcandidate raisedline returns to markline returns to markvalidate: MARK -> rejectvalidate: MARK -> rejectrx_sync_qt0t1t2t3t4t5t6t7t8t9
Figure 2 — a disturbance rejected at validation. Each column is one oversample tick at M = 16, not a fabric-clock cycle and not a bit interval; the ten ticks shown span 10/16 of one bit interval. The candidate is raised at tick 0, the line returns to mark by tick 3, and at the validation point — tick 8, the middle of the start interval — the receiver finds mark rather than space and abandons the candidate.
fsm
A three-state diagram covering start qualification. From Idle, observing a departure from idle moves to Qualifying, where an oversample phase counter runs. While in Qualifying, each oversample tick advances the counter. When the counter reaches the validation point, the receiver samples the line: if it is still at the space level the machine moves to Accepted and the frame begins, and if it has returned to mark the machine returns to Idle without committing. Accepted hands over to the receiver's frame reception, which is Module 6's subject.IdleQualifyingAcceptedcandidate observedcandidate observedos_tick: countos_tick:countat M/2: still spaceat M/2: still spaceat M/2: back at markat M/2: back at mark
Figure 3 — the qualification states only. This is not the receiver's state machine: Module 6 owns that, including the data, parity and stop states and every error transition. What is shown is the decision this chapter makes — a candidate is held while the counter runs, and either confirmed or abandoned at the validation point.

The counter convention, derived rather than asserted

This is where off-by-one errors live, so the convention must be explicit. Take the candidate cycle as phase 0 and advance on each subsequent oversample tick:

Oversample tickos_phase_qrx_sync_qReceiver interpretation
candidate00departure observed; counter loaded
+110qualifying
+220qualifying
+770qualifying
+880validation point — M/2 at M=16: still space, accept

Eight tick intervals have elapsed between the candidate and the validation point, and the counter reads 8 because it was loaded with 0 at the candidate and incremented on each of the eight ticks since. Comparing against M/2 is therefore correct for this convention — and would be wrong by one interval if the counter were loaded with 1, or if the comparison were made before the increment.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Synthesizable SystemVerilog — start qualification only.
// The oversample tick comes from Chapter 5.3; the frame reception that
// follows acceptance is Module 6. Deliberately no data, parity, stop or
// shift-register logic here.
module uart_start_qualify #(
    parameter int unsigned OVERSAMPLE = 16
) (
    input  logic clk,
    input  logic rst_n,
    input  logic rx_sync_i,        // synchronised input (Chapter 5.1)
    input  logic os_tick_i,        // one pulse per oversample interval
    input  logic start_cand_i,     // one-cycle candidate pulse (§1)
    output logic start_accept_o,   // one cycle: frame timing begins NOW
    output logic qualifying_o
);
    // Validation at the middle of the start interval. Written as a named
    // constant derived from the parameter, never as a literal 8 — §5 of
    // Chapter 5.3 shows what happens when OVERSAMPLE later changes.
    localparam int unsigned VALIDATE_AT = OVERSAMPLE / 2;

    // Counter spans 0..VALIDATE_AT, so its largest value is VALIDATE_AT.
    localparam int unsigned PH_W =
        (VALIDATE_AT <= 1) ? 1 : $clog2(VALIDATE_AT + 1);

    initial begin
        if (OVERSAMPLE < 2)
            $fatal(1, "uart_start_qualify: OVERSAMPLE = %0d leaves no room to validate", OVERSAMPLE);
        if (OVERSAMPLE % 2 != 0)
            $warning("uart_start_qualify: OVERSAMPLE = %0d is odd; VALIDATE_AT = %0d truncates, placing the check %s of centre",
                     OVERSAMPLE, VALIDATE_AT, "just short");
    end

    logic [PH_W-1:0] os_phase_q;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            qualifying_o   <= 1'b0;
            os_phase_q     <= '0;
            start_accept_o <= 1'b0;
        end else begin
            start_accept_o <= 1'b0;             // default: one-cycle pulse

            if (!qualifying_o) begin
                if (start_cand_i) begin
                    qualifying_o <= 1'b1;
                    os_phase_q   <= '0;         // phase 0 AT the candidate
                end
            end else if (os_tick_i) begin
                if (os_phase_q == PH_W'(VALIDATE_AT - 1)) begin
                    // This tick makes the count VALIDATE_AT — the moment
                    // the table above calls the validation point.
                    qualifying_o <= 1'b0;
                    if (!rx_sync_i) start_accept_o <= 1'b1;   // still space
                    // else: silently abandon, back to watching
                end else begin
                    os_phase_q <= os_phase_q + 1'b1;
                end
            end
        end
    end
endmodule

Why the comparison is against VALIDATE_AT - 1. The decision is taken on the tick that would carry the count to VALIDATE_AT, not on the following cycle. Comparing against VALIDATE_AT and acting the next tick validates one interval late — a 1/M UI shift of the whole frame, which at M = 16 is 6.25% of a bit and is exactly the kind of error that looks like a rate problem.

Reset and the default assignment. start_accept_o is assigned low at the top of the clocked block and overridden only on the accepting condition, which guarantees a single-cycle pulse without a separate clear. qualifying_o resets inactive so a receiver leaving reset is watching, not mid-qualification.

Odd OVERSAMPLE is warned, not rejected. OVERSAMPLE / 2 truncates, so an odd factor places the check slightly before the true centre. That is a legitimate design point — it still validates — but it is a different validation position than the parameter name suggests, and silently accepting it is how a design acquires an unexplained half-tick offset.

4. The Trade: Latency Against Confidence

Where the validation point sits is a real decision, and both directions cost something.

Validate early — say at M/4 rather than M/2. The receiver commits sooner and is ready for the rest of the frame with more margin in hand. But it only rejects disturbances shorter than a quarter of a bit; anything longer is accepted.

Validate late — at 3M/4, or by requiring the line to remain at space across several checks. Stronger rejection, and every tick spent qualifying is a tick during which the receiver is committed to nothing and the frame is advancing. Push it far enough and the qualification runs past the start interval entirely.

Validate at M/2 is the common choice because it is simultaneously the most informative single point — furthest from both boundaries, so least sensitive to the timing error of Chapter 4.5 — and exactly where a data sample would fall, which means the same mechanism serves both.

5. Verification

Qualification is a mechanism whose whole purpose is behaviour under inputs that a well-formed-frame testbench never produces.

Glitch width is the primary axis, and the interesting values bracket the validation point:

Disturbance widthExpected behaviour
shorter than the synchroniser can registernot observed at all — no candidate
observed, but gone well before M/2candidate raised, rejected at validation
ends just before the validation tickrejected — the boundary case
ends just after the validation tickaccepted — a fabricated frame follows
longer than a full bit intervalaccepted; indistinguishable from a real start at this stage

The fourth row is not a defect. It is the mechanism's specified limit, and a testbench should assert it as expected behaviour rather than treating it as a failure. A validation point rejects disturbances shorter than itself and nothing more, and pretending otherwise leads to a design review conversation in which someone promises glitch immunity the architecture does not provide.

Glitch phase is a second axis. A disturbance of fixed width arriving at different offsets relative to the oversample tick grid is observed at different ticks, so it can land either side of the boundary. Sweeping width alone, at a fixed phase, finds one boundary; sweeping both finds the region.

Two further scenarios belong here and are easy to omit:

  • Reset released mid-frame. The receiver sees a data transition as its first departure from idle, qualifies it successfully — the line does stay at space for that bit — and receives a fabricated frame. This is correct behaviour and the recovery path is worth establishing: the design should return to a sane state within a frame or two, and a test should confirm which.
  • A disturbance during qualification of a real start. The line is at space and a brief excursion to mark occurs before the validation tick. Whether the design cares depends on whether it checks continuously or only at the validation point — the module in §3 checks only at the point, so it accepts. That is a defensible choice, and it is one a testbench should pin down rather than discover.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Testbench SystemVerilog — NOT synthesizable. Drives a disturbance of a
// given width at a given phase, then checks the receiver's verdict.
task automatic inject_glitch(input realtime width, input realtime phase);
    #(phase);
    rx_i <= 1'b0;
    #(width);
    rx_i <= 1'b1;
endtask

// Sweep both axes around the validation point. T_BIT and OVERSAMPLE come
// from the same parameters the DUT was built with, so the expected
// boundary moves with the configuration rather than being hard-coded.
initial begin
    realtime validate_at = T_BIT * VALIDATE_AT / OVERSAMPLE;
    foreach (phase_list[p])
        for (realtime w = validate_at*0.8; w <= validate_at*1.2; w += T_BIT/64) begin
            inject_glitch(w, phase_list[p]);
            // Expectation is derived, not tabulated: a disturbance that
            // has ended before the validation instant must be rejected.
            expect_accept = (w > validate_at);
            @(negedge qualifying);
            assert (start_accept === expect_accept)
              else $error("width %0t phase %0t: accept=%b expected %b", w, phase_list[p], start_accept, expect_accept);
        end
end

The expectation is computed, not listed. A tabulated set of expected results is correct for one OVERSAMPLE and silently wrong after it changes — which is Chapter 5.3's recurring hazard.

Digital injection is not a noise model. This verifies the receiver's response to defined input disturbances. Real analogue behaviour — a slow edge crossing the threshold repeatedly, ringing, a level that hovers near the threshold — is a board-level concern that a digital testbench cannot represent and a digital receiver largely cannot fix.

An assertion worth having, because it states the contract rather than the implementation:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Assertion — acceptance never occurs without qualification having run.
property p_no_unqualified_accept;
    @(posedge clk) disable iff (!rst_n)
        start_accept_o |-> $past(qualifying_o);
endproperty
assert property (p_no_unqualified_accept);

6. What This Means on an FPGA

The candidate is late and you cannot make it early. Two synchroniser stages plus the edge comparison put the candidate two to three clock periods after the physical transition. That delay is fixed and budgeted; what cannot be recovered is where inside a clock period the transition fell. Both were priced in Chapter 4.5.

Validation is the cheapest noise rejection available to the design, and the only one that costs no extra hardware — the counter and the tick are needed anyway. Input filtering, if a board needs it, is a board-level or I/O-level decision that belongs below this logic.

A floating rx_i defeats qualification completely. A drifting input can sit at the space level for far longer than the validation point, so the receiver accepts, fabricates a frame, and repeats indefinitely. Qualification rejects brief disturbances; it has no defence against an input with no defined level, which is a board fix (Chapter 5.1 §6).

7. Understanding Check

8. Summary

Detection is a comparison on the synchronised input and produces a one-cycle candidate pulse. The physical transition and that pulse are separated by fixed synchroniser latency plus an irreducible uncertainty about where inside a clock period the transition fell — the fixed part shifts everything equally, the variable part is Chapter 4.5's δ_origin.

A falling edge is evidence, not proof. Disturbances, ringing, power-up, hot-plug, a floating input and a mid-frame reset release all produce the identical observation. Accepting one costs a fabricated frame, a lost real frame, and a misleading framing error — because acceptance sets the timing origin for everything that follows.

Validation waits to the middle of the start interval and checks the line is still at space. M/2 is the common choice because it is the point least sensitive to timing error and is where a data sample would fall anyway. Earlier validation commits sooner and rejects less; later validation rejects more and spends budget, bounded above by the start interval itself. The mechanism rejects disturbances shorter than the validation delay, and nothing more.

The counter convention must be derived, not asserted: with phase 0 loaded at the candidate, eight tick intervals have elapsed when the counter reads 8, so the decision is taken on the tick carrying the count to M/2. Acting one tick later shifts the whole frame by 1/M of a bit.

And the sharpest correctness question is which event is the origin — the candidate or the acceptance. Both are usable; mixing them places every sample half a bit late, producing corruption that looks like a rate error on a link whose rate is correct.

9. What Comes Next

This chapter has used an oversample tick throughout without saying where it comes from, how many there are per bit interval, or what that choice costs. Chapter 5.3 builds it: what oversampling actually means, why 8× and 16× are the familiar factors and why neither is required by the framing, what a higher factor buys in placement resolution, and what it costs in tick generation — including the fact that the rate a 16× receiver needs turns out to be exactly the frequency Chapter 4.2 said the historic crystals were chosen to produce.

Browse the full path on the UART tutorials index. For the timing origin treated as a protocol object rather than an implementation choice, see Chapter 2.3.

Continue learning

Where this fits

Part of the UART curriculum.