Skip to content
VLSI Mentor

UART · Module 5

Majority Voting, Noise Rejection and the Sampling Window

Three samples around the centre reject a disturbance narrower than the gap between them and fail on one only slightly wider. The window also has width, which costs timing margin — and the same three samples span twice as much of the bit at 8x as at 16x.

Chapter 5.3 gave the receiver a grid of positions and a way to land near the centre of a bit cell. This chapter asks what it should do once it gets there.

The obvious answer is: read the line. One observation, one value, done — and for a clean conductor that is entirely sufficient.

The alternative is to read several positions around the centre and combine them. Many UART IPs do this, usually with three samples and a majority. It is worth understanding precisely, because it is the part of receiver design most surrounded by vague claims — "majority voting gives noise immunity" — and the precise version is much more useful:

Three samples reject a disturbance narrower than the gap between them, and fail on one only slightly wider. The window itself has width, and that width costs timing margin.

Both halves matter. A voter is not free, and at a low oversampling factor the price is not small.

1. One Sample, and What It Is Vulnerable To

A single reading at the interval's centre asks one question: what was the line at this instant?

Against a clean conductor that is a complete answer. The bit cell holds one value for its whole width, and the centre is the point furthest from both boundaries (Chapter 2.5), so the reading is correct with the maximum margin the geometry allows.

It is vulnerable to exactly one thing: a disturbance present at that instant. A coupled transient, a reflection, a brief excursion across the threshold — if it happens to coincide with the sampling instant, the reading is wrong, and the receiver has no way to know. There is no second opinion.

The probability of that coincidence is roughly the disturbance's width as a fraction of the bit interval. A 100 ns transient on a 115,200-baud link, where T_bit ≈ 8680.6 ns, lands on the sampling instant about 1.2% of the times it occurs. Rare, and not zero.

2. Three Samples and a Majority

The standard alternative reads three adjacent grid positions — the centre and one tick either side — and takes the value that appears at least twice.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
positions:  centre − 1,  centre,  centre + 1      (oversample ticks)
decision:   the value present in at least two of the three

The logic is a two-level expression with no state:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Synthesizable SystemVerilog — the vote itself.
// Pure combinational; no clock, no reset. Synthesis produces three
// two-input ANDs and an OR, or whatever the technology's equivalent is.
function automatic logic majority3(input logic a, input logic b, input logic c);
    return (a & b) | (a & c) | (b & c);
endfunction

Each product term covers one of the three ways two samples can agree, and the OR accepts any of them. The function is symmetric in its arguments, which is correct — the vote does not care which sample dissented.

abcvote
1111
1011 — dissenting centre outvoted
1101
0010 — two corrupted, vote flips
0100
0000

The second row is the mechanism working. The fourth row is the mechanism failing, and §4 is about when that happens.

One corrupted sample — vote holds

10 cycles
A receive line is shown over ten oversample ticks at a factor of sixteen, around the centre of one bit interval whose true value is the mark level. A brief disturbance pulls the line to the space level during tick four only. The receiver samples at ticks three, four and five, reading one, zero and one respectively. The majority of those three is one, which is the correct value, so the single corrupted sample is outvoted and the disturbance is rejected.vote = 1 — correctvote = 1 — correctsample A = 1sample A = 1sample B = 0 — disturbedsample B = 0 — disturbedsample C = 1sample C = 1rx_sync_qt0t1t2t3t4t5t6t7t8t9
Figure 1 — a disturbance corrupting one sample, outvoted. Each column is one oversample tick at M = 16, not a fabric-clock cycle; the ten ticks shown are a window around one bit interval's centre. The true value is mark. A transient pulls the line low across tick 4 only, so the three samples read 1, 0, 1 and the vote returns the correct value.

3. What It Rejects

Stated precisely, because the vague version is the problem:

A three-sample majority returns the correct value whenever at most one of the three samples is corrupted.

Since the samples sit one tick apart, a disturbance corrupts two of them only if it spans more than one oversample interval. That gives a concrete threshold:

Mone tickdisturbance must exceed, to corrupt two samples
80.1250 UI1085.1 ns at 115,200 baud
160.0625 UI542.5 ns at 115,200 baud

