Skip to content
VLSI Mentor

UART · Module 14

Building a UART Verification Plan

Deriving checkable requirements from framing, timing, error and status behaviour, and turning the configuration space into a coverage model that reports what testing actually reached.

Thirteen modules produced a UART that works. Every result in them came from a testbench written by the same person who wrote the RTL, checking what that person thought to check — and the most interesting defects were found not by testing harder but by a new consumer arriving with a new access pattern (Chapter 13.4 §5 being the clearest case).

That is the problem a verification plan exists to solve. Not more tests — a stated, checkable set of requirements, and an honest measurement of which ones have actually been exercised.

1. Verify the Specification, Not the Implementation

The distinction sounds pedantic and is the single most consequential decision in a verification plan.

A test written from the RTL asks: does the design do what it does? It will pass. It will keep passing after a refactor that preserves the bug. It encodes the implementation's assumptions, including the wrong ones.

A test written from the specification asks: does the design do what it must? It can fail, which is the only property that makes a passing test informative.

The practical test of which kind you have written: could this check have been written before the RTL existed? If reading the RTL was necessary to write it, it is at best a regression lock and at worst a tautology.

2. From Specification to Checkable Requirements

A requirement is checkable when it names an observable and a condition. "The receiver shall be robust" is not a requirement. Working through the specification the previous modules built:

#RequirementObservableSource
R1A frame is start (space), DATA_W data bits LSB-first, optional parity, stop (mark)the line3.1, 3.2
R2Parity is computed over the data bits only, per the configured modethe line3.3
R3A received byte is delivered with its own parity and framing verdictread port6.4
R4A framing error is reported when the stop interval is not at markread port9.1
R5A parity error is reported and the byte is still deliveredread port6.4
R6A byte lost to a full queue is reported as overrunstatus9.3
R7A line held low past a frame time is reported as a breakstatus9.4
R8The receiver recovers on the next frame after any errorread port6.2
R9Transmit order is preserved; no byte is duplicated or droppedthe line7.4
R10A configuration change does not reformat a frame in flightthe line11.3

Every one names something visible at a boundary. Not one requires looking inside the design, which is what makes them independently checkable.

R5 is the one people get wrong. The intuitive requirement is "a parity error is reported", and the actual specification also says the byte is still delivered — because the receiver cannot know whether the consumer wants it. A plan that omits the second half will happily pass a design that discards the byte.

A flowchart showing how a specification requirement becomes a verification result. A requirement is taken from the specification and the question is asked whether it names an observable at a boundary. If it does not, it is not checkable and must be restated. If it does, the next question is whether the check could have been written before the RTL existed. If it could not, the check has borrowed the implementation and is a regression lock rather than a specification check. If it could, stimulus is generated to reach the condition, an independent predictor computes what must happen, and a scoreboard compares the two. Finally coverage records that the condition was actually reached, because a check that never ran contributes nothing.noyesnoyesRequirement fromthespecificationNames anobservable ata boundary?Not checkable —restate itCould it bewrittenbefore theRTL?Regression lock,not verificationGenerate stimulusthat reaches theconditionIndependentpredictor computeswhat must happenScoreboard comparesCoverage recordsthat it wasreached
Figure 1 — how a requirement becomes a result. The branch that matters is the one on the left: if writing the check needed the RTL, the check is a regression lock rather than a verification of the specification, and it belongs in a different category with different expectations.

3. The Configuration Space Is the Coverage Model

A UART's behaviour depends on more than its data. The plan has to enumerate what varies:

AxisBinsWhy it matters
parity modenone, even, odd, markchanges frame length and the parity rule
data patternall-zero, all-one, mixed, single-one, single-zero11.4 §2's false break needed all-zero
corruptionnone, parity, stop, short stop, glitch, breakeach maps to a requirement
baud offsetslow, nominal, fast4.5's budget

The cross of parity mode against corruption is the one that earns its keep — 24 combinations, and it is where the surprises live. Chapter 14.6 runs all 24 and finds one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// COVERAGE — written by hand, because Icarus Verilog supports no covergroups.
// The bins are the same ones a covergroup would declare; what is lost is the
// tool's bookkeeping, not the thinking. Chapter 14.1 is about choosing them.
localparam int N_PAR  = 4;    // none, even, odd, mark
localparam int N_PAT  = 5;    // all-zero, all-one, alternating, single-1, single-0
localparam int N_ERR  = 6;    // ERR_NONE .. ERR_BREAK
localparam int N_BAUD = 3;    // slow, nominal, fast

