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 Foundations35
  • Object-Oriented SystemVerilog for Verification35
  • Constraint Randomization — Interview-Critical Deep Dive66
  • SystemVerilog Assertions — Interview-Critical Deep Dive63
  • Functional Coverage in SystemVerilog28
  • Testbench Architecture in SystemVerilog38
    • Testbench Anatomy & Layering
      • The Layered Testbench
      • The Static Harness and Environment
      • Tests as Classes
      • The Environment Class
      • Complete Mini Testbench
      • program Blocks: Use or Skip?
    • Stimulus: Generators, Transactions & Drivers
    • Monitors, Scoreboards & Reference Models
    • Testbench Synchronization & Phasing
    • Self-Checking Tests & Regression
    • From Hand-Built TB to UVM
  • Advanced Language & Integration30
  • Senior & Interview Preparation34
  • Principal-Level Verification28
Loading lesson

‹Testbench Anatomy & Layering
  1. Topics
  2. SystemVerilog
  3. Testbench Architecture in SystemVerilog
  4. Testbench Anatomy & Layering
  5. The Layered Testbench

Part 6 · Testbench Architecture · Intermediate

The Layered Testbench

Five testbench layers, why each boundary is a reuse point, and why flat testbenches fail.

On this page

    The five layers

    Each layer speaks one level of abstraction. Lower layers know pins and cycles, and upper layers know scenarios and intent.

    Layer

    Responsibility

    Knows

    Signal

    Interface, clocking blocks, DUT pins

    Pin names and timing

    Command

    Driver and monitor translate one transaction to pin activity and back

    The handshake protocol

    Functional

    Scoreboard and reference model check each transaction

    Expected versus actual data

    Scenario

    Generator produces streams of related transactions

    Stories, not single items

    Test

    Selects scenarios, sets knob values

    Intent, no protocol code

    A layer talks only to its neighbours. That restriction is what keeps pieces replaceable.

    Check yourself Which layers may name a pin, and which layer holds the test intent?

    Only the signal and command layers name pins. Intent lives only in the test layer, which holds scenario selection and knob values and no protocol code.

    Each boundary is a swap point

    Because only the bottom two layers know widths and timing, everything above them survives a DUT retarget. The transaction type is parameterized once, and upper layers never mention widths again.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // snippet
    // One transaction type for every width; layers above the
    // command layer never mention widths again.
    class bus_txn #(int AW = 16, int DW = 32);
      rand bit [AW-1:0] addr;
      rand bit [DW-1:0] data;
      rand bit          write;
    endclass
    
    // Retargeting 32-bit data to 64-bit data changes three places
    // as one parameter set: the interface DW parameter, this
    // typedef, and the driver and DUT widths together. Changing
    // only the typedef silently truncates data against a 32-bit
    // interface, so the three always move as one.
    typedef bus_txn #(.AW(16), .DW(64)) bus_txn64;
    • Swap DUT widths with one parameter set, as above.

    • Reuse a scenario such as write-then-read-back against a register file, a FIFO, or a memory controller behind a matching driver.

    • Replace random generation with file replay: driver, monitor, and scoreboard never notice.

    • Run one scoreboard against RTL and against a gate-level netlist with only the signal layer rebound.

    The flat testbench

    This is the testbench every layered architecture replaced. It works for a homework assignment and fails everywhere else.

    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
    // bad-example
    // FLAT TB — stimulus, driving, and checking tangled in one block
    module tb_flat;
      logic clk = 0, rst_n;
      logic [15:0] addr;  logic [31:0] wdata, rdata;
      logic valid, write, ready;
    
      always #5 clk = ~clk;
      dut u_dut (.*);
    
      initial begin
        rst_n = 0; valid = 0; #20 rst_n = 1;
    
        // test 1: hard-coded write then read, inline "check"
        @(posedge clk); addr = 16'h10; wdata = 32'hDEAD; write = 1; valid = 1;
        @(posedge clk); while (!ready) @(posedge clk);
        valid = 0;
        @(posedge clk); addr = 16'h10; write = 0; valid = 1;
        @(posedge clk); while (!ready) @(posedge clk);
        if (rdata !== 32'hDEAD) $display("FAIL?");  // maybe checked, maybe stale
        valid = 0;
    
        // test 2: copy-paste of test 1 with different numbers...
        $finish;
      end
    endmodule

    Flat habit

    Why it fails at scale

    Hard-coded values

    Only the values the author thought of get tested.

    Inline checking

    Data sampled at the wrong cycle passes silently.

    One block per master

    A second master duplicates everything, with no concurrency story.

    Fused stimulus and driving

    A handshake change means editing every test.

    Human-read logs

    Regressions cannot consume a verdict nobody printed.

    Common pitfall

    Letting the generator wait on clock edges leaks the command layer into the scenario layer. A scoreboard reading DUT pins directly bypasses the monitor, and test classes holding protocol timing do the command layer's job.

    Testbench

    A module-level smoke test that writes four addresses and reads them back through DUT pins. It is deliberately flat: one block holds stimulus, driving and checking. It only shows the pin activity the layers must produce; the class layers appear in the next lessons.

    flat_dut.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    `timescale 1ns/1ps
    module flat_dut (
      input  logic        clk,
      input  logic        rst_n,
      input  logic        valid,
      input  logic        write,
      input  logic [3:0]  addr,
      input  logic [31:0] wdata,
      output logic        ready,
      output logic [31:0] rdata
    );
      logic [31:0] mem [0:15];
      assign ready = 1'b1;
      always @(posedge clk or negedge rst_n) begin
        if (!rst_n) rdata <= 32'h0;
        else if (valid && write) begin
          mem[addr] <= wdata;
          rdata <= 32'h0;
        end
        else if (valid && !write) rdata <= mem[addr];
      end
    endmodule
    
    Check yourself The flat testbench above checks rdata right after the ready loop. What makes that check fragile?

    Stimulus, driving, and checking share one block with no stated sampling rule. A one-cycle shift in the DUT response turns the check stale, and nothing in the structure would flag the shift.

    Interview

    When asked why testbenches are layered, answer with swap points, not tool names.

    • Each boundary is a place to replace one piece without touching the others.

    • Name one example: retargeting data width, or replaying a failing stream through an unchanged driver.

    Related topics

    • Stimulus generation

    Previous

    234 / 363

    Next: The Static Harness and Environment

    Up next

    Up nextThe Static Harness and Environment

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Tests as Classes
    • The Environment Class
    • Complete Mini Testbench