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. Randomizing Scenarios, Not Just Items

Part 6 · Testbench Architecture · Intermediate

Randomizing Scenarios, Not Just Items

One solver call over whole scenarios: correlated items, gaps, and order.

On this page

    The bugs live between transactions

    Per-item solving leaves cross-item patterns to luck: a read after a write to one address, or back-to-back traffic at a FIFO mark. Scenario solving makes the collection the rand object, so relationships become constraints instead of accidents.

    Pattern

    Mechanism

    Guarantee

    Read-after-write

    First and last items share addr

    Hazard pair by construction

    Burst then drain

    Phase split on the index

    Flood precedes drain

    Drive order

    Unique permutation array

    Contents decoupled from order

    Contiguous stream

    Recurrence across items

    Each addr follows the previous

    Scenarios stay tens of items: one solve covers tens of fields times tens of items. Bulk traffic windows one scenario per slice.

    The scenario class

    The next listing is not run in this course's checks. It is a fragment that uses bus_txn 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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    // not-run: fragment: bus_txn is defined elsewhere, no top module
    class rw_hazard_scenario;
      rand int unsigned n;
      rand bus_txn      q[];
      rand bit [3:0]    gap[];
      rand int unsigned order[];
      constraint shape_c {
        n inside {[4:12]};
        q.size() == n;
        gap.size() == n;
        order.size() == n;
      }
      constraint gap_c {
        foreach (gap[i]) {
          gap[i] inside {[0:12]};
          (i < n / 2) -> gap[i] == 0;   // burst first, then drain
        }
      }
      constraint order_c {
        unique {order};
        foreach (order[i]) order[i] < n;   // a permutation of 0..n-1
      }
      constraint hazard_c {
        q[0].write == 1;
        q[n-1].write == 0;
        q[n-1].addr == q[0].addr;
        foreach (q[i]) if (i > 0)
          (int'(q[i].addr) - int'(q[i-1].addr)) inside {[-64:64]};
      }
      function void pre_randomize();
        q = new[12];
        foreach (q[i]) q[i] = new();
        gap = new[12];
      endfunction
    endclass
    
    class walking_addr;
      rand bus_txn q[8];
      function new();
        foreach (q[i]) q[i] = new();   // the solver never constructs elements
      endfunction
      constraint walk_c {
        q[0].addr == 32'h1000_0000;
        foreach (q[i]) if (i > 0)
          q[i].addr == q[i-1].addr + (32'(q[i-1].len) << 2);
      }
    endclass

    Allocation precedes solving: the solver never constructs objects, so pre_randomize (or the constructor, for a fixed-size array) builds every element first. With a null element, tools either error at randomize or leave it unsolved; never rely on either. Address neighbours use signed differences with an edge-safe band, never a variable-bound range that wraps near zero.

    Common mistakes

    Common pitfall

    Constraining array size without constructing elements leaves null handles. Depending on the tool, randomize then errors or leaves those elements unsolved, so the scenario is not what you asked for.

    Interview

    Likely question: how do you guarantee a hazard pair?

    With a scenario object: rand arrays plus rand shape, foreach relations to the previous item, and one solve. Allocation in pre_randomize plus cadence in three levels closes the answer.

    Check yourself Why allocate to the maximum count instead of n?

    The solver resizes storage but never calls new on elements. Full construction up front keeps every indexed element solvable.

    Check yourself What does unique on an index array buy?

    A constrained random permutation. Contents and drive order solve together, so ordering properties become constraints.

    Testbench

    A procedural checker for the hazard_c, gap_c and order_c rules, plus a testbench that runs it on one legal scenario table and one broken table.

    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
    46
    47
    48
    49
    module tb_scenario;
      localparam int N = 6;
      reg [31:0] addr [0:N-1];
      reg        is_write [0:N-1];
      reg [3:0]  gap [0:N-1];
      reg [2:0]  order [0:N-1];
      integer    problems;
      integer    v;
    
      // Counts broken scenario rules in the current table.
      task automatic check_scen(output integer bad);
        integer i, j, d;
        begin
          bad = 0;
          if (!(is_write[0] == 1 && is_write[N-1] == 0)) bad = bad + 1;  // hazard pair
          if (addr[N-1] != addr[0]) bad = bad + 1;
          for (i = 1; i < N; i = i + 1) begin
            d = addr[i] - addr[i-1];                    // signed neighbour step
            if (d < -64 || d > 64) bad = bad + 1;
          end
          for (i = 0; i < N; i = i + 1) begin
            if (gap[i] > 12) bad = bad + 1;
            if (i < N / 2 && gap[i] != 0) bad = bad + 1;  // burst, then drain
            if (order[i] >= N) bad = bad + 1;             // permutation of 0..N-1
            for (j = i + 1; j < N; j = j + 1)
              if (order[i] == order[j]) bad = bad + 1;
          end
        end
      endtask
    
      initial begin
        problems = 0;
        addr[0] = 32'h1000; is_write[0] = 1; gap[0] = 0;  order[0] = 3;
        addr[1] = 32'h1020; is_write[1] = 1; gap[1] = 0;  order[1] = 0;
        addr[2] = 32'h1040; is_write[2] = 1; gap[2] = 0;  order[2] = 5;
        addr[3] = 32'h1030; is_write[3] = 0; gap[3] = 3;  order[3] = 1;
        addr[4] = 32'h1010; is_write[4] = 1; gap[4] = 12; order[4] = 4;
        addr[5] = 32'h1000; is_write[5] = 0; gap[5] = 5;  order[5] = 2;
        check_scen(v);
        if (v != 0) problems = problems + 1;     // legal table must pass
        addr[3] = 32'h1100;                      // neighbour jump beyond 64
        order[5] = 3;                            // duplicate drive slot
        check_scen(v);
        if (v != 3) problems = problems + 1;     // two bad steps plus one duplicate
        if (problems == 0) $display("PASS: legal scenario accepted; broken band and order caught");
        else $fatal(1, "FAIL: %0d scenario checks went wrong", problems);
        $finish;
      end
    endmodule

    Related topics

    • Protocol Legality and Error Injection

    • Generator Patterns

    Previous

    171 / 259

    Next: Generator Patterns

    Up next

    Up nextGenerator Patterns

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Transaction Design for Stimulus
    • Layered Constraint Architecture
    • Shaping Stimulus with Knobs and Policies