Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here5
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive39
  • SystemVerilog Assertions — Interview-Critical Deep Dive41
  • Functional Coverage in SystemVerilog25
  • Testbench Architecture in SystemVerilog24
  • Advanced Language & Integration26
  • Senior & Interview Preparation23
  • Principal-Level Verification34
    • Reusable Testbench Architecture
    • Checking Out-of-Order Traffic
    • Assertion Depth
    • Coverage Modelling for Real Features
    • Capstone: Out-of-Order Memory Controller
      • Spec and Plan
      • Executable Spec: One Bench per Rule
      • Environment
      • Environment Self-Test
      • Checking
      • Testing the Checkers
      • Coverage and Closure
      • Merging Runs and Reading Holes
      • Bug Hunt: Six Bugs and the Full Bench
      • Bug Hunt Results and Sign-Off
    • Principal Judgment
Loading lesson

←Capstone: Out-of-Order Memory Controller
  1. Topics
  2. SystemVerilog
  3. Principal-Level Verification
  4. Capstone: Out-of-Order Memory Controller
  5. Coverage and Closure
On this page

    Part 9 of 9, lesson 26 of 34Part 9 of 9/Principal-Level Verification/Lesson 26 of 34Advanced

    Coverage and Closure

    The capstone coverage rows, their covergroup and cover-property forms, ten short runs merged into a closure curve, and what the holes say about the stimulus.

    The coverage rows

    Each row exists because of a risk in the plan, and each is sampled from transactions or derived state, as in Transaction and Temporal Coverage.

    Row

    What is counted

    Risk it answers

    Goal

    CV1

    Reads outstanding when a read is accepted: 0, 1-3, 4-7, 8+

    Empty and full table; 8+ means this read took the last free entry (any index)

    5, 10, 10, 3

    CV1b

    Read allocated table entry 7 (white-box: the DUT's free_idx)

    The highest entry index is used; depth alone does not show which entry was taken

    3

    CV2

    Read accepted with 2 or more reads outstanding on its own ID

    Within-ID ordering only matters when a queue has depth

    10

    CV3

    Response that overtook an older read on another ID

    Proves reordering happened, so ordering checks had work

    10

    CV4

    Partial writes per port and byte lane (8 bins)

    Strobe handling per port

    5 each

    CV5

    Read of a word written 3 or fewer cycles earlier

    Ordering point between writes and reads

    10

    CV6

    Response stalled 3 or more cycles

    Payload hold under back-pressure

    5

    CV7

    Contention won by port 0, by port 1

    Arbitration both ways

    5 each

    CV8

    Reset with 4 or more reads outstanding

    Reset that actually drops work

    1

    CV9

    Read on an ID that had reads outstanding at the last reset

    State that survives reset

    1

    CV3 and CV2 are activity gates as much as coverage. A clean scoreboard on traffic that never reordered, or never stacked two reads on one ID, proves nothing about R1.

    Covergroup form and the mirror

    On a commercial simulator the read and write rows are covergroups sampled from the monitor. Verilator 5.048 ignores covergroups, so the bench uses a procedural mirror (cap_cov) with the same bins and checks its rows against their goals, the approach of the Coverage Modelling topic.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    // not-run: covergroups are parsed but ignored by Verilator 5.048 (COVERIGN warnings, get_coverage() returns 0) and rejected by Icarus 13; needs a commercial simulator
    // The capstone coverage model as covergroups. The bench runs a procedural mirror of it.
    class cap_cg;
      covergroup cg_read with function sample(int depth, int depth_id, bit raw_close, bit after_reset);
        option.per_instance = 1;
        cp_depth: coverpoint depth {               // CV1: table the read found
          bins empty = {0};
          bins low   = {[1:3]};
          bins high  = {[4:7]};
          bins full  = {[8:$]};                    // this read took the last free entry
        }
        cp_same_id: coverpoint depth_id {          // CV2
          bins deep = {[2:$]};
        }
        cp_raw: coverpoint raw_close { bins hit = {1}; }          // CV5
        cp_reuse: coverpoint after_reset { bins hit = {1}; }      // CV9
        x_depth_same: cross cp_depth, cp_same_id;                 // ordering under load
      endgroup
    
      covergroup cg_write with function sample(bit port, bit [3:0] strb);
        cp_port: coverpoint port;
        cp_lane: coverpoint strb iff (strb != 4'hF) {             // CV4: partial writes only
          wildcard bins lane0 = {4'b???1};                        // overlapping bins: a write
          wildcard bins lane1 = {4'b??1?};                        // counts once in every lane
          wildcard bins lane2 = {4'b?1??};                        // it touches
          wildcard bins lane3 = {4'b1???};
        }
        x_port_lane: cross cp_port, cp_lane;
      endgroup
    
      // White-box: which table entry a read was allocated (DUT's free_idx at a read
      // acceptance). Depth says how busy the table was, not which entry was used.
      covergroup cg_alloc with function sample(bit [2:0] slot);
        cp_slot: coverpoint slot {                 // CV1b
          bins last  = {7};
          bins other = {[0:6]};
        }
      endgroup
    
      function new();
        cg_read = new();
        cg_write = new();
        cg_alloc = new();
      endfunction
    endclass

    The lane bins overlap on purpose. A write with strobe 0101 counts once in lane 0 and once in lane 2, which is what the mirror's loop over set strobe bits does. Bins that partitioned the strobe values instead would put that write in an "other" bin and leave lane 2 looking untested.

    Temporal rows as cover properties

    Rows that describe a shape in time read better as cover property, labelled with their row IDs. CV6 and CV7 could be written either way. The last two read naturally as sequences, although each also has a counter form (the mirror counts overtakes as CV3). cv11_overtake is only an approximation: the other-ID response may belong to a read older than the ID 1 read, and the final ID 1 response may serve an older ID 1 read. An exact overtake needs per-read tickets, which the CV3 mirror keeps.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // snippet: temporal plan rows as cover properties, labelled with their row IDs
    cv6_stall3: cover property (@(posedge clk) disable iff (!rst_n)
      (rs.valid && !rs.ready) [*3] ##1 (rs.valid && rs.ready));          // CV6: held 3 cycles, then taken
    cv7_p1_wins: cover property (@(posedge clk) disable iff (!rst_n)
      rq0.valid && rq1.valid && rq1.ready);                              // CV7: contention, port 1 wins
    cv10_b2b_same_id: cover property (@(posedge clk) disable iff (!rst_n)
      rsp_hs ##1 (rsp_hs && $stable(rsp_pid)));                          // two responses back to back on one ID
    // Approximation: the other-ID response may belong to an older read, and the last
    // response may serve an older ID 1 read. An exact overtake needs tickets (CV3 mirror).
    cv11_overtake: cover property (@(posedge clk) disable iff (!rst_n)
      (rd_acc && acc_pid == 3'd1) ##[1:8] (rsp_hs && rsp_pid != 3'd1) ##[1:8] (rsp_hs && rsp_pid == 3'd1));

    Verilator evaluates cover property only when built with --coverage-user, and the course runner does not pass it; Assertion-Backed Coverage shows the sentinel-cover check that tells "not reached" from "not evaluated". The capstone keeps these rows in the mirror so the runnable bench enforces them.

    Check yourself Why are CV2 and CV3 called activity gates?

    They prove the conditions under which the ordering checks mean something actually occurred: two reads queued on one ID, and a response overtaking an older one. Without them, a clean scoreboard could come from traffic that never tested ordering.

    Related topics

    • Previous: Testing the Checkers

    • Next: Merging Runs and Reading Holes

    • Assertion-Backed Coverage

    • Hole Analysis and Triage

    Previous

    267 / 275

    Next: Merging Runs and Reading Holes

    Up next

    Up nextMerging Runs and Reading Holes

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Spec and Plan
    • Executable Spec: One Bench per Rule
    • Environment