int cp_parity [0:N_PAR-1];
int cp_pattern[0:N_PAT-1];
int cp_err    [0:N_ERR-1];
int cp_baud   [0:N_BAUD-1];
int cross_pe  [0:N_PAR-1][0:N_ERR-1];   // parity mode x error kind

Data patterns are binned by shape, not by value. Binning all 256 byte values would report 256 bins and tell you nothing about which kinds were tried; binning by weight — all-zero, all-one, single-one, single-zero, mixed — names the shapes that break things. All-zero is what produced the false break in Chapter 11.4; single-one is what Chapter 7.5 needed to catch a truncated parity mode.

4. What Coverage Actually Told Us

Here is the result that justifies the whole apparatus. The assembled environment was run against the IP with a reasonable-looking set of directed tests: eight data patterns in 8N1, then three corruption cases in 8E1, then eight transmitted frames checked by an independent monitor.

Everything passed. Nineteen scoreboard comparisons, zero mismatches.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  scoreboard: 19 matches, 0 mismatches
  ---- functional coverage ----
    parity mode     2/4 bins
    data pattern    5/5 bins
    error injection 3/6 bins
    baud offset     1/3 bins
    parity x error  4/24 cross bins

Closing the holes is Chapter 14.6's work. Running the full cross plus the baud corners:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  ---- functional coverage ----
    parity mode     4/4 bins
    error injection 6/6 bins
    baud offset     3/3 bins
    parity x error  24/24 cross bins

And that run found a real behaviour nothing else had — one combination out of twenty-four behaving differently from its twenty-three neighbours. Chapter 14.6 §3.

5. What Coverage Does Not Tell You

It does not mean the checks were good. A run with 100% coverage and no scoreboard reaches every bin and verifies nothing. Coverage measures stimulus; checking is a separate axis, and a plan needs both.

It does not mean the model was right. The bins are a human's guess about what matters. A missing axis is invisible — and the DMA access pattern that found Chapter 13.4's defect was not in any coverage model, because nobody had thought of "how fast is the queue drained" as an axis until a DMA engine existed.

It does not weight bins by risk. A cross bin exercised once counts the same as one exercised ten thousand times, and "parity mode × corruption" bins are not equally likely to break.

100% is not the goal; it is the floor of the space you thought of. The useful question is not whether coverage closed, but which axes are missing — and the honest answer is discovered when something new is attached, which is the pattern every module since 11 has repeated.

6. The Plan Itself

A usable plan is short. For this UART:

SectionContent
RequirementsR1–R10 of §2, each with its observable and its source chapter
Coverage modelthe four axes and the one cross of §3
Stimulusa serial BFM that can drive any baud, any format, and six corruptions — 14.2
Checkingan independent monitor, predictor and scoreboard — 14.5
Corner listthe combinations deliberately targeted — 14.6
Exit criteriaall requirements checked, coverage model closed, zero mismatches

The exit criteria are three conditions and not one. A plan whose exit criterion is "all tests pass" has no way to notice §4's result.

7. Closing the Loop: Which Check Covers Which Requirement

A plan that lists requirements and a suite that lists checks are two documents until something connects them. This is that connection, for the components this module publishes.

#RequirementCovered byWhere
R1frame is start / LSB-first data / parity / stopthe exhaustive predictor walk — 30,720 frames, every slot14.5
R1…and the driver actually emits that480 format combinations compared slot-by-slot against the model14.2
R2parity over the data bits, per modethe walk's population-count check, all 4 modes × 5 widths14.5
R4framing error when the stop is not MARKERR_STOP injected; monitor reports framing, not parity14.4
R5parity error reported and the byte still deliveredERR_PARITY injected; both halves asserted separately14.4
R7a line held low past a frame is a breakERR_BREAK injected; exactly one start edge, one framing error14.4
—the environment's own timing tolerance21-point baud sweep, measured against 0.5/8.514.4

