Introduction to Icarus Verilog

Now that you've written some RTL code, the critical question becomes: does it actually work? Before committing to synthesis and the downstream design flow, you need to verify that your hardware description behaves as intended. This is where Icarus Verilog (often abbreviated as "iverilog") becomes your primary testing tool.

Icarus Verilog is an open-source Verilog simulation and synthesis tool that has been under active development since the late 1990s. Unlike commercial simulators that can cost thousands of dollars, Icarus Verilog provides robust simulation capabilities completely free. While it may lack some advanced features of tools like ModelSim or VCS, it handles the vast majority of simulation tasks you'll encounter in learning and even professional work.

What makes Icarus Verilog particularly valuable for learning is its straightforward command-line interface and transparent operation. You can see exactly what's happening at each step, building intuition about how simulation actually works under the hood.

Understanding the Two-Stage Process

Icarus Verilog operates differently from some simulators you might have encountered. Rather than directly interpreting your Verilog code, it uses a two-stage compilation-and-execution model, similar to how C programs are compiled and then run.

Stage 1: Compilation with iverilog

The first stage uses the iverilog command to compile your Verilog source files into an intermediate format. Think of this as translating your human-readable HDL into a form optimized for simulation execution.

When you run iverilog, the tool performs several operations:

Parsing: It reads your Verilog files and checks for syntax errors. Mismatched parentheses, typos in keywords, or incorrect module declarations will be caught here.

Elaboration: The compiler builds an internal representation of your design hierarchy. If you have a top-level module that instantiates several sub-modules, elaboration resolves these connections and creates a complete design structure.

Optimization: The compiler applies optimizations to make simulation more efficient. It might eliminate redundant logic or restructure certain constructs for faster execution.

Code Generation: Finally, it produces an executable file containing your design in a format ready for simulation. By default, this file is named a.out, but you can specify a different name.

Here's a basic compilation command:

iverilog -o my_design.vvp design.v testbench.v

Let's break this down:

  • iverilog invokes the compiler
  • -o my_design.vvp specifies the output filename (.vvp is conventional for Icarus executables)
  • design.v testbench.v lists the source files to compile

If compilation succeeds without errors, you'll have an executable simulation file ready for the next stage.

Stage 2: Execution with vvp

The second stage uses the vvp command (Verilog Virtual Processor) to actually run your simulation. This executes the compiled file generated by iverilog, processing your testbench and producing results.

vvp my_design.vvp

