Skip to content
VLSI Mentor

UART · Module 14

Negative Testing and Corner-Case Strategy

Choosing corners that can actually fail — frame boundaries, FIFO limits, configuration changes, reset and marginal baud — and a measurement that located the receiver's sampling instant from outside.

"Test the corner cases" is advice nobody disagrees with and almost nobody makes operational. The useful version is narrower: a corner is worth testing when a plausible implementation could get it wrong, and most of the inputs people call corner cases fail that test — they are simply unusual values that the same code path handles identically.

This chapter picks corners on that basis, runs the cross Chapter 14.1 §4 left at four bins of twenty-four, and ends with a measurement that located something inside the receiver from entirely outside it.

1. What Makes a Corner Worth Testing

Looks like a cornerActually a cornerWhy
the byte 0x7Fthe byte 0x00all-zero is the longest low run — it produced the false break in 11.4 §2
a "random" baud ratethe baud at the edge of the budgetthe edge is where the design changes behaviour
a long messagea message of length trigger ± 113.4 §4's residual lives there
many framesback-to-back frames with no gap7.4's ready window is one bit wide
any configuration changea change during a frame11.3 exists for this

The pattern is that a real corner sits at a boundary in the implementation, not at an extreme of the input range. 0xFF is not interesting because it takes the same path as 0xFE; 0x00 is interesting because its bit pattern interacts with a threshold — the break detector's low-run counter.

Which means corners are chosen by reading the design's boundaries, not its code. That is a different activity from Chapter 14.1 §1's prohibition: knowing that the design contains a threshold is architectural knowledge, and the check is still written from the specification.

2. The Full Cross

Chapter 14.1 §4 reported four of twenty-four cross bins after a competent set of directed tests. Twenty-four is small enough to enumerate exhaustively — four parity modes against six corruptions — and enumeration is the right answer whenever the space is that size:

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

Twenty-three of the twenty-four behaved as the specification requires. One did not behave like its neighbours:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- parity=ODD  corruption=glitch
        [rx] byte 17 = ad  pe=1 fe=0
...
-- parity=MARK corruption=glitch
        [rx] byte 23 = a5  pe=0 fe=0

The same byte, the same spike, at the same position in the frame — corrupted under odd parity, clean under mark. Parity mode cannot possibly affect whether a data bit is sampled correctly, so either the design has a bizarre defect or the experiment is not measuring what it appears to.

3. The Corner That Was Probabilistic

It was the experiment. The spike is injected at a fixed fraction of a bit, and whether it is sampled depends on where the receiver's sampling grid happens to land — which depends on the phase of the start edge relative to the clock, which differed between those runs because the preceding traffic differed.

So the corner is probabilistic, and a single trial at a single position measures nothing. Sweeping the spike across the bit, 24 repeats at each position with the inter-frame gap varied so start edges land at different clock phases:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  spike width 6% of a bit, in data bit 3 of 0xA5, 24 repeats each
    spike at    corrupted
        36%         0 / 24
        38%         2 / 24   <- PROBABILISTIC
        40%         9 / 24   <- PROBABILISTIC
        42%        18 / 24   <- PROBABILISTIC
        44%        22 / 24   <- PROBABILISTIC
        46%        15 / 24   <- PROBABILISTIC
        48%         6 / 24   <- PROBABILISTIC
        50%         0 / 24
        52%         0 / 24

A clean probability curve, zero outside roughly 37–49% and peaking at 44%.

4. Probabilistic Corners Change How You Test

A single trial is an anecdote. At 44% the corruption rate is 92%; at 38% it is 8%. A test that injects one spike at one position and passes has established nothing, and a test that fails once and passes on re-run looks like flakiness in the environment.

Three consequences for the plan:

Repeat, and vary the thing that randomises it. Here the randomiser is start-edge phase, varied by changing the inter-frame gap by a non-integer number of bit times. Repeating without varying it would reproduce the same phase and give 0/24 or 24/24.

Report rates, not verdicts. "9 of 24 at 40%" is a measurement; "fails at 40%" is not, and neither is "passes at 40%" from a single trial.

Decide what the requirement actually is. This receiver samples once and claims no glitch immunity (Chapter 14.4 §5), so none of this is a defect — it is a quantification of a known design choice. Chapter 5.4's three-sample majority is the remedy, and the measurement above is what says how much it would buy: immunity to spikes narrower than the voting window, at the price of the window's width in margin.

5. The Corners Worth Targeting in a UART

Frame boundaries. Back-to-back frames with no inter-frame gap; a new start edge in the same interval the stop bit ends. Chapter 7.4's ready window is one bit wide and it is where the duplicate-acceptance defect lives.

