This guide covers how to simulate and verify your Verilog designs using iVerilog and GTKWave before programming your FPGA.
Why Simulate?
- Find bugs early (before synthesis)
- Verify functionality with test vectors
- Understand timing behavior
- Debug complex logic
- Free and fast (no hardware needed)
- Tools Overview
- Installing Tools
- Basic Simulation Workflow
- Running SHA-256 Simulation
- Running SHA-3 Simulation
- Analyzing Waveforms
- Writing Testbenches
- Debugging Techniques
- Advanced Topics
- Troubleshooting
What it does: Compiles and simulates Verilog code
Features:
- Open source and free
- Fast compilation
- IEEE 1364 compliant
- Cross-platform (Linux, Windows, Mac)
- Produces VCD (Value Change Dump) files
What it does: Visualizes simulation waveforms
Features:
- Open source and free
- Reads VCD files
- Interactive waveform viewer
- Signal searching and filtering
- Measurement tools
# Update package list
sudo apt-get update
# Install iVerilog
sudo apt-get install iverilog
# Install GTKWave
sudo apt-get install gtkwave
# Verify installation
iverilog -v
gtkwave --version- Download from https://bleyer.org/icarus/
- Run installer
- Add to PATH:
C:\iverilog\bin
- Download from https://sourceforge.net/projects/gtkwave/files/
- Extract to folder (e.g.,
C:\gtkwave) - Add to PATH:
C:\gtkwave\bin
# Using Homebrew
brew install icarus-verilog
brew install gtkwave
# Verify
iverilog -v
gtkwave --version1. Write Design (module.v)
↓
2. Write Testbench (module_tb.v)
↓
3. Compile with iVerilog
↓
4. Run Simulation
↓
5. View Waveforms in GTKWave
↓
6. Debug and Iterate
Design: counter.v
module counter (
input wire clk,
input wire rst_n,
output reg [7:0] count
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
count <= 8'd0;
else
count <= count + 1;
end
endmoduleTestbench: counter_tb.v
`timescale 1ns / 1ps
module counter_tb;
reg clk;
reg rst_n;
wire [7:0] count;
// Instantiate DUT (Design Under Test)
counter dut (
.clk(clk),
.rst_n(rst_n),
.count(count)
);
// Clock generation: 10ns period (100 MHz)
initial begin
clk = 0;
forever #5 clk = ~clk;
end
// VCD dump for waveform viewing
initial begin
$dumpfile("counter_tb.vcd");
$dumpvars(0, counter_tb);
end
// Test stimulus
initial begin
$display("Starting counter test...");
// Reset
rst_n = 0;
#100;
rst_n = 1;
// Run for 1000ns
#1000;
// Check result
if (count == 8'd100)
$display("PASS: Counter reached 100");
else
$display("FAIL: Counter is %d", count);
$finish;
end
endmoduleCompile and Run:
# Compile
iverilog -o outflow/counter_sim counter.v counter_tb.v
# Run simulation
vvp outflow/counter_sim
# View waveforms
gtkwave outflow/counter_tb.vcdcd /home/zyzyzynn/dev/vaaman# Compile design and testbench
iverilog -o outflow/sha256_sim rtl/sha256_core.v testbench/sha256_tb.v
# Check for compilation errors
# If successful, no output means compilation passed# Execute simulation
vvp outflow/sha256_simExpected Output:
========================================
SHA-256 Core Testbench
========================================
Test 1: Empty string
[PASS] Test 1: Hash matches expected value
Test 2: 'abc'
[PASS] Test 2: Hash matches expected value
Test 3: 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'
[PASS] Test 3: Hash matches expected value
Test 4: Block of zeros with length 0
[PASS] Test 4: Hash matches expected value
Test 5: 'The quick brown fox jumps over the lazy dog'
[PASS] Test 5: Hash matches expected value
Test 6: Back-to-back 'abc' hashes
[PASS] Test 6: Hash matches expected value
[PASS] Test 7: Hash matches expected value
========================================
Test Summary
========================================
Total Tests: 7
Passed: 7
Failed: 0
*** ALL TESTS PASSED! ***
========================================
# Open GTKWave
gtkwave outflow/sha256_tb.vcdIn GTKWave:
- Expand
sha256_tbin left panel - Expand
dut(Design Under Test) - Select signals:
clk,rst_n,start,ready,state,hash_out - Click "Append" or drag to waveform viewer
- Use zoom buttons to see details
iverilog -o outflow/sha3_sim rtl/sha3_core.v testbench/sha3_tb.vvvp outflow/sha3_simExpected Output:
========================================
SHA-3-256 Core Testbench
========================================
Test 1: Empty string
[PASS] Test 1: Hash matches expected value
Test 2: 'abc'
[PASS] Test 2: Hash matches expected value
... (more tests) ...
========================================
Test Summary
========================================
Total Tests: 7
Passed: 7
Failed: 0
*** ALL TESTS PASSED! ***
========================================
gtkwave outflow/sha3_tb.vcd┌─────────────────────────────────────────────────────┐
│ File Edit Search Time Markers View Help │
├─────────────┬───────────────────────────────────────┤
│ │ │
│ Signal │ Waveform Viewer │
│ Tree │ │
│ │ clk _|‾|_|‾|_|‾|_|‾|_ │
│ ○ sha256_tb │ rst_n _____|‾‾‾‾‾‾‾‾‾‾‾ │
│ ○ dut │ start ______|‾|_______|‾|_ │
│ - clk │ ready ‾‾‾‾‾‾|_______|‾‾‾‾ │
│ - rst_n │ state IDLE--LOAD-PREP-PROC-DONE │
│ - start │ │
│ - ready │ │
│ - state │ │
│ │ │
├─────────────┴───────────────────────────────────────┤
│ Time cursor: 1.234 μs Δt: 500 ns │
└─────────────────────────────────────────────────────┘
Method 1: Click and Append
- Select signal in tree
- Click "Append" button
- Signal appears in waveform
Method 2: Drag and Drop
- Drag signal from tree
- Drop in waveform area
Method 3: Search
- Edit → Search for Signal
- Type signal name (e.g., "hash_out")
- Click "Append"
Right-click signal → Data Format:
- Binary: See individual bits
- Hexadecimal: Compact for large buses
- Decimal: Human-readable numbers
- ASCII: For text data
- Analog: Smooth line graph
Zoom:
- Zoom In:
Alt + Scroll Upor click magnifying glass - Zoom Out:
Alt + Scroll Down - Zoom Fit: Click "Zoom Fit" button
- Zoom to Selection: Select region, click "Zoom to Selection"
Markers:
- Primary Marker: Click on waveform
- Secondary Marker:
Shift + Click - Measure Time: Δt shown in status bar
Signal Groups:
- Right-click signals → Insert Blank
- Right-click → Insert Comment → Name the group
- Organize related signals together
`timescale 1ns / 1ps // Time unit / Time precision
module module_name_tb;
// 1. Declare signals
reg input_signals;
wire output_signals;
// 2. Instantiate DUT
module_name dut (
.port1(signal1),
.port2(signal2)
);
// 3. Clock generation
initial begin
clk = 0;
forever #5 clk = ~clk; // 10ns period
end
// 4. VCD dump
initial begin
$dumpfile("module_name_tb.vcd");
$dumpvars(0, module_name_tb);
end
// 5. Test stimulus
initial begin
// Initialize
// Apply inputs
// Check outputs
$finish;
end
endmodule$display("Message: %d", value); // Print to console
$monitor("sig=%b", signal); // Auto-print on change
$time // Current simulation time
$finish // End simulation
$stop // Pause simulation
$dumpfile("file.vcd") // Set VCD filename
$dumpvars(0, module) // Dump all variables%b // Binary
%d // Decimal
%h // Hexadecimal
%o // Octal
%t // Time
%s // String#10 // Wait 10 time units
#10.5 // Wait 10.5 time units
@(posedge clk) // Wait for rising edge
@(negedge rst_n) // Wait for falling edge
@(signal) // Wait for any change
wait (condition) // Wait until condition truemodule memory_tb;
reg clk, rst_n, write_en;
reg [7:0] addr, data_in;
wire [7:0] data_out;
memory dut (
.clk(clk),
.rst_n(rst_n),
.addr(addr),
.data_in(data_in),
.write_en(write_en),
.data_out(data_out)
);
// Clock
initial begin
clk = 0;
forever #5 clk = ~clk;
end
// VCD
initial begin
$dumpfile("memory_tb.vcd");
$dumpvars(0, memory_tb);
end
// Test
initial begin
// Reset
rst_n = 0;
write_en = 0;
addr = 8'd0;
data_in = 8'd0;
#100;
rst_n = 1;
// Write test
@(posedge clk);
addr = 8'd10;
data_in = 8'hAB;
write_en = 1;
@(posedge clk);
write_en = 0;
// Read test
@(posedge clk);
addr = 8'd10;
@(posedge clk);
@(posedge clk);
if (data_out == 8'hAB)
$display("PASS: Memory read correct");
else
$display("FAIL: Expected AB, got %h", data_out);
#100;
$finish;
end
endmodulealways @(posedge clk) begin
if (state == PROCESS)
$display("Round %d: a=%h e=%h", round, a, e);
endalways @(*) begin
if (hash_out == 256'h0) begin
$display("WARNING: Zero hash at time %t", $time);
end
endFor SHA-256:
- ✓ Clock toggles regularly
- ✓ Reset properly initializes state
- ✓
startpulse triggers transition from IDLE - ✓ State progresses: IDLE → LOAD → PREPARE → PROCESS → DONE
- ✓
round_counterincrements 0-63 in PROCESS - ✓
readydeasserts during operation - ✓
hash_outmatches expected value at end
For SHA-3:
- ✓ State array XORs with input in ABSORB
- ✓ 24 rounds execute in PERMUTE
- ✓
round_counterincrements 0-23 - ✓ Output extracted in SQUEEZE
| Problem | Check |
|---|---|
| Hash always zero | Inputs being applied? State machine running? |
| Simulation hangs | Timeout watchdog present? Infinite loop in FSM? |
| X (unknown) values | Uninitialized registers? Missing reset? |
| Wrong hash | Endianness? Test vector correct? Algorithm bug? |
reg [255:0] test_vectors [0:99]; // Array of test vectors
integer i;
initial begin
$readmemh("test_vectors.hex", test_vectors);
for (i = 0; i < 100; i = i + 1) begin
// Apply test_vectors[i]
// Check result
end
endtask check_hash;
input [255:0] expected;
begin
if (hash_out == expected) begin
$display("✓ PASS");
pass_count = pass_count + 1;
end else begin
$display("✗ FAIL: Expected %h, Got %h", expected, hash_out);
fail_count = fail_count + 1;
end
end
endtask# Compile with coverage
iverilog -g2009 -o sim design.v testbench.v
# Run
vvp sim
# Generate coverage report (if tool supports)
# Check which lines were executedinteger start_time, end_time, cycles;
initial begin
wait(ready);
start_time = $time;
// Start operation
start = 1;
@(posedge clk);
start = 0;
wait(ready);
end_time = $time;
cycles = (end_time - start_time) / 10; // Assuming 10ns clock
$display("Operation took %d cycles", cycles);
endError: Undeclared identifier
solution: Check spelling, declare variable before use
Error: Port connection mismatch
solution: Ensure DUT ports match instantiation
Error: Syntax error
solution: Missing semicolon, begin/end mismatch, check line number
Error: Too many errors
solution: Fix first error, often cascades
Warning: Assigned to 'x'
solution: Initialize all registers in reset
Error: $finish without $dumpfile
solution: Add $dumpfile() before $dumpvars()
Problem: No VCD file generated
solution: Check $dumpfile() and $dumpvars() are called
Problem: GTKWave shows no signals
solution: Load VCD file first, then append signals
Problem: Can't find signal
solution: Check hierarchy, signal might be in submodule
Check iVerilog manual:
man iverilogVerbose compilation:
iverilog -v -o sim design.v testbench.vGTKWave help:
Help → Wave Navigator
Help → Manual
# Compile
iverilog -o outflow/output_file design.v testbench.v
# Run simulation
vvp outflow/output_file
# View waveforms
gtkwave outflow/waveform.vcd
# Compile with warnings
iverilog -Wall -o outflow/sim design.v testbench.v
# List all modules
iverilog -M design.vCtrl+O Open VCD file
Ctrl+F Search for signal
Ctrl+G Go to time
Alt+F Zoom fit
Alt+S Zoom to selection
+/- Zoom in/out
←/→ Pan left/right
Ctrl+Z Undo
- Find online SHA-256 calculator
- Hash your name
- Add test case to
sha256_tb.v - Simulate and verify
Modify sha256_tb.v to measure:
- Cycles per hash
- Throughput (bits/cycle)
- Print statistics
Write a testbench that:
- Tests only "abc" input
- Prints only PASS/FAIL
- Under 30 lines of code
- Run SHA-256 simulation
- In GTKWave, find
statesignal - Change to Analog display
- Observe state progression
- Take screenshot
You now know how to:
- ✓ Install iVerilog and GTKWave
- ✓ Compile Verilog designs
- ✓ Run simulations
- ✓ View and analyze waveforms
- ✓ Write testbenches
- ✓ Debug designs
- ✓ Verify SHA-256 and SHA-3 implementations
Next Steps:
- Simulate the provided SHA implementations
- Experiment with different test vectors
- Modify code and observe effects
- When board arrives, synthesize in Efinity IDE
Remember: Simulation is your friend! Always simulate before synthesizing. It's faster, free, and catches bugs early.
Happy simulating!