Build A Small Verilog Workbench With Icarus Verilog And GTKWave

July 23, 2026

FPGA + Verilog · Chapter 4 of 7

The first useful HDL environment is small enough to understand completely.

Icarus Verilog compiles the design and testbench. vvp runs the compiled simulation. The testbench decides pass or fail and writes a VCD signal trace. GTKWave lets you inspect that trace. Verilator is the next tool when linting, speed, or a C++ host boundary matters.

See the complete seven-chapter course

Verilog simulation path through Icarus Verilog, VCD, and GTKWave, alongside a separate FPGA implementation path through Yosys, nextpnr, icepack, and a loader
Original Greenforest I/O diagram. This chapter follows the upper path. The implementation path is shown only to keep a passing simulation distinct from a configured FPGA.

Install The Compact Ubuntu Toolset

On a current Debian or Ubuntu system, begin with the distribution packages:

sudo apt update
sudo apt install iverilog gtkwave make

Package versions depend on the distribution release. Confirm what you actually installed instead of copying a version from a tutorial:

iverilog -V
vvp -V
gtkwave --version
make --version

The official Icarus Verilog documentation covers the compiler and runtime. GTKWave’s project documentation covers the waveform viewer. If the package in your distribution is too old for a particular language feature, pin a known tool release or a reproducible tool bundle rather than casually mixing several moving source builds.

Keep Logisim Beside The Simulator

The source notes listed Logisim as necessary for Boolean logic study and experiment. That role still holds. Use the actively maintained Logisim-evolution release appropriate for your operating system. Distribution repositories may also provide a package, but its age and package name vary.

Logisim and Icarus answer different questions. Logisim makes the hand-built circuit and its wires visible. Icarus executes HDL according to Verilog or SystemVerilog simulation semantics. Rebuilding the same tiny counter both ways is useful: the schematic reveals structure; the HDL testbench makes expected behavior repeatable.

Download The Included Counter Lab

The course publishes a minimal, inspectable lab rather than an unexplained command:

Download those four files into one directory, open a shell there, and run:

make

The Makefile expands the short instruction from the original curriculum into two explicit stages:

iverilog -g2012 -Wall -s counter_tb \
  -o build/counter_tb counter.v counter_tb.v
vvp build/counter_tb

A passing run ends with:

PASS: reset, five updates, and four-bit wrap verified

If you see that message, the simulator checked three claims: reset held the count at zero, five enabled rising edges produced five increments, and eleven more increments wrapped a four-bit value back to zero.

Read The Testbench As An Experiment

The testbench begins with reset asserted. It waits for two positive clock edges, releases reset on a negative edge so the control is stable before the next active edge, then observes the count after five more positive edges. The short #1 delay lets nonblocking register updates settle before the check.

The comparison uses case inequality, !==, so an unknown x or high-impedance z is not mistaken for a good result. On failure, $fatal stops the run with both expected and observed values. This is more useful than opening a waveform and deciding by eye that it “looks about right.”

The waveform remains valuable because it explains a failure and teaches timing. Open it with:

make wave

Or run gtkwave counter.vcd directly. Add clk, reset, and count to the view. Set count to binary or unsigned decimal, then verify that each change follows a rising edge after reset is released.

Use Verilator At The C++ Boundary

The curriculum’s note—“Verilator: invoke and even embed Verilog in C++”—points to a different execution model. Verilator translates synthesizable Verilog or SystemVerilog into a compiled C++ or SystemC model. It is widely used for fast simulation, linting, and integration with software test harnesses.

Install the distribution package to explore it:

sudo apt install verilator
verilator --version
verilator --lint-only -Wall counter.v

For a real C++ simulation, the host program must construct the model, drive inputs, advance time, evaluate the design, and inspect outputs. That explicit host boundary is powerful, but it is more machinery than the first counter needs. The official Verilator guide is the right reference when you make that move.

A Guess Is The Start Of The Test

The source notes placed Erik J. Larson’s The Myth of Artificial Intelligence, informed guesses about codebreaking at Bletchley Park, probability, and “first a guess, then testing veracity” beside the setup checklist. Those fragments do not describe another install dependency. They describe an engineering habit.

Before every run, state the claim: reset should dominate; count should advance only at rising edges; four bits should wrap after sixteen states. Then make the environment capable of falsifying the claim. A waveform supplies detail; a self-checking condition supplies the verdict.

This is the useful relationship among intuition, probability, and verification. An informed guess chooses the next experiment. Repetition and explicit checks determine whether the design deserves trust.

What This Lab Does Not Prove

It does not synthesize the design, choose FPGA resources, assign physical pins, place or route anything, close timing, produce a bitstream, or program a board. It proves the stated simulation behavior under this testbench. Chapter 5 names the project boundaries; chapter 7 follows the separate implementation flow.