So the mechanism's guarantee is: disturbances narrower than one oversample interval are rejected; wider ones may not be. That is a useful, checkable property, and it is considerably narrower than "noise immunity".

Note the direction of the dependence, which is counter-intuitive on first meeting. A higher oversampling factor makes the rejected class smaller — at 16× the samples are closer together, so a shorter disturbance suffices to corrupt two of them. Finer placement resolution and wider glitch rejection pull in opposite directions, and a design cannot maximise both with a fixed three-sample window.

4. What It Cannot Reject

Three failure classes, and only the first is obvious.

A disturbance spanning two sample positions. The fourth row of the truth table. At 16× a transient of 600 ns — only marginally wider than the 542.5 ns threshold — covers two adjacent samples and the vote returns the wrong value confidently. There is no indication that the decision was marginal.

Two corrupted samples — vote flips

10 cycles
A receive line is shown over ten oversample ticks at a factor of sixteen, around the centre of one bit interval whose true value is the mark level. A disturbance pulls the line to the space level across ticks three and four, one tick wider than the previous figure. The receiver samples at ticks three, four and five, reading zero, zero and one. The majority of those three is zero, which is the wrong value, so the vote fails and reports a corrupted bit as though it were correct.vote = 0 — WRONG, silentlyvote = 0 — WRONG, silentlysample A = 0 — disturbedsample A = 0 — disturbedsample B = 0 — disturbedsample B = 0 — disturbedsample C = 1sample C = 1rx_sync_qt0t1t2t3t4t5t6t7t8t9
Figure 2 — the same mechanism failing. Each column is one oversample tick at M = 16. The disturbance is only one tick wider than in Figure 1 and now spans ticks 3 and 4, so two of the three samples read 0 and the vote returns the wrong value. Nothing distinguishes this decision from a correct one at the receiver's level.

Samples that are correlated rather than independent. The vote's logic assumes three separate observations of the same underlying value. If the window has drifted so that it straddles a bit boundary, the outer samples are reading a different interval — not a corrupted version of this one. The vote then faithfully reports the majority of two different bits, which is meaningless. Chapter 5.5 shows exactly when drift produces this.

Systematic misplacement. If the window is centred on the wrong position, every sample in it is wrong in the same direction, and the vote is unanimous. This is the most dangerous failure because the mechanism's own confidence is highest precisely when it is most wrong.

5. The Window Has Width, and Width Costs Margin

This is the half of the trade that folklore omits entirely.

A single sample sits at the centre, 0.5 UI from either boundary. A three-sample window places its outer samples one tick either side, so the outer samples are closer to the boundaries than the centre is:

Mouter sample offsetouter samples atmargin of the outer sampleδ_window
81/8 = 0.1250 UI0.3750 and 0.6250 UI0.3750 UI0.1250 UI
161/16 = 0.0625 UI0.4375 and 0.5625 UI0.4375 UI0.0625 UI
321/32 = 0.03125 UI0.4688 and 0.5313 UI0.4688 UI0.03125 UI

The window's span from first to last sample is 2/M UI — 1/8 UI at 16× and 1/4 UI at 8×. The same three samples occupy twice as much of the bit interval at 8× as at 16×.

That difference is the term Chapter 5.3 named δ_window, and it goes straight into Chapter 4.5's budget:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
δ_sample  =  δ_grid  +  δ_window  +  δ_arch

at M = 16, three-sample:   0.0625 + 0.0625 = 0.1250 UI
at M =  8, three-sample:   0.1250 + 0.1250 = 0.2500 UI

At 8× a three-sample voter consumes half the available half-bit before any drift is considered. Put the mismatch example of Chapter 5.5 alongside it — ε_rel = 1% over an 8N1 frame, giving 0.09 UI of drift at the stop sample:

δ_windowdrift at n=9remaining of 0.5 UI
M = 16, three-sample0.06250.09000.3475
M = 8, three-sample0.12500.09000.2850

Both work comfortably here. But the 8× voter has given up 0.0625 UI of margin relative to the 16× one, purchased narrower glitch rejection (§3) — because its wider window needs a wider disturbance to corrupt two samples, but its outer samples sit closer to the boundaries.

So the three-sample window is not a strictly better choice than a single sample. It trades margin for rejection of one specific disturbance class, and the exchange rate depends on M.