When vvp runs, it:

  • Initializes all signals and variables according to your initial blocks
  • Executes your testbench code sequentially
  • Processes time-based events (# delays, clock edges, etc.)
  • Handles $display, $monitor, and other system tasks to produce output
  • Generates waveform dumps if you've included $dumpfile and $dumpvars

The separation of compilation and execution provides practical benefits. You can compile once and run multiple times with different plusargs (runtime parameters) without recompiling. It also means compilation errors appear immediately, before any simulation begins.

Essential Command-Line Options

While basic compilation is straightforward, several command-line flags give you greater control over the process.

Specifying the Standard Version

Verilog has evolved through several IEEE standards: Verilog-1995, Verilog-2001, Verilog-2005, and SystemVerilog. Different standards support different language features. You can specify which standard to use:

iverilog -g2005 -o design.vvp design.v testbench.v

The -g flag (generation) accepts values like:

  • -g1995 for Verilog-1995
  • -g2001 for Verilog-2001 (adds ANSI-style ports, better generate constructs)
  • -g2005 for Verilog-2005 (more refinements)
  • -g2009 or -g2012 for SystemVerilog features

For most modern designs, -g2005 or -g2009 provides the best balance of features and compatibility.

Include Paths for Header Files

When your design uses include directives to pull in definitions or parameters, you need to tell the compiler where to find these files:

iverilog -I./includes -I../common -o design.vvp design.v testbench.v

The -I flag specifies include directories. You can provide multiple -I flags, and the compiler searches them in order.

Defining Macros from the Command Line

Verilog's `define directive creates macros. You can also define these from the command line:

iverilog -DSIMULATION -DCLOCK_PERIOD=10 -o design.vvp design.v testbench.v

The -D flag defines a macro. This is particularly useful for:

  • Enabling simulation-specific code paths
  • Configuring parameters without modifying source files
  • Creating different build configurations

Warning Control

By default, Icarus Verilog produces warnings for potentially problematic constructs. You can control warning verbosity:

iverilog -Wall -o design.vvp design.v testbench.v

Options include:

  • -Wall enables all warnings (recommended for catching potential issues)
  • -Wno-sensitivity-entire-array disables specific warning types
  • -Winfloop warns about potential infinite loops

Treating warnings seriously improves code quality and helps catch subtle bugs before they become problems.

Viewing Internal Representations

For debugging or educational purposes, you can examine intermediate representations:

iverilog -t null -o design.vvp design.v

The -t flag specifies the target format. Using -t null performs compilation without code generation, useful for checking syntax without simulation.

Working with Multiple Source Files

Real designs rarely fit in a single file. You'll typically have multiple modules, perhaps shared libraries, and certainly separate testbenches. Icarus Verilog provides several ways to manage multi-file projects.

Direct File Listing

The simplest approach lists all files on the command line:

iverilog -o design.vvp alu.v register_file.v control_unit.v cpu.v testbench.v

This works well for small projects but becomes unwieldy as file counts grow.

Using File Lists

For larger projects, create a file list (conventionally with a .f extension) containing all source files:

files.f:

./rtl/alu.v
./rtl/register_file.v
./rtl/control_unit.v
./rtl/cpu.v
./testbenches/cpu_tb.v

Then reference this list during compilation:

iverilog -f files.f -o design.vvp

File lists support comments (lines starting with # or //) and can include compiler directives:

files.f:

# RTL source files
-I./includes
./rtl/alu.v
./rtl/register_file.v

# Testbench files
-DSIMULATION
./testbenches/cpu_tb.v

This approach keeps your compile commands clean while maintaining flexibility.

Hierarchical File Lists

File lists can reference other file lists, enabling modular organization:

rtl.f:

./rtl/alu.v
./rtl/register_file.v
./rtl/control_unit.v

testbench.f:

./testbenches/cpu_tb.v
./testbenches/memory_model.v

master.f:

-f rtl.f
-f testbench.f

Then compile with:

iverilog -f master.f -o design.vvp

This structure mirrors how commercial EDA tools organize large projects.

Common Compilation Patterns

Let's look at practical compilation scenarios you'll encounter regularly.

Basic Design and Testbench

The most common case: a design module and its testbench.

iverilog -g2005 -Wall -o counter.vvp counter.v counter_tb.v
vvp counter.vvp

This compiles both files using Verilog-2005 standard with all warnings enabled, then runs the simulation.

Design with Include Files

When your design uses shared definitions:

iverilog -g2005 -I./includes -Wall -o processor.vvp processor.v processor_tb.v
vvp processor.vvp

The compiler searches ./includes for any files referenced by `include directives.

Parameterized Simulation

Using command-line defines to configure the design:

iverilog -g2005 -DDATA_WIDTH=32 -DDEPTH=1024 -Wall -o fifo.vvp fifo.v fifo_tb.v
vvp fifo.vvp

Your Verilog code can then reference these macros:

module fifo #(
    parameter WIDTH = `DATA_WIDTH,
    parameter DEPTH = `DEPTH
) (
    // port declarations
);

Complete Project Compilation

For a substantial project with multiple directories:

iverilog -g2009 -f filelist.f -I./includes -I./common \
         -DSIMULATION -Wall -o system.vvp
vvp system.vvp
gtkwave waveform.vcd

This comprehensive command:

  • Uses SystemVerilog features (-g2009)
  • Reads files from a list (-f filelist.f)
  • Searches multiple include directories
  • Defines a simulation flag
  • Enables all warnings
  • Produces an executable, runs it, and opens waveforms

Practical Tips and Best Practices

Separate Compilation Directories: Keep compiled files separate from source code. Use output directories like ./sim/ or ./build/:

iverilog -o ./sim/design.vvp design.v testbench.v

Makefiles for Automation: For projects you'll compile repeatedly, create a Makefile:

SIM = design.vvp
SOURCES = design.v testbench.v
INCLUDES = -I./includes

$(SIM): $(SOURCES)
    iverilog -g2005 $(INCLUDES) -Wall -o $@ $^

sim: $(SIM)
    vvp $(SIM)

clean:
    rm -f $(SIM) *.vcd

Then simply run make sim to compile and simulate.

Version Control: Exclude compiled files (.vvp, .vcd) from version control. Add them to .gitignore:

*.vvp
*.vcd
*.out

Incremental Development: Start with simple testbenches and gradually add complexity. Compile frequently to catch errors early rather than debugging massive amounts of code at once.

Conclusion

Mastering Icarus Verilog's compilation and execution process gives you a solid foundation for hardware verification. The two-stage model might seem more complex initially compared to integrated simulators, but this separation provides clarity and flexibility. Understanding command-line options, file management, and compilation patterns prepares you for both simple experiments and large-scale projects.

In the next section, we'll dive into writing effective testbenches that leverage these compilation capabilities to thoroughly verify your designs before synthesis.

Did you find what you were looking for?

Quiz

No quiz questions available for this article.