Skip to content

UVM

Coverpoints

The covergroup's contents — the coverpoint: how it measures a single variable, the values it samples, how those values map to bins, the art of choosing a meaningful partition (boundaries, ranges, special cases) rather than raw range, and how a set of coverpoints encodes the features the plan says to exercise.

Functional Coverage · Module 19 · Page 19.3

The Engineering Problem

The covergroup (Module 19.2) is the container — declared, instantiated, and sampled. But a container measures nothing on its own; what it holds — the coverpoints — are where the actual measurement happens. A coverpoint targets a single variable (or expression) and records which of its values were seen — it's the atom of functional coverage, the one feature a covergroup tracks. And here is where the intent of coverage lives: a coverpoint's meaning comes entirely from how its value space is partitioned into bins. The same variable can be measured uselessly (auto-bins on a 32-bit address → billions of meaningless buckets that never close) or meaningfully (a few functionally-distinct bins — aligned/unaligned, boundaries, ranges). Worse, a coarse partition can read 100% while the case that matters was never separately exercisedfalse confidence at the coverpoint level. The problem this chapter solves is the coverpoint: how it measures one variable, how values map to bins, and the art of choosing the meaningful partition so the measurement means something.

A coverpoint measures a single variable or expression, recording which of its values were seen by partitioning its value space into bins — a bin is hit when the variable takes a value in it, and the coverpoint's coverage is the fraction of its bins hit. It is the atom of coverage: one coverpoint = one feature (e.g. coverpoint t.op measures which operations occurred). Each sample of the covergroup captures every coverpoint's current value and records it into the matching bin. The art is the partition: bins should capture the functionally-distinct cases — boundaries (min, max), meaningful ranges (small/large), special values (aligned/unaligned, error/ok) — not every raw value (a 32-bit field has billions, and auto-bins over them are meaningless and never close). Coverpoints can target expressions (t.addr[1:0] for alignment) and be guarded (iff — sample only when valid). A set of coverpoints in a covergroup is the coverage plan's features, encoded. Critically, a coverpoint is only as meaningful as its bins — a too-coarse partition reaches 100% trivially while missing the corner, the false confidence of 19.1 at the coverpoint granularity. This chapter is the coverpoint: one-variable measurement, value-to-bin mapping, the art of the partition, and how coverpoints encode the plan's features.

What is a coverpoint — how does it measure a single variable, how do that variable's values map to bins, what makes a partition meaningful (rather than raw range or too coarse), and how does a set of coverpoints encode the features the plan says to exercise?

Motivation — why the coverpoint's partition is where the intent lives

The covergroup is plumbing; the coverpoints are where the verification intent is expressed. The reasons the partition matters:

  • The coverpoint is the unit of measurement. The covergroup samples, but each coverpoint is what records a valuewhich of its variable's values were seen. Coverage is computed per coverpoint (fraction of bins hit), then aggregated.
  • The bins define what "covered" means. A coverpoint's coverage is entirely determined by its bins — so how you partition the variable defines what it means to have "covered" it. The partition is the specification of what to exercise.
  • Raw range is meaningless; meaningful partition is the art. A wide variable (32-bit) has too many raw values to bin one-per-value — the coverage would never close and would measure nothing functional. The art is choosing the functionally-distinct cases — the boundaries, ranges, and special values that matter.
  • Too-coarse bins give false 100%. The opposite error: one bin for a whole range reaches 100% on the first sample — trivially — while never checking that the corner (the max, the special value) was exercised. 100% on a coarse coverpoint means nothing.
  • Coverpoints encode the plan. The coverage plan (19.1) lists features to exercise; each becomes a coverpoint (or a cross). So the set of coverpoints is the plan, made executable — reading the coverpoints tells you what the verifier decided matters.

The motivation, in one line: the covergroup samples, but the coverpoint is where the measurement and the intent live — its partition into bins defines what "covered" means, so the art of choosing the functionally-distinct cases (boundaries, ranges, special values — not raw range, not one coarse bin) is what makes a coverpoint meaningful, and a set of well-partitioned coverpoints is the coverage plan, encoded and executable.

Mental Model

Hold a coverpoint as one question on the covergroup's tally sheet, with bins as its answer buckets — and the art is asking the right question with the right buckets:

