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

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
    • Reusable Testbench Architecture
    • Checking Out-of-Order Traffic
    • Assertion Depth
    • Coverage Modelling for Real Features
    • Capstone: Out-of-Order Memory Controller
    • Principal Judgment
      • Escaped Bug Analysis
      • Intermittent Failures
      • Metrics That Lie
      • Scaling Verification
      • System Design Interviews
Loading lesson

‹Principal Judgment
  1. Topics
  2. SystemVerilog
  3. Principal-Level Verification
  4. Principal Judgment
  5. Scaling Verification

Part 9 · Principal-Level Verification · Advanced

Scaling Verification

IP versus SoC verification, reusing checks upward, emulation hand-off, and where gate-level and X-propagation runs earn their cost.

On this page

    A block testbench can try every corner of one IP. An SoC testbench cannot; it runs slower, has more state and costs more per bug. Scaling verification means deciding which question each level answers, and moving checks between levels instead of rewriting them.

    What each level is for

    Level

    Finds best

    Weak at

    Typical speed

    IP (block) simulation

    Functional corners, protocol rules, error paths

    Integration: wiring, clocks, address maps, shared resources

    Fast; many seeds per night

    Subsystem simulation

    Interaction of a few IPs: arbitration, ordering across blocks

    Full-chip boot and software flows

    Medium

    SoC simulation

    Connectivity, address decode, interrupts, reset and clock sequencing, power states

    Deep corners of any one IP

    Slow; few long tests

    Emulation / FPGA prototype

    Software bring-up, long runs, performance, rare events that need billions of cycles

    Visibility and debug turnaround

    Orders of magnitude faster than simulation

    Gate-level simulation

    Reset and initialisation, X issues, synthesis and DFT netlist changes

    Functional coverage; very slow

    Slowest

    The common mistake is to repeat IP-level tests at SoC level. That spends the slowest cycles on questions the block bench already answered. SoC tests should target what only the SoC has: connections, shared resources and system flows.

    Reuse checks upward, not tests

    Stimulus rarely transfers from IP to SoC, because at SoC level the IP is driven by real neighbours. Checks do transfer. An IP-level protocol checker, bound to every instance of the IP, keeps working at subsystem and SoC level and catches integration bugs that the IP bench could never create.

    The bench below is a small illustration. An IP raises req and holds it until gnt. Its checker, written once, says "req holds until gnt, and gnt comes within 4 cycles". One bind line attaches it to every requester instance, in the IP bench and in the SoC. At SoC level two requesters share a round-robin arbiter, with port 0 requesting every cycle.

    req_chk.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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    package chk_pkg;
      int violations = 0;
      int grants_seen = 0;
    endpackage
    
    // IP-level rule, written once: req holds until gnt, and gnt comes within MAXW cycles.
    module req_chk #(parameter int MAXW = 4) (
      input logic clk,
      input logic rst_n,
      input logic req,
      input logic gnt
    );
      int waited = 0;
      logic req_q = 0, gnt_q = 0;
      always @(posedge clk) begin
        if (!rst_n) begin
          waited = 0; req_q = 0; gnt_q = 0;
        end else begin
          if (req_q && !gnt_q && !req) begin
            chk_pkg::violations++;
            $display("%m: req dropped before gnt at %0t", $time);
          end
          if (req && gnt) chk_pkg::grants_seen++;
          waited = (req && !gnt) ? waited + 1 : 0;
          if (waited > MAXW) begin
            chk_pkg::violations++;
            $display("%m: no gnt after %0d cycles at %0t", MAXW, $time);
            waited = 0;
          end
          req_q = req; gnt_q = gnt;
        end
      end
    endmodule
    
    // The IP: raises req when it wants the bus, holds it until gnt.
    module requester (
      input  logic clk,
      input  logic rst_n,
      input  logic want,
      input  logic gnt,
      output logic req
    );
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n)          req <= 1'b0;
        else if (!req || gnt) req <= want;     // next request may follow a grant back to back
    endmodule
    
    // SoC integration: two requesters share one round-robin arbiter.
    module soc (
      input  logic       clk,
      input  logic       rst_n,
      input  logic [1:0] want
    );
      logic [1:0] req, gnt;
      logic last;                               // port granted most recently
      requester u_ip0 (.clk, .rst_n, .want(want[0]), .gnt(gnt[0]), .req(req[0]));
      requester u_ip1 (.clk, .rst_n, .want(want[1]), .gnt(gnt[1]), .req(req[1]));
      always_comb begin
        gnt = 2'b00;
        if (req == 2'b11) gnt[!last] = 1'b1;    // both asking: the other one's turn
        else              gnt = req;
      end
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n)       last <= 1'b1;
        else if (gnt[1])  last <= 1'b1;
        else if (gnt[0])  last <= 1'b0;
    endmodule
    
    // The same bind line runs in the IP bench and here: every requester instance gets the checker.
    bind requester req_chk #(.MAXW(4)) u_chk (.clk(clk), .rst_n(rst_n), .req(req), .gnt(gnt));
    
    module tb_soc_reuse;
      timeunit 1ns; timeprecision 1ps;
      logic clk = 0, rst_n = 0;
      logic [1:0] want = 0;
      always #5 clk = ~clk;
      soc dut (.*);
      initial begin
        std::process p = std::process::self();
        p.srandom(7);
        repeat (2) @(negedge clk);
        rst_n = 1;
        repeat (300) begin
          @(negedge clk);
          want[0] = 1'b1;                                // port 0 always busy: the stress case
          want[1] = ($urandom_range(0, 1) == 1);
        end
        @(negedge clk); want = 0;
        repeat (4) @(negedge clk);
        if (chk_pkg::violations == 0 && chk_pkg::grants_seen > 100)
          $display("PASS: IP checker bound at SoC level saw %0d grants, no starvation or dropped req", chk_pkg::grants_seen);
        else $fatal(1, "FAIL: %0d checker violations, %0d grants", chk_pkg::violations, chk_pkg::grants_seen);
        $finish;
      end
    endmodule

    With the round-robin arbiter the bench passes. Replace the round-robin line with fixed priority (if (req[0]) gnt[0] = 1'b1;) and the checker in dut.u_ip1.u_chk reports "no gnt after 4 cycles" again and again; the bench ends FAIL: 60 checker violations. The IP bench, with one requester and an always-granting model, could never show this starvation. The check came for free at SoC level because it was bound, not copied. See bind Basics.

    In UVM, the same idea is a passive agent: the IP environment's monitor, checkers and coverage reused at SoC level with the driver turned off.

    Handing work to emulation

    Emulation is worth its setup cost when a question needs far more cycles than simulation can give: booting software, long traffic soak tests, performance counters over real workloads, and rare events such as counter wrap after 2^32 events. Before handing over:

    • Make the checks synthesisable or move them to the transaction level. Many class-based scoreboards do not run inside an emulator; plan which checks run on the host and how fast transactions stream out.

    • Add end-of-run audits that need no waveform: credit and tag counts, error counters, signatures of memory regions.

    • Decide in advance how a failure comes back to simulation: a checkpoint, a trace of transactions, or a seed and cycle number.

    Without a way back, an emulation failure becomes a week of lab-style debug.

    When gate-level and X-propagation runs pay off

    RTL simulation is X-optimistic. In an if statement an X condition is treated as false and takes the else branch (IEEE 1800-2017, 12.4). A case with an X selector matches no item and takes the default. Real gates propagate the unknown, so RTL can pass where silicon has an uninitialised flop driving logic.

    design.sv
    1
    2
    3
    4
    5
    // snippet: X-optimism in RTL
    logic en;                 // no reset: X after power-up
    always_comb
      if (en) y = a;          // en is X: RTL takes the else branch, y = b
      else    y = b;          // gates: y is X wherever a and b differ

    Most commercial simulators offer an X-propagation mode that makes RTL more pessimistic; the option names and exact semantics are vendor-specific, so read your simulator's manual. Verilator is 2-state and cannot show X at all, which is why the snippet is not run here.

    Run

    Earns its cost when

    Keep it small by

    RTL X-propagation

    Blocks with non-reset flops, power gating, retention, or reset domain crossings

    Running reset and power-sequence tests only

    Zero-delay gate-level

    Netlist changes after synthesis or DFT insertion, reset and init checks, sign-off of X behaviour

    A short list of boot, reset and mode-change tests

    Timing-annotated gate-level

    Asynchronous paths, clock-domain and reset sequencing that static timing cannot model, final confidence on critical interfaces

    A few tests on the paths static timing does not cover

    Functional coverage is not the goal of gate-level runs. Their job is to confirm that what was verified at RTL is what was built, and that the chip comes out of reset in a known state.

    Common pitfall

    Copying IP tests into the SoC regression "for safety". They take the slowest cycles to answer questions already answered, and they break whenever the SoC configuration changes. Bind the IP checkers instead, and write SoC tests for SoC questions.

    Interview

    Question: "How do you decide what to verify at IP level versus SoC level?" Answer: IP level owns functional corners and protocol rules, with high seed counts; SoC level owns connectivity, address maps, shared-resource arbitration, interrupts, resets, clocks and power; checks move up by binding or passive agents; emulation takes long-running and software-driven questions; gate-level runs a small set of reset, init and X tests.

    Check yourself Why did the starvation bug not show up in the IP bench?

    The IP bench had one requester and a grant model that always answered. Starvation needs a second requester sharing an arbiter, which exists only at SoC level. The IP checker caught it because it was bound to every instance.

    Check yourself A block passes RTL regression and fails in gate-level simulation with X on an output right after reset. What is the most likely cause, and why did RTL miss it?

    A flop with no reset feeding logic that matters after reset. RTL is X-optimistic: an X condition in an if takes the else branch, so RTL picked a value; the gates propagated the X.

    Related topics

    • Previous: Metrics That Lie

    • Next: System Design Interviews

    • bind and Checker Modules

    • Methodology Selection

    Previous

    258 / 259

    Next: System Design Interviews

    Up next

    Up nextSystem Design Interviews

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Escaped Bug Analysis
    • Intermittent Failures