UART · Module 14
Verifying the Transmitter
Checking bit timing, frame structure, ordering and the ready/busy contract against the specification rather than the RTL — and measuring the fractional baud generator from outside.
The transmitter has been checked many times already, and always by the design's own receiver. Loopback is a fine functional test and it has one structural weakness: both ends share a timing generator, so a transmitter emitting bits at entirely the wrong rate loops back perfectly (Chapter 11.5 §2 measured exactly that).
This chapter checks the transmitter against a nominal grid instead — an ideal bit period computed from the specification, not taken from the design.
1. Structure, Predicted Before It Is Observed
The predictor computes what must appear on the wire, from the configuration alone:
// Predict the exact line pattern a correct transmitter must emit.
// Returned LSB-first as: start, data[0..nbits-1], [parity], stop.
task automatic predict_tx_bits(input logic [8:0] data, input int nbits,
input int parity_mode,
output logic [15:0] bits, output int nbit_total);
int i, b;
begin
bits = '0; b = 0;
bits[b] = 1'b0; b++; // start
for (i = 0; i < nbits; i++) begin bits[b] = data[i]; b++; end
if (parity_mode != P_NONE) begin
bits[b] = bfm_parity_bit(parity_mode, data, nbits); b++;
end
bits[b] = 1'b1; b++; // stop
nbit_total = b;
end
endtaskIt is written from Chapter 3.1 and 3.2, not from uart_tx. Four properties fall straight out of it, and each is a requirement from Chapter 14.1 §2:
-- 1. structure: what the specification says must appear
0x55 8N1 must be 10 intervals: start + 8 data LSB-first + stop
pass predicted frame is 10 intervals
pass predicted start bit is SPACE
pass predicted stop bit is MARK
pass predicted data is LSB-firstThe LSB-first check is worth its line. 0x55 is 1010_0101, so the first data interval must be 1 and the second 0. A transmitter that shifted MSB-first would produce a perfectly well-formed frame carrying 0xAA, and a loopback test against a receiver that also assembled MSB-first would pass.
2. Content, Through an Independent Monitor
-- 2. eight patterns through an independent monitor
pass monitor observed all 8 frames
pass every transmitted byte matches what was queuedEight patterns chosen by shape rather than value — alternating, inverse-alternating, nibble-split both ways, single-one at each end, all-ones-but-one, and a mixed value. That is Chapter 14.1 §3's pattern binning applied.
The monitor decodes using its own nominal bit period. It is the same algorithm a receiver uses, implemented from the specification, and it therefore cannot follow the transmitter into a timing error.
3. Timing, Measured Against an Ideal
This is what loopback cannot do. The ideal bit period at 115,200 baud is exactly
1 / 115200 s = 8680.5556 nsand the transmitter runs on a 100 MHz clock, so it cannot produce that period. It must approximate it. Timestamping every edge on the wire and keeping the intervals that are a single bit long:
-- 3. bit period, measured against the IDEAL grid
single-bit intervals: 20 at 8680 ns (868 clk), 1 at 8690 ns (869 clk), 0 other
measured mean 8680.4762 ns (ideal 8680.5556 ns)
mean error -9.1 ppm
measured single-bit 8680.0000 .. 8690.0000 ns
error at the short end -64.0 ppm4. The Ready/Busy Contract
Chapter 7.4 designed the transmitter to accept the next byte while the current one is still going out, so that back-to-back frames have no gap. That is a claim about when tx_ready_o re-asserts, and it is checkable from outside by watching the pin and the flag together.
-- 4. ready/busy contract
frame lasted 9147 clocks (= 10.54 bit times)
ready high for 868 of them; first went low at clock 0
ready rose at clock 8279 = bit time 9.54 of the frame
pass ready is high for part of the frame (a holding register exists)
pass ready is NOT high for the whole frame (the register fills)
pass busy clears only after the line returns to MARKReady rises at bit time 9.54 of a 10.54-bit-time frame and stays high for exactly one bit period. Bit time 9.54 is the start of the stop interval — so the transmitter opens its holding register for precisely the stop bit, which is exactly the design Chapter 7.4 described, confirmed by an observer that knows nothing about it.
The frame is 10.54 bit times rather than 10. The extra half is launch latency: the byte was accepted, and the start bit begins on the next baud tick rather than immediately. That is real and it is not a defect — it is the granularity of a design whose transmitter advances on a bit-rate enable (Chapter 8.1).
0x55 on the wire, and the ready window
12 cycles5. Ordering
Eight frames were driven back to back and compared in order. Ordering is a separate requirement from content (Chapter 14.1 R9), and a scoreboard that compares sets rather than sequences will not catch a transmitter that swaps two bytes.
The check is that the nth observed frame equals the nth queued byte — not that the multiset matches. The distinction sounds academic until a FIFO pointer bug reverses two entries, which is a defect that preserves the multiset exactly.
6. The Same Environment, Built as a UVM Agent
Everything this module has built so far is a directed testbench: a driver module, a monitor module, a predictor, and a test that calls them in order. That is the right instrument for a link with one wire in each direction, and it is what the published suites use.
A UVM agent is the same three components with the connections made explicit and the stimulus separated from the sequencing. Nothing about the checking changes — and that is the point worth making, because the methodology is often sold as though it improved the checking. It does not. It improves the reuse and the layering.
// ---------------------------------------------------------------------------
// uart_frame_item — one frame, described the way a test thinks about it
//
// The fields are the SPECIFICATION's parameters, not the design's signals.
// There is no `tx_o`, no oversample count and no clock here: a transaction
// says what should appear on the wire, and the driver decides how.
// ---------------------------------------------------------------------------
class uart_frame_item extends uvm_sequence_item;
`uvm_object_utils(uart_frame_item)
rand bit [8:0] data;
rand int nbits; // 5..9
rand int parity_mode; // P_NONE, P_EVEN, P_ODD, P_MARK
rand int stop_halves; // 2 = 1 bit, 3 = 1.5, 4 = 2
rand real tbit_ns; // the argument that makes the BFM useful
rand int err; // ERR_NONE .. ERR_BREAK
// observed-only fields, filled in by the monitor
bit parity_err, frame_err;
constraint c_format { nbits inside {[5:9]};
parity_mode inside {[0:3]};
stop_halves inside {[2:4]}; }
constraint c_data { data < (1 << nbits); }
// Baud error is the axis that matters, so it is randomised in BINS
// rather than uniformly: the interesting values cluster at the edge of
// the tolerance window, not in the middle of it.
constraint c_baud { tbit_ns dist { 8680.5556 := 40,
[8160 : 8420] := 15, // about -5%
[8940 : 9200] := 15, // about +5%
[7800 : 8160] := 5,
[9200 : 9560] := 5 }; }
// Clean traffic must dominate, or the error cases stop being corners.
constraint c_err { err dist { 0 := 70, [1:5] := 30 }; }
function new(string name = "uart_frame_item");
super.new(name);
endfunction
endclassThe driver is a thin wrapper around the BFM this module already built — which is the argument for having built it as a standalone component first:
class uart_line_driver_c extends uvm_driver #(uart_frame_item);
`uvm_component_utils(uart_line_driver_c)
virtual uart_line_if vif;
task run_phase(uvm_phase phase);
vif.tx <= 1'b1; // idle MARK before anything
forever begin
seq_item_port.get_next_item(req);
drive_frame(req);
seq_item_port.item_done();
end
endtask
// The whole body is the send_frame task of Chapter 14.2, reached through
// a virtual interface instead of a hierarchical name. The LOGIC did not
// move; only the plumbing did.
protected task drive_frame(uart_frame_item t);
...
endtask
endclassThe sequence library is where transmitter verification actually lives. The agent is infrastructure; these are the tests:
// Every byte value, at the nominal rate -- the coverage floor.
class uart_all_bytes_seq extends uvm_sequence #(uart_frame_item);
`uvm_object_utils(uart_all_bytes_seq)
task body();
for (int d = 0; d < 256; d++) begin
`uvm_do_with(req, { data == d; nbits == 8; parity_mode == 0;
stop_halves == 2; err == 0;
tbit_ns == 8680.5556; })
end
endtask
endclass
// The full format cross -- Chapter 14.6 section 2.
class uart_format_cross_seq extends uvm_sequence #(uart_frame_item);
`uvm_object_utils(uart_format_cross_seq)
task body();
foreach_format: for (int nb = 5; nb <= 9; nb++)
for (int pm = 0; pm <= 3; pm++)
for (int sh = 2; sh <= 4; sh++)
repeat (4)
`uvm_do_with(req, { nbits == nb; parity_mode == pm;
stop_halves == sh; err == 0; })
endtask
endclass
// The baud sweep of Chapter 14.4, as a sequence rather than a loop in a test.
class uart_baud_sweep_seq extends uvm_sequence #(uart_frame_item);
`uvm_object_utils(uart_baud_sweep_seq)
rand int lo_pct = -10, hi_pct = 10;
task body();
for (int e = lo_pct; e <= hi_pct; e++)
`uvm_do_with(req, { nbits == 8; parity_mode == 0; err == 0;
tbit_ns == 8680.5556 * (100.0 + e) / 100.0; })
endtask
endclass7. Verification
Check structure separately from content. §1 checks that the predicted frame has the right shape before any frame is transmitted. If that fails, the predictor is wrong and every subsequent comparison is meaningless.
// Assertion — the transmit line is idle at MARK whenever nothing is in
// flight. Written from Chapter 3.1, not from uart_tx, and it is what stops
// a design from emitting a spurious start bit after reset.
property p_idle_is_mark;
@(posedge clk) disable iff (!rst_n)
!tx_busy_o |-> tx_o;
endproperty
// Assertion — busy must cover the whole frame. If busy clears while the
// line is still at SPACE, a driver that gates a transceiver on busy will
// truncate the final character — Chapter 13.4 section 4.
property p_busy_covers_the_frame;
@(posedge clk) disable iff (!rst_n)
$fell(tx_busy_o) |-> tx_o;
endpropertyAssert the mean bit period, not each interval. §3's dither means an interval-by-interval check fails on a correct design. The specification constrains the average.
Drive the handshake correctly, and know why. §4's callout is the single most common transmitter-testbench defect, and it manifests as a duplicated byte that looks like an RTL bug.
Do not check the transmitter with the design's own receiver alone. It is a useful test and it cannot see a systematic timing error, because both halves share a generator. That is not a hypothetical — it is measured in Chapter 11.5 §2.
8. Debugging
9. What This Means in Practice
A scope measurement is this chapter's §3. On hardware, measuring the bit period on the transmit pin is the first check worth making, and the number to compare against is 1 / baud, computed independently of the device.
Expect dither on any fractional design. A scope triggered on single edges will show two populations one clock apart. That is correct, and mistaking it for jitter has sent people looking for a power-supply problem.
The ready window is short. One bit period at the end of the frame — 8.68 µs at 115,200 baud, and 0.33 µs at 3 Mbaud. A driver that polls rather than responding to the handshake may miss it entirely at high rates, which presents as poor throughput rather than as an error.
Launch latency is real and is up to one bit time. Frames are 10.54 bit times here, not 10. Any budget that assumes exactly N × T_bit for a burst will drift.
10. Understanding Check
11. Summary
Check the transmitter against a nominal grid, not against the design's own receiver. Loopback shares a timing generator and therefore cannot see a systematic rate error at all.
Predict the frame before observing it. Ten intervals, start at space, stop at mark, data LSB-first — four checks that come from the specification and that a bit-reversed transmitter cannot pass even though its frames are well formed.
The fractional baud generator is measurable from outside: 20 intervals of 868 clocks and 1 of 869, a mean of 8680.4762 ns against an ideal of 8680.5556, −9.1 ppm where an integer divider would sit permanently at −64 ppm.
Assert the mean, not each interval — the dither is the mechanism, not a fault.
Ready returns for exactly one bit period, at bit time 9.54 of a 10.54-bit-time frame: the stop interval, confirming the holding-register design from outside, and explaining the duplicated-byte testbench defect precisely.
Frames are 10.54 bit times, not 10, because launch waits for the next bit-rate enable. Any burst budget assuming N × T_bit will drift.
And the discipline underneath all of it: no branch of any check used the design's own tick.
12. What Comes Next
The transmitter emits what the specification demands. Chapter 14.4 turns the environment around and drives the receiver — with legal traffic, with marginal traffic at the edge of its timing budget, and with traffic that is simply illegal.
That is where the BFM's arbitrary bit period earns its place: the receiver's tolerance is a specification claim that has been derived, quoted and assumed for eleven modules, and it is about to be measured against the RTL.
Browse the full path on the UART tutorials index. For the fractional divider this chapter measured, read back to Chapter 8.3.
Continue learning
Related tutorials
- Related topic
Verifying the Receiver
Driving a receiver with legal, marginal and deliberately illegal traffic, and checking sampling behaviour, reported status and recovery against the specification.
- Related topic
Complete RX RTL Architecture
One synthesizable receiver assembled from the module's five preceding chapters — assumptions stated first, walked block by block with the invariant each maintains, then reviewed the way a reviewer would, including the defects found during its own development.
- Related topic
The TX FSM and Frame Sequencing
Five states, eight transitions, and one rule that keeps the line honest: tx_o is a register written only inside the bit-boundary branch. Includes the off-by-one that emits nine data bits, and the state table the RTL was checked against.
- Related topic
Ready/Busy Handshake and Back-to-Back Frames
The write-side contract stated in cycles — what ready promises, how it differs from busy, when payload ownership transfers, and how accepting at the final boundary lets frames run with no gap and no shortened stop bit.
Where this fits
Part of the UART curriculum.
