Coverage lies
You will learn
Why a coverage number can be a check that checked nothing, and how to spot it.
A coverage percentage is a claim about the tests, and claims can be empty. The widget found the empty kind: 11 specs refused at parse, 0 blocked only by prose, and 23 broken on purpose as fixtures -- but elsewhere in the corpus a 'test' that is only prose passes every check while executing nothing. The tells are always the same: a number with no denominator, a pass with no assert that ran, a sweep that cannot fail. Ask of every percentage: what would make this number move?
Try it
In the prose sweep, find the difference between 'refused at parse' and 'blocked only by prose'; then write a test whose pass executes no assert.

11 specs refused at parse, 0 blocked only by prose, 23 broken on purpose as fixtures.
specs/fpga/testbench/fifo_tb.t27
// FIFO testbench spec
// This spec exercises a small synchronous FIFO with a power-of-two depth.
// It provides write/read helpers, a combined transaction function,
// and a set of tests covering basic operation, edge cases, and invariants.
const DEPTH : u32 = 8;
struct Fifo {
mem : [DEPTH]u32,
head : u32,
tail : u32,
fill_count : u32,
}
var fifo : Fifo = Fifo {
mem = [0, 0, 0, 0, 0, 0, 0, 0],
head = 0,
tail = 0,
fill_count = 0,
};
var wr_en : bool = false;
var rd_en : bool = false;
var wr_data : u32 = 0;
var rd_data : u32 = 0;
fn reset() {
fifo.head = 0;
fifo.tail = 0;
fifo.fill_count = 0;
wr_en = false;
rd_en = false;
wr_data = 0;
rd_data = 0;
}
fn tick() {
if wr_en && !rd_en && !full() {
fifo.mem[fifo.tail] = wr_data;
fifo.tail = (fifo.tail + 1) % DEPTH;
fifo.fill_count = fifo.fill_count + 1;
} else if rd_en && !wr_en && !empty() {
rd_data = fifo.mem[fifo.head];
fifo.head = (fifo.head + 1) % DEPTH;
fifo.fill_count = fifo.fill_count - 1;
} else if wr_en && rd_en {
if !full() && !empty() {
rd_data = fifo.mem[fifo.head];
fifo.mem[fifo.tail] = wr_data;
fifo.head = (fifo.head + 1) % DEPTH;
fifo.tail = (fifo.tail + 1) % DEPTH;
} else if !full() && empty() {
fifo.mem[fifo.tail] = wr_data;
fifo.tail = (fifo.tail + 1) % DEPTH;
fifo.fill_count = fifo.fill_count + 1;
} else if full() && !empty() {
rd_data = fifo.mem[fifo.head];
fifo.head = (fifo.head + 1) % DEPTH;
fifo.fill_count = fifo.fill_count - 1;
}
}
wr_en = false;
rd_en = false;
}
fn full() -> bool {
return fifo.fill_count == DEPTH;
}
fn empty() -> bool {
return fifo.fill_count == 0;
}
fn write_word(data : u32) -> bool {
if full() {
return false;
}
wr_en = true;
wr_data = data;
tick();
wr_en = false;
return true;
}
fn read_word() -> u32 {
if empty() {
return 0xFFFF;
}
rd_en = true;
tick();
rd_en = false;
return rd_data;
}
fn on_comb(data: u32) -> bool {
return write_word(data);
}
test test_basic_write {
reset();
var ok = write_word(0xDEAD);
assert ok == true;
assert fifo.fill_count == 1;
}
test test_basic_read {
reset();
write_word(0xBEEF);
var data = read_word();
assert data == 0xBEEF;
assert fifo.fill_count == 0;
}
test test_full_after_writes {
reset();
var i = 0;
while i < DEPTH {
var ok = write_word(i);
assert ok == true;
i = i + 1;
}
assert full() == true;
var ok = write_word(0xFFFF);
assert ok == false;
}
test test_empty_after_read {
reset();
write_word(0x1234);
write_word(0x5678);
var d1 = read_word();
var d2 = read_word();
assert d1 == 0x1234;
assert d2 == 0x5678;
assert empty() == true;
}
test test_overflow_rejected {
reset();
var i = 0;
while i < DEPTH {
write_word(i);
i = i + 1;
}
var ok = write_word(0xABCD);
assert ok == false;
assert fifo.fill_count == DEPTH;
}
test test_underflow_returns_sentinel {
reset();
var data = read_word();
assert data == 0xFFFF;
assert fifo.fill_count == 0;
}
test test_overflow_then_underflow {
reset();
var i = 0;
while i < DEPTH {
write_word(i);
i = i + 1;
}
var extra = write_word(0xFFFF);
assert extra == false;
var j = 0;
while j < DEPTH {
var data = read_word();
assert data == j;
j = j + 1;
}
var sentinel = read_word();
assert sentinel == 0xFFFF;
}
test test_rapid_alternating {
reset();
var i = 0;
while i < 20 {
var ok = write_word(i);
assert ok == true;
var data = read_word();
assert data == i;
i = i + 1;
}
assert empty() == true;
}
test test_wrap_behavior {
reset();
var i = 0;
while i < 16 {
write_word(i);
i = i + 1;
}
var j = 0;
while j < 8 {
var data = read_word();
assert data == (j + 8);
j = j + 1;
}
assert fifo.fill_count == 8;
}
test test_simultaneous_rw {
reset();
write_word(0x42);
wr_en = true;
rd_en = true;
wr_data = 0x43;
tick();
wr_en = false;
rd_en = false;
invariant fill_count == 1;
}
test test_on_comb_writes_when_not_full {
reset();
var result = on_comb(0xAAAA);
assert result == true;
assert fifo.fill_count == 1;
assert fifo.mem[0] == 0xAAAA;
}
test test_on_comb_rejects_when_full {
reset();
var i = 0;
while i < DEPTH {
write_word(i);
i = i + 1;
}
var result = on_comb(0xBBBB);
assert result == false;
assert fifo.fill_count == DEPTH;
}All lessons
Module 1 · Why verify
Designs that compile and are wrong, the model that decides, and the plan written before the code.
Module 2 · Testbenches
Stimulus, checks and a verdict, written as one spec beside the design it judges.
Module 3 · Waveforms
A trace of every signal, read the way a hardware engineer reads it, and two runs compared.
Module 4 · Conformance vectors
Cases with the answer written beside them, kept where the compiler can reach them.
Module 5 · Cosimulation
Spec, simulator and board agreeing on the bench Artix-7 XC7A200T, and what to do when they do not.
Module 6 · Coverage
What the tests touched: lines, toggles, states, and what that number hides.
Module 7 · Formal
Assertions that hold every cycle, bounded search for a counterexample, and why a proof needs induction.
Module 8 · Mutation
Break the design on purpose and count what the tests catch.
Module 9 · Sign-off
One command, every receipt, a clean verdict you can show.