6. Implementing the Window

The samples must be captured at three separate ticks and the decision taken once.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Synthesizable SystemVerilog — three-sample window and vote.
// The phase counter and os_tick come from Chapter 5.3; the shift register
// that consumes bit_value_o is Chapter 6.3. This produces one decided bit
// per interval and nothing else.
module uart_sample_vote #(
    parameter int unsigned OVERSAMPLE = 16
) (
    input  logic clk,
    input  logic rst_n,
    input  logic rx_sync_i,
    input  logic os_tick_i,
    input  logic [$clog2(OVERSAMPLE)-1:0] phase_i,
    input  logic active_i,
    output logic bit_value_o,
    output logic bit_valid_o        // one cycle, when the vote is decided
);
    localparam int unsigned CENTER = OVERSAMPLE / 2;

    initial begin
        // The window needs a tick either side of centre, so the centre
        // cannot be at position 0 or at the last position.
        if (OVERSAMPLE < 4)
            $fatal(1, "uart_sample_vote: OVERSAMPLE = %0d cannot hold a three-sample window around the centre", OVERSAMPLE);
    end

    logic s_first_q, s_mid_q;

    function automatic logic majority3(input logic a, input logic b, input logic c);
        return (a & b) | (a & c) | (b & c);
    endfunction

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            s_first_q   <= 1'b1;        // idle level, per Chapter 5.1
            s_mid_q     <= 1'b1;
            bit_value_o <= 1'b1;
            bit_valid_o <= 1'b0;
        end else begin
            bit_valid_o <= 1'b0;

            if (active_i && os_tick_i) begin
                unique case (phase_i)
                    ($clog2(OVERSAMPLE))'(CENTER - 2): s_first_q <= rx_sync_i;
                    ($clog2(OVERSAMPLE))'(CENTER - 1): s_mid_q   <= rx_sync_i;
                    ($clog2(OVERSAMPLE))'(CENTER):     begin
                        // Third sample is read combinationally on this tick
                        // and voted immediately — no fourth storage element.
                        bit_value_o <= majority3(s_first_q, s_mid_q, rx_sync_i);
                        bit_valid_o <= 1'b1;
                    end
                    default: ;   // every other phase: hold
                endcase
            end
        end
    end
endmodule

The phase constants are derived, never literals, for the reason Chapter 5.3 §6 gave: a hard-coded 7 is correct at one factor and silently wrong at every other.

Why the comparisons are CENTER-2, CENTER-1, CENTER. Chapter 5.3 established that an event fires on the tick carrying the count to a position. Three consecutive captures therefore happen on the ticks reaching CENTER-1, CENTER and CENTER+1 — so the comparisons are one lower, and the window is centred on the position the single-sample receiver would have used. An off-by-one here shifts the whole window and costs margin asymmetrically, which §5 showed is the expensive direction.

OVERSAMPLE < 4 is fatal. At M = 2 the centre is position 1 and there is no position either side; at M = 3 the truncated centre is 1 and CENTER-2 is negative. The window architecture simply does not exist below 4, and elaborating a design that pretends otherwise produces samples at wrapped phases.

Two storage elements, not three. The third sample is voted on the cycle it is read. Registering it would add a cycle of latency and a flip-flop for nothing.

Reset values are the idle level, so a receiver leaving reset does not carry a fabricated space into its first vote.

7. Verification

The rejection threshold is the property to characterise, and it should be found empirically rather than assumed:

Disturbance widthExpected
narrower than one oversample intervalrejected — at any phase
exactly one intervalboundary — depends on phase relative to the tick grid
wider than one intervalmay corrupt two samples and flip the vote
wider than the whole windowcorrupts all three; vote unanimous and wrong

The third and fourth rows are specified behaviour, not bugs, and a testbench should assert them as expected. Asserting that a voter rejects everything is asserting a property the architecture does not have, and the usual response — widening the window — costs margin per §5.

Phase is an axis as much as width. A disturbance of fixed width lands on different numbers of samples depending on where it falls relative to the tick grid, so sweeping width at one phase finds one boundary while sweeping both finds the region.