Queue limits. Exactly full, exactly empty, and — the one that matters — a push and a pop on the same clock edge when full. That last is what found Chapter 13.4 §5's duplicate-push defect, which eleven testbenches and 186 checks had missed because nothing else drained a full queue at bus speed.

Message lengths around the trigger. trigger, trigger ± 1, and lengths that are not a multiple of the trigger — Chapter 13.4 §4's residual bytes.

Configuration changes in flight. A parity change while a frame is transmitting, which Chapter 11.3 exists to defer. The corner is that the change arrives during the frame, not that it happens at all.

Reset mid-frame. Chapter 12.4 §4 measured a transmitter reset producing a well-formed frame with the wrong byte, and a receiver reset inventing a byte never sent. Both silent.

The edge of the timing budget, from both directions, with several patterns — Chapter 14.4 §2.

Data patterns that interact with thresholds. 0x00 under even parity produces a ten-interval low run, which is what tripped the hardcoded break threshold in Chapter 11.4 §2.

6. Negative Testing Is Not the Same as Corner Testing

Two different activities that get conflated:

Corner testing drives legal inputs at the boundaries of the design's behaviour. Everything in §5 is legal traffic.

Negative testing drives inputs the specification says are illegal and checks that the response is the specified one. A stop bit at space, a flipped parity bit, a break — none of these can be produced by a correct transmitter, and all of them have required responses.

The common failure is to test that illegal input does not crash, which is a much weaker claim than that it produces the specified status. Chapter 14.4 §3's requirement — the error is reported and the byte is delivered — is exactly the kind that a "does not crash" test passes while missing the point.

And some illegal inputs have no specified response, which the plan must record rather than invent. Two simultaneous start edges, or a break shorter than the threshold, are inputs where any behaviour is acceptable; a test asserting a particular one is asserting a requirement nobody wrote.

7. Mutating the Checker, Not the Design

Every other module in this curriculum mutates the RTL to prove its testbench can see a defect. This module has no RTL. What it has is the checker, and the same question applies with more force: if the environment were wrong, would anything notice?

Thirteen defects were installed across the three verification components, one at a time, each verified to have actually changed the source before its result was scored.

#ComponentDefect installedResult
M1predictordata emitted MSB-first instead of LSB-firstkilled, 3
M2predictoreven and odd parity swappedkilled, 3
M3predictorstart bit emitted as MARKkilled, 2
M4predictorstop length ignored — always one bitkilled, 2
M5driverbit period fixed, the argument ignoredkilled, 2
M6driverERR_PARITY does not actually invert the bitkilled, 1
M7driverparity counted over one bit too manykilled, 1
M8monitorsamples at 1.0 bit — the boundary, not the centrekilled, 2
M9amonitorthe MARK re-arm loop removedsurvived
M9bmonitorarmed on a level instead of an edgesurvived
M9cmonitorboth of the above at oncekilled, 4
M10monitorparity expectation invertedkilled, 2
M11monitorframing error never reportedkilled, 2

M5 is the module's own thesis as a mutant. Replace the bit-period argument with a constant and the BFM becomes the thing §1 of Chapter 14.2 argues against: a driver that can only ever send at one rate. Two checks fail — and they are the two that measure the interval rather than reading back the byte. Every data check still passes, because at the nominal rate a fixed-period driver is indistinguishable from a correct one.

M6 and M7 each kill exactly one check, and both of those checks exist for that defect alone. M6 is a fault injector that does not inject — the most dangerous defect a verification environment can have, because the coverage report says the case was exercised. It is caught by a single line asserting that the corrupted frame actually differs from the clean one.

8. Module 14 Verification Evidence

Every listing published in this module was extracted from the page you are reading, compiled with a real tool, and simulated.

Three components, three languages, nine implementations:

ComponentSystemVerilogVerilog-2001VHDL-2008Chapter
uart_line_driver (BFM)✅✅✅ (package)14.2
uart_line_monitor✅✅✅ (read port)14.4
uart_predictor✅✅✅14.5

The VHDL column has two footnotes rather than none, and they are the interesting part of the translation: the driver becomes a package of procedures because VHDL has no cross-entity subprogram call, and the monitor gains an explicit read port because VHDL has no hierarchical reach into an instance. Both are more ceremony and both state the contract more clearly.

Nine testbenches, identical check counts across all three languages:

SuiteChecksVerilog-2001SystemVerilogVHDL-2008
uart_predictor1919 / 019 / 019 / 0
uart_line_driver1414 / 014 / 014 / 0
uart_line_monitor2121 / 021 / 021 / 0
Total per language5454 / 054 / 054 / 0

