SystemVerilog · Module 11
Wildcard, Illegal & Ignore Bins
The three special-purpose bin forms — wildcard for bit-pattern matching, illegal for must-never-occur scenarios that fire runtime errors, and ignore for valid-but-irrelevant values. The decision tree for picking between illegal, ignore, narrower bins, and constraints, plus the tool-specific severity model.
Module 11 · Page 11.5
Three Special-Purpose Bin Forms
Module 11.4 covered the four counted named-bin forms — single-value, range, multi-value, and array. This page covers three special-purpose forms that change what the coverage measurement means:
wildcard bins— pattern-matches values using?as a don't-care bit, so one bin covers every value matching a bit pattern.illegal_bins— declares values the design must never produce; the simulator raises a runtime error or warning if any sample lands in the bin.ignore_bins— declares valid-but-uninteresting values that are silently excluded from the coverage denominator.
Each form solves a problem the counted forms cannot. Knowing when to reach for each — and when not to reach for it (the alternatives are often better) — is the discipline this page builds.
Wildcard Bins — Bit-Pattern Matching
wildcard bins lets you declare a bin whose value set is described by a bit pattern with ? as the don't-care character. Any sampled value whose non-? bits match the pattern lands in the bin. The form is useful when the verification plan's scenario is "any value sharing this bit pattern" rather than a specific value or a contiguous range.
Syntax and semantics
covergroup cg_addr_patterns;
cp_addr: coverpoint addr {
// Match: top nibble is all-1s — values 0xF0..0xFF
wildcard bins high_nibble_f = {8'b1111_????};
// Match: low nibble is all-0s — values 0x00, 0x10, 0x20, ..., 0xF0
wildcard bins low_nibble_0 = {8'b????_0000};
// Match: any byte where bits[7] == 1 AND bit[0] == 1
// (an odd value in the upper half)
wildcard bins odd_upper = {8'b1???_???1};
// Multiple patterns share one bin — OR semantics inside {}
wildcard bins low_or_high = {8'b0000_????, 8'b1111_????};
}
endgroupWhat this does. Four wildcard bins. The first matches any byte whose top nibble is 1111 — sixteen distinct values lighting one bin. The second matches any byte whose low nibble is 0000. The third matches any byte where bit 7 is set and bit 0 is set, regardless of the bits in between — sixty-four distinct values. The fourth combines two patterns under one named bin via OR semantics inside the {} literal.
Where wildcard bins earn their keep. Protocol fields whose semantics are bit-position-driven rather than value-driven:
- AXI
AWUSER/ARUSERsideband bits where specific bit positions encode independent flags. - Interrupt vectors where the active line is identified by a single set bit.
- DMA descriptor flags whose "any one of these mode bits set" is the scenario.
- Address aliasing — "any address ending in
0x00" maps to "page-aligned access" as a single scenario.
Writing the equivalent as named bins would require enumerating every matching value (sixteen for an 8-bit one-nibble pattern, sixty-four for a two-bit-fixed pattern); the wildcard form names the scenario directly.
Wildcard semantics — what ? actually matches
The ? character in a wildcard bin matches 0 and 1 in that bit position. Critically, it does not match x or z — values containing x/z bits do not land in a wildcard bin (or any bin) at all. This is the source of every "the simulation drove the pattern but the bin didn't hit" debugging session involving wildcard bins.
covergroup cg;
cp: coverpoint signal_8b {
wildcard bins all_ones_low = {8'b????_1111};
}
endgroup
// signal_8b = 8'b0000_1111 → matches all_ones_low ✓
// signal_8b = 8'b1010_1111 → matches all_ones_low ✓
// signal_8b = 8'b1111_1111 → matches all_ones_low ✓
// signal_8b = 8'b1010_1110 → does NOT match ✗ (bit 0 ≠ 1)
// signal_8b = 8'b1010_111x → does NOT match ✗ (x in low nibble)
// signal_8b = 8'b1010_111z → does NOT match ✗ (z in low nibble)
//
// X/Z propagation kills the sample silently — no error, no warning,
// the bin just doesn't increment. If your design has X-propagation,
// audit it before trusting wildcard-bin coverage numbers.Illegal Bins — Catching "Must Never Occur"
illegal_bins declares values that the design must never produce during a correct simulation. They are not counted in coverage (they have no role in the 100% calculation). When a sample lands in an illegal bin, the simulator raises a runtime error or warning — making the violation immediately visible rather than letting it corrupt the coverage database silently.
Syntax and behaviour
typedef enum bit [2:0] {
OP_NOP = 3'b000,
OP_READ = 3'b001,
OP_WRITE = 3'b010,
OP_RFR = 3'b011
// Encodings 3'b100..3'b111 are reserved — spec says they must never occur
} cmd_e;
covergroup cg_cmd;
cp_cmd: coverpoint command {
// ── Legal commands — counted toward coverage ───────────────
bins nop = {OP_NOP};
bins rd = {OP_READ};
bins wr = {OP_WRITE};
bins rfr = {OP_RFR};
// ── Illegal — spec violation if produced ──────────────────
illegal_bins reserved = {[3'b100 : 3'b111]};
}
endgroup
// If command == 3'b100 is ever sampled:
// • Simulator raises: "Coverage illegal bin 'reserved' was hit"
// • Severity is tool-dependent (warning vs error vs fatal)
// • The illegal bin is NOT counted toward cp_cmd's denominator
// • cp_cmd's 100% is { nop, rd, wr, rfr } all hitThe intent rule: illegal_bins describes a design contract. The contract is "this value will never appear at this point of the design." Sampling the bin means the contract is broken — either the RTL has a bug, or the spec has a hole that the RTL is filling unexpectedly.
Tool-specific severity
The LRM allows simulators discretion in how strictly to enforce illegal_bins. Major simulators differ:
| Simulator | Default behaviour | Override knob |
|---|---|---|
| VCS | Warning per hit | -cm_warn_illegal_bins fatal |
| Questa / ModelSim | Warning per hit | set CovIllegalSeverity fatal (.do script) |
| Xcelium | Warning per hit | -coverage_options error_on_illegal_bin |
| Verilator | Warning per hit (cov enabled) | +verilator+coverage+illegal-fatal |
The pattern is consistent: default is warning-per-hit; the verification environment opts into fatal severity. For coverage models inside CI regressions, fatal severity is the right default — a hit on an illegal bin should stop the simulation, not be lost in the warning torrent that nightly regressions produce. For interactive debug runs, warning severity is sometimes more useful (so the simulation continues past the violation and the engineer can inspect what happened next).
You can also force fatal severity per covergroup via type_option:
covergroup cg_critical;
type_option.illegal_bins_error = 1; // any illegal-bin hit → fatal
cp_state: coverpoint state {
bins legal_states = {IDLE, ACTIVE, DONE};
illegal_bins encoding_error = {2'b11};
}
endgroupIllegal bins vs assertions — the trade-off
The same "must never happen" intent can often be expressed as a SystemVerilog Assertion (SVA) instead of an illegal bin:
// ── Form A — illegal_bins (Module 11) ──────────────────────────────
covergroup cg_state;
cp_state: coverpoint state {
bins legal = {IDLE, ACTIVE, DONE};
illegal_bins encoding_error = {2'b11};
}
endgroup
// ── Form B — concurrent assertion (Module 12) ──────────────────────
property p_state_valid;
@(posedge clk) state inside {IDLE, ACTIVE, DONE};
endproperty
assert property (p_state_valid);Both detect the violation. The choice:
| Aspect | illegal_bins | SVA assertion |
|---|---|---|
| Trigger granularity | At each sample event | At each clock edge (or other property event) |
| Reports | Surfaces in coverage report alongside the bin definition | Separate assertion-fail log |
| Timing windows | Postponed region only — sub-cycle illegal values missed | Configurable region — can catch sub-cycle transients |
| Coverage integration | The bin name appears in the coverage audit naturally | A separate map links assertions to verification-plan rows |
| Severity control | illegal_bins_error / per-tool knob | assert / assume / cover modifiers + $error / $fatal |
| Performance | Cheap — coverage was already being sampled | Per-edge property evaluation, can be more expensive |
The rule that works in practice: use illegal_bins when the violation is naturally a coverage event (specific value in a specific field at sample time), use an SVA assertion when the violation is naturally a temporal property (value sequence across cycles, ordering relations between signals, no-glitch requirements). Many environments use both for the same scenario — the assertion catches the temporal version, the illegal bin documents the scenario in the coverage audit.
Ignore Bins — Excluding Valid-but-Irrelevant Values
ignore_bins declares values that are legal for the design but irrelevant for the coverage measurement. They are not counted in coverage (they contribute nothing to the denominator) and they do not fire any error when hit. The simulator silently skips the sample for this coverpoint.
Syntax and behaviour
covergroup cg_packet_type;
cp_type: coverpoint packet_type {
// ── Counted bins ──────────────────────────────────────────
bins data_pkt = {[0:3]};
bins control_pkt = {[4:7]};
// ── Reserved encoding range — valid per spec, not in plan
// (typically test-mode-only or future-feature values)
ignore_bins reserved = {[8:15]};
}
endgroup
// Coverage denominator = 2 bins (data_pkt + control_pkt)
// If packet_type ∈ [8:15] appears:
// • No error, no warning
// • The sample is silently dropped for cp_type
// • Other coverpoints in the same covergroup still sample normally
//
// Without ignore_bins, automatic-bin behaviour would create bins for
// values [8:15] too, making 100% impossible unless the test layer
// deliberately drives reserved values.When ignore_bins is the right tool
The legitimate uses for ignore_bins:
- Reserved encodings the spec defines as valid but the plan doesn't test. Future-feature opcodes, test-mode flags, calibration values — they exist in the value space but aren't part of the verification scope.
- Unreachable values inside a range. A
bit [3:0] burst_lenfield whose RTL is hardcoded to only ever drive 1, 2, 4, 8 leaves the other twelve values unreachable —ignore_bins others = {3, [5:7], [9:15]};keeps them out of the denominator. - Cross-coverage combinations the architecture forbids. A
crossbetween opcode and operand-size where some combinations are architecturally impossible — theignore_binsform inside the cross (ignore_bins div_large = binsof(cp_opcode) intersect {DIV} && binsof(cp_size.large);) drops only those combinations. - Values that exist because the SystemVerilog type is wider than the design uses. A 16-bit field where the RTL only drives the low 8 bits —
ignore_bins upper_unused = {[16'h0100 : 16'hFFFF]};reflects the actual reachable scope.
When ignore_bins is the wrong tool
The misuse case is using ignore_bins to make a coverage number look better by excluding values the test layer simply hasn't reached. That is converting a verification gap into a verification cover-up — the dashboard improves while the spec rows still aren't exercised.
covergroup cg_addr;
cp_addr: coverpoint paddr {
bins csr_region = {[32'h0000_0000 : 32'h0000_00FF]};
bins ram_region = {[32'h1000_0000 : 32'h1FFF_FFFF]};
// ── ANTI-PATTERN: regression couldn't hit the MMIO region,
// so it was ignored to "close" coverage
ignore_bins mmio_region = {[32'h2000_0000 : 32'h2FFF_FFFF]};
}
endgroup
// Dashboard: cp_addr 100%.
// Verification plan: MMIO region row says "exercise base, mid, last byte."
// Reality: the MMIO region was never exercised. The ignore_bins entry hides it.Every ignore_bins declaration needs a documented justification: "this value range is unreachable because [architectural reason]," or "this range is reserved per spec section X and is intentionally out of scope." A reviewer should be able to read the ignore_bins line, read the cited reason, and verify the reason is real. Without that audit step, ignore bins become the gap-hiding tool the dashboard reads but the spec doesn't.
Illegal vs Ignore vs Narrower Bin vs Constraint — A Decision Tree
The same coverage outcome ("this value range is not part of my coverage measurement") can be achieved four different ways. They are not equivalent and the choice matters at audit time.
| Approach | What it does | Use when |
|---|---|---|
illegal_bins | Not counted; fires error if hit | The value must never occur — design contract violation |
ignore_bins | Not counted; silent if hit | The value is legal but architecturally out of scope |
Narrower bins (exclude by omission) | Sample silently drops if no bin matches | The value is one of several you simply don't plan to track |
| Test-layer constraint | Value is never generated, so never sampled | The test should not produce the value at all |
The differences in audit semantics are decisive:
illegal_binsdocuments "this is a contract violation, surface it loudly." A reviewer reading the covergroup sees the design promise.ignore_binsdocuments "this is intentionally out of scope, and here is why." A reviewer reading the covergroup with theoption.commentsees the architectural justification.- Exclusion by omission documents nothing — a reviewer reading the covergroup cannot tell whether the engineer forgot the bin or deliberately omitted it. Almost always the wrong choice for sign-off coverage.
- Test-layer constraint is the right tool when the value should not be generated in the first place. It does not appear in the coverage source at all; the constraint controls the input distribution. Use this for "we never drive these opcodes in any test" rather than for "we have a coverpoint that ignores these opcodes."
A simple working rule: if the spec calls the value "illegal," use illegal_bins. If the spec calls the value "reserved" or "test-mode-only" or "future-feature," use ignore_bins with a citation. If the value is legal but the test simply doesn't drive it, write a test or a constraint — not a coverage bin.
A Complete Working Example — All Three Forms in One Covergroup
The pattern below combines all three special-purpose bin forms in a realistic memory-controller covergroup, alongside named bins from Module 11.4.
typedef enum bit [2:0] {
CMD_NOP = 3'b000,
CMD_READ = 3'b001,
CMD_WRITE = 3'b010,
CMD_RFR = 3'b011
// 3'b100..3'b111 are reserved — spec says they must never appear
} mem_cmd_e;
class mem_txn;
rand mem_cmd_e cmd;
rand bit [15:0] addr;
rand bit [ 7:0] flags;
covergroup cg_mem;
// ── Named bins on commands; illegal bin on reserved encodings
cp_cmd: coverpoint cmd {
bins nop = {CMD_NOP};
bins rd = {CMD_READ};
bins wr = {CMD_WRITE};
bins rfr = {CMD_RFR};
illegal_bins reserved = {[3'b100 : 3'b111]};
option.comment = "vplan §4.1 — legal cmd encodings + reserved violation";
}
// ── Range bins on addr; ignore bin on architecturally unused
cp_addr: coverpoint addr {
bins boot_rom = {[16'h0000 : 16'h0FFF]};
bins sram = {[16'h1000 : 16'h3FFF]};
bins dram = {[16'h4000 : 16'hBFFF]};
bins mmio = {[16'hC000 : 16'hEFFF]};
// The 0xF000..0xFFFF region is reserved for future test-mode
// registers. Documented out of scope for the current release.
ignore_bins test_mode_reserved = {[16'hF000 : 16'hFFFF]};
option.comment = "vplan §4.2 — release-N memory regions; "
+ "F000+ reserved for release-N+1";
}
// ── Wildcard bins on flags — bit-pattern scenarios
cp_flags: coverpoint flags {
// Privilege flag (bit 7) set, all other bits free
wildcard bins privileged = {8'b1???_????};
// Cache-hint bits (bits 3:2) both set, others free
wildcard bins cache_hint = {8'b????_11??};
// Reset shape: bits 7 and 0 both set, middle bits free
wildcard bins handshake = {8'b1???_???1};
option.comment = "vplan §4.3 — flag-bit scenarios per AXI sideband spec";
}
endgroup
function new();
cg_mem = new();
endfunction
function void post_randomize();
cg_mem.sample();
endfunction
endclassWhat this code does. Models a memory-controller transaction with three coverpoints, each using a different special-purpose bin form alongside named bins. cp_cmd uses illegal_bins for the reserved encodings — if the constraint solver ever produces one, the simulator fires an error and surfaces the contract violation immediately. cp_addr uses ignore_bins for the future-feature region — those addresses can legally appear but they are not counted in the coverage denominator. cp_flags uses wildcard bins to express three bit-pattern scenarios that would otherwise require enumerating dozens of matching values.
How to simulate it. Compile with coverage enabled, run 1000 randomised transactions, dump coverage with the simulator's reporter.
Expected output (urg-style, partial).
Group cg_mem coverage 91.7%
cp_cmd nop 100.0% ( 242 hits )
rd 100.0% ( 278 hits )
wr 100.0% ( 268 hits )
rfr 100.0% ( 212 hits )
reserved ILLEGAL ( 0 hits ) ← good
cp_addr boot_rom 100.0% ( 261 hits )
sram 100.0% ( 251 hits )
dram 100.0% ( 230 hits )
mmio 100.0% ( 258 hits )
test_mode_reserved IGNORE ( 0 hits — excluded)
cp_flags privileged 100.0% ( 514 hits )
cache_hint 100.0% ( 244 hits )
handshake 0.0% ( 0 hits ) ← gap
(coverage denominator excludes illegal_bins and ignore_bins)What to read out of this. The reserved illegal bin reads ILLEGAL — 0 hits, which is the right outcome — the design contract held, no reserved encoding ever appeared. The test_mode_reserved ignore bin reads IGNORE — excluded, meaning the coverage denominator does not count it; if any sample landed there, it was silently dropped. The wildcard bin handshake is at 0%, a real gap — random stimulus happened not to produce a flags value with both bit 7 and bit 0 set; the next directed test is the closure path.
Debug Lab — "The Illegal Bin Reads Zero. Are We Safe, or Is the Simulator Lying?"
A subtle failure mode: an illegal_bins declaration that reads zero hits can mean either "the design contract held" (good) or "the illegal bin is silently disabled / mis-scoped / never sampled" (the bug ships). Distinguishing the two takes diligence.
covergroup cg_state @(posedge clk);
cp_state: coverpoint state {
bins legal = {IDLE, ACTIVE, DONE};
illegal_bins encoding_err = {2'b11};
}
endgroup
// In the testbench:
initial begin
// ── BUG: the covergroup is constructed AFTER the first reset
// pulse, during which the RTL momentarily produces 2'b11.
rst_n = 0;
@(posedge clk);
state = 2'b11; // illegal encoding briefly held during reset
@(posedge clk);
rst_n = 1;
cg_state cg = new(); // ← constructed too late, the violation already passed
end
// After regression:
// cp_state.legal 100.0%
// cp_state.encoding_err ILLEGAL (0 hits)
// No simulator error.
// Engineers conclude the design contract held.The covergroup compiles, the regression runs, the report says encoding_err: ILLEGAL (0 hits). The verification lead reviews the report and concludes the design has never produced the illegal encoding — confidence in the contract is high, sign-off proceeds. Bring-up later discovers a reset-window glitch where state briefly holds 2'b11 for one cycle before the FSM moves to IDLE. The illegal encoding was happening in simulation too — the covergroup simply wasn't constructed in time to see it.
Three independent factors converged on the false-clean report:
- The covergroup was constructed after the first reset pulse. Any
@(posedge clk)sampling event that fired during reset was lost — the handle was null. - The
illegal_binscheck runs as part of coverage sampling; if sampling does not happen, the check does not run. An illegal bin that never gets sampled cannot fire a violation report — it simply reads zero, indistinguishable from a real clean. - The reporting tool does not distinguish "zero hits because the contract held" from "zero hits because the bin was never sampled." Both render as
ILLEGAL (0 hits).
The bug is structural: the illegal-bin contract is enforced only at sample time, and the engineer never verified that the sampling event covered the windows where the violation could occur. A clean illegal bin is informative only when the bin was sampled at the moments the contract applies.
Two-part fix. First, ensure the covergroup is constructed before any simulation activity that could violate the contract — typically in an initial block before the first reset edge. Second, add a sanity check that confirms the illegal bin was exposed to values it could have hit, not just that it didn't hit them.
// Construct the covergroup before any clock activity.
cg_state cg = new(); // top-level instantiation, lives at time 0
initial begin
rst_n = 0;
@(posedge clk);
state = 2'b11;
@(posedge clk);
rst_n = 1;
end
// In a separate audit covergroup, deliberately sample all state encodings
// (including the illegal one) into a parallel coverpoint with no illegal_bins.
// This proves the sampling path was exercising the value space.
covergroup cg_state_audit @(posedge clk);
cp_state_raw: coverpoint state {
bins idle = {IDLE};
bins active = {ACTIVE};
bins done = {DONE};
bins reserved = {2'b11}; // counted here — confirms exposure
}
endgroup
// If cg_state_audit.cp_state_raw.reserved is nonzero AND
// cg_state.cp_state.encoding_err is zero,
// the discrepancy means the illegal_bins covergroup is mis-constructed —
// not that the design is clean.The discipline rule that prevents recurrence: a clean illegal bin is only evidence when the sampling event has been audited to cover the windows where the contract applies. For RTL contracts that apply at reset, the covergroup must be alive at time 0. For contracts that apply at specific protocol phases, the covergroup's sampling event must fire during those phases. Without that audit, an illegal_bins ILLEGAL (0) report is a guess, not a proof.
The complementary tool: write an SVA assertion for the same property. The assertion's sub-cycle granularity catches the reset-window glitch the illegal_bins form misses. Many production environments use both — the assertion is the temporal contract, the illegal bin is the coverage-report audit trail.
Industry Context — Where These Forms Earn Their Keep
- AXI/AXI-Lite/AHB protocol verification.
illegal_binson the encoded response codes — the spec definesOKAY,EXOKAY,SLVERR,DECERR(2-bitBRESP/RRESP), and reserved encodings are illegal. The covergroup makes them illegal bins; the assertion library makes them assertion violations; both surface the same violation differently. - DDR4 / LPDDR5 controller verification. Wildcard bins on the command pattern (
RAS/CAS/WE/CS4-bit combinations) — every legal DDR command is a 4-bit pattern, the reserved combinations are wildcard-binned as illegal. The bit-pattern form makes the coverage source legible against the JEDEC command-encoding table. - PCIe TLP-type coverage. Ignore bins on TLP types the device class doesn't implement (a Root Complex's coverage model on an Endpoint's exclusive TLP types, for instance). The ignore-bin form documents the device-class scope cleanly.
- UVM register-model coverage. The UVM library's built-in coverage hooks routinely use
illegal_binson register fields whose RAL access modes areRO, where any write activity is a contract violation — the illegal bin surfaces the bug, the RAL report ties it to the field name, the dashboard shows the violation. - Aerospace/defence flows. DO-254 and ARP4754A artefacts require traceability between every spec row and its verification evidence;
illegal_binswithoption.commentciting the spec section becomes part of the certification package.
The form choice is rarely controversial in mature flows — protocol spec violations are illegal bins, out-of-scope encodings are ignore bins with citations, bit-pattern scenarios are wildcard bins. The discipline is consistent enough across vendors that an engineer moving between teams reads the same shapes.
Interview Q&A — Ten Questions You Will Be Asked
A counted bin whose value set is described by a bit pattern with ? as a don't-care character. Any sampled value whose non-? bits match the pattern lands in the bin. Useful for protocol fields whose verification scenarios are described by bit position rather than by specific values — privilege flags, interrupt vectors, AXI sideband bits.
Coding Guidelines — Seven Rules for Special-Purpose Bin Discipline
- Match the bin form to the spec language. "Illegal" →
illegal_bins. "Reserved" / "test-mode-only" / "future-feature" →ignore_binswith citation. "Bit pattern" →wildcard bins. "Value range" / "value list" → countedbins. The reviewer should read the bin form and recognise the verification-plan row directly. - Every
ignore_binscites an architectural reason inoption.comment. Either the value is unreachable (cite the RTL constraint that makes it unreachable) or it is out-of-scope (cite the verification-plan row). Code review rejects ignore-bin additions without the citation. - For CI regression covergroups, force fatal severity on illegal bins. Default warning severity loses violations in the warning torrent. Use
type_option.illegal_bins_error = 1or the simulator's fatal-on-illegal flag. Interactive debug runs can opt out per-tool. - Audit illegal-bin exposure with a parallel counted bin. A clean
illegal_bins ILLEGAL (0)is evidence only when paired with an audit coverpoint that confirms the value space was being sampled. Production covergroups maintain the audit pair on every contract-critical illegal bin. - Construct covergroups before any simulation activity that could violate the contract they audit. For reset-window contracts, the covergroup must be alive at time 0 — typically declared at module/program scope rather than inside an
initialblock. - Pair illegal_bins with SVA for sub-cycle temporal contracts.
illegal_binsruns in the Postponed region — sub-cycle violations are invisible. SVA assertions run with configurable region granularity. Use both for the same scenario when the contract has a sub-cycle component. - Wildcard bins are for bit-pattern scenarios, not value ranges. When the spec describes "any value where bit N is set," use
wildcard bins. When it describes "any value in [lo:hi]," use a range bin. The choice matters for source legibility, not for coverage outcome.
Summary
wildcard bins, illegal_bins, and ignore_bins are the three special-purpose bin forms that extend named-bin discipline beyond counted partitioning. Wildcard bins handle bit-pattern scenarios. Illegal bins encode "must never happen" design contracts and fire runtime violations. Ignore bins document architectural out-of-scope with the discipline rule that every ignore bin cites its reason. Each form has a correct use case and a misuse case the protocol identifies — the value of the page is the decision tree that picks the right tool. Module 11.6 builds the cross-coverage primitive, which inherits all three special-purpose forms (illegal and ignore versions inside cross use the binsof/intersect syntax to scope across the cross's Cartesian product).