Coverage, written the way this toolchain allows. Icarus Verilog 13.0 has no covergroups — confirmed by a minimal test, not assumed — so the model is arrays of counters. The shape matters more than the syntax:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The configuration cross of section 3, as counters. Every bin is a
// REQUIREMENT-bearing axis, not a convenient one: widths and parity modes
// because R1 and R2 are defined per format, error kinds because R4, R5 and
// R7 each need their own injection, and baud offset because the tolerance
// window is the one continuous axis in the design.
integer cov_width  [5:9];
integer cov_parity [0:3];
integer cov_stop   [2:4];
integer cov_err    [0:5];
integer cov_baud   [0:20];        // -10% .. +10% in 1% steps

// Crosses are the point; the marginals are nearly free and nearly useless.
integer cov_fmt_x  [5:9][0:3][2:4];

task cov_sample;
    input integer nb, pm, sh, er, baud_idx;
    begin
        cov_width[nb]++;  cov_parity[pm]++;  cov_stop[sh]++;
        cov_err[er]++;    cov_baud[baud_idx]++;
        cov_fmt_x[nb][pm][sh]++;
    end
endtask

// And the check that makes coverage mean something: every bin non-zero.
task cov_report;
    integer nb, pm, sh, holes;
    begin
        holes = 0;
        for (nb = 5; nb <= 9; nb = nb + 1)
        for (pm = 0; pm <= 3; pm = pm + 1)
        for (sh = 2; sh <= 4; sh = sh + 1)
            if (cov_fmt_x[nb][pm][sh] == 0) begin
                holes = holes + 1;
                $display("  HOLE: width=%0d parity=%0d stop=%0d never driven",
                         nb, pm, sh);
            end
        $display("  format cross: %0d of 60 bins hit, %0d holes", 60-holes, holes);
    end
endtask

8. Verification of the Verification

Test the testbench before trusting it. The BFM of Chapter 14.2 is checked against itself — driver into monitor, no DUT — before either is pointed at the design. If the driver and monitor disagree about a frame, the problem is in the environment, and finding that out while debugging the DUT is expensive.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  pass 8N1 0x55 recovered
  pass 8E1 0xA5 with flipped parity -> parity error
  pass stop held at SPACE -> framing error
  pass 7O1 0x2A recovered
  == 8 checks, 0 failures ==

Inject a known defect and confirm the environment catches it. An environment that has never failed has not been shown to be able to fail. The error-injection modes exist partly for this: ERR_PARITY must produce a reported parity error, and if it does not, the checker is broken rather than the design.

Check that the predictor is not the design. Grep the testbench for imports of the design's packages and for hierarchical references into the DUT. Both are mechanical checks and both catch §1's leaks.

9. Understanding Check

10. Summary

Verify the specification, not the implementation. The practical test is whether the check could have been written before the RTL — and if not, it is a regression lock rather than verification.

Three leaks to guard against: peeking at internal state, sharing the design's own definitions, and borrowing its timing. Each looks like reuse and each removes the independence that makes a mismatch mean something.

A requirement is checkable when it names an observable and a condition. Ten of them come straight out of Modules 3 through 11, and the one most often stated wrongly is that a parity error must be reported and the byte still delivered.

The configuration space is the coverage model: parity mode, data pattern shape, corruption kind and baud offset — with the parity-by-corruption cross as the axis that earns its keep.

The result that justifies all of it: a competent set of directed tests produced 19 matches and 0 mismatches while reaching 4 of 24 cross bins and 1 of 3 baud bins. The tests were not bad; a green run simply is not evidence of completeness.

Coverage measures stimulus, not checking, not quality, and not the axes nobody thought of. 100% is the floor of the space you imagined — and the defects that mattered most in this curriculum were each found when something new was attached and brought an axis with it.

11. What Comes Next

The plan names a stimulus generator it does not yet have. Chapter 14.2 builds it: a serial bus-functional model that can drive a UART line at an arbitrary bit period — including deliberately wrong ones — in any frame format, with six kinds of corruption available on demand.

It is the piece that makes the baud axis of §3 reachable at all, and the chapter ends by measuring something the design has never been asked: exactly how much baud error this receiver tolerates.

Browse the full path on the UART tutorials index. For the timing budget the baud axis exercises, read back to Chapter 4.5.

Continue learning

Where this fits

Part of the UART curriculum.