Verilog Simulation on Linux with VS Code, Icarus Verilog & GTKWave
A practical guide to writing, compiling, and simulating Verilog HDL on Linux using free, lightweight tools — no expensive EDA software required.
Table of Contents
- Verilog Simulation on Linux with VS Code, Icarus Verilog & GTKWave
Prerequisites
- A Linux system (Ubuntu/Debian, Fedora, Arch, etc.)
- VS Code installed (download here)
- Basic familiarity with the terminal
Installation
Install Icarus Verilog (the compiler/simulator) and GTKWave (the waveform viewer) via your package manager.
Ubuntu / Debian:
sudo apt update
sudo apt install iverilog gtkwave
Fedora:
sudo dnf install iverilog gtkwave
Arch Linux:
sudo pacman -S iverilog gtkwave
Verify the installation:
iverilog -v
gtkwave --version
Setting Up VS Code
Install the following extensions for a proper HDL editing experience:
1. Verilog-HDL/SystemVerilog (syntax highlighting + linting)
Open the Extensions panel (Ctrl+Shift+X) and search for:
- Verilog-HDL/SystemVerilog/Bluespec SystemVerilog by mshr-h
2. (Optional) TerosHDL
A more full-featured HDL extension with documentation generation and tool integration. Search for TerosHDL in the Extensions panel.
Configure Linting (optional)
To enable real-time linting with iverilog, add this to your VS Code settings.json (Ctrl+Shift+P → Open User Settings JSON):
{
"verilog.linting.linter": "iverilog",
"verilog.linting.iverilog.arguments": "-Wall"
}
Project Structure
A clean project layout to follow:
my_project/
├── src/
│ └── my_module.v # Your design files
├── tb/
│ └── tb_my_module.v # Your testbenches
├── sim/
│ └── wave.vcd # Generated waveform files (auto-created)
└── .vscode/
└── tasks.json # Build/run tasks
Writing Your First Module
Create src/my_module.v. Here's a simple 4-bit counter as an example:
// src/my_module.v
module counter (
input wire clk,
input wire rst,
output reg [3:0] count
);
always @(posedge clk or posedge rst) begin
if (rst)
count <= 4'b0000;
else
count <= count + 1;
end
endmodule
Writing a Testbench
Create tb/tb_my_module.v. The testbench instantiates your module, drives inputs, and dumps signals to a .vcd waveform file.
// tb/tb_my_module.v
`timescale 1ns / 1ps
module tb_counter;
// Inputs (driven by testbench)
reg clk;
reg rst;
// Outputs (observed from DUT)
wire [3:0] count;
// Instantiate the Device Under Test (DUT)
counter uut (
.clk (clk),
.rst (rst),
.count (count)
);
// Clock generator: toggle every 5ns → 100MHz clock
initial clk = 0;
always #5 clk = ~clk;
// Stimulus
initial begin
// Set up waveform dumping
$dumpfile("sim/wave.vcd");
$dumpvars(0, tb_counter);
// Apply reset
rst = 1;
#20;
rst = 0;
// Run for 200ns then stop
#200;
$display("Simulation complete.");
$finish;
end
// Monitor output in terminal
initial begin
$monitor("Time=%0t | rst=%b | count=%d", $time, rst, count);
end
endmodule
Key points:
$dumpfileand$dumpvarsenable waveform recording — always include these.$monitorprints signal changes to the terminal automatically.$finishstops the simulation cleanly.
Compiling and Running the Simulation
Step 1 — Create the sim output directory
mkdir -p sim
Step 2 — Compile with iverilog
iverilog -o sim/counter_sim src/my_module.v tb/tb_my_module.v
| Flag | Meaning |
|---|---|
-o sim/counter_sim | Output executable name |
src/my_module.v | Design source file(s) |
tb/tb_my_module.v | Testbench file |
You can add -Wall for warnings:
iverilog -Wall -o sim/counter_sim src/my_module.v tb/tb_my_module.v
Step 3 — Run the simulation
vvp sim/counter_sim
Expected terminal output:
Time=0 | rst=1 | count=0
Time=20000 | rst=0 | count=0
Time=25000 | rst=0 | count=1
Time=35000 | rst=0 | count=2
...
Simulation complete.
The file sim/wave.vcd is now generated and ready for viewing.
Viewing Waveforms in GTKWave
Launch GTKWave with your waveform file:
gtkwave sim/wave.vcd &
Adding signals to the viewer
- In the SST (Signal Search Tree) panel on the left, expand
tb_counter. - Select the signals you want (e.g.,
clk,rst,count). - Click Append to add them to the wave view.
- Press
Ctrl+Shift+For click the zoom-to-fit button to see the full waveform.
Saving your signal layout
Go to File → Write Save File to save your GTKWave layout as a .gtkw file. Next time, open it directly:
gtkwave sim/wave.vcd sim/layout.gtkw &
Automating with VS Code Tasks
Instead of typing compile/run commands every time, wire them up as VS Code tasks.
Create .vscode/tasks.json:
{
"version": "2.0.0",
"tasks": [
{
"label": "Compile Verilog",
"type": "shell",
"command": "iverilog -Wall -o sim/counter_sim src/my_module.v tb/tb_my_module.v",
"group": "build",
"problemMatcher": []
},
{
"label": "Run Simulation",
"type": "shell",
"command": "vvp sim/counter_sim",
"group": "test",
"dependsOn": "Compile Verilog",
"problemMatcher": []
},
{
"label": "Open GTKWave",
"type": "shell",
"command": "gtkwave sim/wave.vcd &",
"group": "test",
"problemMatcher": []
},
{
"label": "Build and Simulate",
"dependsOn": ["Compile Verilog", "Run Simulation"],
"dependsOrder": "sequence",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}
]
}
Running tasks:
Ctrl+Shift+B— runs the default build task (Build and Simulate)Ctrl+Shift+P→ Tasks: Run Task — pick any task from the list
Tips and Common Errors
Common Errors
unknown module type
You forgot to include a source file in the iverilog command. Make sure all .v files are listed.
warning: macro ... undefined
Add `timescale 1ns/1ps at the top of your testbench.
sim/wave.vcd not created
Make sure the sim/ directory exists before running (mkdir -p sim), and that $dumpfile path matches.
GTKWave shows no signals You need to add signals manually via the SST panel — they don't appear automatically.
Useful iverilog Flags
| Flag | Description |
|---|---|
-Wall | Enable all warnings |
-g2012 | Use SystemVerilog 2012 standard |
-I <dir> | Add an include directory |
-D <macro> | Define a preprocessor macro |
Workflow Summary
Write .v files in VS Code
↓
iverilog → compiles to executable
↓
vvp → runs simulation, prints $monitor output, writes .vcd
↓
gtkwave → visualize waveforms
Going Further
- Verilator — compiles Verilog to C++ for much faster simulation of large designs
- Yosys — open-source synthesis tool, pairs well with iverilog for a full open-source RTL flow
- SymbiYosys — formal verification on top of Yosys
- OpenLane — full open-source ASIC flow if you want to go all the way to layout