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
    • Testbench Anatomy & Layering
    • Stimulus: Generators, Transactions & Drivers
      • Transaction Design for Stimulus
      • Layered Constraint Architecture
      • Shaping Stimulus with Knobs and Policies
      • Protocol Legality and Error Injection
      • Randomizing Scenarios, Not Just Items
      • Generator Patterns
      • Driver Design
      • Mixing Directed and Random Stimulus
    • Monitors, Scoreboards & Reference Models
    • Self-Checking Tests & Regression
    • From Hand-Built TB to UVM
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Stimulus: Generators, Transactions & Drivers
  1. Topics
  2. SystemVerilog
  3. Testbench Architecture in SystemVerilog
  4. Stimulus: Generators, Transactions & Drivers
  5. Shaping Stimulus with Knobs and Policies

Part 6 · Testbench Architecture · Intermediate

Shaping Stimulus with Knobs and Policies

Knobs and policy objects that reshape stimulus without editing constraints.

On this page

    Knobs as gates and weights

    A knob is control state that shapes solving without being payload. State knobs enter the solver as constants, while variable dist weights act as runtime dials read at each call.

    Knob kind

    Mechanism

    Example

    Gate

    Implication fixes a field

    allow_err == 0 turns err off

    Weight

    Variable dist share

    err_pct sets the injected share

    Ceiling

    Range bound

    len_max caps burst length

    Config

    Randomized knob object

    stim_cfg applied per phase

    Knobs randomize per phase while payload randomizes per item. That cadence gives each run coherent character instead of white noise.

    Policies for multi-axis intent

    The next listing is not run in this course's checks. It is a fragment that uses bus_txn and txn_policy from elsewhere.

    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
    // not-run: fragment: bus_txn and txn_policy are defined elsewhere, no top module
    class knobbed_txn;
      rand bit [31:0] addr;
      rand bit [3:0]  len;
      rand bit        err;
      bit [3:0]      len_max = 4'd15;
      bit            allow_err = 1'b0;
      int unsigned   err_pct = 0;
      int unsigned   w_low = 8;
      int unsigned   w_high = 2;
      constraint len_c { len inside {[1:len_max]}; }
      constraint err_c {
        allow_err == 0 -> err == 0;
        err dist { 1 := err_pct, 0 := (100 - err_pct) };
      }
      constraint addr_c {
        addr dist { [32'h0000_0000:32'h0000_FFFF] :/ w_low,
                    [32'hF000_0000:32'hFFFF_FFFF] :/ w_high };
      }
    endclass
    
    class policed_txn;
      rand bus_txn    txn;
      rand txn_policy pol[$];
      function new();
        txn = new();
      endfunction
      function void add(txn_policy p);
        if (p == null) $fatal(1, "null policy added");
        p.bind_txn(txn);
        pol.push_back(p);
      endfunction
    endclass

    The container relies on handle aliasing: each policy constrains through a handle to the same transaction, and one call solves all blocks together. Rand class handles are standard IEEE 1800 (clause 18.4), not a vendor idiom, but free simulators support them only in part. Bind every policy before solving and guard nulls at insertion.

    • Policies compose orthogonal axes at runtime with no new classes and no base edits.

    • Policies only AND: replacing or widening a block still needs named-block override or soft bases.

    • One axis recurring suits a subclass, one call site suits inline, and a matrix suits policies.

    Common mistakes

    Common pitfall

    Solving the policy alone and expecting the transaction to follow misses the direction. Randomize the container so the transaction and its policies solve together.

    Interview

    Likely question: one sequence must serve bring-up and stress with no code changes?

    State-variable knobs gate constraints and feed dist weights, set from a randomized config object. Knob cadence stays per phase while payload stays per item.

    Check yourself Both dist weights are zero. What happens at solve time?

    The distribution is degenerate and the solve cannot proceed. Knob objects need sanity constraints such as a nonzero weight total.

    Check yourself When do policies beat subclasses?

    When intent spans orthogonal axes such as region by burst by injected share. Subclasses explode combinatorially while policies mix at runtime.

    Testbench

    A deterministic stand-in for a weight knob and a gate knob, plus a testbench that checks both. The stand-in fixes the share exactly. A real dist gives about 80 percent, so a check on real solves needs a tolerance band.

    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
    module tb_knobs;
      localparam int N = 100;
      localparam int WR_PCT = 80;
      localparam int INJ_PCT = 5;
      // Weight stand-in: i*mult mod 100 is a permutation of 0..99 when
      // mult shares no factor with 100, so exactly pct items fall below pct.
      function automatic bit pick(int i, int mult, int pct);
        return ((i * mult) % 100) < pct;
      endfunction
      // Gate knob: allow == 0 forces injection off, whatever the weight.
      function automatic bit gated_inject(bit allow, int i);
        return allow && pick(i, 53, INJ_PCT);
      endfunction
      int writes, inj_closed, inj_open;
      initial begin
        writes = 0; inj_closed = 0; inj_open = 0;
        for (int i = 0; i < N; i++) begin
          writes     += pick(i, 37, WR_PCT);
          inj_closed += gated_inject(1'b0, i);
          inj_open   += gated_inject(1'b1, i);
        end
        if (writes == WR_PCT && inj_closed == 0 && inj_open == INJ_PCT)
          $display("PASS: weight gives 80 of 100 writes; gate closed injects 0, gate open injects 5");
        else
          $fatal(1, "FAIL: %0d writes, %0d injected with gate closed, %0d with gate open",
                 writes, inj_closed, inj_open);
        $finish;
      end
    endmodule

    Related topics

    • Layered Constraint Architecture

    • Protocol Legality and Error Injection

    Previous

    169 / 259

    Next: Protocol Legality and Error Injection

    Up next

    Up nextProtocol Legality and Error Injection

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Transaction Design for Stimulus
    • Randomizing Scenarios, Not Just Items
    • Generator Patterns