UART · Module 5
Oversampling Strategies: 8×, 16× and the Alternatives
Oversampling is a grid of sample positions inside each bit interval. A higher factor buys placement resolution and costs tick rate, counter width and generator accuracy — and 16× became conventional for an arithmetic reason the standard crystal frequencies make obvious.
Chapter 5.2 used an oversample tick throughout — to count to the validation point, to place the check at the middle of the start interval — without saying where it comes from or how many there are. This chapter supplies it.
The mechanism answers the third of Chapter 5.1's four decisions: where inside each interval should the line be read? A receiver acts on its own clock edges, and Chapter 2.5 established it should aim at the centre of each bit cell. Oversampling is the standard way of getting enough timing resolution to aim.
It is also the place where a piece of UART folklore needs dismantling — and, satisfyingly, where the arithmetic explains exactly where that folklore came from.
1. What Oversampling Is
The receiver generates a periodic event faster than the bit rate, by an integer factor. Call the factor M:
f_os = M x f_baud oversample tick rate
T_os = T_bit / M = 1/M UI interval between ticksThat is the whole definition. It does not mean the receiver reads the line M times per bit — it may read once, or three times, or any number (Chapter 5.4). What it provides is a grid of M equally spaced positions inside every bit interval, any of which the receiver can choose to act on.
Worked at 115,200 baud, where T_bit ≈ 8680.556 ns:
M | f_os | T_os | T_os in UI |
|---|---|---|---|
| 4 | 460.8 kHz | 2170.139 ns | 0.2500 |
| 8 | 921.6 kHz | 1085.069 ns | 0.1250 |
| 16 | 1.8432 MHz | 542.535 ns | 0.0625 |
| 32 | 3.6864 MHz | 271.267 ns | 0.03125 |
The tick is a clock enable in the clk domain, not a second clock. Everything in this module and in Module 6 is clocked by clk and qualified by os_tick. Chapter 8.1 gives the full argument; the short version is that a divided clock creates a domain to constrain, distribute and cross in exchange for nothing the enable does not provide.
2. Where 16× Came From
The conventional factor has an arithmetic origin, and Chapter 4.2 already laid the groundwork without saying what it was for.
16 x 115,200 = 1,843,200 Hz = 1.8432 MHzThat is exactly the crystal frequency Chapter 4.2 identified as dividing cleanly onto the whole classic rate series. The relationship it noted there — 1,843,200 = 115,200 x 16 — was stated as the reason the fastest rate needed a divisor of 1 "once the sampling factor is accounted for". This is the sampling factor.
Check the consequence directly:
1.8432 MHz / (16 x 115,200) = 1 exactly
1.8432 MHz / (8 x 115,200) = 2 exactlyA receiver clocked from that crystal gets its 16× oversample tick with no division at all, and its 8× tick by dividing by two. Every slower rate in the series follows by further halving, because the series is powers of two.
So 16× is not a protocol requirement, a theoretical optimum, or a noise-immunity threshold. It is the factor that made the arithmetic free on the hardware the convention grew up on. That is a perfectly good engineering reason, and it stopped applying the moment designs moved to fabric clocks chosen for other purposes.
3. The Grid, and Where the Samples Go
Fix the origin at the accepted start, per Chapter 5.2's convention: tick 0 at the candidate, with the start interval spanning ticks 0 to M−1.
start interval centre = M/2 ticks = 0.5 UI
data[k] centre = (k + 1.5) x M ticks = (k + 1.5) UI
stop centre (8N1) = 9.5 x M ticks = 9.5 UIThe +1.5 is the start interval's full width plus half of the data interval — the geometry Chapter 2.2 established, now expressed in ticks.
| Position | ticks at M=8 | ticks at M=16 | UI |
|---|---|---|---|
| start centre | 4 | 8 | 0.5 |
| data[0] centre | 12 | 24 | 1.5 |
| data[1] centre | 20 | 40 | 2.5 |
| data[7] centre | 68 | 136 | 8.5 |
| stop centre (8N1) | 76 | 152 | 9.5 |
Note that M/2 is where Chapter 5.2 put the validation check — the same position a start-interval sample would use. That is not a coincidence: it is why the same counter serves both, and why validating at the interval centre costs nothing extra.
Sixteen positions per bit — the first ten
10 cycles4. What the Factor Buys: Placement Resolution
The receiver can only act on tick boundaries, so its sampling instant lands on a grid of spacing 1/M UI. How far that can be from the true centre depends on the architecture, and the distinction matters enough to state carefully.
The grid step is 1/M UI. At 16× that is 0.0625 UI; at 8×, 0.125 UI.
The worst placement error is not the same as the grid step. If the receiver's grid happens to be offset such that the intended centre falls midway between two ticks, the nearest available position is 1/(2M) UI away. That is the best case bound — 0.03125 UI at 16×, 0.0625 at 8×.
But the grid's own phase is set by the observed candidate, which Chapter 5.2 showed is itself quantised. A receiver that starts its tick counter at the candidate inherits that offset, and cannot round to a nearer position because it has no finer representation of where the true boundary was. In that architecture the placement error can approach a full 1/M UI, which is the conservative figure Chapter 4.5 used.
5. What the Factor Costs
Raising M is not free, and the costs are in different places from the benefit.
The tick must be generated M times faster, and Chapter 4.2's divisibility problem gets M times harder. From a 100 MHz fabric clock:
100,000,000 / 115,200 = 15,625 / 18 ≈ 868.0556 (Chapter 4.3)
100,000,000 / (8 x 115,200) = 15,625 / 144 ≈ 108.5069
100,000,000 / (16 x 115,200) = 15,625 / 288 ≈ 54.2535None divides. Worse, the divisor shrinks as M rises — from 868 down to 54 — and Chapter 4.3 established that rounding error scales roughly as 1/N_div. So a higher oversampling factor makes the tick generator's own rate error larger, partially offsetting the placement resolution it was bought for. The fractional techniques of Chapter 4.4 exist partly for this reason, and Module 8 builds the generator.
Counters get wider and busier. The phase counter must reach M−1, so it needs $clog2(M) bits, and the frame-position counter must reach (N_frame − 0.5) x M ticks. At M = 16 and 8N1 that is 152 — eight bits — where at M = 8 it is 76.
And logic runs M times more often. The tick is an enable, so the receiver's state advances on it; more ticks per bit means more activity, which matters for power on a link that is otherwise almost idle.
M = 8 | M = 16 | M = 32 | |
|---|---|---|---|
| placement grid | 0.125 UI | 0.0625 UI | 0.03125 UI |
| tick rate at 115,200 | 921.6 kHz | 1.8432 MHz | 3.6864 MHz |
| fabric clocks/tick at 100 MHz | ≈ 108.5 | ≈ 54.3 | ≈ 27.1 |
| phase counter width | 3 bits | 4 bits | 5 bits |
| enable activity | baseline | 2× | 4× |
There is no universally correct value. The factor is chosen so that δ_grid fits the budget Chapter 4.5 computes for the specific link — and if the budget has room, a lower factor is cheaper in every dimension.
6. The Phase Counter
// Synthesizable SystemVerilog — the oversample phase counter only.
// os_tick_i comes from the generator of Module 8. The bit counter, shift
// register and frame sequencing that consume these events are Chapter 6.3;
// this module produces the positions and nothing else.
module uart_os_phase #(
parameter int unsigned OVERSAMPLE = 16
) (
input logic clk,
input logic rst_n,
input logic os_tick_i, // one clk cycle, M times per bit interval
input logic rephase_i, // accepted start: restart the grid here
input logic active_i, // inside a frame
output logic sample_o // one cycle, at the interval centre
);
// Every position is DERIVED from the parameter. A literal 8 here is
// the defect §7 describes: correct at M=16 and silently wrong after.
localparam int unsigned CENTER = OVERSAMPLE / 2;
localparam int unsigned PH_W =
(OVERSAMPLE <= 1) ? 1 : $clog2(OVERSAMPLE);
initial begin
if (OVERSAMPLE < 2)
$fatal(1, "uart_os_phase: OVERSAMPLE = %0d gives no positions to choose between", OVERSAMPLE);
if (OVERSAMPLE % 2 != 0)
$warning("uart_os_phase: OVERSAMPLE = %0d is odd; CENTER = %0d sits one half-tick before the true centre",
OVERSAMPLE, CENTER);
end
logic [PH_W-1:0] phase_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
phase_q <= '0;
sample_o <= 1'b0;
end else begin
sample_o <= 1'b0; // default: one-cycle pulse
if (rephase_i) begin
// The accepted start defines the grid's phase. Everything
// downstream is counted from here — Chapter 5.2 §4.
phase_q <= '0;
end else if (active_i && os_tick_i) begin
// Fire the sample on the tick that CARRIES the count to
// CENTER, not on the one after it. One tick of error is
// 1/M UI: 6.25% of a bit at M = 16.
sample_o <= (phase_q == PH_W'(CENTER - 1));
phase_q <= (phase_q == PH_W'(OVERSAMPLE - 1))
? '0 // wrap into the next interval
: phase_q + 1'b1;
end
end
end
endmoduleWidth derivation. The counter spans 0 .. OVERSAMPLE-1, so $clog2(OVERSAMPLE) bits — 4 at M = 16. The OVERSAMPLE <= 1 guard exists because $clog2(1) is 0 and a zero-width vector is illegal, the same hazard Chapter 4.3 met.
Why CENTER - 1. The sample fires on the tick that advances the count to CENTER. Comparing against CENTER and acting on the next tick places every sample one grid position late — 1/M UI, which is 6.25% of a bit at M = 16 and is exactly the kind of systematic offset that gets blamed on the baud rate.
Odd factors are warned, not rejected. OVERSAMPLE / 2 truncates, so M = 15 gives CENTER = 7 where the true centre is 7.5 — the sample lands half a tick early. That is a usable design; it is not what the name suggests, and a silent half-tick offset is worth a message.
rephase_i is the architectural statement. The grid's phase is not free-running: it is anchored at the accepted start, which is what makes Chapter 2.3's per-frame re-acquisition concrete. A design whose phase counter free-ran and merely observed the start would inherit accumulated error from previous frames.
7. Verification
The oversampling factor is a first-class axis. A parameterised receiver must be exercised at every supported value, and the interesting cases are the boundaries: the smallest legal factor, the largest, and any odd value if odd factors are permitted. The §6 defect is invisible at the design's default and appears only when the parameter moves.
Assert the position, not just the outcome. A test that only checks the received byte cannot distinguish a sample at the centre from one near a boundary — both read correctly on a clean line. Recording the phase at which each sample fired, and asserting it equals the derived CENTER, catches the off-by-one and the hard-coded literal directly:
// Assertion — the sample event occurs only at the derived centre position.
property p_sample_at_center;
@(posedge clk) disable iff (!rst_n)
sample_o |-> $past(phase_q == CENTER - 1) && $past(os_tick_i);
endproperty
assert property (p_sample_at_center);
// Assertion — no sample event while the receiver is not in a frame.
property p_no_sample_when_idle;
@(posedge clk) disable iff (!rst_n)
!active_i |-> !sample_o;
endproperty
assert property (p_no_sample_when_idle);Both state a contract rather than restating the implementation: the first says where samples are allowed to happen, the second says when.
Check the phase wrap. The counter returning to 0 at OVERSAMPLE - 1 is where a width error shows up — a counter one bit too narrow wraps early and every subsequent interval is short. A test that runs a full frame and asserts the sample count equals the frame's interval count catches it; a test that checks one interval does not.
And exercise the rephase. Back-to-back frames (Chapter 3.4) mean rephase_i arrives immediately after the previous frame ended. A design that only re-phases from an idle state mis-handles the first frame of a burst.
8. What This Means on an FPGA
The tick generator is the hard part, not the phase counter. §5 showed the divisor falls as M rises, so the generator's own rate error grows. At 100 MHz and 115,200 the 16× divisor is about 54.25 — a fractional part of a quarter, which Chapter 4.3's integer rounding would leave as a real error. This is where the fractional techniques of Chapter 4.4 earn their place, and it is a case where they clearly do.
Choose the factor from the budget, not from tradition. Compute δ_grid for a candidate factor, put it into Chapter 4.5's inequality with the rest of the terms, and take the smallest factor that fits with the reserve you want. On a link with an accurate far end and a short frame, 8× may leave ample margin and cost half the tick rate.
If the board has a serial-family crystal, the arithmetic changes completely. §2 showed that 1.8432 MHz gives a 16× tick with no division. That remains true, and a design with such a clock available has a genuinely free high oversampling factor — which is exactly why the convention formed.
Never generate an os_clk. The tick is an enable. A divided clock here would create a domain crossing between the tick generator and everything in Modules 5 and 6, for no benefit.
9. Understanding Check
10. Summary
Oversampling generates a periodic tick at f_os = M x f_baud, giving a grid of M positions spaced 1/M UI apart inside every bit interval. The tick is a clock enable, never a second clock. It does not mean the line is read M times per bit.
16× became conventional because 16 x 115,200 = 1.8432 MHz — exactly the crystal frequency Chapter 4.2 showed divides cleanly onto the whole classic rate series. On that hardware the 16× tick required no division at all. That is an arithmetic accident of the era, not a protocol requirement, a theoretical optimum, or a noise-immunity threshold.
Positions are derived from the factor: start centre at M/2 ticks, data[k] centre at (k + 1.5) x M. The start centre coincides with Chapter 5.2's validation point, which is why one counter serves both.
The factor buys placement resolution — δ_grid, between 1/(2M) and 1/M UI depending on whether the receiver can re-phase to a finer estimate — and this resolves the δ_sample term Chapter 4.5 deferred, decomposing it into grid placement, decision-window spread, and architecture-specific terms.
It costs a tick M times faster, and therefore a divisor M times smaller — 54.25 rather than 868 at 16× from 100 MHz — so the generator's own rate error grows as the placement error shrinks. It also costs counter width and enable activity. No factor is universally correct: choose the smallest whose δ_grid fits the budget.
Every position must be a named constant derived from the parameter. A literal 7 is correct at M = 16 and silently wrong everywhere else — the defect that survives review because it passes at the default.
11. What Comes Next
The grid gives the receiver a place to look. Chapter 5.4 asks what it should do when it gets there: read once, or read several positions around the centre and combine them. Multi-sample decisions reject a class of disturbance that a single reading cannot — and fail on a class that looks very similar. The chapter also produces the second term in δ_sample, because a window has width, and at 8× the same three samples span twice as much of the bit as they do at 16×.
Browse the full path on the UART tutorials index. For where the oversample tick's own rate error comes from, and the fractional arithmetic that reduces it, see Chapter 4.4.
Continue learning
Related tutorials
- Related topic
Integer Dividers and Baud-Rate Error
The ratio is a fraction and a counter holds an integer, so rounding is a design decision with a measurable cost. Three policies, the actual rate each produces, and why the error belongs in units of a bit period rather than as a bare percentage.
- Related topic
Clock Enable vs Generated Clock
Both architectures produce one event per bit interval and both simulate correctly. One adds a clock domain, a constraint and a CDC review to a design that needed none of them.
- Related topic
What a UART Actually Is
Two digital systems need to exchange a small amount of data over very few wires, and no clock travels with it. A UART is the logic that answers that problem — it converts between locally meaningful parallel data and timed activity on a single line, and the timing agreement it depends on is what the rest of the curriculum builds.
- Related topic
Synchronous vs Asynchronous Serial Links
A forwarded clock is a sampling reference generated by the same source as the data. Remove it and the receiver must assemble one from a configured rate, an observable event in the signal, and its own local clock — the responsibility shift that turns a receiver into a state machine and shapes every UART design decision that follows.
Where this fits
Part of the UART curriculum.