162 checks across the three languages, 0 failures. Behind those 54 checks per language sit 30,720 exhaustively-enumerated frames in the predictor suite, 480 format combinations compared slot-by-slot in the driver suite, and 256 byte values plus a 21-point baud sweep in the monitor suite.

Tooling: Icarus Verilog 13.0 (-g2001 and -g2012) and NVC 1.23.0 for VHDL-2008.

Three numbers that agree across three independent implementations:

MeasurementPredictedVerilogSystemVerilogVHDL
baud tolerance window±5.88%−6% … +6%−6% … +6%−6% … +6%
8N1 frame length20 half-slots202020
predictor sweep runtime—30,724 ns30,724 ns30,724 ns

The last row is a curiosity rather than a result, but a satisfying one: the Verilog and VHDL predictor sweeps finish at the same simulated nanosecond because they are doing the same 30,720 things in the same order.

9. Verification

Enumerate small crosses rather than sampling them. Twenty-four bins is cheap to run exhaustively, and the one anomalous cell was the entry point to the chapter's real result. Random selection over twenty-four bins would probably have missed it and certainly would not have flagged it as odd.

Detect probabilistic behaviour explicitly. A bin that is neither 0/N nor N/N is telling you something:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Flag any corner whose outcome is neither always nor never — those are
// the ones where a single trial is an anecdote.
if (bad > 0 && bad < tot) $display("<- PROBABILISTIC");

Vary the hidden variable, not just the seed. Repeating with the same inter-frame gap reproduces the same start-edge phase and gives a deterministic answer to a probabilistic question.

Keep the corner list and the mutant together. The list guards against regression; the mutant (Chapter 14.5 §4) guards against the environment silently losing the ability to fail.

Record non-requirements next to corners. §4's glitch result is not a bug, and a plan that does not say so will have the measurement reported as one.

10. Debugging

11. What This Means in Practice

On hardware, glitch sensitivity looks like a bad cable. A receiver with a vulnerable window this narrow corrupts a byte occasionally and only when noise happens to land in it — which presents as intermittent corruption correlated with nothing obvious.

The remedy is a design change, not a test. Three-sample majority voting (Chapter 5.4) narrows the vulnerable window to spikes wider than the voting span, at the cost of margin. The measurement in §3 is what turns that trade into numbers.

Sampling is at 43.75%, not 50%, and it is worth knowing. It is where the timing budget's asymmetry comes from, and it means the margin on the late side of a bit is slightly larger than on the early side — which matters when the far end is known to run fast.

Model every new master. The most valuable single test in Module 13 was a DMA engine model, and it existed because someone asked what a DMA engine would do differently rather than assuming the FIFO was covered.

12. Understanding Check

13. Summary

A corner is worth testing when a plausible implementation could get it wrong, which means it sits at a boundary in the design rather than at an extreme of the input range.

Enumerate small crosses. Twenty-four bins ran exhaustively, twenty-three behaved as specified, and the twenty-fourth was the entry point to the chapter's result.

One anomalous cell was a property of the experiment, not the design — and asking whether the axis could physically matter, rather than filing a defect, is what made that visible.

The glitch corner is probabilistic: a clean curve from 0/24 at 36% through 22/24 at 44% and back to 0/24 at 50%. A single trial measures nothing, repetition must vary the hidden variable, and results are rates rather than verdicts.

That curve located the receiver's sampling instant at 44% of a bit, against an RTL that samples at phase 7 of 16 — 43.75%. The receiver samples slightly early, not at the centre.

And it resolved Chapter 14.2 §5's open question: the baud tolerance window is biased positive because the sampling point is biased negative.

None of the defects this curriculum found came from a corner list. They came from structural questions and from attaching new consumers — which is where the remaining risk sits, and why every new master deserves a model rather than an assumption.

14. What Comes Next

Module 14 is complete. There is a verification plan, a coverage model, a stimulus generator that can be wrong on purpose, an independent monitor, a reference model, a scoreboard, and a corner strategy — and all of it is held together by module instances, hierarchical task calls and arrays of counters, because that is what the tool supports.

That works, and it does not scale. Every test rebuilds its own connections; stimulus cannot be constrained declaratively; coverage is hand-maintained; and there is no way to reuse the environment across a different DUT without editing it.

Modules 15 and 16 replace the scaffolding with UVM: the agent, sequencer, driver and monitor as classes; sequences as objects that can be composed and randomised; the factory and configuration database that let one environment serve many tests; and the analysis ports that connect a scoreboard without wiring. The roles do not change — this module defined them — and what changes is that they stop being modules and start being a reusable structure.

Browse the full path on the UART tutorials index. For the single-sample decision this chapter quantified, read back to Chapter 5.4.

Continue learning

Where this fits

Part of the UART curriculum.