Functional Verification in VLSI — VLSI Trainers
VLSI Trainers Verilog Series · 18 / 18
Verilog Series · Module 03

Functional Verification in VLSI

Understand simulation, synthesis, and how testbenches are used to verify that a design works correctly before it reaches silicon.

🔍 Simulation vs Synthesis — Overview

In the VLSI design flow, simulation and synthesis are two distinct but equally critical steps. Together they ensure that a design is both functionally correct and physically realizable as hardware.

🖥 Simulation

  • Runs the design in software
  • Checks functional correctness
  • Uses test inputs and checks outputs
  • Done before synthesis
  • Tool: ModelSim, VCS, Xcelium

⚙️ Synthesis

  • Translates RTL to hardware gates
  • Produces a gate-level netlist
  • Targets FPGA or ASIC
  • Done after simulation
  • Tool: Synopsys DC, Cadence Genus
Key insight: Simulation proves the design is logically correct. Synthesis proves it is physically implementable. Both must pass before tape-out.

🖥 What is Simulation?

After a design has been specified and entered in Verilog, it must be checked to ensure it is functionally correct. Simulation is the process of running the design through a software simulator to observe how it behaves.

📥
Apply Stimulus
Input test vectors (signals, waveforms, sequences) are applied to the design under test.
Simulate Behavior
The simulator executes the Verilog model, propagating values through all concurrent blocks in time.
📤
Capture Outputs
Outputs are observed via waveforms ($dumpvars) or printed using $display / $monitor.
Compare & Debug
Outputs are compared against expected values. Mismatches reveal bugs — fix RTL and re-simulate.
Why simulate early? Bugs found in simulation cost almost nothing to fix. The same bug found after tape-out can cost millions of dollars and months of schedule.

Types of Simulation

🔷
RTL Simulation
Runs on the original Verilog source. Fast, no timing. Used to verify logical correctness first.
🔶
Gate-Level Simulation (GLS)
Runs on the synthesized netlist. Includes cell delays from the standard cell library. Catches timing issues.
Timing Simulation
Adds SDF (Standard Delay Format) back-annotated delays. Closest to real hardware behavior.
🔁
Formal Verification
Mathematically proves properties of the design without needing test vectors. Exhaustive but tool-intensive.

⚙️ What is Synthesis?

Synthesis translates a high-level HDL description (Verilog or VHDL) into a gate-level netlist — a structural representation of logic gates and flip-flops that can be mapped to an FPGA or fabricated as an ASIC.

Fig 1 — What Synthesis Does
INPUT
RTL / Behavioral
Verilog / VHDL
Synthesis Tool
DC / Genus / Yosys
OUTPUT
Gate-Level Netlist
mapped to std cells
TARGET
FPGA / ASIC

What the Synthesis Tool Does

Synthesis limitation: Not all Verilog is synthesizable. initial blocks, #delay, $display, and many behavioral constructs are simulation-only. Always write RTL with synthesis in mind.

Incremental Synthesis Process

Synthesis is typically done bottom-up — starting with smaller sub-modules at behavioral or data-flow level, progressively refining each until the full design is available as gate/RTL-level modules ready for place-and-route.

🔄 The Complete Design Flow

Simulation and functional verification sit at the heart of the VLSI design flow, repeated at multiple stages:

Fig 2 — VLSI Design Flow with Verification Gates
1
Write RTL
Design in Verilog / VHDL at behavioral or data-flow level
2
RTL Simulation ✓
Testbench drives DUT — verify functional correctness
3
Synthesis
RTL → gate-level netlist using std cell library
4
Gate-Level Simulation ✓
Re-verify netlist — check for synthesis mismatches
5
Static Timing Analysis (STA)
Verify setup/hold timing across all paths
6
Place & Route (P&R)
Physical layout of cells and metal routing
7
Post-Layout Simulation ✓
Timing simulation with back-annotated SDF delays
8
Tape-Out
Send GDSII to foundry for fabrication

