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
    • Senior Debug Playbook
    • Reviewing Verification Code
    • Verification Strategy: Spec to Sign-Off
    • SystemVerilog Interview Q&A Bank
    • Capstone: Verify a Parameterized FIFO
      • Spec Analysis and Verification Plan
      • Interface and Harness
      • Generator, Driver and Environment
      • Monitor, Scoreboard and Model
      • Assertions and Coverage
      • Regression, Holes and Sign-off
  • Principal-Level Verification34
Loading lesson

←Capstone: Verify a Parameterized FIFO
  1. Topics
  2. SystemVerilog
  3. Senior & Interview Preparation
  4. Capstone: Verify a Parameterized FIFO
  5. Generator, Driver and Environment
On this page

    Part 8 of 9, lesson 20 of 23Part 8 of 9/Senior & Interview Prep/Lesson 20 of 23Intermediate

    Generator, Driver and Environment

    Transactions with scenario knobs, a phased generator, a one-cycle driver and the environment that wires them, as listings of one capstone environment.

    The previous lesson built the interface and the harness. This lesson fills the environment: the stimulus side that decides what to push and pop, and the environment class that builds every mailbox and starts every thread. The listings are fragments of one environment; they compile together with the interface, the monitors, the scoreboard and a FIFO.

    Transactions and the generator

    The transaction carries two constraint layers. The legality layer encodes what is always true, and the scenario layer uses soft constraints and knob fields so tests dial traffic shape without new classes.

    These listings are class declarations with no top module, so this course's checks do not run them.

    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
    // not-run: fragment: class declarations only, no top module
    typedef enum bit [1:0] { OP_PUSH, OP_POP, OP_BOTH, OP_IDLE } op_e;
    
    class fifo_txn #(parameter int WIDTH = 8);
      // randomized fields
      rand op_e             op;
      rand bit [WIDTH-1:0]  data;
      rand int unsigned     gap;        // idle cycles before this op
    
      // scenario knobs — set by the generator, not randomized
      int push_wt  = 5;                 // relative op weights
      int pop_wt   = 5;
      int both_wt  = 2;
      int idle_wt  = 1;
      int max_gap  = 3;
    
      // legality layer (hard)
      constraint c_gap_legal { gap <= max_gap; }
    
      // scenario layer (weights via knobs; soft data shaping)
      constraint c_op_dist {
        op dist { OP_PUSH := push_wt, OP_POP  := pop_wt,
                  OP_BOTH := both_wt, OP_IDLE := idle_wt };
      }
      constraint c_data_interesting {
        if (WIDTH > 1 && WIDTH < 30) {
          soft data dist { 0 := 1, ((1 << WIDTH) - 1) := 1, [1:(1 << WIDTH) - 2] :/ 8 };
        } else {
          // WIDTH 1 has no middle values; very wide WIDTH skips the
          // enumerated middle range (1 << WIDTH overflows an int).
          soft data dist { 0 := 1, ((1 << WIDTH) - 1) := 1 };
        }
      }
    
      function void display(string tag);
        $display("[%0t] %s op=%s data=0x%0h gap=%0d",
                 $time, tag, op.name(), data, gap);
      endfunction
    endclass
    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
    // not-run: fragment: needs fifo_txn from the listing above and has no top module
    typedef enum { SCN_SMOKE, SCN_BURST_FILL, SCN_DRAIN,
                   SCN_MIXED, SCN_BACKPRESSURE } scenario_e;
    
    class generator #(parameter int WIDTH = 8);
      mailbox #(fifo_txn #(WIDTH)) gen2drv;
      int n_txns = 200;
    
      function new(mailbox #(fifo_txn #(WIDTH)) mb);
        gen2drv = mb;
      endfunction
    
      // configure one txn's knobs for a scenario phase
      function void shape(fifo_txn #(WIDTH) t, scenario_e s);
        case (s)
          SCN_BURST_FILL:   begin t.push_wt=10; t.pop_wt=0;  t.both_wt=0; t.idle_wt=0; t.max_gap=0; end
          SCN_DRAIN:        begin t.push_wt=0;  t.pop_wt=10; t.both_wt=0; t.idle_wt=0; t.max_gap=0; end
          SCN_MIXED:        begin t.push_wt=5;  t.pop_wt=5;  t.both_wt=3; t.idle_wt=1; t.max_gap=2; end
          SCN_BACKPRESSURE: begin t.push_wt=8;  t.pop_wt=1;  t.both_wt=0; t.idle_wt=1; t.max_gap=1; end
          default:          begin t.push_wt=6;  t.pop_wt=4;  t.both_wt=1; t.idle_wt=1; t.max_gap=3; end
        endcase
      endfunction
    
      task run_scenario(scenario_e s, int count);
        fifo_txn #(WIDTH) t;
        repeat (count) begin
          t = new();
          shape(t, s);
          if (!t.randomize())
            $fatal(1, "generator: randomize failed in scenario %0d", s);
          gen2drv.put(t);              // bounded mailbox: backpressure for free
        end
      endtask
    
      // default mix: fill, drain, then sustained mixed traffic
      task run();
        run_scenario(SCN_BURST_FILL,   n_txns/4);  // drive toward full (F-02)
        run_scenario(SCN_DRAIN,        n_txns/4);  // drive toward empty (F-03)
        run_scenario(SCN_MIXED,        n_txns/2);  // hammer simultaneity (F-07)
      endtask
    endclass
    Diagram
    FILL LEVEL OVER ONE DEFAULT RUN
    
      DEPTH ┤        ╭────╮ burst-fill parks at full ── F-02, F-05 hit
            │       ╱      ╲
            │      ╱        ╲ drain to empty ── F-03, F-06 hit
            │     ╱          ╲      ╭─╮ ╭──╮
            │    ╱            ╲    ╱   ╲╱   ╲   mixed: boundary crossings,
          0 ┤───╯              ╰──╯          ╰─ simultaneity, wrap-around
            └────────────────────────────────────────► time
            flat 50/50 instead: ~~~~~ half-full noise, boundaries never seen
    • A flat 50/50 push/pop mix hovers around half-full forever. Phased scenarios walk the fill level to each boundary instead.

    • Re-shaping traffic is an assignment to knobs, not a new class. Directed tests pin data because the distribution is soft.

    • The generator checks every randomize call. A knob typo fails loudly instead of freezing stimulus.

    Driver and environment

    The driver translates each transaction into pin wiggles on the driver clocking block. The environment builds every mailbox, starts every thread, and wires reset and end-of-test discipline.

    These listings are class declarations that depend on the other listings, with no top module, so this course's checks do not run them.

    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
    // not-run: fragment: needs fifo_if and fifo_txn from the listings above and has no top module
    class fifo_driver #(parameter int WIDTH = 8);
      virtual fifo_if #(WIDTH).DRV vif;
      mailbox #(fifo_txn #(WIDTH)) gen2drv;
    
      function new(virtual fifo_if #(WIDTH).DRV v,
                   mailbox #(fifo_txn #(WIDTH)) mb);
        vif = v;
        gen2drv = mb;
      endfunction
    
      task run();
        fifo_txn #(WIDTH) t;
        forever begin
          gen2drv.get(t);
          repeat (t.gap) @(vif.drv_cb);   // pacing gap: idle cycles before the op
          if (!vif.rst_n) continue;       // reset-skip: never drive into reset
          case (t.op)
            OP_PUSH: begin vif.drv_cb.in_valid <= 1'b1; vif.drv_cb.in_data <= t.data; vif.drv_cb.out_ready <= 1'b0; end
            OP_POP:  begin vif.drv_cb.in_valid <= 1'b0; vif.drv_cb.out_ready <= 1'b1; end
            OP_BOTH: begin vif.drv_cb.in_valid <= 1'b1; vif.drv_cb.in_data <= t.data; vif.drv_cb.out_ready <= 1'b1; end
            OP_IDLE: begin vif.drv_cb.in_valid <= 1'b0; vif.drv_cb.out_ready <= 1'b0; end
          endcase
          @(vif.drv_cb);                  // hold one cycle so the handshake can complete
          vif.drv_cb.in_valid <= 1'b0;    // return to idle: every op is one cycle wide
          vif.drv_cb.out_ready <= 1'b0;
        end
      endtask
    endclass
    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
    50
    // not-run: fragment: needs the generator, driver, monitor and scoreboard classes and has no top module
    class fifo_env #(parameter int WIDTH = 8, parameter int DEPTH = 16);
      virtual fifo_if #(WIDTH) vif;
      mailbox #(fifo_txn #(WIDTH)) gen2drv;
      mailbox #(logic [WIDTH-1:0]) mb_in, mb_out;   // 4-state: an X word survives
      generator #(WIDTH) gen;
      fifo_driver #(WIDTH) drv;
      fifo_in_mon #(WIDTH) in_mon;
      fifo_out_mon #(WIDTH) out_mon;
      fifo_sb #(WIDTH) sb;
      int n_txns = 200;
    
      function new(virtual fifo_if #(WIDTH) v);
        vif = v;
        gen2drv = new(1);               // bounded: generator blocks when driver stalls
        mb_in = new();
        mb_out = new();
        gen = new(gen2drv);
        drv = new(vif, gen2drv);
        in_mon = new(vif, mb_in);
        out_mon = new(vif, mb_out);
        sb = new(mb_in, mb_out);
        gen.n_txns = n_txns;
      endfunction
    
      // reset asserted mid-traffic: the model drops in-flight data (spec rule 8)
      task reset_watcher();
        forever begin
          @(negedge vif.rst_n);
          sb.flush();
        end
      endtask
    
      task run();
        fork
          drv.run();
          in_mon.run();
          out_mon.run();
          sb.run();
          reset_watcher();
        join_none
        gen.run();                      // blocking: returns after n_txns
        // num() is a method call, not a signal, so wait() on it may never wake: poll per clock
        while (gen2drv.num() != 0 || mb_in.num() != 0 || mb_out.num() != 0) @(vif.mon_cb);
        repeat (5) @(vif.mon_cb);        // settle: last beats cross the scoreboard
        // the pins carry no count, so the test supplies DUT occupancy
        // with a hierarchical probe: the capstone FIFO exposes count
        sb.report(tb_top.dut.count);
      endtask
    endclass
    • Every op maps to pins explicitly: push and pop each hold for one cycle, then return to idle.

    • The bounded mailbox couples generator and driver loosely. A slow DUT stalls the generator, not the test.

    • Reset has two wirings: the driver skips reset cycles, and the watcher flushes the scoreboard on reset assert.

    Driving DUT inputs from raw interface signals instead of the driver clocking block brings the races back.

    Likely question: “Burst traffic never reaches full. What do you change?”

    • Phase the stimulus: burst-fill, then drain, then mixed.

    • Shape with knobs, not new classes: push weight up, gap to zero.

    • Prove the corner with a cover bin, not a waveform glance.

    Check yourself The generator sets push weight 10 and pop weight 0 with max_gap 0. What fill level does the run approach, and which features does that target?

    Full. Sustained pushes with no pops park at full, targeting F-02 (full exact) and F-05 (push blocked).

    Check yourself A teammate writes the data shaping as a hard data inside {'0, '1};. A directed test then pins data to A5 with an inline constraint and randomize fails. What is wrong?

    Scenario shaping went into a hard constraint, so it fights the directed test. Shaping must be soft, as c_data_interesting is, so directed tests override it without conflict.

    Related topics

    • Next: Monitor, Scoreboard and Model

    • Testbench: generator patterns

    Previous

    238 / 275

    Next: Monitor, Scoreboard and Model

    Up next

    Up nextMonitor, Scoreboard and Model

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Spec Analysis and Verification Plan
    • Assertions and Coverage
    • Regression, Holes and Sign-off