Part 5 · Functional Coverage · Intermediate

Cross Coverage Patterns

Classic intent crosses — opcode×operand corner, burst×length×alignment, state×event, error×recovery — and a worked spec-to-cross example.

Four patterns that cover most real crosses

Most production crosses are instances of a small number of patterns. Recognizing the pattern tells you which coverpoints to build, how coarse the bins should be, and which cells to prune.

The pattern catalog

  1. Opcode × operand-corner — does every operation handle the special values (zero, max, negative-min)? Drives ALU/FPU bugs out.

  2. Burst-type × length × alignment — does every transfer shape meet every address alignment? Drives interconnect/DMA bugs out.

  3. State × input-event — was every input observed in every state where it is legal? Drives FSM bugs out.

  4. Error-type × recovery-path — was every error seen down every recovery route? Drives error-handling bugs out.

systemverilog
// PATTERN 1: opcode x operand-corner
covergroup alu_cg @(posedge clk iff op_valid);
  cp_op : coverpoint opcode { bins arith[] = {OP_ADD, OP_SUB, OP_MUL, OP_DIV}; }
  cp_b  : coverpoint operand_b {
    bins zero = {0};
    bins max  = {32'h7FFF_FFFF};
    bins min  = {32'h8000_0000};
    bins mid  = default;
  }
  x_op_corner : cross cp_op, cp_b {
    // div-by-zero is the plan's #1 corner: keep it as its own bin
    bins div_zero = binsof(cp_op) intersect {OP_DIV} && binsof(cp_b.zero);
  }
endgroup

// PATTERN 3: state x input-event
covergroup fsm_cg @(posedge clk);
  cp_state : coverpoint ctrl_state { bins s[] = {IDLE, BUSY, DRAIN}; }
  cp_evt   : coverpoint event_in   { bins e[] = {EV_REQ, EV_ABORT, EV_TIMEOUT}; }
  x_se : cross cp_state, cp_evt {
    // spec: ABORT is ignored in IDLE — impossible to observe an effect
    ignore_bins idle_abort = binsof(cp_state.s) intersect {IDLE} &&
                             binsof(cp_evt.e)   intersect {EV_ABORT};
  }
endgroup

Worked example — from spec table to cross

The strongest coverage models are transcriptions of a spec table. Here is a DMA burst table and the cross it becomes — every spec cell maps to a cross cell, and the spec's forbidden cells become pruning.

diagram
SPEC TABLE 7-2: supported burst shapes (DMA controller)

  burst type │ len 1 │ len 2-8 │ len 9-16 │ unaligned addr
  ───────────┼───────┼─────────┼──────────┼────────────────
  INCR       │  yes  │   yes   │   yes    │  yes
  WRAP       │  NO*  │   yes   │  pow2    │  NO*
  FIXED      │  yes  │   yes   │   NO*    │  yes
  (*) = forbidden by spec section 7.2  illegal, not ignored

  MAPPING
  spec column "burst type"  cp_btype (3 bins)
  spec column "len"         cp_len   (3 bins)
  spec column "unaligned"   cp_align (2 bins)
  full product 3×3×2 = 18 cells
  spec-forbidden cells      illegal_bins (runtime check)
  pow2-only WRAP lengths    handled in cp_len bin shaping
systemverilog
covergroup dma_cg @(posedge clk iff desc_done);
  cp_btype : coverpoint btype { bins incr = {INCR}; bins wrap = {WRAP};
                                bins fixed = {FIXED}; }
  cp_len   : coverpoint blen  { bins l1 = {1}; bins l2_8 = {[2:8]};
                                bins l9_16 = {[9:16]}; }
  cp_align : coverpoint addr[1:0] { bins aligned = {0}; bins unalign = {[1:3]}; }

  x_shape : cross cp_btype, cp_len, cp_align {
    // Spec 7.2 forbidden cells -> runtime checks
    illegal_bins wrap_len1   = binsof(cp_btype.wrap)  && binsof(cp_len.l1);
    illegal_bins wrap_unal   = binsof(cp_btype.wrap)  && binsof(cp_align.unalign);
    illegal_bins fixed_long  = binsof(cp_btype.fixed) && binsof(cp_len.l9_16);
  }
endgroup

Why this transcription discipline pays

  • Reviewability — a reviewer can hold the spec table next to the cross and verify cell-for-cell.

  • Traceability — each illegal_bins comment cites the spec section; sign-off audits become mechanical.

  • Hole triage speed — any empty cell maps straight to a spec row, so 'is it reachable?' is answered by the table.

  • Change resilience — when the spec table gains a row, the diff to the covergroup is obvious and local.

Interview angle: "Design a coverage model for this burst table" is a common whiteboard task. The winning move is to map columns to coverpoints, state the product size out loud (3×3×2 = 18), convert the spec's 'NO' cells into illegal_bins, and say which remaining cells you would name explicitly because the plan ranks them highest. That demonstrates pattern fluency, not just syntax.

Key takeaways

  • Most useful crosses are one of four patterns: op×corner, shape×alignment, state×event, error×recovery.

  • Transcribe spec tables cell-for-cell — columns become coverpoints, forbidden cells become illegal_bins.

  • Name the plan-critical cells as explicit cross bins so reports surface them by name.

  • A cross a reviewer can check against the spec in one sitting is worth more than an exhaustive one nobody audits.

Common pitfalls

  • Inventing crosses bottom-up from available signals instead of top-down from spec tables.

  • Translating a spec 'NO' cell into ignore_bins — a forbidden behavior deserves a runtime check, not silence.

  • Burying the one plan-critical combination inside an anonymous auto product — name it.

  • Letting the coverage model drift after a spec table changes — re-diff the table against the cross every spec revision.