Functional Verification

Any hardware design process requires testing. Testing has two dimensions:

🔷 Functional Testing

  • Does the design do what it’s supposed to do?
  • Checked by applying test vectors and comparing outputs to a golden reference
  • Performed via simulation using a testbench
  • Language-level: Verilog, SystemVerilog, UVM

⏱ Timing Testing

  • Does the design meet its timing requirements?
  • Checked via Static Timing Analysis (STA) and timing simulation
  • Ensures setup and hold margins are met at all flip-flops
  • Tools: PrimeTime, Tempus
Both dimensions matter. A design can be functionally correct but still fail in silicon if it violates timing — or pass timing analysis but compute the wrong result. You need both.

🧪 The Testbench

A testbench is a Verilog module written specifically to test another module — called the Design Under Test (DUT). The testbench has no ports of its own; it instantiates the DUT internally and drives it with stimulus.

Fig 3 — Testbench Architecture
TESTBENCH MODULE (no ports) Stimulus Generator initial / always $readmemh $random inputs DUT Design Under Test instantiated inside TB outputs Output Checker $display $monitor assert / compare Clock & Reset always #5 clk=~clk

Key Characteristics of a Testbench

🚫
No Ports
A testbench module has no input/output ports. It is self-contained and not intended for synthesis.
📦
Instantiates DUT
The design being tested is instantiated inside the testbench and connected to internal signals.
Generates Stimulus
Uses initial blocks, always, and timing controls (#delay) to apply test vectors in sequence.
🔍
Checks Outputs
Compares DUT outputs against expected values using if statements, assertions, or $display.

📝 Testbench Structure & Code

Testbenches are written at the behavioral level — the constructs there are flexible enough to generate any type of test signal.

Fig 4 — Complete Testbench Example (4-bit Adder)

Testbench for a 4-bit adder with carry — self-checking
// ─── Testbench module — no ports ───────────────────────────────
module adder_tb;

  // 1. Declare signals to connect to DUT
  reg  [3:0] a, b;
  reg        cin;
  wire [3:0] sum;
  wire       cout;

  // 2. Instantiate the Design Under Test (DUT)
  adder_4bit dut (
    .a(a), .b(b), .cin(cin),
    .sum(sum), .cout(cout)
  );

  // 3. Apply stimulus in an initial block
  initial begin
    $dumpfile("adder_tb.vcd");   // for waveform viewing
    $dumpvars(0, adder_tb);

    // Test case 1: 3 + 4 + 0 = 7
    a = 4'd3; b = 4'd4; cin = 0;
    #10;
    if ({cout,sum} !== 5'd7)
      $display("FAIL: expected 7, got %0d", {cout,sum});
    else
      $display("PASS: 3+4+0 = %0d", {cout,sum});

    // Test case 2: 15 + 1 + 0 = 16 (carry out!)
    a = 4'd15; b = 4'd1; cin = 0;
    #10;
    if ({cout,sum} !== 5'd16)
      $display("FAIL: expected 16, got %0d", {cout,sum});
    else
      $display("PASS: 15+1+0 = %0d", {cout,sum});

    // Test case 3: random sweep
    repeat(20) begin
      a = $random; b = $random; cin = $random;
      #10;
    end

    $display("Simulation complete.");
    $finish;
  end

endmodule

Fig 5 — Clock Generation Pattern

Standard patterns for clock and reset in a testbench
module clocked_tb;

  reg clk, rst;

  // Clock generation — toggles every 5 time units → 10ns period
  initial clk = 0;
  always #5 clk = ~clk;

  // Reset sequence — assert for 2 cycles, then release
  initial begin
    rst = 1;          // assert reset
    @(posedge clk);  // wait for rising edge
    @(posedge clk);  // hold for 2 cycles
    rst = 0;          // release reset
  end

  // DUT instantiation
  my_dut u1 (.clk(clk), .rst(rst), ...);

endmodule

Useful System Tasks in Testbenches

TaskPurposeExample
$displayPrint once at the moment it executes$display("val=%0d", x);
$monitorAuto-print whenever listed signals change$monitor("%t a=%b", $time, a);
$strobePrint at end of current time step$strobe("q=%b", q);
$dumpvarsRecord waveforms to VCD file for GTKWave$dumpvars(0, tb);
$readmemhLoad hex test vectors from a file$readmemh("vec.hex", mem);
$finishEnd simulation$finish;
$randomGenerate pseudo-random 32-bit integera = $random % 16;

🌳 Hierarchical Addressing

When testing a module, you may need to access signals buried deep inside sub-modules — signals that are not exposed as ports. Verilog allows this through hierarchical addressing using the dot (.) operator.

Fig 6 — Hierarchical Path to an Internal Signal
// Design hierarchy:
//   top_tb  →  dut (my_cpu)  →  alu_inst  →  carry_out

module top_tb;

  my_cpu dut ( ... );

  initial begin
    // Access internal signal using hierarchical path
    $display("ALU carry = %b", top_tb.dut.alu_inst.carry_out);

    // You can also force/release internal values for debug
    force top_tb.dut.alu_inst.carry_out = 1'b1;
    #20;
    release top_tb.dut.alu_inst.carry_out;
  end

endmodule
Fig 7 — Module Hierarchy Tree
top_tb  (testbench)
└── dut  instance of my_cpu
├── alu_inst  instance of alu
└── carry_out  wire (internal)
├── reg_file  instance of regfile
└── ctrl_unit  instance of controller
Access path: top_tb.dut.alu_inst.carry_out
Use with care: Hierarchical access bypasses the module boundary and can make testbenches tightly coupled to the design internals. Prefer exposing debug signals as ports or using dedicated debug interfaces in production flows.

🛠 Verification Methods

Multiple techniques exist for functional verification, ranging from basic directed testing to advanced formal methods:

🎯
Directed Testing
Basic

Manually crafted test cases targeting specific scenarios. Good for corner cases. Time-consuming and may miss unknown bugs.

🎲
Constrained Random
Recommended

Automatically generates random inputs within constraints. Finds bugs that directed tests miss. Foundation of SystemVerilog / UVM flows.

📊
Coverage-Driven
Recommended

Tracks which design states/conditions have been exercised. Simulation stops only when coverage goals are met — not just when tests pass.

🔢
Formal Verification
Advanced

Mathematically proves or disproves properties using model checking. Exhaustive — no test vectors needed. Used for protocols and safety-critical blocks.

🏗
UVM Testbench
Advanced

Universal Verification Methodology — industry-standard reusable testbench architecture in SystemVerilog. Used on all major SoC projects.

🔍
Assertions (SVA)
Protocol

Inline behavioral properties embedded in RTL using SystemVerilog Assertions. Immediately flag protocol violations during simulation.

📊 Simulation vs Synthesis — Comparison

AspectSimulationSynthesis
PurposeVerify functional correctnessGenerate hardware implementation
What it producesWaveforms, log files, pass/fail reportsGate-level netlist (Verilog / EDIF)
When it runsBefore and after synthesisAfter RTL simulation passes
Timing included?RTL sim: No  |  Post-layout sim: YesYes — timing constraints drive optimization
Supports initial?✅ Yes❌ No — simulation-only construct
Supports #delay?✅ Yes❌ No — ignored or causes errors
ToolsModelSim, VCS, Xcelium, IcarusSynopsys DC, Cadence Genus, Yosys
TargetSoftware (host PC)FPGA bitstream or ASIC GDSII
Common pitfall: A design that simulates correctly can still fail after synthesis if it contains latches from incomplete if/case statements, or uses constructs the synthesis tool doesn’t support. Always run gate-level simulation after synthesis to catch these issues.
Compiler Directives, Hierarchy & UDP☰ Verilog Series IndexNext
Scroll to Top