Ethernet · Module 26
RTL and Verification Review
546 declared coverage bins, 243 unreachable by construction, and one regression that is 51.832% or 93.399% depending on the denominator — plus the twenty bins random stimulus will never reach.
Chapter 26.1 held a review where every item was a number somebody had to produce. This chapter holds the same review one stage later, and the numbers are harder because the most quoted one — coverage — is a fraction whose denominator the verification team chooses.
Start with the arithmetic this chapter exists to produce.
| Bins | Share | |
|---|---|---|
| declared in the functional coverage model | 546 | 100% |
| unreachable by construction | 243 | 44.51% |
| reachable | 303 | 55.49% |
| reached by a full week of a 500-licence farm | 283 | 51.83% of declared, 93.40% of reachable |
| needing a directed test | 20 | 6.60% of reachable |
One regression run. Two defensible coverage numbers — 51.83% and 93.40% — and neither of them is wrong. The difference between them is 243 bins somebody decided could not happen, and that decision is recorded in a file that no review in this track's experience has ever read.
A coverage percentage is a fraction. The numerator is measured and the denominator is declared, and only one of them is audited.
This chapter's central question is the one Chapter 25.6 §20's fourth property asked in one line — compare bound properties against covered ones and report the difference — asked now of a whole verification plan: what could a closed coverage model have failed to reach?
There are exactly three answers and a review that cannot tell them apart has not reviewed anything.
| A bin that was not hit | Why | What it means |
|---|---|---|
| unreachable by construction | the design cannot produce it | correct to exclude, IF the argument is right |
| reachable, not reached in budget | too rare for random stimulus | needs a directed test |
| reachable, and nobody classified it | nobody looked | the finding |
And the third row is the default. A coverage tool reports a percentage; it does not report which of the three each unhit bin belongs to, because that classification is an argument rather than a measurement.
What this chapter does not own. Chapter 26.1 covered architecture and integration, and Chapter 26.3 covers performance closure and the observability debug needs; Chapter 26.4 covers how the whole of it is probed in an interview. This is the pass held when RTL exists and the verification plan claims to be closed.
Four numbers carry the chapter, and all four are derived here rather than quoted.
| Number | What it is | Section |
|---|---|---|
| 546 / 243 / 303 | the coverage model, classified | 4 |
| ±6 | how far the exclusion list moves on one environmental parameter | 6 |
| 23.24 simulator-years | what the rarest reachable bin costs at 95% confidence | 8 |
| 332 of 557 — 59.6% | properties whose antecedent needs traffic, from Chapter 21.4 §20 | 10 |
The last row is the one that makes an assertion suite a reviewable object rather than a count. That chapter measured its own corpus and found that three of every five published properties go silent on a link that never comes up — they do not fail, they stop evaluating — so a regression on a dead link reports 557 passes. A verification review that records "557 properties, 557 passing" has recorded a number that is true and means nothing.
1. Scope — A Coverage Number With Its Denominator
One rule, and it is Chapter 26.1 §1's rule restated for a fraction: a coverage percentage without its denominator's provenance is not a review item.
The failure mode is specific and it is not laziness. A coverage model is built by the same team that builds the testbench, from the same understanding of the design, and every bin it does not contain is invisible to every number it produces. So the model's completeness cannot be measured by the model.
| The quoted number | What it actually asserts | Auditable? |
|---|---|---|
| "94% functional coverage" | 94% of the bins we wrote were hit | no — the model is unaudited |
| "100% after exclusions" | every bin we did not exclude was hit | no — the exclusions are unaudited |
| "303 reachable bins, 283 hit, 20 directed tests" | three numbers that can each be wrong in a stateable way | yes |
Row three is what this chapter's review produces, and the cost of producing it is one classification per unhit bin.
Why the third row is a different KIND of statement. Rows one and two report a measurement over a set. Row three reports the set, and a set can be argued with: somebody can point at a bin in the excluded list and say that one is reachable, here is the sequence. Nobody can argue with 94%.
The reviewable object is not the coverage number. It is the classification of the bins that were not hit — and that classification is an argument, so it has to be written down.
Which gives this chapter the same four-outcome structure Chapter 26.1 §12 arrived at, applied to bins instead of thresholds.
| Outcome | What it means | Who closes it |
|---|---|---|
| HIT | the bin was covered | nobody |
| UNREACHABLE | an argument exists and is recorded | a reviewer, by attacking the argument |
| DIRECTED | reachable, too rare, a test exists | the test's author |
| UNCLASSIFIED | nobody looked | the finding |
And a review that cannot express the fourth outcome converts every one of them into the second, because the quickest way to close a coverage report is to exclude what did not hit.
This chapter is not a BCE chapter and Section 12 says why. Its units are bins, simulation-hours, engineer-weeks, and properties whose evaluation count is zero — and mixing any of them with area produces a number with no referent, because area is a property of the object that ships and verification effort is a property of the process that produced confidence in it.
2. The Twenty Items
Every row is a number somebody has to produce, the chapter that derives it, and the kind of threshold it is measured against. No row is an adjective.
| # | The item | This design's number | Derived in | Threshold kind |
|---|---|---|---|---|
| 1 | functional coverage bins declared | 546 | Section 4 | structural |
| 2 | bins unreachable by construction | 243 — 44.51% | Section 5 | derived |
| 3 | bins reachable | 303 | Section 4 | derived |
| 4 | bins reached inside the regression budget | 283 — 93.40% of reachable | Section 8 | derived |
| 5 | bins needing a directed test | 20 | Section 8 | derived |
| 6 | directed tests that exist | must equal item 5 | Section 8 | structural |
| 7 | exclusion entries whose argument is recorded | must equal item 2 | Section 6 | structural |
| 8 | exclusion entries that move with a deployment parameter | 6 | Section 6, Chapter 26.1 §12 | environmental |
| 9 | properties bound in the regression | 557 in the audited corpus | Chapter 21.4 §20 | structural |
| 10 | properties whose cover count is zero at end of run | must be 0, or each classified | Chapter 25.6 §20 | derived |
| 11 | share of properties whose antecedent requires traffic | 332 of 557 — 59.6% | Chapter 21.4 §20 | derived |
| 12 | simulation-hours available per week | 20 000 — 500 licences × 40 h | Section 8 | environmental |
| 13 | simulation-hours to reach the rarest reachable bin | 203 764 — 23.24 simulator-years | Section 8 | derived |
| 14 | toggle-coverage bins in the MAC datapath | 28 332 — 2 per flop | Chapter 19.7 §19's ~14 166 flops | structural |
| 15 | error classes the taxonomy names | 12 | Chapter 21.2 §1 | structural |
| 16 | classes the injector's five actions can produce | 9 — so 3 need another route | Chapter 20.5 §2 | structural |
| 17 | reachable frame shapes qualifying for 2+ classes | 7 of 12 — 58.3% | Chapter 21.2 §2 | derived |
| 18 | clock-domain crossings with a bound protocol check | every one, or the count of exceptions | Chapter 18.1 §19 | structural |
| 19 | reset and bring-up orders exercised | the count, against the orders the SoC can produce | Chapter 19.5 §18 | environmental |
| 20 | engineer-weeks of directed-test work | 6 — 20 tests at 1.5 engineer-days | Section 12 | derived |
Twenty rows. Nine chapters named. And the structure of the list is the point: an item is complete when somebody can say what observation would change its value.
Rows 15 and 16 together are the item nobody writes. Chapter 21.2 §1's taxonomy has twelve classes; Chapter 20.5 §2's injector has five actions — stomp, shorten, lengthen, dribble, suppress idle — and they produce nine of the twelve. So three classes have no injector action at all, and a verification plan that lists "error injection: complete" has covered nine twelfths of a taxonomy it names in full. The item is the number three, and the row next to it is how those three get produced.
Three of the rows are worth reading twice.
Row 6 against row 5. These two must be equal and they are almost never compared. Row 5 is a property of the stimulus and the schedule; row 6 is a property of the test list — so they are produced by different people, at different times, and a difference between them is invisible to both. Section 8 computes row 5 from the traffic model; the review's job is to put the two numbers on the same line.
Row 7 against row 2. Same shape, different failure. Row 2 is a count the tool reports; row 7 is a count of written arguments, and the gap between them is the part of the model that was excluded without a reason. In this design the gap is the whole finding: 243 bins excluded, and Section 6 shows that six of them move when a deployment parameter changes.
Row 11. It is the only row whose value this chapter takes from an audit somebody else already performed, and it is the strongest row in the list because of it. Chapter 21.4 §20 counted its own corpus rather than estimating: 238 properties in Module 19, 162 of them traffic-dependent; 204 in Module 20, 95; 115 in Module 21 so far, 75. Three measurements, one total, 59.6%.
3. RTL 1 — The Verification Package and the Bin Model
The package holds the four axes, the four outcomes and the constants the rest of the chapter computes with. The module classifies one bin.
// ---------------------------------------------------------------------
// verifreview_pkg -- the coverage model's axes, the four outcomes a bin
// can have, and the constants Sections 4 to 8 derive.
//
// The axes are taken from chapters that already own them rather than
// invented here: the seven RMON size buckets from Chapter 19.7 Section 2,
// the twelve error classes from Chapter 21.2 Section 1, three tag depths
// and two directions. Seven times thirteen times three times two is the
// 546 bins Section 4 counts.
// ---------------------------------------------------------------------
package verifreview_pkg;
// The four axes. ERR_NONE is the thirteenth value of the error axis:
// twelve classes plus "no error", which is the bin most frames land in.
typedef enum logic [2:0] {
SZ_LE64, SZ_65_127, SZ_128_255, SZ_256_511,
SZ_512_1023, SZ_1024_1518, SZ_GE1519
} size_bucket_e; // 7 -- Chapter 19.7 Section 2
typedef enum logic [3:0] {
ERR_NONE, ERR_FCS, ERR_ALIGN, ERR_RUNT,
ERR_GIANT, ERR_SYMBOL, ERR_DROP, ERR_FIFO_OVF,
ERR_DESC, ERR_FILTER, ERR_VLANMEM, ERR_MTUMIS,
ERR_JABBER
} err_class_e; // 13 -- Chapter 21.2 Section 1
typedef enum logic [1:0] { TAG_NONE, TAG_SINGLE, TAG_DOUBLE } tag_e;
typedef enum logic { DIR_RX, DIR_TX } dir_e;
localparam int N_SIZE = 7;
localparam int N_ERR = 13;
localparam int N_TAG = 3;
localparam int N_DIR = 2;
localparam int N_BINS = N_SIZE * N_ERR * N_TAG * N_DIR; // 546
// The four outcomes of Section 1's table. UNCLASSIFIED is the default
// and it is the one the review exists to count.
typedef enum logic [1:0] {
BIN_UNCLASSIFIED, // nobody looked -- the finding
BIN_HIT, // covered
BIN_UNREACHABLE, // an argument exists, and is recorded
BIN_DIRECTED // reachable, too rare, a test exists
} bin_outcome_e;
// Why a bin is unreachable. A reason is not a boolean: a reviewer
// attacks the REASON, so the reason has to be nameable.
typedef enum logic [2:0] {
UR_NONE,
UR_LENGTH, // the class needs a length this bucket excludes
UR_DIRECTION, // the class is a receive-side decision
UR_CONFIG, // excluded only at this build's parameters
UR_ENVIRONMENT // excluded only at this deployment's parameters
} unreach_reason_e;
// A bin, flattened. The index is the review's identifier for it and
// the exclusion list is a list of these indices.
typedef struct packed {
size_bucket_e sz;
err_class_e ec;
tag_e tg;
dir_e dr;
} bin_key_t;
function automatic int unsigned bin_index(bin_key_t k);
return ((int'(k.sz) * N_ERR + int'(k.ec)) * N_TAG + int'(k.tg))
* N_DIR + int'(k.dr);
endfunction
// Section 6: the peer MTU decides where an MTU-mismatch bin becomes
// reachable, and it is Chapter 26.1 Section 12's environmental
// threshold appearing inside a coverage model.
localparam int MTU_BUCKET_576 = 4; // mismatch visible from SZ_512_1023
localparam int MTU_BUCKET_1024 = 5; // from SZ_1024_1518
localparam int MTU_BUCKET_1500 = 6; // SZ_GE1519 only
endpackageClassification, Model or Production? A CLASSIFICATION MODEL. It computes no design behaviour; it decides, for one bin, which of the four outcomes applies and — when the outcome is BIN_UNREACHABLE — which reason supports it.
// ---------------------------------------------------------------------
// bin_model -- one bin in, one outcome out, with the reason attached.
//
// The module's whole value is that it separates UNREACHABLE from
// UNCLASSIFIED. A coverage tool reports both as "not hit".
// ---------------------------------------------------------------------
module bin_model
import verifreview_pkg::*;
#(
// The peer's maximum transmission unit, expressed as the first size
// bucket in which a mismatch can be observed. Section 6 shows the
// exclusion count moving by six as this parameter moves.
parameter int MTU_BUCKET = MTU_BUCKET_1024
)(
input logic clk,
input logic rst_n,
input logic bin_valid,
input bin_key_t bin_key,
input logic was_hit, // the coverage tool's report
input logic has_directed, // a directed test names it
input logic has_argument, // an exclusion argument exists
output logic out_valid,
output bin_outcome_e outcome,
output unreach_reason_e reason,
output logic reason_is_soft // CONFIG or ENVIRONMENT
);
// Structural unreachability. Every arm here is a fact about the frame
// definition or the direction, and each cites the chapter that owns it.
function automatic unreach_reason_e structural(bin_key_t k);
// A runt is shorter than 64 octets, so it lives in one bucket only.
if (k.ec == ERR_RUNT && k.sz != SZ_LE64)
return UR_LENGTH;
// A giant and a jabber are longer than the MTU. Chapter 21.2 Sec 1.
if ((k.ec == ERR_GIANT || k.ec == ERR_JABBER) && k.sz != SZ_GE1519)
return UR_LENGTH;
// Five classes are receive-side facts. A transmitter emits whole
// octets by construction, so an alignment error has no TX form.
if (k.dr == DIR_TX &&
(k.ec == ERR_SYMBOL || k.ec == ERR_ALIGN ||
k.ec == ERR_FIFO_OVF || k.ec == ERR_FILTER ||
k.ec == ERR_VLANMEM))
return UR_DIRECTION;
return UR_NONE;
endfunction
// Environmental unreachability. This arm is the one Section 6 is
// about: it is true at this build's parameter and false at another.
function automatic unreach_reason_e environmental(bin_key_t k);
if (k.ec == ERR_MTUMIS && int'(k.sz) < MTU_BUCKET)
return UR_ENVIRONMENT;
return UR_NONE;
endfunction
unreach_reason_e r_struct, r_env;
always_comb begin
r_struct = structural(bin_key);
r_env = environmental(bin_key);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
out_valid <= 1'b0;
outcome <= BIN_UNCLASSIFIED;
reason <= UR_NONE;
reason_is_soft <= 1'b0;
end else begin
out_valid <= bin_valid;
if (bin_valid) begin
if (was_hit) begin
outcome <= BIN_HIT;
reason <= UR_NONE;
reason_is_soft <= 1'b0;
end else if (r_struct != UR_NONE) begin
outcome <= BIN_UNREACHABLE;
reason <= r_struct;
reason_is_soft <= 1'b0;
end else if (r_env != UR_NONE) begin
outcome <= BIN_UNREACHABLE;
reason <= r_env;
reason_is_soft <= 1'b1; // Section 6 counts these
end else if (has_directed) begin
outcome <= BIN_DIRECTED;
reason <= UR_NONE;
reason_is_soft <= 1'b0;
end else begin
// Not hit, no structural reason, no environmental reason,
// no directed test. The honest outcome, and the finding.
outcome <= has_argument ? BIN_UNREACHABLE
: BIN_UNCLASSIFIED;
reason <= has_argument ? UR_CONFIG : UR_NONE;
reason_is_soft <= has_argument;
end
end
end
end
endmoduleWhat it teaches: that the interesting output is reason, not outcome. A coverage tool already computes was_hit; nothing in a standard flow computes why an unhit bin is acceptable. And the reason has a type — structural reasons are theorems about the frame definition, environmental reasons are claims about a deployment — so reason_is_soft separates the exclusions a reviewer can check from the ones that need somebody outside the room, which is exactly Chapter 26.1 §12's derived-against-environmental split arriving inside a coverage model.
Deliberately simplified: has_argument is an input rather than a text field, so the model records that an argument exists and not what it says. A real flow keeps the argument in the exclusion file and this bit is the answer to "is the field non-empty". The two structural arms are also a summary rather than a proof — a complete implementation derives them from the same frame-shape predicates Chapter 21.2 §2 uses, so that a change to the taxonomy propagates. And MTU_BUCKET is a parameter here where in practice it is a comment in a spreadsheet.
Production implication: the outcome to instrument is BIN_UNCLASSIFIED, and it is the one no coverage report has a column for. A flow that reports hit, excluded and total has three numbers; this model has four, and the fourth is the count of bins that were neither hit, nor argued about, nor scheduled for a directed test. In a plan that claims closure that number must be zero, and the cost of proving it is one boolean per unhit bin. The failure when it is not measured is specific: the closure report is true and the design is untested in a part of its input space nobody has named.
4. The Coverage Model, Counted
Four axes, and every one of them belongs to a chapter that already derived it.
| Axis | Values | Where it comes from |
|---|---|---|
| frame size | 7 — the RMON buckets | Chapter 19.7 §2 |
| error class | 13 — twelve classes plus none | Chapter 21.2 §1 |
| tag depth | 3 — untagged, single, double | Chapter 13.2 §2 |
| direction | 2 | — |
Seven times thirteen times three times two is 546. That is the model a verification plan declares, and it is the denominator of every coverage percentage the project will quote for the next eighteen months.
Now classify it. Three kinds of reason, from Section 3's unreach_reason_e, and the counts are exact.
| Reason | Rule | Bins | Share of 546 |
|---|---|---|---|
UR_LENGTH — runt | a runt is shorter than 64 octets, so it exists in one bucket | 36 | 6.59% |
UR_LENGTH — giant and jabber | both are longer than the MTU, so both exist in one bucket | 72 | 13.19% |
UR_DIRECTION | five classes are receive-side facts — symbol, alignment, FIFO overflow, filter, VLAN membership | 105 | 19.23% |
UR_ENVIRONMENT | an MTU mismatch is invisible below the peer's MTU | 30 | 5.49% |
| unreachable, total | — | 243 | 44.51% |
| reachable | — | 303 | 55.49% |
Each row's arithmetic is one multiplication. A runt outside SZ_LE64 is six buckets times three tag depths times two directions — 36. Giant and jabber outside SZ_GE1519 is two classes times the same 36 — 72. Each receive-side class on the transmit side is seven buckets times three tag depths — 21 — and there are five of them, 105. An MTU mismatch below the peer's bucket is five buckets times three times two — 30.
Nearly half of a coverage model that took a week to write describes frames that cannot exist. That is not a defect. The defect is that the number 243 appears nowhere in the flow.
And now the two coverage numbers. Section 8 shows a full week of a 500-licence farm reaching 283 bins.
| The report says | Against | The number |
|---|---|---|
| "functional coverage" | 546 declared bins | 51.832% |
| "functional coverage after exclusions" | 303 reachable bins | 93.399% |
Same run. Same 283 bins. A difference of 41.568 percentage points, produced entirely by which denominator the report was configured with — and both configurations are defensible, which is why both appear in real projects and why neither is a review item.
5. RTL 2 — The Reachability Classifier
Section 3 classified one bin. This module sweeps the whole model and produces Section 4's five counts, which are the numbers the review reads.
// ---------------------------------------------------------------------
// reachability_classifier -- sweep all 546 bins, count the four
// outcomes, and expose the two coverage fractions separately.
//
// The module exists to make the two denominators VISIBLE AT ONCE.
// A flow that can only express one of them forces a choice that then
// gets defended rather than examined.
// ---------------------------------------------------------------------
module reachability_classifier
import verifreview_pkg::*;
#(
parameter int MTU_BUCKET = MTU_BUCKET_1024
)(
input logic clk,
input logic rst_n,
input logic sweep_start,
// One bit per bin from the coverage tool, and two from the plan.
input logic hit_vec [N_BINS],
input logic directed_vec [N_BINS],
input logic argument_vec [N_BINS],
output logic sweep_done,
output logic [15:0] n_declared,
output logic [15:0] n_unreachable,
output logic [15:0] n_reachable,
output logic [15:0] n_hit,
output logic [15:0] n_directed,
output logic [15:0] n_unclassified,
output logic [15:0] n_soft_exclusions, // Section 6's six
// The two fractions, in parts per million so both fit one unit.
output logic [19:0] cov_vs_declared_ppm,
output logic [19:0] cov_vs_reachable_ppm
);
logic [15:0] idx;
logic running;
// The bin at the current index, reconstructed from the flat index.
bin_key_t k;
always_comb begin
k.dr = dir_e'( idx % N_DIR );
k.tg = tag_e'((idx / N_DIR) % N_TAG );
k.ec = err_class_e'((idx / (N_DIR*N_TAG)) % N_ERR );
k.sz = size_bucket_e'( idx / (N_DIR*N_TAG*N_ERR) );
end
logic this_unreach, this_soft;
always_comb begin
this_unreach = 1'b0;
this_soft = 1'b0;
if (k.ec == ERR_RUNT && k.sz != SZ_LE64)
this_unreach = 1'b1;
else if ((k.ec == ERR_GIANT || k.ec == ERR_JABBER) &&
k.sz != SZ_GE1519)
this_unreach = 1'b1;
else if (k.dr == DIR_TX &&
(k.ec == ERR_SYMBOL || k.ec == ERR_ALIGN ||
k.ec == ERR_FIFO_OVF || k.ec == ERR_FILTER ||
k.ec == ERR_VLANMEM))
this_unreach = 1'b1;
else if (k.ec == ERR_MTUMIS && int'(k.sz) < MTU_BUCKET) begin
this_unreach = 1'b1;
this_soft = 1'b1; // moves with the deployment
end
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
idx <= '0;
running <= 1'b0;
sweep_done <= 1'b0;
n_declared <= '0;
n_unreachable <= '0;
n_reachable <= '0;
n_hit <= '0;
n_directed <= '0;
n_unclassified <= '0;
n_soft_exclusions <= '0;
end else begin
sweep_done <= 1'b0;
if (sweep_start) begin
idx <= '0;
running <= 1'b1;
n_declared <= '0;
n_unreachable <= '0;
n_reachable <= '0;
n_hit <= '0;
n_directed <= '0;
n_unclassified <= '0;
n_soft_exclusions <= '0;
end else if (running) begin
n_declared <= n_declared + 16'd1;
if (this_unreach) begin
n_unreachable <= n_unreachable + 16'd1;
n_soft_exclusions <= n_soft_exclusions +
16'(this_soft ? 1 : 0);
end else begin
n_reachable <= n_reachable + 16'd1;
if (hit_vec[idx])
n_hit <= n_hit + 16'd1;
else if (directed_vec[idx])
n_directed <= n_directed + 16'd1;
else if (!argument_vec[idx])
// Reachable, not hit, no test, no argument. The finding.
n_unclassified <= n_unclassified + 16'd1;
end
if (idx == 16'(N_BINS - 1)) begin
running <= 1'b0;
sweep_done <= 1'b1;
end else begin
idx <= idx + 16'd1;
end
end
end
end
// Both fractions, always, and never one of them.
always_comb begin
cov_vs_declared_ppm = (n_declared == 0) ? 20'd0
: 20'((int'(n_hit) * 1000000) / int'(n_declared));
cov_vs_reachable_ppm = (n_reachable == 0) ? 20'd0
: 20'((int'(n_hit) * 1000000) / int'(n_reachable));
end
endmoduleClassification, Model or Production? A REVIEW INSTRUMENT. It runs on a report rather than on a design, and its outputs are the five numbers Section 2's items 1 to 5 require.
What it teaches: that the two coverage fractions cost one extra divide and are almost never both reported. cov_vs_declared_ppm at 518 315 and cov_vs_reachable_ppm at 933 993 are the same run seen through two denominators, and a flow that emits one of them has made an editorial decision that then becomes the project's definition of coverage. The module also teaches why n_unclassified has to be counted separately from n_directed: both are reachable bins that were not hit, and only one of them has somebody's name attached.
Deliberately simplified: the sweep is one bin per cycle over an unpacked array of 546 bits, which is a testbench structure rather than silicon — this module is never synthesised. this_unreach duplicates Section 3's structural and environmental functions rather than instantiating bin_model, which keeps the sweep combinational at the cost of one place the two can diverge; a real implementation instantiates the model and the duplication becomes a conformance check. And the fractions truncate rather than round, which loses up to one part per million and is the right trade for an integer divide.
Production implication: the output to put in the sign-off report is not either fraction — it is n_reachable, n_hit, n_directed and n_unclassified as four integers. They sum to a number somebody can check, each one can be argued with individually, and the two percentages can be recomputed from them by anybody who wants a percentage. A report that carries the four integers cannot be quoted misleadingly; one that carries a single percentage almost always is, not through bad faith but because the denominator travels in a configuration file and the number travels in an email.
6. The Exclusion List Is the Coverage Model
Section 4 counted 243 unreachable bins as though the count were a fact. It is not. It is the output of a rule, and one of the four rules has a parameter in it.
The MTU-mismatch rule from Section 3's environmental function: a mismatch is invisible in frames below the peer's maximum transmission unit. Which bucket that is depends on a number nobody in the design team owns — Chapter 26.1 §12's environmental threshold, appearing here as a line in an exclusion file.
| The peer's MTU | Mismatch first visible in | Excluded | Reachable |
|---|---|---|---|
| 576 octets — the IPv4 minimum | SZ_512_1023 | 237 | 309 |
| 1 024 octets | SZ_1024_1518 | 243 | 303 |
| 1 500 octets — the standard MTU | SZ_GE1519 | 249 | 297 |
The exclusion list moves by twelve bins across the range and by six between adjacent rows, and every row is the same design, the same RTL and the same testbench. What changed is a number in somebody else's network.
Now put the regression's 283 hits against each row.
| The peer's MTU | Reachable | Reported coverage | Bins never tested and never argued about |
|---|---|---|---|
| 576 | 309 | 91.586% | 6 — excluded on an assumption that is false here |
| 1 024 | 303 | 93.399% | 0 |
| 1 500 | 297 | 95.286% | 0 — but 6 directed tests exist that can never pass |
Row one is the failure and row three is the false alarm, and they are not symmetric.
Excluding a reachable bin removes the test. Including an unreachable one adds a test that fails forever. The first is silent; the second is loud on the first night of regression.
That asymmetry is the whole of Section 20's argument and it is worth stating as an engineering fact before it becomes a property. An excluded bin is not sampled. The coverage tool does not instrument it, so it produces no evidence in either direction — not a pass, not a fail, not a warning, and not a suspicious figure above 100%. A wrong exclusion is invisible to the flow that contains it, because the flow's response to an exclusion is to stop looking.
The opposite error announces itself. A bin wrongly left in the reachable set becomes a directed test that never hits, and a test that never hits is on somebody's failure list every morning until they either fix it or exclude it. So the error that costs nothing to make also costs nothing to detect, and the error that costs a design its coverage is undetectable by construction.
7. RTL 3 — The Exclusion Auditor
Section 5 counted bins. This module audits the file that decides which bins get counted.
// ---------------------------------------------------------------------
// exclusion_auditor -- compares the exclusion file against the
// classifier's own rules and reports four disagreements, three of which
// no standard flow produces.
//
// It is the only module in this chapter that treats the exclusion list
// as EVIDENCE rather than as configuration.
// ---------------------------------------------------------------------
module exclusion_auditor
import verifreview_pkg::*;
(
input logic clk,
input logic rst_n,
input logic entry_valid,
input bin_key_t entry_key,
input logic entry_has_argument, // the file's reason field
input unreach_reason_e entry_claimed, // the reason it claims
// The classifier's independent opinion of the same bin.
input logic model_unreachable,
input unreach_reason_e model_reason,
input logic model_soft,
output logic [15:0] n_entries,
output logic [15:0] n_with_argument,
output logic [15:0] n_soft, // depends on a deployment
output logic [15:0] n_reason_mismatch, // file and model disagree
output logic [15:0] n_model_disagrees, // file excludes, model does not
output logic file_is_reviewed, // every entry has an argument
output logic file_is_sound // and the model agrees with all
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_entries <= '0;
n_with_argument <= '0;
n_soft <= '0;
n_reason_mismatch <= '0;
n_model_disagrees <= '0;
end else if (entry_valid) begin
n_entries <= n_entries + 16'd1;
// Item 7 of Section 2: an entry without a sentence is an entry
// nobody can attack.
if (entry_has_argument)
n_with_argument <= n_with_argument + 16'd1;
// The six. An exclusion whose truth depends on a number outside
// the room is the reviewer's whole budget.
if (model_soft || entry_claimed == UR_ENVIRONMENT)
n_soft <= n_soft + 16'd1;
// The file says one thing and the rules say another. Both may be
// defensible; the point is that nobody has ever compared them.
if (model_unreachable && (entry_claimed != model_reason))
n_reason_mismatch <= n_reason_mismatch + 16'd1;
// The file excludes a bin the rules say is reachable. This is the
// silent error of Section 6, and this counter is the only place
// in the flow where it becomes a number.
if (!model_unreachable)
n_model_disagrees <= n_model_disagrees + 16'd1;
end
end
// Two verdicts, and they are deliberately different questions.
always_comb begin
file_is_reviewed = (n_entries == n_with_argument);
file_is_sound = file_is_reviewed && (n_model_disagrees == 16'd0);
end
endmoduleClassification, Model or Production? A REVIEW INSTRUMENT, and the most useful one in the chapter.
What it teaches: that an exclusion list can be checked against something, which is the assumption the whole flow lacks. n_model_disagrees is the counter that catches Section 6's silent error — the file excludes a bin and the rules say it is reachable — and it exists only because the rules were written down in Section 3 rather than left in the exclusion file's comment field. The module also teaches the difference between file_is_reviewed and file_is_sound: the first says every entry has a sentence attached, the second says the sentences agree with an independent model. A file can be fully reviewed and entirely wrong, which is Chapter 26.1 §10's witness problem arriving one stage later.
Deliberately simplified: entry_claimed is an enumerated reason where a real exclusion file has free text, so the comparison against model_reason is exact here and fuzzy in practice — which is an argument for constraining the file's reason field to an enumeration rather than for abandoning the check. The model's opinion arrives as inputs rather than by instantiating reachability_classifier, which keeps this module a pure comparator. And there is no counter for the opposite error — a bin the model says is unreachable and the file does not exclude — because that error is loud: it becomes a directed test that never hits.
Production implication: n_soft is the number to put at the top of the review's agenda, and in this design it is six. Those six exclusions are true at one peer MTU and false at another, so they are not properties of the design at all — they are properties of a network the design will be sold into, and the correct handling is the one Chapter 26.1 §12 arrived at for every environmental threshold: name the owner, expose the parameter, and re-run the classifier when the parameter changes. A project that ships one exclusion file for every deployment has hard-coded somebody else's MTU into its definition of "verified".
8. What Random Stimulus Cannot Reach
Section 4 said 303 bins are reachable. This section asks how long reaching them takes, and the answer is the chapter's second finding.
The traffic model, stated so it can be argued with. Four distributions, each a stated assumption rather than a measurement.
| Axis | Distribution | Source of the shape |
|---|---|---|
| size | SZ_LE64 50%, SZ_1024_1518 28%, the other five 5%, SZ_GE1519 2% | the bimodal data-centre profile of Chapter 19.7 §9 |
| tag | untagged 60%, single 39%, double 1% | Chapter 13.3 §2 — Q-in-Q is an edge case |
| direction | 50 / 50 | a symmetric testbench |
| error | total error rate 1.12 × 10⁻⁴, split across twelve classes | Chapter 21.2 §1's classes at injection rates |
And the simulator, which is the term everybody leaves out.
| Quantity | Value | How |
|---|---|---|
| design clock | 1.2 GHz | Chapter 23.3 §2's pipeline clock |
| simulator throughput | 2 000 cycles per wall-clock second | an assertion-heavy UVM environment |
| slowdown against real time | 600 000× | 1.2 × 10⁹ ÷ 2 × 10³ |
| mean frame on the wire | 510.09 octets | the size distribution plus 20 octets of preamble and gap, Chapter 8.1 §2 |
| mean frame time at 100 Gb/s | 40.81 ns | 510.09 × 8 ÷ 10¹¹ |
| cycles per frame | 48.97 | 40.81 ns × 1.2 GHz |
| wire throughput | 2.4505 × 10⁷ frames/s | 1 ÷ 40.81 ns |
| simulator throughput | 40.84 frames/s | 2 000 ÷ 48.97 |
A 100 Gb/s port delivers 24.5 million frames a second. The simulator that verifies it delivers 40.84. Every coverage argument in this chapter is a consequence of that ratio.
Now the rarest reachable bin. A jabber, in SZ_GE1519, double-tagged, on the receive side: 2% × 1 × 10⁻⁶ × 1% × 50% = 1 × 10⁻¹⁰.
| Frames | Simulation | On the wire | |
|---|---|---|---|
| one expected hit | 1 × 10¹⁰ | 68 012 hours — 7.76 simulator-years | 408.1 s |
| 95% confidence of one hit | 2.996 × 10¹⁰ | 203 764 hours — 23.24 simulator-years | 1 222.6 s — twenty minutes |
Twenty minutes of a real link against twenty-three years of a simulator, for one bin. And the ratio is not a surprise once it is written down — it is the 600 000× slowdown, and nothing about the coverage model changes it.
The distribution over all 303 reachable bins, at 95% confidence each.
| Simulation needed per bin | Bins | Share of 303 |
|---|---|---|
| more than 1 hour | 261 | 86.1% |
| more than one overnight run — 8 h | 254 | 83.8% |
| more than one day | 235 | 77.6% |
| more than one week | 167 | 55.1% |
| more than one year | 35 | 11.6% |
Which looks catastrophic and is not, because bins are collected from one stimulus stream and streams run in parallel. A farm changes the arithmetic.
| The farm | Value |
|---|---|
| licences | 500 |
| hours per licence per week | 40 |
| simulation-hours per week | 20 000 |
| frames per week | 2.9407 × 10⁹ |
| bins reached at 95% confidence | 283 of 303 — 93.399% |
| bins NOT reached | 20 |
Twenty bins. That is Section 2's item 5, and it is the number the whole section was built to produce. It is not a coverage hole and it is not a bug — it is a work item, and its correct disposition is twenty directed tests.
9. RTL 4 — The Stimulus Budget Model
The module that turns a bin's probability into a schedule, and a schedule into the count of directed tests the plan needs.
// ---------------------------------------------------------------------
// stimulus_budget -- given a bin's probability and the farm's frame
// budget, decide whether random stimulus will reach it and, if not,
// require a directed test.
//
// The arithmetic is deliberately the 95%-confidence form rather than
// the expectation form: a bin whose EXPECTED hit count is one is missed
// about 37% of the time, and a plan built on expectations is wrong on a
// third of its rarest bins.
// ---------------------------------------------------------------------
module stimulus_budget #(
// Probabilities arrive scaled by 1e12 so that 1e-10 is 100 and the
// whole range of interest fits in 40 bits without a real type.
parameter longint unsigned FRAMES_PER_WEEK = 64'd2_940_700_000,
// ln(0.05) / ln(1-p) is about 3/p for small p. The 3 is exact enough
// below p = 1e-3 and this model is only used below 1e-6.
parameter int unsigned CONF_MULT = 3
)(
input logic clk,
input logic rst_n,
input logic bin_valid,
input logic [39:0] bin_p_e12, // probability x 1e12
input logic has_directed_test,
output logic out_valid,
output logic reachable_by_random,
output logic needs_directed,
output logic gap, // needs one and has none
output logic [47:0] frames_needed_e0,
output logic [31:0] weeks_needed,
output logic [15:0] n_needing_directed,
output logic [15:0] n_directed_present,
output logic [15:0] n_gaps
);
// 3 / p, with p expressed times 1e12. The divide is by the scaled
// probability, so the 1e12 cancels into the numerator.
function automatic longint unsigned frames_for(logic [39:0] p_e12);
if (p_e12 == 40'd0) return 64'hFFFF_FFFF_FFFF; // never
return (64'(CONF_MULT) * 64'd1_000_000_000_000) / 64'(p_e12);
endfunction
longint unsigned need;
always_comb begin
need = frames_for(bin_p_e12);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
out_valid <= 1'b0;
reachable_by_random <= 1'b0;
needs_directed <= 1'b0;
gap <= 1'b0;
frames_needed_e0 <= '0;
weeks_needed <= '0;
n_needing_directed <= '0;
n_directed_present <= '0;
n_gaps <= '0;
end else begin
out_valid <= bin_valid;
if (bin_valid) begin
frames_needed_e0 <= 48'(need);
weeks_needed <= 32'((need + FRAMES_PER_WEEK - 1)
/ FRAMES_PER_WEEK);
if (need <= FRAMES_PER_WEEK) begin
reachable_by_random <= 1'b1;
needs_directed <= 1'b0;
gap <= 1'b0;
end else begin
reachable_by_random <= 1'b0;
needs_directed <= 1'b1;
n_needing_directed <= n_needing_directed + 16'd1;
if (has_directed_test) begin
n_directed_present <= n_directed_present + 16'd1;
gap <= 1'b0;
end else begin
// Section 2's items 5 and 6 differing. The plan believes
// this bin is covered and nothing will cover it.
n_gaps <= n_gaps + 16'd1;
gap <= 1'b1;
end
end
end
end
end
endmoduleClassification, Model or Production? A PLANNING MODEL. It computes a schedule from a probability, and its output is a test list rather than a design decision.
What it teaches: that the confidence multiplier is not a detail. At CONF_MULT = 1 the model plans for the expected hit, and a Poisson process with an expectation of one produces zero hits 36.8% of the time — so a plan built on expectations misses about a third of its rarest bins and discovers it in the last week. At CONF_MULT = 3 the miss probability is 5%. The module also teaches that weeks_needed is the honest unit for a planning conversation: 10.2 weeks of the whole farm is a sentence a programme manager can act on, where 3 × 10¹⁰ frames is not.
Deliberately simplified: probabilities arrive as a pre-computed scalar per bin, which assumes the four axes are independent — and they are not: a jabber is correlated with large frames by definition, so the true probability of the rarest bin is not the product of its marginals. The independence assumption overestimates the rarity of correlated bins and underestimates it for anti-correlated ones, and a real model samples the joint distribution from a traffic capture. frames_for also uses 3/p rather than ln(0.05)/ln(1−p), which is accurate to better than 0.1% for every probability this chapter uses and wrong above about 10⁻³.
Production implication: n_gaps is the output that belongs in the sign-off report and it is one integer. It is the count of bins that need a directed test and do not have one — Section 2's item 5 minus item 6 — and it is computable the day the coverage model is written, months before the regression that would have revealed it. A plan that computes n_gaps early converts a last-week coverage crisis into an early staffing decision; one that does not will find the same twenty bins in the final week and close them with an exclusion, because by then that is the only option left that fits the schedule.
10. The Assertion Corpus, Audited
Coverage bins are one half of a verification plan and properties are the other, and the properties half has a defect the bins half does not: a property that is never evaluated reports the same result as a property that passed.
Chapter 21.4 §20 measured this on the track's own corpus rather than asserting it.
| Corpus | Properties | With an antecedent that requires traffic | Share |
|---|---|---|---|
| Module 19 — the datapath | 238 | 162 | 68.1% |
| Module 20 — the environment | 204 | 95 | 46.6% |
| Module 21 as far as 21.4 | 115 | 75 | 65.2% |
| all three | 557 | 332 | 59.6% |
Three of every five published properties stop evaluating on a link that never comes up. Not fail — stop. Their antecedents need a frame, the link produces none, and a regression bound to all 557 reports 557 passes on a design that does not work.
A property's pass count says how often it was true. Its cover count says how often anybody asked. A report with the first and not the second is a report of the testbench's activity, not the design's correctness.
Which makes Section 2's item 10 the single cheapest improvement available to any verification plan in this track: count the bound properties whose cover count is zero at the end of the regression, and classify each one. Three outcomes, exactly parallel to Section 1's three for bins.
| A property that never fired | Why | What it means |
|---|---|---|
| not bound in this configuration | generate removed it | correct — Chapter 25.6 §20's first repair |
| bound, antecedent unreachable in this build | a parameter excludes it | correct, if the argument is recorded |
| bound, antecedent reachable, never seen | the stimulus never produced it | the finding — and it is also a coverage hole |
Row three is the interesting one because it connects the two halves of this chapter. A property whose antecedent is reachable and never seen is describing a bin that was never hit, so the assertion corpus and the coverage model are measuring the same gap from two directions — and a plan that audits both has two independent witnesses for the same claim, which is Chapter 26.1 §10's independence requirement satisfied for free.
The arithmetic on this design's own plan. Applying the audited 59.6% to a MAC-scale corpus of 240 bound properties:
| Properties | |
|---|---|
| bound in the regression | 240 |
| with a traffic-dependent antecedent, at 59.6% | 143 |
| that fire on a healthy link | 240 |
| that fire on a link that never trains | 97 |
| that FAIL on a link that never trains | 0 |
Ninety-seven properties evaluate and 143 go quiet, and the report says 240 passed. The only signal that anything is wrong is the cover count, and it is the one number a pass/fail summary does not carry.
11. RTL 5 — The Bound-Against-Covered Reconciler
// ---------------------------------------------------------------------
// bound_covered_reconciler -- walk the regression's property table and
// separate the properties that never fired into the three outcomes of
// Section 10, so that "bound minus covered" becomes a classified list
// rather than a number to be explained away.
// ---------------------------------------------------------------------
module bound_covered_reconciler (
input logic clk,
input logic rst_n,
input logic prop_valid,
input logic prop_bound, // survived elaboration
input logic [31:0] prop_cover_count, // times the antecedent fired
input logic [31:0] prop_pass_count,
input logic [31:0] prop_fail_count,
input logic prop_excluded_by_cfg,// a generate removed its peer
input logic prop_has_argument, // somebody wrote the reason
input logic prop_needs_traffic, // Chapter 21.4's category
output logic [15:0] n_bound,
output logic [15:0] n_covered,
output logic [15:0] n_silent, // bound, never evaluated
output logic [15:0] n_silent_explained, // and classified
output logic [15:0] n_silent_unexplained,
output logic [15:0] n_traffic_dependent,
output logic [19:0] traffic_dep_ppm,
output logic suite_is_reconciled,
output logic report_is_honest // pass count has a denominator
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_bound <= '0;
n_covered <= '0;
n_silent <= '0;
n_silent_explained <= '0;
n_silent_unexplained <= '0;
n_traffic_dependent <= '0;
end else if (prop_valid && prop_bound) begin
n_bound <= n_bound + 16'd1;
if (prop_needs_traffic)
n_traffic_dependent <= n_traffic_dependent + 16'd1;
if (prop_cover_count != 32'd0) begin
n_covered <= n_covered + 16'd1;
end else begin
// Bound and never evaluated. A pass count of zero here is
// indistinguishable from a pass count of zero on a property
// that fired and never held -- except that fail_count settles it.
n_silent <= n_silent + 16'd1;
if (prop_excluded_by_cfg || prop_has_argument)
n_silent_explained <= n_silent_explained + 16'd1;
else
n_silent_unexplained <= n_silent_unexplained + 16'd1;
end
end
end
always_comb begin
traffic_dep_ppm = (n_bound == 0) ? 20'd0
: 20'((int'(n_traffic_dependent) * 1000000)
/ int'(n_bound));
// The useful verdict is not "everything fired" -- it is "everything
// that did not fire has a sentence attached".
suite_is_reconciled = (n_silent_unexplained == 16'd0);
// And a pass count means nothing without the cover count beside it.
report_is_honest = suite_is_reconciled && (n_covered != 16'd0);
end
endmoduleClassification, Model or Production? A REVIEW INSTRUMENT.
What it teaches: that prop_fail_count is the input that disambiguates the two zeros. A property with pass_count == 0 has either never been evaluated or been evaluated and failed every time, and the pass count alone cannot tell them apart — which is the assertion-side form of Section 4's two-denominator problem. The module also teaches why traffic_dep_ppm belongs in telemetry rather than in a verdict: it is a property of the suite's style, not of any run, and a suite at 595 833 ppm is not broken — it is normal, and it is the number that predicts how a regression will behave on a link that does not train.
Deliberately simplified: prop_needs_traffic is an input classification where a real flow derives it by walking each property's antecedent for signals in the receive datapath, which is a static analysis rather than a runtime one. The module also treats "bound" as a single bit where a suite with multiple configurations has a property bound in some builds and not others — so a complete implementation reconciles per configuration and then across them, and the cross-configuration total is the one that catches a property no build ever binds.
Production implication: n_silent_unexplained is the number to gate on, and gating on it is different from gating on n_silent. A healthy suite has a large n_silent — configurations differ, features are optional, and generate blocks do their job. A healthy suite has n_silent_unexplained of zero, because every silent property has one line saying which of the three reasons applies. The cost is one line per silent property, once, and the benefit is that the day a property stops firing because somebody renamed a signal, the count moves and somebody sees it — instead of the property joining the 143 that were always quiet.
12. The Units of a Verification Review
Every chapter of this track since Chapter 19.1 has priced something in BCE — one bit of usable on-die SRAM, 0.35 gate-equivalents, twenty to a flip-flop. This chapter prices nothing in BCE, and the reason is not that verification has no cost.
It is that verification's cost is not a property of the object that ships.
| Area | Verification effort | |
|---|---|---|
| what it is a property of | the silicon | the process that produced confidence in the silicon |
| when it is fixed | at tape-out, forever | never — it accrues after tape-out too |
| who pays it | every unit shipped | the programme, once |
| what halving it does to the other | nothing | nothing |
Row four is the one that forbids the sum. A design that halves its area may double its verification cost — a shared resource is smaller and harder to reason about — so the two numbers are not merely in different units, they are not even correlated in sign. Adding them produces a quantity that gets larger when the design gets better, which is the definition of a number with no referent.
So this chapter uses four units, and they do not convert into one another.
| Unit | What it measures | Section | This design |
|---|---|---|---|
| bins | the size of the claim | 4 | 546 declared, 303 reachable |
| simulation-hours | what random stimulus costs | 8 | 20 000 per week available; 204 036 to close by random |
| engineer-weeks | what deliberate stimulus costs | this section | 6 |
| properties with zero evaluations | what the suite silently did not check | 10 | 143 of 240 on an untrained link |
Four units, four questions, and no exchange rate between any pair of them. A review that reports one number has answered one of the four.
The engineer-week arithmetic, stated so it can be argued with.
| Value | |
|---|---|
| bins needing a directed test | 20 |
| effort per directed test | 1.5 engineer-days — write, debug, review, land |
| total | 30 engineer-days |
| at five days a week | 6 engineer-weeks |
| as a share of a 24-engineer-month programme — 104 engineer-weeks | 5.77% |
Six engineer-weeks against 204 036 simulation-hours is the comparison Section 8 made, and it is the only place the two units meet: not in a sum, but in a schedule, where they compete for the same calendar. That is the correct relationship between incomparable units — they are compared by what they displace, not by conversion.
13. RTL 6 — The Effort Reconciler
// ---------------------------------------------------------------------
// effort_reconciler -- hold the four units side by side without ever
// summing them, and report the one relationship that is real: what each
// route costs in the calendar.
//
// The module's central refusal is that it has NO output combining a
// simulation-hour with an engineer-day. Section 12 is the reason.
// ---------------------------------------------------------------------
module effort_reconciler #(
parameter int unsigned FARM_HOURS_PER_WEEK = 20000,
parameter int unsigned ENG_DAYS_PER_TEST_X10 = 15, // 1.5 days
parameter int unsigned ENG_DAYS_PER_WEEK = 5
)(
input logic clk,
input logic rst_n,
input logic reconcile,
input logic [15:0] bins_declared,
input logic [15:0] bins_reachable,
input logic [15:0] bins_hit,
input logic [15:0] bins_needing_directed,
input logic [47:0] frames_to_close_by_random,
input logic [31:0] sim_frames_per_hour,
input logic [15:0] props_bound,
input logic [15:0] props_silent,
// Four outputs in four units. None of them is a sum of the others.
output logic [15:0] u_bins_outstanding,
output logic [31:0] u_sim_hours_to_close,
output logic [15:0] u_eng_weeks_to_close,
output logic [15:0] u_props_unevaluated,
// And two dimensionless ratios, which are the only figures that
// transfer to another programme.
output logic [19:0] unreachable_share_ppm,
output logic [19:0] silent_share_ppm,
// The calendar comparison: the two routes, in weeks, side by side.
output logic [31:0] weeks_if_random,
output logic [15:0] weeks_if_directed,
output logic directed_is_cheaper
);
logic [31:0] sim_hours;
logic [31:0] eng_days_x10;
always_comb begin
sim_hours = (sim_frames_per_hour == 0) ? 32'hFFFF_FFFF
: 32'(frames_to_close_by_random / 48'(sim_frames_per_hour));
eng_days_x10 = 32'(bins_needing_directed) * 32'(ENG_DAYS_PER_TEST_X10);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
u_bins_outstanding <= '0;
u_sim_hours_to_close <= '0;
u_eng_weeks_to_close <= '0;
u_props_unevaluated <= '0;
weeks_if_random <= '0;
weeks_if_directed <= '0;
end else if (reconcile) begin
u_bins_outstanding <= bins_reachable - bins_hit;
u_sim_hours_to_close <= sim_hours;
// Round up: a fraction of an engineer-week is a whole week of
// somebody's calendar.
u_eng_weeks_to_close <= 16'((eng_days_x10 +
(10*ENG_DAYS_PER_WEEK) - 1)
/ (10*ENG_DAYS_PER_WEEK));
u_props_unevaluated <= props_silent;
weeks_if_random <= (sim_hours + FARM_HOURS_PER_WEEK - 1)
/ FARM_HOURS_PER_WEEK;
weeks_if_directed <= 16'((eng_days_x10 +
(10*ENG_DAYS_PER_WEEK) - 1)
/ (10*ENG_DAYS_PER_WEEK));
end
end
always_comb begin
unreachable_share_ppm =
(bins_declared == 0) ? 20'd0
: 20'((int'(bins_declared - bins_reachable) * 1000000)
/ int'(bins_declared));
silent_share_ppm =
(props_bound == 0) ? 20'd0
: 20'((int'(props_silent) * 1000000) / int'(props_bound));
// The only legitimate comparison between the two routes: which one
// occupies fewer weeks of the project's calendar.
directed_is_cheaper = (32'(weeks_if_directed) < weeks_if_random);
end
endmoduleClassification, Model or Production? A REVIEW INSTRUMENT, and the one whose design decisions are mostly refusals.
What it teaches: that a module can be defined by the port it does not have. There is no total_cost output, no weighting parameter and no exchange rate — and every one of those would have been easy to add and impossible to interpret. The comparison the module does make, directed_is_cheaper, is in weeks: 10.2 weeks of the whole farm against 6 engineer-weeks, which is a real comparison because both routes consume the same calendar. The two ppm outputs are the module's other teaching: they are the only figures here that mean anything on a different project.
Deliberately simplified: ENG_DAYS_PER_TEST_X10 is one constant for all twenty tests, and the twenty are not equally hard — a double-tagged jabber is a constrained-random sequence with two overrides; a FIFO-overflow bin needs a backpressure model. A real estimate is per-test, and the spread is wide enough that the total can be wrong by a factor of two. The farm figure also assumes perfect parallel efficiency, where a real farm loses licences to other projects, to failed jobs and to compile time. Both simplifications push the same way — they make the directed route look relatively better than it is — and Section 18 lists them as assumptions rather than hiding them.
Production implication: the report this module produces has four numbers and two ratios, and it fits on one line of a status e-mail: "20 bins outstanding, 204 036 simulation-hours or 6 engineer-weeks to close, 143 properties unevaluated; 44.5% of the model is unreachable and 59.6% of the suite needs traffic." Every clause is a different unit, none of them has been converted into another, and a reader can act on any one of them independently — which is the practical test of whether a set of units was chosen honestly.
14. What a Verification Review Must Never Do
Six prohibitions. Each is something a review does under schedule pressure, and each converts a finding into a pass.
| # | Never | Because |
|---|---|---|
| 1 | quote a coverage percentage without its denominator | Section 4 — 51.832% and 93.399% are the same run |
| 2 | accept an exclusion without an argument | Section 6 — the flow's response to an exclusion is to stop measuring |
| 3 | close a bin by editing the model rather than testing it | Section 8 — an afternoon against six engineer-weeks, and only one of them verifies |
| 4 | report a pass count without a cover count | Section 10 — 143 of 240 properties can pass by never being asked |
| 5 | sum a simulation-hour and an engineer-day | Section 12 — the two are not correlated, even in sign |
| 6 | treat a reference bin list as this design's model | the axes are this design's; the distribution is this deployment's |
Prohibition 3 is the one that needs a mechanism rather than a rule, because it is the only one whose violation looks like progress. A bin closed by an exclusion and a bin closed by a test are indistinguishable in the coverage report — both stop appearing in the uncovered list — so the review needs the two counts separately or it cannot tell diligence from tidying.
The number that distinguishes them is item 6 of Section 2 minus item 5: directed tests that exist, against directed tests the stimulus arithmetic says are needed. When the first is smaller, the difference was closed with an editor.
And prohibition 6 has a specific form in this chapter. Section 8's traffic distribution is a model, and every probability in Section 8's table is a number about a deployment. Reuse the axes; re-derive the distribution. A coverage model whose bins come from Chapter 21.2 and whose probabilities come from the previous project has a correct numerator and a denominator from somebody else's network.
15. RTL 7 — Verification Telemetry
// ---------------------------------------------------------------------
// verif_telemetry -- the numbers a verification review should be able
// to read at any moment, not only at sign-off.
//
// Everything here is a count or a ratio of counts. Nothing here is a
// verdict; Section 16 owns those.
// ---------------------------------------------------------------------
module verif_telemetry (
input logic clk,
input logic rst_n,
input logic clear,
// From the classifier, Section 5.
input logic sweep_done,
input logic [15:0] bins_declared,
input logic [15:0] bins_unreachable,
input logic [15:0] bins_reachable,
input logic [15:0] bins_hit,
input logic [15:0] bins_directed,
input logic [15:0] bins_unclassified,
input logic [15:0] soft_exclusions,
// From the exclusion auditor, Section 7.
input logic [15:0] excl_entries,
input logic [15:0] excl_with_argument,
input logic [15:0] excl_model_disagrees,
// From the reconciler, Section 11.
input logic [15:0] props_bound,
input logic [15:0] props_silent,
input logic [15:0] props_silent_unexplained,
// Events the review wants counted over time rather than at the end.
input logic excl_added,
input logic directed_test_added,
output logic [19:0] cov_declared_ppm,
output logic [19:0] cov_reachable_ppm,
output logic [19:0] unreachable_ppm,
output logic [19:0] excl_reviewed_ppm,
output logic [19:0] silent_ppm,
output logic [15:0] c_exclusions_added,
output logic [15:0] c_directed_added,
output logic [15:0] c_unclassified,
output logic v_denominator_missing,
output logic v_exclusion_unargued,
output logic v_exclusion_contradicted,
output logic v_silent_unexplained,
output logic v_closed_by_editing,
output logic v_soft_exclusion_present
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n || clear) begin
c_exclusions_added <= '0;
c_directed_added <= '0;
end else begin
if (excl_added) c_exclusions_added <= c_exclusions_added + 16'd1;
if (directed_test_added) c_directed_added <= c_directed_added + 16'd1;
end
end
always_comb begin
cov_declared_ppm = (bins_declared == 0) ? 20'd0
: 20'((int'(bins_hit) * 1000000) / int'(bins_declared));
cov_reachable_ppm = (bins_reachable == 0) ? 20'd0
: 20'((int'(bins_hit) * 1000000) / int'(bins_reachable));
unreachable_ppm = (bins_declared == 0) ? 20'd0
: 20'((int'(bins_unreachable) * 1000000)
/ int'(bins_declared));
excl_reviewed_ppm = (excl_entries == 0) ? 20'd1000000
: 20'((int'(excl_with_argument) * 1000000)
/ int'(excl_entries));
silent_ppm = (props_bound == 0) ? 20'd0
: 20'((int'(props_silent) * 1000000) / int'(props_bound));
c_unclassified = bins_unclassified;
// Six flags. Each one names a specific sentence somebody must add.
v_denominator_missing = (bins_reachable == 16'd0) &&
(bins_declared != 16'd0);
v_exclusion_unargued = (excl_with_argument < excl_entries);
v_exclusion_contradicted = (excl_model_disagrees != 16'd0);
v_silent_unexplained = (props_silent_unexplained != 16'd0);
// The prohibition-3 detector: exclusions grew faster than tests.
v_closed_by_editing = (c_exclusions_added > c_directed_added) &&
(c_exclusions_added > 16'd0);
v_soft_exclusion_present = (soft_exclusions != 16'd0);
end
endmoduleClassification, Model or Production? A PRODUCTION-INTENT INSTRUMENT, and the one block in this chapter that would survive into a real flow unchanged, because everything it consumes is already produced somewhere in a modern verification database.
What it teaches: that v_closed_by_editing is computable and nobody computes it. Exclusions added and directed tests added are both events with timestamps in any version-controlled flow; the comparison is a subtraction, and it is the only automatic signal that Section 14's third prohibition was violated. The module also teaches the shape of excl_reviewed_ppm's empty case: a project with no exclusions is fully reviewed by definition, so the ratio returns a million rather than zero — a small decision that stops an empty file from raising a flag.
Deliberately simplified: every ratio is an integer divide with truncation, and bins_hit is treated as a single scalar where a real flow has per-covergroup hit counts that merge across runs with their own subtleties. v_closed_by_editing also has no time window, so one legitimate bulk exclusion early in a project keeps the flag set for its lifetime; a real implementation compares the two counts over a rolling window, which is Chapter 19.7 §14's differencing-counter argument applied to process metrics rather than to frames.
Production implication: the flag that earns its place is v_soft_exclusion_present, because it is the one that changes when nothing about the project changes. Six of this design's 243 exclusions are true at one peer MTU and false at another, so the flag is set for every deployment and the correct response is not to clear it — it is to record which six, name their owner, and re-run Section 5's classifier when the owner answers. A flag that cannot be cleared by the team that sees it is doing exactly what Chapter 26.1 §12's DEFERRED outcome exists for.
16. RTL 8 — The Verification Conformance Monitor
Seven checks. A plan that passes all seven has a coverage number somebody can defend; one that fails any of them has a number that means less than it appears to.
// ---------------------------------------------------------------------
// verif_conformance -- the sign-off gate for Section 2's twenty items,
// expressed as seven checks over the outputs of Sections 5, 7, 11 and 13.
//
// The gate's design rule: every check must be able to FAIL on a plan
// that a project would actually submit. A check that no real plan fails
// is Section 20's refused property with a different name.
// ---------------------------------------------------------------------
module verif_conformance (
input logic clk,
input logic rst_n,
input logic evaluate,
// Section 5.
input logic [15:0] bins_declared,
input logic [15:0] bins_reachable,
input logic [15:0] bins_hit,
input logic [15:0] bins_unclassified,
// Section 7.
input logic [15:0] excl_entries,
input logic [15:0] excl_with_argument,
input logic [15:0] excl_model_disagrees,
input logic [15:0] excl_soft,
input logic excl_soft_owners_named,
// Section 9.
input logic [15:0] bins_needing_directed,
input logic [15:0] directed_tests_present,
// Section 11.
input logic [15:0] props_bound,
input logic [15:0] props_covered,
input logic [15:0] props_silent_unexplained,
// Section 13 -- present so the report cannot omit a unit.
input logic both_denominators_reported,
output logic c1_denominators_reported,
output logic c2_model_classified,
output logic c3_exclusions_argued,
output logic c4_exclusions_uncontradicted,
output logic c5_soft_exclusions_owned,
output logic c6_directed_tests_match,
output logic c7_suite_reconciled,
output logic [2:0] n_failures,
output logic conformant,
output logic gate_can_fail // the self-check
);
logic [2:0] fails;
always_comb begin
// 1. Section 4: the report carries both fractions, or it carries
// an editorial decision.
c1_denominators_reported = both_denominators_reported &&
(bins_reachable != 16'd0);
// 2. Section 5: no bin is reachable, unhit and unclassified.
c2_model_classified = (bins_unclassified == 16'd0);
// 3. Section 7, item 7 of Section 2.
c3_exclusions_argued = (excl_with_argument == excl_entries);
// 4. Section 6's silent error: the file excludes what the rules
// say is reachable.
c4_exclusions_uncontradicted = (excl_model_disagrees == 16'd0);
// 5. Section 7: the six that depend on a deployment have owners.
c5_soft_exclusions_owned = (excl_soft == 16'd0) ||
excl_soft_owners_named;
// 6. Section 2, item 5 against item 6. A shortfall here is the
// difference that got closed with an editor.
c6_directed_tests_match = (directed_tests_present >=
bins_needing_directed);
// 7. Section 11: every silent property has a sentence.
c7_suite_reconciled = (props_silent_unexplained == 16'd0) &&
(props_covered <= props_bound);
fails = 3'(!c1_denominators_reported) + 3'(!c2_model_classified)
+ 3'(!c3_exclusions_argued) + 3'(!c4_exclusions_uncontradicted)
+ 3'(!c5_soft_exclusions_owned) + 3'(!c6_directed_tests_match)
+ 3'(!c7_suite_reconciled);
// Every check above compares two quantities the plan supplies, so
// a plan exists that fails each one. This bit records that the gate
// is not Section 20's refused property.
gate_can_fail = (bins_declared != 16'd0) && (props_bound != 16'd0);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_failures <= '0;
conformant <= 1'b0;
end else if (evaluate) begin
n_failures <= fails;
conformant <= (fails == 3'd0) && gate_can_fail;
end
end
endmoduleClassification, Model or Production? A SIGN-OFF GATE.
What it teaches: that gate_can_fail is not decoration. Every other output of this module is a comparison between two numbers the plan supplies, and a plan that supplies zeros passes all seven checks — zero bins declared, zero exclusions, zero properties, seven passes. That is the exact failure Section 20 refuses, arriving inside the gate built to catch it, and the defence is one conjunct: a plan with no bins and no properties is not conformant, it is empty. The module also teaches why check 6 uses >= rather than ==: a plan with more directed tests than the arithmetic demands is over-verified, which is not a defect, and a check that punished it would push teams to delete tests.
Deliberately simplified: both_denominators_reported is an input bit where a real gate parses the generated report and confirms that both figures appear in it — which is the kind of check that is easy to write and easy to satisfy trivially, since a report can contain both numbers in a footnote. The honest implementation checks the summary line. excl_soft_owners_named is similarly one bit for six entries, where the useful form is six names.
Production implication: run this gate at every regression, not at sign-off. Six of the seven checks are computable from artefacts that already exist the day the coverage model is written — only check 2 needs a regression to have run — so a plan can be measured against its own sign-off criteria months before sign-off. The practical consequence is that c6_directed_tests_match fails in week three instead of week thirty, when twenty directed tests are still a staffing question rather than a schedule crisis, and the twenty exclusion lines that would otherwise close the report never get written.
17. The Review, Priced Against What It Prevents
The review costs a day of three people's time. Here is the arithmetic on what it buys, with each row's escape route named.
| Item | If the review skips it | What it costs later |
|---|---|---|
| both denominators | the project defines coverage as one of them | an 18-month argument about whether 93% is good, with no shared definition |
| the exclusion audit | 243 entries, none attacked | six of them wrong for this deployment, and no test will ever say so |
n_unclassified | reachable unhit bins look like exclusions | the design is untested in a region nobody has named |
| the stimulus budget | 20 bins discovered in the last week | 20 exclusion lines, because six engineer-weeks no longer fit |
| bound against covered | 143 properties pass by never being asked | a regression that reports 240 passes on a link that never trains |
| the units | one number is quoted for four questions | a plan optimised for the number that is easiest to move |
Row four is the one with a date on it. Section 8's arithmetic is computable in week three and its conclusion does not change: twenty bins, six engineer-weeks. The same conclusion reached in week thirty is not actionable, and the only remaining action is the one Section 14 prohibits.
18. What the Review Assumes
Six assumptions. Each is stated because a reader on a different design needs to know which of this chapter's numbers survive the journey.
| # | Assumption | If it is false |
|---|---|---|
| 1 | the four axes are the model | a fifth axis multiplies every count in Section 4 |
| 2 | the axes are independent | Section 8's rarest-bin probability is wrong, usually optimistic |
| 3 | the simulator runs 2 000 cycles per wall-clock second | Section 8's hours scale inversely, and nothing else moves |
| 4 | the traffic distribution is this deployment's | every probability in Section 8 is somebody else's network |
| 5 | Chapter 21.4 §20's 59.6% generalises | Section 10's 143 becomes a different number; the mechanism does not change |
| 6 | the exclusion rules are complete | the 243 is a floor, not a count |
Assumption 2 is the one that fails first and it fails in the direction that matters. The rarest bin — a jabber, above 1 518 octets, double-tagged — has a probability computed as a product of four marginals, and a jabber is correlated with large frames by definition. So the true probability is higher than 10⁻¹⁰ and the true simulation cost is lower than 23.24 simulator-years. How much lower is not knowable from marginals, which is why a real model samples a joint distribution from a capture and this one states the limitation instead.
Assumption 3 deserves a table because it is the assumption readers will most want to substitute.
| Simulator throughput | Frames per second | Farm-week frames | Bins needing a directed test |
|---|---|---|---|
| 500 cycles/s | 10.21 | 7.35 × 10⁸ | more than 20 |
| 2 000 cycles/s | 40.84 | 2.94 × 10⁹ | 20 |
| 8 000 cycles/s | 163.37 | 1.18 × 10¹⁰ | fewer than 20 |
| an emulator at 1 MHz | 20 421 | 1.47 × 10¹² | near zero |
Row four is the honest escape from this chapter's whole argument and it deserves saying plainly: an emulator changes the arithmetic by three orders of magnitude and most of Section 8's conclusions with it. What it does not change is Sections 4, 6 and 10 — the model's denominator, the exclusion file and the silent properties — because none of those is a throughput problem. An emulator closes twenty bins and leaves 243 unaudited exclusions exactly where they were.
Assumption 6 is the one that cannot be discharged from inside. Section 5's classifier knows four rules; a fifth rule would find more unreachable bins, and there is no procedure that says the list of rules is complete. So 243 is a lower bound on unreachability and 303 is an upper bound on reachability — which means Section 4's 93.399% is a pessimistic coverage figure and 51.832% is pessimistic by more. The review's honest form of this is one sentence: the reachable count is an upper bound, and here are the four rules that produced it.
19. The Cost, Accounted — in Simulation-Hours and Engineer-Weeks
Every chapter of this track has closed its accounting section with a BCE figure. This one closes with four numbers in four units and a deliberate refusal to add them.
| Quantity | Value | Unit |
|---|---|---|
| coverage model declared | 546 | bins |
| unreachable by construction | 243 — 44.51% | bins |
| reachable | 303 | bins |
| reached in one farm-week | 283 — 93.399% of reachable | bins |
| outstanding | 20 | bins |
| regression budget | 20 000 per week | simulation-hours |
| to close the outstanding 20 by random | 204 036 — 10.2 farm-weeks | simulation-hours |
| to close them with directed tests | 6 | engineer-weeks |
| properties bound | 240 | properties |
| silent on an untrained link | 143 — 59.6% | properties |
| silent and unexplained | target 0 | properties |
| exclusion entries | 243 | file lines |
| whose truth depends on a deployment | 6 | file lines |
And the one comparison the four units support, which is the calendar.
| Route | Calendar | What else stops |
|---|---|---|
| 20 directed tests | 6 engineer-weeks, in parallel with everything else | nothing |
| random closure | 10.2 weeks of 500 licences | every other regression |
| 20 exclusion lines | one afternoon | nothing — and nothing is verified |
Three routes to the same closed report. Two of them cost something and one of them costs nothing, and the one that costs nothing is the one that will be taken if the review does not produce the number 20 in week three.
20. Properties Worth Asserting, and One Worth Refusing
Fifty-four properties in six groups. They are properties about a verification plan rather than about a datapath, and the same rules apply: an antecedent that can be reached, a consequent that can be false, and a cover that proves the first.
Group A — the bin model, Section 3.
// 1. A hit bin is never also classified unreachable. If it is, one of
// the two inputs is lying and the exclusion file is the suspect.
p_hit_not_unreachable: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && was_hit) |-> (outcome == BIN_HIT));
// 2. Every unreachable outcome carries a reason. A reason is what a
// reviewer attacks; an outcome without one cannot be argued with.
p_unreachable_has_reason: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && outcome == BIN_UNREACHABLE) |-> (reason != UR_NONE));
// 3. And a reachable outcome carries none, so the two are exclusive.
p_reachable_no_reason: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && outcome != BIN_UNREACHABLE) |-> (reason == UR_NONE));
// 4. reason_is_soft is set exactly for the two deployment-dependent
// reasons. Section 6's six bins are found by this bit.
p_soft_iff_env: assert property (@(posedge clk) disable iff (!rst_n)
out_valid |-> (reason_is_soft ==
(reason == UR_ENVIRONMENT || reason == UR_CONFIG)));
// 5. A runt outside the smallest bucket is unreachable for a LENGTH
// reason, never for a direction one.
p_runt_reason: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && bin_key.ec == ERR_RUNT && bin_key.sz != SZ_LE64)
|-> (outcome == BIN_UNREACHABLE && reason == UR_LENGTH));
// 6. A transmit-side alignment error is unreachable for a DIRECTION
// reason at every size.
p_tx_align_reason: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && bin_key.ec == ERR_ALIGN && bin_key.dr == DIR_TX)
|-> (outcome == BIN_UNREACHABLE && reason == UR_DIRECTION));
// 7. The default, and the finding: not hit, no rule, no test, no
// sentence is UNCLASSIFIED and nothing else.
p_default_unclassified: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && !was_hit && !has_directed && !has_argument &&
reason == UR_NONE) |-> (outcome == BIN_UNCLASSIFIED));
// 8. Structural reasons never depend on a parameter.
p_structural_stable: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && (reason == UR_LENGTH || reason == UR_DIRECTION))
|-> !reason_is_soft);
c_each_outcome_seen: cover property (@(posedge clk)
out_valid && outcome == BIN_UNCLASSIFIED);Group B — the classifier, Section 5.
// 9. The three counts partition the declared model exactly.
p_counts_partition: assert property (@(posedge clk) disable iff (!rst_n)
sweep_done |-> (n_unreachable + n_reachable == n_declared));
// 10. And the declared count is the model's size, not a running total.
p_declared_is_model: assert property (@(posedge clk) disable iff (!rst_n)
sweep_done |-> (n_declared == 16'(N_BINS)));
// 11. The reachable bins partition into the three dispositions.
p_reachable_partition: assert property (@(posedge clk) disable iff (!rst_n)
sweep_done |-> (n_hit + n_directed + n_unclassified <= n_reachable));
// 12. Both fractions are produced, and neither is produced alone.
p_both_fractions: assert property (@(posedge clk) disable iff (!rst_n)
(sweep_done && n_declared != 0 && n_reachable != 0)
|-> (cov_vs_declared_ppm <= cov_vs_reachable_ppm));
// 13. The declared fraction can never exceed the reachable one, which
// is the arithmetic form of "excluding bins raises coverage".
p_declared_le_reachable: assert property (@(posedge clk) disable iff (!rst_n)
sweep_done |-> (cov_vs_declared_ppm <= cov_vs_reachable_ppm));
// 14. Soft exclusions are a subset of exclusions.
p_soft_subset: assert property (@(posedge clk) disable iff (!rst_n)
sweep_done |-> (n_soft_exclusions <= n_unreachable));
// 15. A sweep that starts finishes, in a bounded number of cycles.
p_sweep_terminates: assert property (@(posedge clk) disable iff (!rst_n)
sweep_start |-> ##[1:N_BINS+2] sweep_done);
c_sweep_completed: cover property (@(posedge clk) sweep_done);
c_unclassified_nonzero: cover property (@(posedge clk)
sweep_done && n_unclassified != 16'd0);Group C — the exclusion auditor, Section 7.
// 16. Entries with an argument never exceed entries.
p_argued_subset: assert property (@(posedge clk) disable iff (!rst_n)
(n_with_argument <= n_entries));
// 17. file_is_reviewed is exactly the equality, not a threshold.
p_reviewed_is_equality: assert property (@(posedge clk) disable iff (!rst_n)
file_is_reviewed == (n_entries == n_with_argument));
// 18. Soundness implies reviewedness, and never the converse.
p_sound_implies_reviewed: assert property (@(posedge clk) disable iff (!rst_n)
file_is_sound |-> file_is_reviewed);
// 19. A disagreement between the file and the model is recorded, and
// this is the ONLY place Section 6's silent error becomes visible.
p_disagreement_counted: assert property (@(posedge clk) disable iff (!rst_n)
(entry_valid && !model_unreachable)
|-> ##1 (n_model_disagrees == $past(n_model_disagrees) + 16'd1));
// 20. An entry the model calls soft is counted soft regardless of what
// the file claims, because the file is the thing under audit.
p_soft_from_model: assert property (@(posedge clk) disable iff (!rst_n)
(entry_valid && model_soft)
|-> ##1 (n_soft == $past(n_soft) + 16'd1));
// 21. A sound file has no disagreements, by construction.
p_sound_no_disagree: assert property (@(posedge clk) disable iff (!rst_n)
file_is_sound |-> (n_model_disagrees == 16'd0));
c_file_unsound: cover property (@(posedge clk)
n_entries != 16'd0 && !file_is_sound);
c_soft_present: cover property (@(posedge clk) n_soft != 16'd0);Group D — the stimulus budget, Section 9.
// 22. A bin reachable by random never also needs a directed test.
p_random_xor_directed: assert property (@(posedge clk) disable iff (!rst_n)
out_valid |-> (reachable_by_random != needs_directed));
// 23. A gap is exactly "needs one and has none".
p_gap_definition: assert property (@(posedge clk) disable iff (!rst_n)
out_valid |-> (gap == (needs_directed && !has_directed_test)));
// 24. Zero probability is never reachable by random, however long the
// run. This is the bin that a coverage model should not contain.
p_zero_p_never: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && $past(bin_p_e12) == 40'd0) |-> !reachable_by_random);
// 25. Frames needed falls as probability rises -- monotonicity, which
// is the cheapest guard against a scaling error in the divide.
p_frames_monotone: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && $past(bin_p_e12) > $past(bin_p_e12, 2) &&
$past(bin_p_e12, 2) != 40'd0)
|-> (frames_needed_e0 <= $past(frames_needed_e0)));
// 26. Weeks needed is at least one whenever frames needed is non-zero.
p_weeks_ceiling: assert property (@(posedge clk) disable iff (!rst_n)
(out_valid && frames_needed_e0 != 48'd0) |-> (weeks_needed >= 32'd1));
// 27. Gaps are a subset of the bins needing a directed test.
p_gaps_subset: assert property (@(posedge clk) disable iff (!rst_n)
(n_gaps <= n_needing_directed));
c_gap_seen: cover property (@(posedge clk) gap);Group E — the assertion reconciler, Section 11.
// 28. Covered properties never exceed bound ones.
p_covered_le_bound: assert property (@(posedge clk) disable iff (!rst_n)
(n_covered <= n_bound));
// 29. The three categories partition the bound suite.
p_suite_partition: assert property (@(posedge clk) disable iff (!rst_n)
(n_covered + n_silent == n_bound));
// 30. And silence partitions into explained and not.
p_silence_partition: assert property (@(posedge clk) disable iff (!rst_n)
(n_silent_explained + n_silent_unexplained == n_silent));
// 31. A property with a non-zero cover count is never counted silent.
p_covered_not_silent: assert property (@(posedge clk) disable iff (!rst_n)
(prop_valid && prop_bound && prop_cover_count != 32'd0)
|-> ##1 (n_silent == $past(n_silent)));
// 32. Reconciliation is about the unexplained count, never about zero
// silence -- a healthy suite has silent properties.
p_reconciled_definition: assert property (@(posedge clk) disable iff (!rst_n)
suite_is_reconciled == (n_silent_unexplained == 16'd0));
// 33. An honest report needs a non-empty denominator as well as a
// reconciled suite.
p_honest_needs_covered: assert property (@(posedge clk) disable iff (!rst_n)
report_is_honest |-> (n_covered != 16'd0));
// 34. Traffic-dependent properties are a subset of the bound suite.
p_traffic_subset: assert property (@(posedge clk) disable iff (!rst_n)
(n_traffic_dependent <= n_bound));
c_silent_but_reconciled: cover property (@(posedge clk)
n_silent != 16'd0 && suite_is_reconciled);Group F — effort, telemetry and the gate, Sections 13, 15 and 16.
// 35. The effort reconciler never emits a quantity combining units.
// Expressed as what it DOES emit: four independent outputs.
p_units_independent: assert property (@(posedge clk) disable iff (!rst_n)
reconcile |-> ##1 (u_bins_outstanding == $past(bins_reachable) -
$past(bins_hit)));
// 36. Engineer-weeks round up -- a part week is a whole week of a
// calendar, and rounding down understates every plan.
p_eng_weeks_ceiling: assert property (@(posedge clk) disable iff (!rst_n)
(reconcile && $past(bins_needing_directed) != 16'd0)
|-> ##1 (u_eng_weeks_to_close >= 16'd1));
// 37. The dimensionless ratios are bounded, which catches a divide
// whose operands were swapped.
p_ppm_bounded: assert property (@(posedge clk) disable iff (!rst_n)
(unreachable_share_ppm <= 20'd1000000) &&
(silent_share_ppm <= 20'd1000000));
// 38. Telemetry: the editing detector fires only when exclusions have
// actually been added.
p_editing_needs_exclusions: assert property (@(posedge clk) disable iff (!rst_n)
v_closed_by_editing |-> (c_exclusions_added != 16'd0));
// 39. An empty exclusion file is fully reviewed, not unreviewed.
p_empty_file_reviewed: assert property (@(posedge clk) disable iff (!rst_n)
(excl_entries == 16'd0) |-> (excl_reviewed_ppm == 20'd1000000));
// 40. The unargued flag is exactly the inequality.
p_unargued_flag: assert property (@(posedge clk) disable iff (!rst_n)
v_exclusion_unargued == (excl_with_argument < excl_entries));
// 41. Every conformance check is a comparison between two supplied
// quantities, so each one can be false. Expressed per check:
p_c2_can_fail: assert property (@(posedge clk) disable iff (!rst_n)
(evaluate && bins_unclassified != 16'd0) |-> ##1 !c2_model_classified);
// 42. Check 6 is a floor, not an equality: extra directed tests are
// never a failure.
p_c6_is_floor: assert property (@(posedge clk) disable iff (!rst_n)
(evaluate && directed_tests_present > bins_needing_directed)
|-> ##1 c6_directed_tests_match);
// 43. Conformance requires the gate itself to be non-trivial.
p_conformant_needs_gate: assert property (@(posedge clk) disable iff (!rst_n)
conformant |-> $past(gate_can_fail));
// 44. An empty plan is never conformant. This is the property that
// stops the gate from becoming Section 20's refused one.
p_empty_plan_fails: assert property (@(posedge clk) disable iff (!rst_n)
(evaluate && bins_declared == 16'd0) |-> ##1 !conformant);
// 45. The failure count matches the failing checks.
p_failure_count: assert property (@(posedge clk) disable iff (!rst_n)
(evaluate && c1_denominators_reported && c2_model_classified &&
c3_exclusions_argued && c4_exclusions_uncontradicted &&
c5_soft_exclusions_owned && c6_directed_tests_match &&
c7_suite_reconciled) |-> ##1 (n_failures == 3'd0));
// 46. Soft exclusions without owners fail check 5, always.
p_c5_needs_owners: assert property (@(posedge clk) disable iff (!rst_n)
(evaluate && excl_soft != 16'd0 && !excl_soft_owners_named)
|-> ##1 !c5_soft_exclusions_owned);
// 47. A contradicted exclusion fails check 4, always.
p_c4_contradiction: assert property (@(posedge clk) disable iff (!rst_n)
(evaluate && excl_model_disagrees != 16'd0)
|-> ##1 !c4_exclusions_uncontradicted);
// 48. And the gate is stable between evaluations.
p_gate_stable: assert property (@(posedge clk) disable iff (!rst_n)
!evaluate |-> ##1 (conformant == $past(conformant)));
// The covers that make the whole group mean something. Each one is a
// plan a project would really submit.
c_plan_with_unclassified: cover property (@(posedge clk)
evaluate && bins_unclassified != 16'd0);
c_plan_closed_by_editing: cover property (@(posedge clk)
evaluate && directed_tests_present < bins_needing_directed);
c_plan_conformant: cover property (@(posedge clk) evaluate && conformant);
c_empty_plan_submitted: cover property (@(posedge clk)
evaluate && bins_declared == 16'd0);
c_soft_unowned: cover property (@(posedge clk)
evaluate && excl_soft != 16'd0 && !excl_soft_owners_named);
c_both_fractions_reported: cover property (@(posedge clk)
evaluate && both_denominators_reported);21. Verification Scenarios
Fifty-eight scenarios in six groups, plus one directed test random stimulus will not produce.
Group 1 — the bin model (10).
| # | Scenario | Expect |
|---|---|---|
| 1 | a hit bin | BIN_HIT; reason = UR_NONE |
| 2 | a runt in SZ_65_127 | BIN_UNREACHABLE; UR_LENGTH; not soft |
| 3 | a jabber in SZ_512_1023 | BIN_UNREACHABLE; UR_LENGTH |
| 4 | a jabber in SZ_GE1519 | reachable — the one bucket it lives in |
| 5 | a transmit-side symbol error | UR_DIRECTION |
| 6 | a transmit-side alignment error | UR_DIRECTION — a transmitter emits whole octets |
| 7 | an MTU mismatch in SZ_256_511, MTU_BUCKET = 5 | UR_ENVIRONMENT; reason_is_soft high |
| 8 | the same bin, MTU_BUCKET = 4 | reachable — the six bins of Section 6 |
| 9 | not hit, no rule, no test, no argument | BIN_UNCLASSIFIED — the finding |
| 10 | the same with an argument | BIN_UNREACHABLE; UR_CONFIG; soft |
Group 2 — the classifier (10).
| # | Scenario | Expect |
|---|---|---|
| 11 | a full sweep | n_declared = 546 |
| 12 | the same | n_unreachable = 243; n_reachable = 303 |
| 13 | MTU_BUCKET = 4 | 237 and 309 |
| 14 | MTU_BUCKET = 6 | 249 and 297 |
| 15 | 283 hits, MTU_BUCKET = 5 | 518 315 ppm and 933 993 ppm |
| 16 | 283 hits, MTU_BUCKET = 4 | 915 857 ppm against reachable |
| 17 | 303 hits | 1 000 000 ppm against reachable, 554 945 against declared |
| 18 | zero reachable bins | cov_vs_reachable_ppm = 0 — the guarded divide |
| 19 | a sweep interrupted | sweep_done low; counts hold |
| 20 | n_soft_exclusions | 6 at MTU_BUCKET = 5 |
Group 3 — the exclusion auditor (10).
| # | Scenario | Expect |
|---|---|---|
| 21 | 243 entries, 243 arguments | file_is_reviewed high |
| 22 | 243 entries, 200 arguments | low; v_exclusion_unargued |
| 23 | every entry agreeing with the model | file_is_sound high |
| 24 | one entry the model calls reachable | n_model_disagrees = 1; file_is_sound LOW |
| 25 | the same entry's effect on coverage | none — an excluded bin is not sampled |
| 26 | an entry claiming UR_LENGTH where the model says UR_DIRECTION | n_reason_mismatch = 1; both still exclusions |
| 27 | six entries the model calls soft | n_soft = 6 |
| 28 | an empty exclusion file | reviewed and sound; excl_reviewed_ppm = 1 000 000 |
| 29 | a file of 20 entries added in the final week | v_closed_by_editing if tests did not also grow |
| 30 | the same with 20 directed tests added | flag clear |
Group 4 — the stimulus budget (10).
| # | Scenario | Expect |
|---|---|---|
| 31 | p = 10⁻¹⁰, one farm-week | not reachable by random; needs directed |
| 32 | the same, no directed test | gap high; n_gaps increments |
| 33 | the same with a test | gap low |
| 34 | p = 10⁻⁶ | 3 × 10⁶ frames — inside one farm-week |
| 35 | p = 0 | never reachable, at any budget |
| 36 | the rarest bin's frames at 95% | 2.996 × 10¹⁰ |
| 37 | the same in simulation | 203 764 hours — 23.24 simulator-years |
| 38 | the same on the wire at 100 Gb/s | 1 222.6 s — twenty minutes |
| 39 | CONF_MULT = 1 instead of 3 | a third of the rarest bins planned as reachable and missed |
| 40 | the whole model swept | 20 bins needing a directed test |
Group 5 — the assertion reconciler (10).
| # | Scenario | Expect |
|---|---|---|
| 41 | 240 bound, 240 covered | n_silent = 0; reconciled |
| 42 | 240 bound, 97 covered | n_silent = 143 — the untrained link |
| 43 | the same, all 143 explained | suite_is_reconciled HIGH — and correctly so |
| 44 | the same, 12 unexplained | low; v_silent_unexplained |
| 45 | a property with pass 0 and fail 0 | silent, not failing — the two zeros |
| 46 | a property with pass 0 and fail 40 | covered, and failing — a real bug |
| 47 | a property excluded by generate | not bound; outside every count |
| 48 | 143 of 240 traffic-dependent | traffic_dep_ppm = 595 833 |
| 49 | the regression on a healthy link | 240 covered; the 143 fire normally |
| 50 | n_covered = 0 on a completed run | report_is_honest LOW — nothing was asked |
Group 6 — effort, telemetry and the gate (8).
| # | Scenario | Expect |
|---|---|---|
| 51 | 20 bins, 1.5 engineer-days each | 6 engineer-weeks |
| 52 | 3 × 10¹⁰ frames at 40.84 f/s | 204 036 simulation-hours; 10.2 farm-weeks |
| 53 | the two compared | directed_is_cheaper high |
| 54 | 243 of 546 unreachable | unreachable_share_ppm = 445 054 |
| 55 | an empty plan submitted to the gate | gate_can_fail LOW; conformant LOW |
| 56 | 25 directed tests where 20 are needed | check 6 passes — a floor, not an equality |
| 57 | six soft exclusions, no owners | check 5 fails |
| 58 | all seven checks clear on a non-empty plan | conformant high |
22. Debugging a Coverage Report That Closed
Six symptoms, and five of them arrive after the coverage report said 100%.
| Symptom | First question | Where to look |
|---|---|---|
| a field escape in a frame shape the plan lists as covered | was that bin excluded? | Section 6 — an excluded bin is not sampled, in either direction |
| a directed test that has never hit | is its bin actually reachable? | Section 7 — the loud half of the asymmetry; the model says no |
| coverage that jumped in the final week without new tests | how many exclusions were added? | Section 15's v_closed_by_editing |
| a regression reporting all passes on a broken link | what was the cover count? | Section 10 — 143 of 240 can pass by never being asked |
| two teams quoting different coverage for one run | which denominator? | Section 4 — 51.832% and 93.399% |
| a bin nobody can explain and nobody excluded | was it ever classified? | Section 5's n_unclassified — the default outcome |
Row one has a procedure and it is the one worth memorising. Take the escaping frame, compute its four axis values, form the bin index, and grep the exclusion file for it. If it is there, read the argument; if there is no argument, the investigation is over and the answer is that nobody ever decided this frame could not happen.
Row three's procedure is shorter. git log the exclusion file. Every line added in the last month is a bin somebody chose not to test, and the review's question is whether a sentence was added with it.
23. Misconceptions
Six, in the wrong-model / what-it-costs / corrected-model form this track has used since Chapter 1.1.
Misconception 1 — "Coverage is a percentage."
Wrong model. One number, between zero and one hundred, that summarises how thoroughly a design was tested.
What it costs. Section 4's arithmetic: the same 283 hit bins are 51.832% or 93.399% depending on a setting in a configuration file. The cost is eighteen months of a project in which two teams quote different figures for the same run and neither is wrong, and the schedule gets managed against whichever one is easier to move — which is always the one with the larger denominator, because exclusions raise it for free.
Corrected model. Coverage is a fraction, and a fraction has two numbers. Report hit, reachable and declared as integers; anybody who wants a percentage can divide. A report carrying three integers cannot be quoted misleadingly; one carrying a percentage almost always is, because the denominator travels in a config file and the number travels in an e-mail.
Misconception 2 — "An exclusion is a fact about the design."
Wrong model. A bin gets excluded because the design cannot produce it, and that is a property of the RTL.
What it costs. Six of this design's 243 exclusions are properties of somebody else's network. Section 6: the MTU-mismatch rule's threshold is the peer's MTU, so the exclusion list is 237 entries at one deployment and 249 at another — and shipping one file for every deployment hard-codes a customer's configuration into the definition of "verified".
Corrected model. Exclusions have two kinds, exactly like Chapter 26.1 §12's thresholds. Structural exclusions are theorems about the frame definition and a reviewer can check them in a meeting. Environmental exclusions are claims about a deployment, they need an owner, and the classifier must be re-run when the owner answers. Section 3's reason_is_soft is one bit and it separates the two.
Misconception 3 — "A wrong exclusion will show up eventually."
Wrong model. If we exclude something we should not have, a test will fail or a coverage hole will appear.
What it costs. The asymmetry of Section 6, and it is total. A coverage flow's response to an exclusion is to stop instrumenting the bin — so a wrongly excluded bin produces no pass, no fail, no warning and no anomalous percentage. Section 21's directed test is exactly this: two files, seven identical observables, and the difference visible in nothing the flow prints.
Corrected model. The only thing that catches a wrong exclusion is an independent model of reachability, and Section 5 is 60 lines of it. The opposite error — leaving an impossible bin in — is caught free of charge by a directed test that never hits, which is why projects have a strong intuition that exclusions are self-correcting. They are self-correcting in exactly one direction, and it is not the dangerous one.
Misconception 4 — "More simulation will close the last few bins."
Wrong model. The regression is at 93%; another week or two of farm time will finish it.
What it costs. Section 8's arithmetic: closing the last twenty bins by random stimulus needs 204 036 simulation-hours — 10.2 weeks of all 500 licences, running nothing else. And the reason is not the design: it is that the simulator runs 600 000 times slower than the wire, so twenty minutes of a real link is 23.24 simulator-years for the rarest bin. Another week of farm time closes approximately none of them.
Corrected model. The tail of a coverage model is a directed-test problem, and it is knowable in week three. Probability per bin, frames per farm-week, the count that does not fit — twenty bins, six engineer-weeks. A project that computes it early spends six engineer-weeks; one that computes it late spends an afternoon on exclusions, because by then that is all that fits.
Misconception 5 — "All 240 assertions passed, so the design is behaving."
Wrong model. A green regression means every bound property held.
What it costs. Chapter 21.4 §20's measurement: 332 of 557 published properties — 59.6% — have antecedents that need traffic. On a link that never trains, 143 of a 240-property suite stop evaluating and 97 evaluate; the report says 240 passed. The cost is a week of debugging a "working" design, because the instrument that would have said otherwise reported success.
Corrected model. A pass count means nothing without a cover count. Chapter 25.6 §20's fourth property is one line and every tool already computes both halves — but assert the classified form rather than the equality, because a healthy suite legitimately has silent properties and a check that fires every night gets deleted.
Misconception 6 — "Verification cost and area cost can be traded off."
Wrong model. Spend more die on a simpler block and save verification effort; the two are a single budget.
What it costs. They are not correlated, even in sign. Chapter 23.5 §6's programmable array is 78.6 MAC datapaths and harder to verify, because its behaviour is chosen by software the plan never sees. Chapter 19.4's CRC engine is large and among the easiest blocks in the MAC to verify exhaustively, because its specification is a polynomial. Two examples in one track, opposite signs.
Corrected model. Four units, no exchange rate, and one comparison — the calendar. Section 12: bins, simulation-hours, engineer-weeks and silent properties are four answers to four questions, and the only legitimate comparison between a simulation-hour and an engineer-week is which one occupies more of the schedule.
24. Interview Questions
Six, with what a strong answer contains.
1. What is your coverage?
A strong answer refuses the single number and gives three integers. "546 declared, 303 reachable, 283 hit" — and then volunteers the 243, because the follow-up question is always about the denominator and the candidate who raises it first has demonstrated they know it exists. A weak answer is a percentage. A weak answer that sounds strong is a percentage with a confidence adjective attached.
2. You have 243 excluded bins. How do you know they are right?
The strong answer is that you do not, and here is the check. An independent reachability model, applied to the file, producing the count of disagreements — Section 7's n_model_disagrees. The stronger half of the answer is the asymmetry: a wrongly excluded bin is silent and a wrongly included one is loud, so the check is only needed in one direction and that is the direction nothing else covers.
3. Your regression reports 240 of 240 assertions passing. What else do you need to know?
The cover counts. And the number to have ready is Chapter 21.4 §20's 59.6% — the measured share of published properties whose antecedent needs traffic. On a link that never trains, 143 of those 240 stop evaluating and the report is identical. A candidate who has this number has read a corpus rather than an opinion.
4. How long would it take to close the last twenty coverage bins?
Two answers, both of them arithmetic. By random stimulus: the rarest bin needs 3 × 10¹⁰ frames, which at 40.84 frames per simulated second is 204 036 hours — 10.2 weeks of a 500-licence farm. By directed test: twenty tests, 1.5 engineer-days each, six engineer-weeks. The strong part of the answer is that these are different units that meet only in the schedule, and the third route — twenty lines in the exclusion file — takes an afternoon and verifies nothing.
5. Why is verification effort not measured in gate-equivalents?
Because area is a property of the object and effort is a property of the process. Area scales with units shipped and is fixed at tape-out; effort is spent once and partly recovered on the next design. And they are not correlated in sign — Section 12's two examples run in opposite directions in the same track. A quantity that can move either way against another is not convertible into it.
6. Write the property that catches a wrong exclusion.
The trap is that the natural answer is a counting identity, and Section 20 is about why it fails. n_hit + n_directed + n_unclassified + n_unreachable == n_declared is falsifiable, useful and invariant under exactly the error — swap one reachable bin for one unreachable one and every term is unchanged. The property that works is a membership check: no bin the rules call reachable may appear in the file. A strong answer names the distinction between a cardinality and a set, and keeps the counting identity as well, because it catches a different bug.
25. Questions and Answers
26. What's Next
This chapter took Chapter 26.1's grammar — every item a number, every number a deriving chapter — and applied it where the numbers are fractions whose denominators the team chooses.
Three results are worth carrying forward.
| Result | Value | Why it travels |
|---|---|---|
| unreachable share of a cross-product model | 44.51% | a property of how coverage models are written |
| share of properties needing traffic | 59.6% | measured across three modules in Chapter 21.4 §20 |
| simulator against wire | 600 000× | it is the clock ratio, and it sets every stimulus argument |
What is left of Module 26.
| Next | What it adds |
|---|---|
| Chapter 26.3 — Performance and Debug Review | the seven environmental thresholds Chapter 26.1 §12 deferred, the percentile that 912.88 ns is missing, and what observability the measurement costs |
| Chapter 26.4 — How Ethernet Is Actually Probed | the whole of it as an interview subject, and what the track's 124 rejected-property classes have in common |
And the sentence this chapter ends on is the one Chapter 25.6 §20 and Chapter 26.1 §21 both reached, now with a second edge on it. A check is worth exactly the set of observations that would have refuted it — and Section 20 adds that a check over a collection's size has a large refutation set which excludes every swap inside the collection. So the set can be big and still have the wrong shape, and the only way to know is to ask what the requirement was about: which things, or how many.
Continue learning
Related tutorials
- Related topic
Assertions
This track has written 1 848 named properties across 89 chapters and reused 47 of the names — while six structural families cover 93% of them.
- Related topic
Layering as an Engineering Contract
A layer boundary costs a register stage, a translation and a forgone optimisation, continuously. It buys a re-verification count of one instead of many — and because the cost is visible and the benefit is not, boundaries erode one reasonable local decision at a time.
- Related topic
A Parallel CRC-32 Engine in RTL
The wide next-state function is linear, so it is generated rather than derived — and the cost per bit falls as the datapath widens. What is designed is the final partial word, where eight sub-networks exist and real traffic reaches two of them.
- Related topic
Packet Generation
A weight is a per-frame marginal, so it reaches a frame's own properties and nothing else — and 48 of the parser's 64 alignment offsets are unreachable from the transmit side at any weight.
Standards & specifications
- Governing standard
- IEEE Std 802.3 (Ethernet)(opens IEEE in a new tab)
Defines the Ethernet MAC, the media-independent interfaces and the physical-layer sublayers, including framing, access control, auto-negotiation and per-rate PHY specifications. VLAN tagging, priority and time-sensitive shaping are defined by IEEE 802.1, not by 802.3.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the Ethernet curriculum.
