UART · Module 8
RX and TX Baud Timing: One Generator or Two?
A receiver needs sixteen ticks per bit interval and a transmitter needs one. Sharing a base is excellent when that base is fractional and 73 times worse when it is not — and it does nothing at all for the receiver's phase uncertainty.
Chapter 7.3 §6 raised an asymmetry and deliberately declined to resolve it:
| Receiver | Transmitter | |
|---|---|---|
| Ticks per bit interval | OVERSAMPLE — typically 16 | 1 |
| Why | must find a boundary it was never told about | defines the boundary |
Both halves need timing from the same fabric clock and the same nominal rate, and they need it at rates that differ by a factor of sixteen. That is the question this chapter settles.
It has an answer that is not obvious, and it is worth stating up front because it inverts the usual intuition: sharing one generator is the better architecture — but only if the shared generator is fractional. A shared integer base is dramatically worse than two independent integer dividers, for a reason that follows directly from Chapter 8.3 §3.
1. The Receiver's Rate
From Chapter 5.3, the receiver divides each bit interval into M positions, so it needs M enables per interval:
f_sample = BAUD_HZ × M [Hz] = [baud] × [dimensionless]At 115,200 baud and M = 16:
f_sample = 115,200 × 16 = 1,843,200 Hz1,843,200 sample enables per second, against the transmitter's 115,200. That number should look familiar: Chapter 4.2 showed 1.8432 MHz is itself a standard crystal frequency, which is exactly why 16× became conventional — a board with that crystal gets the receiver's tick with no division at all.
On a 100 MHz clock it is not free, and the divisor is where the trouble starts:
| baud | M | sample rate | ideal divisor @ 100 MHz | integer divisor | rate error |
|---|---|---|---|---|---|
| 9,600 | 16 | 153,600 | 651.0417 | 651 | +0.0064 % |
| 115,200 | 8 | 921,600 | 108.5069 | 109 | −0.4523 % |
| 115,200 | 16 | 1,843,200 | 54.2535 | 54 | +0.4694 % |
| 1,000,000 | 16 | 16,000,000 | 6.2500 | 6 | +4.1667 % |
The oversample divisor is M times smaller than the bit-rate divisor, so by Chapter 8.3 §3's law its relative rounding error is roughly M times larger. At 115,200 and 16× the divisor is 54 and the error is 0.47% — against 0.0064% for the bit-rate divisor of 868.
This is the fact the whole chapter turns on.
2. Three Architectures
A — independent generators. One for the receiver at M × BAUD, one for the transmitter at BAUD.
B — shared base. One generator at M × BAUD feeding the receiver directly, with a divide-by-M counter producing the transmitter's enable.
C — shared parameters, separate state. Two generators, but their divisors or increments derived from one set of parameters at elaboration.
They differ in resources, in coupling, and — the part that decides it — in accuracy.
3. The Accuracy Result
Machine-computed at 100 MHz and 115,200 baud, M = 16:
| Architecture | TX rate | TX error | vs independent |
|---|---|---|---|
A — independent integer dividers (N = 868) | 115,207.3733 | +0.0064 % | baseline |
B — shared integer base (N = 54, then ÷16) | 115,740.7407 | +0.4694 % | 73× worse |
B — shared fractional base, ACC_W = 16 | 115,203.8574 | +0.00335 % | 1.9× better |
B — shared fractional base, ACC_W = 24 | 115,200.1321 | +0.00011 % | 58× better |
4. What Sharing Does Not Fix
This is the distinction most easily blurred, and getting it wrong leads to a real design error.
A shared generator makes the receiver's and transmitter's enables coherent with each other. It does nothing whatever about the receiver's phase relative to the incoming data.
The receiver's sampling positions are anchored to the start edge it detects on the wire (Chapter 5.2 §4, Chapter 6.1 §4). That edge is produced by a different device with a different oscillator. No amount of internal generator sharing changes where it falls, how uncertain its observation is, or how the far end's frequency error accumulates across the frame:
| Quantity | Source | Does sharing help? |
|---|---|---|
δ_grid — placement onto the tick grid | our M | no — set by M, not by sharing |
δ_origin — quantisation of the observed edge | our clk period | no |
ε_rel — relative rate mismatch | the far end's oscillator | no |
our own contribution to ε_rel | our generator's accuracy | yes — this one |
Only the last row improves, and §3 quantifies by how much. Everything Chapter 5.5 showed about sampling drift across a frame is untouched — the receiver still re-anchors on every start bit, still places samples on a grid with 1/M resolution, and still accumulates displacement at n × ε_rel.
The practical error this prevents: concluding that because one generator now drives both halves, an internal loopback test proves the timing is correct. It proves the two halves agree with each other, which they would even if the shared generator were 5% off. Only a test against an independent far end, or a measurement of the actual tick rate, checks the thing that matters.
5. Choosing
| A — independent | B — shared base | C — shared parameters | |
|---|---|---|---|
| Generators | 2 | 1 + a small counter | 2 |
| Accuracy | each independent | excellent if fractional, poor if integer | each independent |
| RX and TX rates locked? | no — can drift by their rounding | yes, exactly | no |
| Different RX/TX rates possible? | yes | no — locked to M | yes |
| Resource cost | 2 adders/counters | 1 adder + $clog2(M) counter | 2 |
| Failure mode | two divisors to get right | one wrong base ruins both | parameters drift apart |
Architecture B with a fractional base is the recommendation for an ordinary UART, where both halves run at the same configured rate: it is the cheapest, the most accurate, and it guarantees the two enables cannot disagree.
Architecture A is right when the two halves may run at different rates — an uncommon but real configuration, and one a shared base structurally cannot support because the transmitter's rate is defined as the base divided by M.
Architecture C is the weakest of the three. It pays for two generators without buying the independence that justifies them, and it introduces a maintenance hazard: two derivations that must stay consistent, with nothing enforcing it.
6. The Composed RTL
// ---------------------------------------------------------------------------
// 8.4 — composed timing: one oversample base serving RX directly and TX
// through a divide-by-OVERSAMPLE. Illustrative composition, not a UART IP.
// ---------------------------------------------------------------------------
module uart_timing_gen #(
parameter int unsigned CLK_HZ = 100_000_000,
parameter int unsigned BAUD_HZ = 115_200,
parameter int unsigned OVERSAMPLE = 16
) (
input logic clk,
input logic rst_n,
output logic os_tick_o, // OVERSAMPLE per bit interval — RX
output logic baud_tick_o // 1 per bit interval — TX
);
localparam int unsigned OS_W = (OVERSAMPLE <= 1) ? 1 : $clog2(OVERSAMPLE);
initial begin
if (OVERSAMPLE < 1)
$fatal(1, "uart_timing_gen: OVERSAMPLE must be >= 1");
if (longint'(BAUD_HZ) * OVERSAMPLE > longint'(CLK_HZ))
$fatal(1, "uart_timing_gen: %0d x %0d exceeds CLK_HZ %0d",
BAUD_HZ, OVERSAMPLE, CLK_HZ);
end
// The base runs at OVERSAMPLE x BAUD_HZ. RX consumes it directly.
uart_baud_tick_frac #(
.CLK_HZ (CLK_HZ),
.BAUD_HZ (BAUD_HZ * OVERSAMPLE),
.ACC_W (24)
) u_base (
.clk (clk), .rst_n (rst_n), .baud_tick_o (os_tick_o)
);
// TX derives its bit enable by counting OVERSAMPLE base ticks. This costs
// one small counter and guarantees the two enables cannot drift apart.
logic [OS_W-1:0] os_cnt_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
os_cnt_q <= '0;
baud_tick_o <= 1'b0;
end else begin
baud_tick_o <= 1'b0;
if (os_tick_o) begin
if (os_cnt_q == OS_W'(OVERSAMPLE - 1)) begin
os_cnt_q <= '0;
baud_tick_o <= 1'b1;
end else begin
os_cnt_q <= os_cnt_q + 1'b1;
end
end
end
end
endmoduleFour details are deliberate.
The base is the fractional generator, not the integer one. §3 is the whole argument. Instantiating Chapter 8.2's divider here would make the composition 73× less accurate than not sharing at all — the single most consequential line in the block.
BAUD_HZ * OVERSAMPLE is passed to the base, so the base generator needs no knowledge of oversampling. It produces a rate; the composition decides what that rate means.
The elaboration guard checks BAUD_HZ × OVERSAMPLE ≤ CLK_HZ in longint, because that product overflows 32 bits for high rates and large factors, and a wrapped comparison would pass a configuration that cannot work. At 1 Mbaud and 16× the required base is 16 MHz — a sixth of a 100 MHz clock, which is fine; at 8 Mbaud it would exceed it, and the guard says so rather than silently producing an unreachable rate.
The TX counter divides the base, it does not generate independently. That is what locks the two enables together: every sixteenth os_tick_o is also a baud_tick_o, exactly, forever. Simulation over three million fabric clocks: 55,312 oversample ticks and 3,457 baud ticks — a ratio of exactly 16, with the baud interval measured at 868–869 clocks as the fractional base requires.
7. Configuring Two Halves at One Rate
A UART link normally runs both directions at the same rate, which raises a design question the composition above answers implicitly.
One rate parameter, or two? With a shared base there is structurally only one — the transmitter's rate is the base divided by M, so there is nothing separate to configure. That removes an entire class of bug: a receiver and transmitter configured to different rates by a software error cannot happen, because there is no second register to disagree.
The cost is that they cannot be different when that is wanted. Split rates are unusual but not unheard of — a device streaming telemetry fast while accepting commands slowly. A design that must support it needs architecture A and two configurations, and it should then also verify the two independently rather than assuming a single loopback test covers both.
Whichever is chosen, the rate should be one field, not a divisor and an increment. Exposing N_DIV or INC directly to software leaks the generator's architecture into its interface: changing from an integer divider to a fractional one would then change the register map. Exposing a rate and deriving the generator value keeps that free. Where that derivation happens — elaboration or at runtime — is Chapter 8.5's subject, and the register interface itself belongs to Module 13.
8. Verification
Assert the ratio, exactly. Every M-th oversample tick must be a bit tick, with no drift over any run length. Counting both and checking the quotient is a stronger check than measuring either rate:
composed : os_tick=55312 baud_tick=3457 baud interval 868..869 clksFrom simulation over three million fabric clocks — a ratio of exactly 16, and baud intervals in the two-value set the fractional base predicts.
Check the base rate, not the derived rate. An error in the base propagates to both consumers, so measuring the transmitter's rate alone can mask which generator is wrong. Measure os_tick_o directly and compare against BAUD_HZ × M.
Do not verify timing by internal loopback alone. §4's point stated as a test procedure: a loopback exercises the receiver against the transmitter, both driven by the same generator, so it passes whatever that generator's rate happens to be. It is an excellent test of framing, bit order and handshake, and it is worthless as a test of rate accuracy. Rate must be checked against an independent reference or measured directly.
Sweep OVERSAMPLE. The divide-by-M counter's width is derived, and values that are not powers of two exercise the terminal comparison rather than a natural wrap — the same hazard Chapter 6.3 §2 identified in the receiver's phase counter.
// Assertion — a bit tick never occurs without a coincident oversample tick,
// which is what "subsample" means structurally.
property p_baud_implies_os;
@(posedge clk) disable iff (!rst_n)
baud_tick_o |-> os_tick_o;
endproperty
assert property (p_baud_implies_os);
// Assertion — the subsample counter stays within its modulus.
property p_os_count_in_range;
@(posedge clk) disable iff (!rst_n)
os_cnt_q <= OS_W'(OVERSAMPLE - 1);
endproperty
assert property (p_os_count_in_range);The first is worth writing because it fails on the most likely restructuring error: someone replacing the subsample counter with an independent generator "to decouple the two", which silently reintroduces the drift the composition existed to prevent.
9. What This Means on an FPGA
The sharing is nearly free. One 25-bit adder for the base and a 4-bit counter for the subsample, against two generators in the independent architecture. The saving is small in absolute terms and the accuracy gain is the real reason to prefer it.
The oversample enable has higher fanout and a higher toggle rate. It pulses 1,843,200 times per second rather than 115,200 and reaches every timing-dependent register in the receiver. At UART scale this is still unremarkable — a few dozen endpoints toggling at under 2 MHz — but it is the one signal in the design worth glancing at if a power or fanout report looks unexpected.
Probe os_tick_o and baud_tick_o together. The ratio is directly visible and is the first check on a composition: sixteen of one per one of the other, always. A ratio that is right but a rate that is wrong points at the base generator; a ratio that is wrong points at the subsample counter.
Size the base generator from the highest rate the design supports, not the nominal one. The guard catches BAUD_HZ × OVERSAMPLE > CLK_HZ at elaboration, and for a runtime-configurable rate the same relationship must hold for the fastest configuration — which is the composition's equivalent of Chapter 8.2's DIV_MAX argument, inverted.
10. Understanding Check
11. Summary
The receiver needs M ticks per bit interval and the transmitter needs one, so a shared generator must run at BAUD_HZ × M — 1,843,200 Hz at 115,200 baud and 16×, which is itself a standard crystal frequency and the reason 16× became conventional.
The oversample divisor is M times smaller than the bit-rate divisor, so its relative rounding error is roughly M times larger: 54 instead of 868, 0.47% instead of 0.0064%.
That single fact decides the architecture, and it points both ways:
| TX error at 100 MHz / 115,200 | vs independent | |
|---|---|---|
| independent integer dividers | +0.0064 % | baseline |
| shared integer base | +0.4694 % | 73× worse |
| shared fractional base, 24-bit | +0.00011 % | 58× better |
Share the base — with a fractional generator. It is cheaper, far more accurate, and it locks the two enables together so they cannot disagree. A shared integer base is the worst of the three options.
Sharing does not touch the receiver's phase uncertainty. The sampling phase comes from the start edge on the wire, produced by another device's oscillator. Sharing improves our own contribution to the rate mismatch and nothing else — and the practical consequence is that an internal loopback test cannot validate rate accuracy, because both halves are wrong together.
Verified in simulation: 55,312 oversample ticks to 3,457 bit ticks — exactly 16 — over three million fabric clocks.
12. What Comes Next
Four chapters have produced working generators and an architecture to compose them. What they have not done is state a contract: which parameter values are legal, what happens when an illegal one is supplied, what the generator does during and immediately after reset, and how a rate change should reach it safely.
Chapter 8.5 closes the module with exactly that. It defines the legal range and hardware consequence of every parameter; separates elaboration-time errors from runtime programming errors from accuracy warnings, which are three different things routinely conflated; specifies reset behaviour down to the first tick; and weighs compile-time specialisation against runtime programmability rather than building both and calling it flexible.
Browse the full path on the UART tutorials index. For the oversampling grid this chapter feeds, read back to Chapter 5.3; for the transmitter's single enable, Chapter 7.3.
Continue learning
Related tutorials
- Related topic
Integer Divider RTL
The counter that turns a fabric clock into a bit-interval enable — terminal count derived rather than asserted, width from the parameters, and a runtime-programmable divisor with a commit policy that cannot corrupt an interval in flight.
- Related topic
Fractional Accumulator Baud Generation
A phase accumulator keeps the remainder an integer divider discards, so the average rate converges while individual intervals alternate between two lengths — and a too-narrow accumulator is worse than the divider it replaced.
- 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.
- Related topic
Frame Configurations: 8N1 and the Configuration Space
8N1 names three of the four choices a UART link depends on and omits the one most likely to be wrong. Reading the shorthand, computing what each configuration costs in intervals and line time, and why a longer frame spends timing margin as well as throughput.
Where this fits
Part of the UART curriculum.