Test the unanimous-and-wrong case explicitly. Deliberately misplace the window — by injecting a start-qualification offset, or by configuring a factor the phase constants were not derived for — and confirm the receiver produces clean, consistent, wrong data. This sounds perverse and is valuable: it is the signature §4 warned about, and a team that has seen it in simulation recognises it in the lab.

Assertions worth having, both stating contracts rather than restating the implementation:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Exactly one vote decision per bit interval while receiving.
property p_one_vote_per_interval;
    @(posedge clk) disable iff (!rst_n)
        bit_valid_o |=> !bit_valid_o until_with (os_tick_i && phase_i == CENTER - 2);
endproperty
assert property (p_one_vote_per_interval);

// No decision while the receiver is not in a frame.
property p_no_vote_when_idle;
    @(posedge clk) disable iff (!rst_n)
        !active_i |-> !bit_valid_o;
endproperty
assert property (p_no_vote_when_idle);

And a scoreboard should check the vote against the samples, not just the byte. A byte-level comparison cannot distinguish a correct vote from a lucky one. Recording the three sample values and the decided bit lets the checker verify the majority function independently of what the data happened to be.

8. What This Means on an FPGA

The logic is trivial and the margin is not. The vote is a handful of LUTs and two flip-flops. The δ_window of §5 is a real subtraction from the timing budget, and at 8× it is as large as the grid term itself. Compute the budget before adding a voter, not after.

Consider whether the disturbance class is real for your board. Voting rejects transients narrower than one oversample interval — 542.5 ns at 16× and 115,200 baud. If the board's actual noise environment produces disturbances much wider than that, the voter costs margin and rejects nothing that matters; if it produces much narrower ones, a single sample was already unlikely to coincide with them.

At high rates the numbers move. At 1 Mbaud the 16× oversample interval is about 62.5 ns, so the rejected class shrinks accordingly — while board-level transients do not. The mechanism is most effective at the low rates it grew up with.

Bring the vote out during bring-up if you can. Capturing the three samples alongside the decided bit turns §4's failure classes into observations: one dissent means the mechanism worked, two means it was defeated, and unanimous-but-wrong means the window is misplaced.

9. Understanding Check

10. Summary

A single sample at the centre is a complete answer on a clean conductor, vulnerable only to a disturbance coinciding with that instant — roughly the disturbance's width as a fraction of the bit interval.

A three-sample majority reads the centre and one tick either side and takes the value appearing at least twice. Its guarantee is exact: correct whenever at most one sample is corrupted, which means it rejects disturbances narrower than one oversample interval — 542.5 ns at 16× and 115,200 baud. A disturbance only slightly wider corrupts two samples and flips the vote silently.

A higher oversampling factor shrinks the rejected class, because the samples move closer together. Placement resolution and glitch rejection pull in opposite directions.

Three failures are outside the mechanism: a disturbance spanning two samples; samples that are correlated rather than independent, which happens when drift makes the window straddle a boundary; and systematic misplacement, where all three samples agree and the vote is unanimously wrong. The last is the most dangerous, because voting is most confident exactly when it is most wrong — and it removes the variable-error clue a single misplaced sample would have given.

The window has width and width costs margin. Outer samples sit 1/M UI from the centre, so δ_window is 0.0625 UI at 16× and 0.125 UI at 8× — where, combined with the grid term, a three-sample voter consumes half the available half-bit before drift is counted.

So voting is a trade, not an improvement: margin for rejection of one specific disturbance class, at an exchange rate set by M. If a design's binding constraint is timing rather than noise, a voter does not help.

11. What Comes Next

Every mechanism in this module has been developed against a static picture: the sampling positions sit where the receiver placed them and stay there. They do not.

Chapter 5.5 puts the receiver architecture and Module 4's clock mismatch together and walks the sampling point across a frame. It shows the positions migrating tick by tick, identifies exactly which bit fails first and why it is always the last, and — using this chapter's window arithmetic — shows the outer sample of a voting window crossing a boundary before the centre does. It is where Modules 2, 4 and 5 finally meet.

Browse the full path on the UART tutorials index. For the error-detection mechanism that operates on the decided bits rather than the samples, and its own sharp limits, see Chapter 3.3.

Continue learning

Where this fits

Part of the UART curriculum.