A coverpoint is one question on the covergroup's tally sheet — "which values of this one variable did we see?" — and its bins are the answer buckets. Each sample drops a tally into the bucket matching the variable's current value. The coverpoint is covered when every bucket has at least one tally, and the whole point rests on choosing buckets that capture the functionally-distinct cases: too many buckets (one per raw value) and you never fill them all; too few (one bucket for everything) and you fill it trivially while learning nothing. Recall the covergroup as a structured tally sheet (Module 19.2). A coverpoint is one columnone question — on that sheet, say "which operation?" or "which size?". Under the question are answer buckets (the bins): for "which operation?", the buckets read, write, RMW; for "which size?", perhaps small, large, boundary. Each time the covergroup is sampled, this coverpoint looks at its variable's current value and drops a tally into the bucket that value falls in. The coverpoint is "covered" when every bucket has at least one tally — every answer was seen at least once. Now, the entire usefulness rests on choosing the buckets well. If you make too many buckets — one per raw value of a 32-bit field — you'd need billions of tallies to fill them all, so the column never fills and the measurement is useless. If you make too fewone bucket labeled "any value" — then the very first tally fills it, the column reads "covered" immediately, and you've learned nothing (you never checked whether the max value, or the special case, ever occurred). The art is the middle: buckets that carve the variable at its functionally-meaningful seams — the boundaries (min, max), the ranges that behave differently (small vs large), the special values (aligned vs unaligned, error vs ok). Those buckets, filled, mean something: every functionally-distinct case was exercised. So a coverpoint is one question with well-chosen answer buckets — and the quality of the coverpoint is the quality of the buckets.

So a coverpoint is one question on the tally sheet, with bins as answer buckets: each sample drops a tally in the bucket matching the value, and the coverpoint is covered when every bucket is filled. The art is choosing buckets at the functionally-distinct seamsnot one-per-raw-value (never fills, useless), not one-bucket-for-all (fills trivially, meaningless), but the boundaries, ranges, and special values that matter. A coverpoint is only as good as its buckets: ask the right question, and partition the answer into the cases that are functionally distinct.

Visual Explanation — a coverpoint partitions one variable into bins

The defining picture is the partition: a coverpoint takes one variable and splits its value space into bins, each hit when the value lands in it.

A coverpoint maps one variable's value into one of its binssampled value1..45..63== 64Variable: sizeone variable measuredcoverpoint cp_sizepartition into binsbin smallvalues 1..4bin largevalues 5..63bin max (boundary)value == 6412
Figure 1 — a coverpoint partitions one variable's value space into bins. The coverpoint targets a single variable (here, size). Its value space is partitioned into bins — small for 1 to 4, large for 5 to 64, and a boundary bin for the maximum, 64. When the covergroup is sampled, the coverpoint maps the variable's current value to the bin it falls in and marks that bin hit. The coverpoint is covered when all its bins are hit. The partition is the design choice: it defines which distinctions the coverpoint measures.

The figure shows a coverpoint as a partition. The coverpoint (cp_size, the brand-colored center) targets a single variable (size) and splits its value space into bins: small (values 1–4), large (values 5–63), and a boundary bin (max, value == 64). When the covergroup is sampled, the coverpoint maps the variable's current value to the bin it falls in and marks that bin hit: a size of 3 hits small, a size of 30 hits large, a size of 64 hits max. The coverpoint is covered when all its bins are hit. The crucial reading is that the partition is a design choicehere, the verifier decided that size has three functionally-distinct cases worth tracking: the small range, the large range, and the boundary (the maximum, the most likely to expose an off-by-one or overflow bug — hence the warning-colored bin, the corner that matters). A different partition (e.g. one bin for all of 1–64, or 64 bins one-per-value) would measure something differentcoarser (trivially 100%) or finer (never closes). The boundary bin (max == 64, separated from the large range) is the key design decision: by carving out the maximum into its own bin, the coverpoint requires that size == 64 be exercised specifically before it reads 100% — otherwise a large bin covering 5–64 would read hit on size == 7 without ever testing the boundary. The diagram is the anatomy of measurement: one variablea chosen partitionbins hit by the values that land in themcovered when all bins are hit. The partitionsmall / large / boundaryis the coverpoint's specification of what to exercise. A coverpoint is a variable plus a partition; the partition is where the intent lives.

RTL / Simulation Perspective — declaring coverpoints and their bins

In code, a coverpoint is one line inside a covergroupcoverpoint <variable> { <bins> } — and its bins are what give it meaning. The example shows coverpoints with auto bins, explicit bins, expression targets, and a guard.

