Coverage and tests for the checker

Measure which situations your stimulus reached with covergroups, and prove that the checker can fail by running it against deliberately broken FIFOs.

Lesson
7.5
Time
30 min
Runs on
Coverage build
Check
5 questions

A FIFO regression reached 100% functional coverage, passed every night for months, and still let a FIFO with a flipped data bit reach silicon. A refactor had moved the data comparison into a branch that never ran, so the scoreboard compared nothing. Coverage had measured what the stimulus did, and nothing had measured whether the checker could fail.

Coverage rises; checking never happens
012345coverage (%)628897100data comparisonserrors reportedcoverage goal met

One column per week of regressions. Every number looks healthy except the one nobody printed.

Two questions, two tools

A testbench has to answer two separate questions. Coverage asks which situations the stimulus produced: did a read and a write ever arrive together at full? Checking asks whether the FIFO behaved correctly in the situations that did occur.

QuestionAnswered byFooled by
Did the stimulus reach every situation in the plan?covergroups and scenario countersstimulus that never reaches a corner
Was the FIFO right whenever it was checked?scoreboard and assertionsa checker that cannot fail
Can the checker fail?running it against broken designstesting only the correct design

Each question needs its own evidence.

The three questions depend on each other: checking means little where the stimulus never went, and coverage means little if the checks could not fail.

A green run with full coverage proves neither of the last two. The third question is the one most teams never ask, and it is the subject of the second half of this lesson.

A covergroup for the FIFO

fifo_coverage holds two covergroups. The first crosses the fill level before the edge (empty, in between or full) with the requests (idle, read, write or both). That gives 12 combinations, including the ones the test plan worries about most. The second records resets while empty and while holding data.

Bins are named for what they mean, such as empty and both, so a coverage report reads like the test plan. When a bin stays at zero, its name says which situation the stimulus never produced.

Both are sampled with the scoreboard's count from before the edge, so the bins describe the situation the FIFO faced, independently of anything the FIFO reported. get_coverage() on a covergroup returns the average of its coverpoints and crosses, so a cross with 12 bins pulls the total down until most of them are hit.

Test the checker with broken FIFOs

To show that a checker can fail, give it designs that are wrong on purpose. The FIFO in this lesson has a test-only variable fault that the testbench sets before each run. Fault 0 is the correct design, and each other number breaks one rule, with the same numbering as the faulty FIFOs in the problem bank.

The example runs the same seeded stimulus against every variant, with a fresh scoreboard each time. A broken FIFO must be caught, with at least one error; the correct one must be clean. An error on the correct design is a false alarm, and a broken design with no errors is missed.

FaultWhat breaksMeasured result
0nothing: the correct designclean
1bit 0 of read data flippedcaught, 25 errors
2read_valid stuck at 1 after a readcaught, 34 errors
3read_valid never risescaught, 26 errors
5read and write together return the new itemcaught, 8 errors
6write pointer wraps at 4 although the depth is 3caught, 15 errors
8full never risescaught, 16 errors
9reset keeps the stored itemscaught, 8 errors

Results of the example: 60 random operations plus the directed cases, seed 7, depth 3.

The error counts say how often each bug became visible with this stimulus. Fault 5 shows up only when a read and a write meet with items stored, so a stimulus that rarely produces that situation would catch it rarely, or never.

When a fault is missed

A missed fault has one of two causes. Either the stimulus never produced the situation where the bug shows, which coverage can reveal, or the checker looked at the wrong thing, which coverage cannot.

The second kind is the opening story. A checker that compares read_data only when the FIFO raises read_valid misses a read_valid stuck at 1, because the stale data it then compares is still the item it expected. Running the checker against fault 2 exposes that hole in a second.

Run the broken designs whenever the scoreboard changes, not just once. A checker that caught every fault last month can lose that ability in a single refactor, which is exactly what happened in the opening story.

Keep the broken designs next to the correct one, in the same regression. The problem bank's Catch the faulty FIFOs asks you to write such a checker as a module and grades it against these faults.

LayerComponentJobReusesStatus
Testfifo_test, fifo_configchooses depth, seed and length, runs the environment, gives the verdictModule 4 classeslesson 7.7
Generatorfifo_generatorcreates fifo_op objects: directed, then seeded randomModules 4 and 5built in 7.3
Mailboxesmailbox #(fifo_op), mailbox #(fifo_obs)carry operations and observations between processesModule 3built in 7.3
Driverfifo_driverdrives one operation per clock through drv_cbModule 6built in 7.3
Monitorfifo_monitorsamples the pins through mon_cb into a new fifo_obsModule 6built in 7.4
Scoreboardfifo_scoreboardpredicts each edge with the queue exp_q and comparesModules 2 and 4built in 7.4
Assertionsfifo_propschecks timing rules on the pins at every edgeModule 6built in 7.4
Coveragefifo_coverage, fifo_scenariosmeasures which situations the stimulus reachedModule 5 stimulusthis lesson
DUTfifothe design under testlesson 7.1 rulesbuilt in 7.2

The layered testbench after lesson 7.5.

Try it

The correct design is clean, every broken design is caught, and the stimulus reached all 12 level and request combinations plus a reset while holding data. All three questions now have an answer.

Change new(req, 60, 7) to new(req, 0, 7), so only the directed cases run, and predict which faults are still caught and what the coverage becomes.

RUNVerilator (coverage build) · simulator service
Edit the code, then run it
Expected: fault 0 correct design clean (0 errors) fault 1 read data bit 0 flipped caught (25 errors) fault 2 read_valid stuck high caught (34 errors) fault 3 read_valid never rises caught (26 errors) fault 5 read and write: new value caught (8 errors) fault 6 write pointer wraps at 4 caught (15 errors) fault 8 full never rises caught (16 errors) fault 9 reset keeps stored entries caught (8 errors) stimulus coverage: level x request 100.0%, reset 100.0%

Check yourself

Predict the outputThis covergroup is sampled three times. Predict the exact output.
module tb;
  bit [1:0] level, req;
  covergroup cg;
    cp_level: coverpoint level { bins empty = {0}; bins middle = {1}; bins full = {2}; }
    cp_req:   coverpoint req   { bins idle = {0}; bins read = {1}; bins write = {2}; bins both = {3}; }
    level_x_req: cross cp_level, cp_req;
  endgroup
  cg c = new();
  initial begin
    level = 0; req = 2; c.sample();   // write at empty
    level = 1; req = 3; c.sample();   // both in between
    level = 2; req = 1; c.sample();   // read at full
    $display("coverage=%0.1f", c.get_coverage());
  end
endmodule
Spot the bugEach option is step 1 of check(), the check of the previous edge's answer. The harness runs the correct FIFO and fault 2, read_valid stuck at 1. The checker must stay clean on the first and catch the second. Which option misses fault 2?
ConceptA regression reports 100% functional coverage and 0 errors. What does that establish?
ConceptWhy does the example run the correct FIFO, fault 0, along with the broken ones?
ConceptA broken FIFO that returns the newly written item when a read and a write meet with items stored is missed. Coverage shows the "both, in between" bin was never hit. What should you change?

Remember

  • Coverage tells you what the stimulus reached; checking tells you whether the FIFO was right there.
  • Sample covergroups from the model's own state, and remember that a large cross dominates the total.
  • Prove the checker can fail by running it against deliberately broken designs, along with the correct one.
  • A missed fault means the stimulus never reached it, or the checker looked at the wrong signal.
  • Never let a checker decide when to compare from a signal it is supposed to check.

Practice