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. Interface and Harness
On this page

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

    Interface and Harness

    The capstone interface with its clocking blocks, the static harness, and a runnable handshake test for a depth-4 FIFO.

    The interface

    One interface carries both sides plus the flags. The monitors need cross-side visibility, so splitting push and read sides would blind them.

    The listing below is an interface with no testbench, so this course's checks do not run it on its own.

    fifo_if.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
    // not-run: fragment: an interface only, no top module
    interface fifo_if #(parameter int WIDTH = 8)
              (input logic clk, input logic rst_n);
      timeunit 1ns; timeprecision 1ps;
    
      // write (push) side
      logic             in_valid;
      logic             in_ready;
      logic [WIDTH-1:0] in_data;
    
      // read (pop) side
      logic             out_valid;
      logic             out_ready;
      logic [WIDTH-1:0] out_data;
    
      // status
      logic             full;
      logic             empty;
    
      // driver clocking: sample pre-edge, drive 2ns after the edge
      clocking drv_cb @(posedge clk);
        default input #1step output #2ns;
        output in_valid, in_data, out_ready;
        input  in_ready, out_valid, out_data, full, empty;
      endclocking
    
      // monitor clocking: inputs only — a monitor cannot drive, by construction
      clocking mon_cb @(posedge clk);
        default input #1step;
        input in_valid, in_ready, in_data;
        input out_valid, out_ready, out_data;
        input full, empty;
      endclocking
    
      modport DRV (clocking drv_cb, input rst_n);
      modport MON (clocking mon_cb, input rst_n);
    endinterface
    • Both clocking blocks sample pre-edge values, exactly what the DUT flops see.

    • Only the three TB-driven signals are clocking outputs, driven 2ns past the edge, clear of hold.

    • The monitor block is input-only, so a monitor cannot cause contention by construction.

    • Reset stays outside the clocking blocks, since reset logic needs raw asynchronous access.

    Watch in_ready and full. The fourth push parks the FIFO at full, and the 0xB1 offer is ignored while the pop proceeds. Each slot shows values just after its rising edge: the edge that starts slot 5 saw in_ready 0 from slot 4, so 0xB1 was not taken, and in_valid drops before the next edge.

    • Slot 0: reset: in_valid 0, in_ready 1, full 0, empty 1.

    • Slot 1: first push 0xA1, empty falls.

    • Slot 2: push 0xA2.

    • Slot 3: push 0xA3.

    • Slot 4: fourth push 0xA4 fills the FIFO: full 1, in_ready 0.

    • Slot 5: out_ready 1: the pop proceeds while in_valid is still 1, so 0xB1 is ignored and count falls to 3.

    • Slot 6: drain continues, count 2.

    • Slot 7: count 1, out_valid still 1.

    Harness

    tb_top owns the clock, the reset, the single interface instance, the DUT and the environment. Nothing else lives at the top.

    This harness is not run on its own. It needs the fifo RTL from the last lesson of this topic and the environment classes from the next one.

    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
    51
    52
    53
    54
    55
    56
    57
    // not-run: fragment: needs the fifo RTL (last capstone lesson) and the env classes (next lesson)
    module tb_top;
      timeunit 1ns; timeprecision 1ps;
      parameter int WIDTH = 8;
      parameter int DEPTH = 16;
    
      logic clk;
      logic rst_n;
    
      // clock: 100 MHz
      initial clk = 0;
      always #5 clk = ~clk;
    
      // reset: assert async, deassert at the inactive (falling) edge,
      // so no flop sees rst_n change at the same edge it samples
      initial begin
        rst_n = 0;
        repeat (4) @(posedge clk);
        @(negedge clk);
        rst_n = 1;
      end
    
      // the one real interface instance
      fifo_if #(.WIDTH(WIDTH)) fif (.clk(clk), .rst_n(rst_n));
    
      // DUT hookup
      fifo #(.WIDTH(WIDTH), .DEPTH(DEPTH)) dut (
        .clk       (clk),
        .rst_n     (rst_n),
        .in_valid  (fif.in_valid),
        .in_ready  (fif.in_ready),
        .in_data   (fif.in_data),
        .out_valid (fif.out_valid),
        .out_ready (fif.out_ready),
        .out_data  (fif.out_data),
        .full      (fif.full),
        .empty     (fif.empty)
      );
    
      // environment: generator, driver, monitors, scoreboard (built below)
      fifo_env #(.WIDTH(WIDTH), .DEPTH(DEPTH)) e;
    
      initial begin
        e = new(fif);
        wait (rst_n == 1);
        repeat (2) @(posedge clk);   // two clean edges: no stimulus into reset
        e.run();
        $display("capstone run complete");
        $finish;
      end
    
      // global watchdog — a hang becomes a report, not a lost regression slot
      initial begin
        #2ms;
        $fatal(1, "GLOBAL TIMEOUT: test did not finish");
      end
    endmodule
    • The test ends with $finish. Without it only the watchdog would stop the run.

    • The timeunit gives the unitless #5 its unit. #2ms is a time literal and carries its own.

    • Reset deasserts at a falling edge, away from the rising edge the flops sample on, and the environment waits two more edges.

    • Exactly one interface instance exists. Every component receives a virtual handle to it.

    Testbench

    A depth-4 FIFO with a directed handshake test. It pushes four words, proves full, then pops all four in order and proves empty.

    fifo4.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
    module fifo4 (
      input  logic       clk,
      input  logic       rst_n,
      input  logic       in_valid,
      output logic       in_ready,
      input  logic [7:0] in_data,
      output logic       out_valid,
      input  logic       out_ready,
      output logic [7:0] out_data,
      output logic       full,
      output logic       empty
    );
      localparam int DEPTH = 4;
      logic [7:0] mem [DEPTH];
      logic [1:0] wr_ptr = 2'd0, rd_ptr = 2'd0;
      logic [2:0] count = 3'd0;
      wire push = in_valid && in_ready;
      wire pop = out_valid && out_ready;
      assign full = (count == 3'd4);
      assign empty = (count == 3'd0);
      assign in_ready = !full;
      assign out_valid = !empty;
      assign out_data = mem[rd_ptr];
      always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
          count <= 3'd0;
          wr_ptr <= 2'd0;
          rd_ptr <= 2'd0;
        end else begin
          if (push) mem[wr_ptr] <= in_data;
          if (push) wr_ptr <= wr_ptr + 2'd1;
          if (pop) rd_ptr <= rd_ptr + 2'd1;
          if (push && !pop) count <= count + 3'd1;
          else if (pop && !push) count <= count - 3'd1;
        end
      end
    endmodule
    

    Forgetting the watchdog lets a hung corner test burn the nightly regression slot. Forgetting $finish after run() leaves the watchdog as the only way the test ever ends.

    Check yourself Why does the interface keep push and read sides together instead of splitting them?

    The monitors need cross-side visibility. Splitting the sides would blind them to each other, and the scoreboard needs both streams.

    Check yourself At the fourth push the FIFO is full and the producer keeps offering 0xB1 while the consumer starts popping. Is 0xB1 pushed in that cycle?

    No. in_ready is low while full is high, so the offer is not taken that cycle. It is accepted only after the pop frees a slot and in_ready rises, per Q-003.

    Related topics

    • Next: Generator, Driver and Environment

    Previous

    237 / 275

    Next: Generator, Driver and Environment

    Up next

    Up nextGenerator, Driver and Environment

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Monitor, Scoreboard and Model
    • Assertions and Coverage
    • Regression, Holes and Sign-off
    clkin_validin_readyin_data0a1a2a3a4b1out_readyout_validfullempty
    Four pushes fill a depth-4 FIFO, then a drain with the producer still offering