coverpoints inside a covergroup: the variable, the bins, expressions, and guards
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup txn_cg with function sample(bus_txn t);
 
  // simplest: AUTO bins — one bin per enum value (good for SMALL enumerations)
  cp_op: coverpoint t.op { bins ops[] = {READ, WRITE, RMW}; }   // 3 meaningful bins
 
  // EXPLICIT bins — partition a numeric range into FUNCTIONALLY-DISTINCT cases (the art)
  cp_size: coverpoint t.size {
    bins small    = {[1:4]};      // small range
    bins large    = {[5:63]};     // large range
    bins max_size = {64};         // BOUNDARY carved out → must be hit specifically (Figure 1)
  }
 
  // EXPRESSION coverpoint — measure a DERIVED feature, not a raw field
  cp_align: coverpoint t.addr[1:0] { bins aligned = {0}; bins unaligned = {[1:3]}; }
 
  // GUARDED coverpoint — sample only when the value is MEANINGFUL (iff condition)
  cp_resp: coverpoint t.resp iff (t.valid) { bins ok = {OKAY}; bins err = {SLVERR, DECERR}; }
 
  // ✗ ANTI-PATTERN: auto bins on a WIDE variable → billions of meaningless bins, never closes
  // cp_addr: coverpoint t.addr;   // 32-bit → auto_bin_max buckets of nonsense — DON'T
 
endgroup
// each coverpoint measures ONE variable; its BINS define what "covered" means for that feature

The code shows coverpoints as one-variable measurements, defined by their bins. Auto bins (cp_op with bins ops[] = {READ, WRITE, RMW}) — one bin per value, good for small enumerations (3 operations → 3 meaningful bins). Explicit bins (cp_size) — partition a numeric range into functionally-distinct cases: small (1–4), large (5–63), and max_size (64, the boundary carved out so it must be hit specifically, per Figure 1) — this is the art. Expression coverpoint (cp_align: coverpoint t.addr[1:0]) — measures a derived feature (the low two address bitsalignment), not a raw field — aligned (0) vs unaligned (1–3). Guarded coverpoint (cp_resp ... iff (t.valid)) — samples only when the value is meaningful (the response is valid), avoiding recording garbage when resp is don't-care. The anti-pattern (commented cp_addr: coverpoint t.addr on a 32-bit field) — auto bins over a wide variable → billions of meaningless bins (or auto_bin_max buckets of nonsense), never closesdon't. The shape to carry: a coverpoint is one variable (coverpoint t.<field> or an expression), and its binsauto for small enums, explicit for ranges and boundaries, guarded with iff for conditional validity — define what "covered" means. The bins are the meaning: cp_size with the boundary carved out measures something different (and more) than cp_size with one coarse bin. The expression and guard features let coverpoints measure derived and conditional features — exactly the functionally-distinct cases the plan names. Each coverpoint measures one variable; its bins are where you encode what matters.

Verification Perspective — choosing the meaningful partition

The central skill of coverpoint design is choosing the partition — the boundary between a meaningful measurement and a useless one. Seeing the three partition regimes shows the art.

Three partition regimes: too fine (never closes), too coarse (trivial 100%), meaningfulone bin per rawvalueone bin for allbins at the seamsnever closestrivial 100%100% means somethingWide variablee.g. 32-bit size/addrToo fine (auto-bins)billions of binsToo coarse (one bin)any value hitsMeaningful partitionbins at the seamsMeasures nothingnever closesMeasures nothingfalse 100%Measures intentreal completeness12
Figure 2 — three ways to partition a variable, only one meaningful. Too fine: auto-bins on a wide 32-bit variable create billions of bins (one per raw value) — the coverpoint never closes and measures nothing functional. Too coarse: one bin for the entire range reaches 100% on the first sample — trivially covered, learning nothing. Meaningful: a partition at the functionally-distinct seams (boundaries, ranges, special values) — covered only when each distinct case is exercised, so 100% actually means something. The art is the middle partition.

The figure shows the three partition regimes. Too fine (auto-bins on a wide 32-bit variable) → billions of bins (one per raw value) → the coverpoint never closes and measures nothing functional (you'd need to hit every value). Too coarse (one bin for the entire range) → 100% on the first sample → trivially covered, learning nothing (any value hits the one bin). Meaningful (a partition at the functionally-distinct seamsboundaries, ranges, special values) → covered only when each distinct case is exercised, so 100% actually means something. The verification insight is that the art is the middle partition — and both extremes fail in opposite ways that are both useless. The too-fine failure is obvious (coverage stuck near 0%, never closes); the too-coarse failure is insidious (coverage reads 100%, looks done, but means nothing — the false confidence of 19.1, now at the coverpoint level). The brand-colored meaningful partition is the goal: bins that carve the variable at the seams where behavior changes — the boundary (min/max, where off-by-one bugs live), the ranges that exercise different logic (small vs large transfers), the special values (aligned/unaligned, zero/nonzero, error/ok). When those bins are all hit, 100% means every functionally-distinct case was exercisedreal completeness. The skill is asking: "what values of this variable behave differently in the design?" — and putting a bin at each. The diagram is the partition decision: too fine (never closes, measures nothing) and too coarse (trivial 100%, measures nothing) bracket the meaningful middle (bins at the seams, 100% means intent) — and coverpoint design is finding that middle. Partition where the design's behavior changes, not by raw value count.

Runtime / Execution Flow — a set of coverpoints encodes the plan

At run time, a covergroup's set of coverpoints together measure the transaction's features — each coverpoint one feature the plan names. The flow shows how the plan becomes coverpoints.

From coverage plan to a set of coverpoints encoding each featurecoverage plan lists features → each feature becomes one coverpoint with meaningful bins → the set of coverpoints is the plan, executablecoverage plan lists features → each feature becomes one coverpoint with meaningful bins → the set of coverpoints is the plan, executable1Plan lists the featuresfrom the spec: every operation, each size class and boundary, eachalignment, each response — the scenarios to exercise (Module 19.1).2Each feature → one coverpointcp_op, cp_size, cp_align, cp_resp — each measuring one variablewith bins at its functionally-distinct cases.3Sampling records them togetherone cg.sample(t) captures every coverpoint's value at once — thetransaction's features, all measured.4The set is the plan, executablereading the coverpoints tells you what the verifier decidedmatters; their coverage is the plan's completeness.
Figure 3 — a set of coverpoints encodes the coverage plan's features. The coverage plan lists the features to exercise: every operation, both size classes including the boundary, both alignments, both response types. Each feature becomes one coverpoint with bins at its functionally-distinct cases: cp_op, cp_size, cp_align, cp_resp. Sampling the covergroup records all of them at once. The set of coverpoints is the plan made executable — reading them tells you exactly what the verifier decided matters.

The flow shows how a set of coverpoints encodes the plan. Plan (step 1): the coverage plan lists the featuresevery operation, each size class and the boundary, each alignment, each response type — the scenarios to exercise (Module 19.1). Map (step 2): each feature becomes one coverpointcp_op, cp_size, cp_align, cp_resp — each measuring one variable with bins at its functionally-distinct cases. Sample (step 3): one cg.sample(t) captures every coverpoint's value at once — the transaction's features, all measured together. Read (step 4): the set of coverpoints is the plan, made executablereading them tells you what the verifier decided matters, and their coverage is the plan's completeness. The runtime insight is that coverpoints are the executable form of the coverage plan: the plan is a list of features to exercise, and each feature is realized as one coverpoint with a meaningful partition. So building coverage is translating the plan into coverpoints — and reading the coverpoints (in code or in the coverage report) reconstructs the plan: "these are the features this team decided were worth measuring." This is why coverpoint design is a specification activity, not just coding: choosing which variables to measure and how to partition them is deciding what "thoroughly verified" means for this design. The set sampled together (one sample() records all coverpoints) reflects that they all describe the same transaction — op, size, align, resp are facets of one observed event. The flow is the plan-to-coverpoints translation: plan lists features → each becomes a coverpoint with meaningful bins → sampled together → the set is the executable plan. The coverpoints are where the plan stops being a document and becomes a measurement. Read a covergroup's coverpoints and you read the team's definition of "enough."

Waveform Perspective — values mapping to bins over time

A coverpoint is visible as values mapping to bins over successive samples: as the variable takes values, the matching bins get hit — and a bin never reached is a gap. The waveform shows range bins filling and a boundary bin left empty.

A coverpoint's bins fill as the variable takes values; an unreached bin is a gap

12 cycles
A coverpoint's bins fill as the variable takes values; an unreached bin is a gapsize=3 sampled → maps to bin small (1..4) → b_small hitsize=3 sampled → maps …size=30 → bin large (5..63) → b_large hitsize=30 → bin large (5…size=7 → bin large again (any value in range hits it)size=7 → bin large aga…b_max (boundary, 64) NEVER hit → coverage GAP — coverpoint not coveredb_max (boundary, 64) N…clksize--3--30--7------------sampleb_smallb_largeb_maxt0t1t2t3t4t5t6t7t8t9t10t11
Figure 4 — values mapping to bins over samples. The size variable takes values 3, 30, 7 on successive sampled transactions. The coverpoint cp_size maps each into its bin: 3 hits small (1 to 4), 30 and 7 hit large (5 to 63). The bins small and large fill — but max_size (the boundary, value 64) is never reached, so it stays empty: a coverage gap. The coverpoint is not covered until the boundary is exercised. Note 7 and 30 both hit large — a range bin is hit by any value in its range, which is why carving the boundary into its own bin matters.

The waveform shows values mapping to bins over samples. The size variable takes values 3, 30, 7 on successive sampled transactions. The coverpoint cp_size maps each into its bin: size=3 hits small (1–4), size=30 and size=7 both hit large (5–63). The bins small and large fill — but max_size (the boundary, value 64) is never reached, so it stays empty: a coverage gap. The crucial reading is two-fold. First, each sampled value maps to exactly one bin — the coverpoint records size=3 → small, size=30 → large — demonstrating the value-to-bin mapping that is the coverpoint's operation. Second, size=7 and size=30 both hit large — a range bin is hit by any value in its range, so hitting large once covers it regardless of which large value occurred. This is exactly why carving the boundary into its own bin matters: if max_size were folded into large (a bin covering 5–64), then size=7 would read large hit and the coverpoint could reach 100% without ever exercising size=64 — the boundary bug would escape (the false 100% of Figure 2). By separating max_size, the coverpoint stays uncovered (the gap is visible, b_max empty) until size=64 is specifically exercised. The picture to carry is that a coverpoint fills its bins as values land in them, a range bin is satisfied by any value in range, and an unreached bin (the boundary, here) is a visible gap that keeps the coverpoint honest. Reading the waveform this way — which bins filled, which value mapped where, which bin is still empty? — is reading the coverpoint's progress. The small and large filling while max_size stays empty is the signature of a well-partitioned coverpoint doing its job: it refuses to read 100% until the boundary — the case that matters — is exercised. A range bin is hit by any value in range; carve the boundary out so it must be hit on its own.

DebugLab — the coverpoint at 100% that missed the boundary bug

100% coverage that meant nothing because the bins were too coarse

Symptom

A team's cp_size coverpoint read 100%fully covered — and they signed off the size dimension as thoroughly exercised. In silicon, a bug surfaced at size == 64 (the maximum transfer): an off-by-one in the length counter corrupted the last beat. Reviewing the coverage, cp_size had one bin — bins all = {[1:64]} — covering the entire range. It read 100% after the very first transaction (any size hit the one bin), so it had always shown "fully covered"even though size == 64 had been driven only a handful of times by chance and the boundary had never been a required, tracked case. The 100% was real (the bin was hit) but meaningless (it never required the boundary).

Root cause

The coverpoint's partition was too coarseone bin for the whole range — so it reached 100% trivially on any value, never requiring the functionally-distinct cases (especially the boundary) to be exercised:

why a 100% coverpoint missed a boundary bug — the bins were too coarse
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
✗ TOO-COARSE partition — one bin for the whole range:
  cp_size: coverpoint t.size { bins all = {[1:64]}; }
  // ANY size (1, 7, 64...) hits the single bin → 100% after the FIRST transaction
  // reads "fully covered" forever — but NEVER requires size==64 (the boundary) specifically
  // → off-by-one bug at size==64 escapes a 100%-covered coverpoint (FALSE confidence)
 
✓ MEANINGFUL partition — carve the functionally-distinct cases, especially the boundary:
  cp_size: coverpoint t.size {
    bins min      = {1};        // minimum boundary
    bins mid      = {[2:63]};   // the bulk range
    bins max_size = {64};       // MAXIMUM boundary — must be hit specifically
  }
  // now 100% REQUIRES size==1 AND size==64 to be exercised → the boundary is a tracked gap
  // until size==64 occurs, max_size stays empty → coverage < 100% → the bug's scenario is flagged

This is the too-coarse-bins bug — the false 100% of 19.1 at the coverpoint granularity. The coverpoint had one bin (bins all = {[1:64]}) for the entire size range, so any size hit it and it read 100% after the first transaction — forever showing "fully covered". But 100% of one coarse bin means nothing: it never required size == 64 (the boundary) to be exercised specifically, so the off-by-one bug there escaped a "fully covered" coverpoint. The coverage was real but meaningless: the bin was hit, but the bin didn't distinguish the case that mattered. The fix is a meaningful partitioncarve the functionally-distinct cases, especially the boundaries: bins min = {1} (minimum), bins mid = {[2:63]} (bulk), bins max_size = {64} (maximummust be hit specifically). Now 100% requires size == 1 and size == 64 to be exercised — the boundary is a tracked gap: until size == 64 occurs, max_size stays empty, coverage reads < 100%, and the bug's scenario is flagged as unexercised. The general lesson, and the chapter's thesis: a coverpoint is only as meaningful as its bins — a too-coarse partition (one bin for a whole range) reaches 100% trivially while never requiring the functionally-distinct cases (boundaries, special values) to be exercised, giving false confidence; so partition the variable at its meaningful seamscarve out the boundaries and special values into their own bins — so that 100% coverage actually means the important cases were exercised, not merely that some value occurred. Coverage at 100% is only as honest as the partition; coarse bins make 100% a lie — carve the corners into their own bins.

Diagnosis

The tell is 100% coverage that coexists with an escaped bug in a specific value. Diagnose too-coarse bins:

  1. Inspect the coverpoint's bins. If one bin spans a whole range, 100% only means some value in that range occurred — not that the boundaries did.
  2. Check whether the boundary has its own bin. A boundary or special value folded into a range bin is never required specifically; it should be carved out.
  3. Ask what 100% requires. For each coverpoint, list which exact values must occur to reach 100% — if the corner isn't among them, the partition is too coarse.
  4. Map escaped bugs to bins. A bug at a value that shares a bin with common values was never a tracked, required case.
Prevention

Partition at the meaningful seams so 100% means something:

  1. Carve boundaries into their own bins. Min, max, and special values get dedicated bins so they must be hit specifically.
  2. Bin at the seams where behavior changes. Identify the value ranges that exercise different logic and give each its own bin.
  3. Avoid one-bin-for-all and auto-bins on wide variables. The former is trivial 100%; the latter never closes — both measure nothing.
  4. Review what 100% requires, not just that it's reached. A coverpoint at 100% should mean every functionally-distinct case was exercised.

The one-sentence lesson: a coverpoint is only as meaningful as its bins — a too-coarse partition (one bin for a whole range) reaches 100% trivially while never requiring the boundaries and special values to be exercised, so partition the variable at its functionally-distinct seams and carve the corners into their own bins, so that 100% coverage actually means the important cases were exercised, not merely that some value occurred.

Common Mistakes

  • Auto-bins on a wide variable. A 32-bit field auto-binned creates billions of meaningless bins that never close; partition it into functionally-distinct cases.
  • One coarse bin for a whole range. Any value hits it, so it reads 100% trivially while never requiring the boundaries — carve the corners into their own bins.
  • Folding the boundary into a range bin. A boundary (min/max) hidden in a range bin is never required specifically; give it a dedicated bin.
  • Sampling a value that's only valid under a condition. Without an iff guard, a coverpoint records garbage when the field is don't-care; guard it with the validity condition.
  • Measuring raw fields instead of derived features. Use expression coverpoints (e.g. addr[1:0] for alignment) to measure the functionally-distinct feature, not the raw value.
  • Treating 100% as done without checking what it requires. A coverpoint at 100% is only meaningful if its bins require the corners; review the partition, not just the percentage.

Senior Design Review Notes

Interview Insights

A coverpoint measures a single variable or expression by partitioning its value space into bins and recording which bins were hit — it's the atom of functional coverage, where one coverpoint corresponds to one feature. You declare it inside a covergroup as coverpoint, then the variable, then a set of bins in braces. The variable is the one thing this coverpoint measures — for example coverpoint t.op measures which operations occurred, coverpoint t.size which sizes. The bins partition that variable's value space: each bin is a value or a range you care about, and a bin is hit when the variable takes a value in it. When the covergroup is sampled, every coverpoint captures its variable's current value and drops it into the matching bin. The coverpoint's coverage is the fraction of its bins that have been hit, and the coverpoint is fully covered when every bin has been hit at least once. So the coverpoint answers one question: which values of this variable have we seen? The mental model is one question on the covergroup's tally sheet, with the bins as answer buckets — each sample drops a tally into the bucket matching the value, and the coverpoint is covered when every bucket has a tally. The crucial thing is that the coverpoint's meaning comes entirely from its bins: how you partition the variable defines what covered means. A coverpoint on operation with three bins for read, write, and read-modify-write means covered requires all three operations. The same variable with one bin for everything means covered requires just one transaction. So a coverpoint is a variable plus a partition, and the partition is where the verification intent lives — it's the verifier deciding which distinctions of this variable matter enough to require exercising.

A good partition carves the variable at its functionally-distinct seams — the boundaries, the ranges that exercise different logic, and the special values — so that 100% coverage means every distinct case was exercised. The two ways to get it wrong are too fine and too coarse, and both measure nothing. Too fine is auto-binning a wide variable: if you write coverpoint t.addr on a 32-bit address with automatic bins, SystemVerilog tries to create a bin per value — billions of them, or capped at some auto_bin_max of arbitrary buckets. The coverpoint can never close because you'd have to hit every value, and the bins don't correspond to anything functional. Coverage sits near zero forever and tells you nothing. Too coarse is the opposite: one bin for the entire range, like bins all equals the whole span. Any value hits the single bin, so the coverpoint reads 100% after the very first transaction — trivially covered — while never requiring any specific value. This is the insidious one because it looks done: 100% coverage that means nothing, the false confidence problem at the coverpoint level. A bug at the boundary value escapes because the boundary was never a tracked, required case. The meaningful middle is to ask: what values of this variable behave differently in the design? Then put a bin at each. For a size field that might be the minimum, the bulk range, and the maximum — carving the boundaries into their own bins so they must be hit specifically. For an address, the alignment via an expression coverpoint on the low bits. For a response, ok versus error. When those bins are all hit, 100% genuinely means every functionally-distinct case was exercised. So good coverpoint design is a specification activity: choosing which distinctions matter and binning at exactly those seams, avoiding both the never-closes extreme and the trivially-100% extreme.

Because a range bin is hit by any value in its range, so if the boundary is folded into a range bin, the coverpoint can reach 100% without ever exercising the boundary specifically — and boundaries are exactly where bugs like off-by-one and overflow live. Consider a size field from 1 to 64 with a bin large covering 5 to 64. The first time any large-ish size occurs, say 7, the large bin is hit, and as far as that bin is concerned it's covered. The coverpoint can read 100% with size 64 — the maximum, the boundary — never having occurred. Now suppose there's an off-by-one bug in the length counter that only corrupts the transfer at size 64. The coverage report says the size dimension is fully covered, the team signs off, and the boundary bug ships, because the boundary was never a required, tracked case — it shared a bin with common values that did occur. If instead you carve the maximum into its own bin, bins max_size equals 64, then the coverpoint cannot reach 100% until size 64 specifically occurs. Until then, max_size stays empty, coverage reads below 100%, and the boundary scenario is flagged as unexercised — visible as a gap that keeps the coverpoint honest. This is the general principle: bins should require the cases that matter, and boundaries matter disproportionately because that's where edge-case bugs concentrate. The same applies to other special values — zero, the minimum, an all-ones pattern, an error code — each should get its own bin if its behavior is distinct, so that reaching 100% requires exercising it. The waveform view makes it concrete: when 7 and 30 both hit the large bin, you see that any value in range satisfies it, which is precisely why the boundary needs separating. Carving the corner out converts it from an accidental, untracked occurrence into a required, measured case.

A set of coverpoints is the coverage plan made executable — each feature the plan says to exercise becomes one coverpoint with bins at its functionally-distinct cases, so reading the coverpoints reconstructs the plan. The coverage plan, derived from the spec, is a list of features and scenarios to exercise: every operation type, each size class including the boundary, each address alignment, each response type, and so on. To implement it, you translate each feature into a coverpoint. The operation feature becomes coverpoint cp_op with bins for read, write, read-modify-write. The size feature becomes cp_size with bins for small, large, and the boundary. The alignment feature becomes an expression coverpoint on the low address bits with aligned and unaligned bins. The response feature becomes cp_resp with ok and error bins. These coverpoints live together in one covergroup for the transaction, and a single sample call records all of them at once, because they're all facets of the same observed transaction. So the covergroup with its set of coverpoints is the plan in executable form. This has an important consequence: coverpoint design is a specification activity, not just coding. Choosing which variables to measure and how to partition them is deciding what thoroughly verified means for this design. And it works in reverse for review: reading a covergroup's coverpoints tells you exactly what the verifier decided matters — these features, partitioned these ways, are this team's definition of enough. The coverage percentage on those coverpoints is then the plan's completeness measure. If the plan says exercise the boundary but no coverpoint has a boundary bin, the plan isn't actually being measured. So the set of coverpoints is where the plan stops being a document and becomes a live measurement, and the quality of that translation — good partitions at the right seams — determines whether the coverage number reflects the plan's intent.

An expression coverpoint measures a derived value computed from fields rather than a raw field, and a guarded coverpoint uses an iff condition to sample only when the value is meaningful — both let coverpoints measure the functionally-distinct feature precisely rather than the raw data. An expression coverpoint targets an expression instead of a plain variable. The classic example is alignment: you don't care about the full address value, you care whether it's aligned, which is whether the low bits are zero. So you write coverpoint t.addr with the low two bits selected, with bins aligned for zero and unaligned for the nonzero cases. This measures the derived feature — alignment — directly, instead of binning the raw 32-bit address, which would be the never-closes anti-pattern. Other examples are coverpoints on a computed category, a field comparison, or a function of multiple fields. The point is that the meaningful feature is often a function of the raw data, and an expression coverpoint measures that function. A guarded coverpoint uses iff with a condition, so the coverpoint only samples when the condition holds. You use it when a field is only meaningful under some circumstance. For instance, a response code might be valid only when a valid bit is set; sampling it otherwise records a don't-care or garbage value into a bin, polluting the coverage. Writing coverpoint t.resp iff t.valid means the coverpoint records resp only on transactions where valid is true, so the bins reflect only real responses. This mirrors the data-checking principle of not comparing don't-cares — here you don't cover don't-cares. You'd guard a coverpoint whenever the variable's value is conditionally meaningful: a field valid only in certain modes, a status only defined after completion, a payload only present for certain operations. Both features serve the same goal: making the coverpoint measure exactly the functionally-distinct, meaningful cases — expression coverpoints by deriving the feature, guarded coverpoints by excluding the moments when the field is meaningless.

Exercises

  1. Partition a field. For an 8-bit length field where 0, 1, and 255 behave specially, write the coverpoint bins that make 100% require exercising those cases.
  2. Spot the false 100%. Given coverpoint t.size with one bin for the whole range, explain why 100% is meaningless and how to fix it.
  3. Derive a feature. Write an expression coverpoint that measures whether an address is aligned, without binning the raw address.
  4. Guard a coverpoint. Explain when a response coverpoint needs an iff guard and what goes wrong without it.

Summary

  • A coverpoint measures a single variable or expression by partitioning its value space into bins and recording which bins were hit — the atom of functional coverage (one coverpoint = one feature), with coverage = the fraction of its bins hit.
  • Each sample of the covergroup captures every coverpoint's current value and records it into the matching bin; a range bin is hit by any value in its range, so the coverpoint is covered only when every bin is hit.
  • The art is the partition: bins at the functionally-distinct seamsboundaries (min/max), meaningful ranges (small/large), special values (aligned/unaligned, error/ok) — not auto-bins on a wide variable (never closes, measures nothing) and not one coarse bin (trivial 100%, measures nothing).
  • Coverpoints can measure derived features (expression coverpoints, e.g. addr[1:0] for alignment) and be guarded (iff — sample only when valid); a set of coverpoints is the coverage plan's features, made executable.
  • The durable rule of thumb: a coverpoint is only as meaningful as its bins — partition the variable at its functionally-distinct seams and carve the boundaries and special values into their own bins, so that 100% coverage actually means the important cases were exercised, not merely that some value occurred; auto-bins on a wide field never close and one coarse bin is a false 100%, so always ask what 100% on this coverpoint actually requires.

Next — Coverage Bins: the next layer of detail — bins themselves: automatic versus explicit bins, value and range and wildcard bins, transition bins (value sequences), illegal and ignore bins, and how the bin definitions precisely control what each coverpoint requires and excludes.