ljcucc blog

Hi! Welcome to my new blog (https://blog.ljc.cc)!! (。・ω・。)ノ And I'll post here about any new news, research, ideas and more... Welcome and have a stay here! Come and join the offical discuss chat in matrix #ljcucc-blog-discuss:matrix.org #ljcucc-blog-discuss:matrix.org

Hi! Welcome to my new blog (https://blog.ljc.cc)!! (。・ω・。)ノ And I'll post here about any new news, research, ideas and more... Welcome and have a stay here! Come and join the offical discuss chat in matrix #ljcucc-blog-discuss:matrix.org #ljcucc-blog-discuss:matrix.org

Writing and Testing Verilog on macOS / Linux (and even Android) with Icarus Verilog

Estimated reading time: 8 minutes

Available in 繁體中文  

Are you like me? You’re in class, asked to write Verilog using Quartus II, only to find out it’s a Windows-only application? Σ(’◉⌓◉’) This leaves us students with Macs or Linux installations staring blankly at our screens. Don’t worry! This post will tell you how to handle Verilog design on macOS and Linux, and even do some proof-of-concept testing! * (You can even run it on Android phones/tablets!)

Software Installation

I recommend these two software tools. If you don’t have other preferences, you can start directly with the following:

  • Icarus Verilog
  • GTKWave
    • If you want a pure CLI experience, you can use yne/vcd. (See introduction below)

Icarus Verilog*

In the chip design flow, iverilog primarily handles “functional verification” of Verilog code. This means it allows you to compile Verilog code (often referred to as RTL design, or Register Transfer Level design), and then execute it using its built-in simulator vvp to confirm if the logical behavior matches expectations. After simulation, it generates a vcd (Value Change Dump) waveform file, making it convenient for you to analyze signal changes.

Icarus Verilog focuses on code compilation and functional simulation, but cannot handle the subsequent “hardware implementation” parts, such as logic synthesis, layout, or burning to FPGA/ASIC. Therefore, the goal of this article is to help you focus on “writing correct code,” without involving actual hardware implementation! (ゝ∀・)

Installation is straightforward; you can use package managers for all of them:

Linux

sudo apt update
sudo apt install iverilog

Android (w/ Termux)

If you have an Android phone or tablet, you can also install it on your device via Termux.

pkg update
pkg install iverilog

macOS

(Please make sure you have Homebrew installed)

brew install icarus-verilog

There are many software options for viewing vcd files. The ones introduced here are all open-source solutions:

GTKWave (GUI)

GTKWave is an open-source, graphical browser used for viewing vcd waveform files. It can open .vcd waveform files generated by Icarus Verilog, allowing you to graphically examine the timing and logical states of various signals in your design.

Installation

Linux

sudo apt update
sudo apt install gtkwave

For Android, running a GUI requires an extra step, so I recommend yne/vcd for simplicity.

macOS

Currently, GTKWave on macOS Sonoma seems to have some issues and requires patches to run. If it really doesn’t work, you can use the method below. (#250)

# Please make sure you have brew installed: https://brew.sh
brew install --cask gtkwave

yne/vcd (CLI)

If you prefer a pure text interface, or wish to quickly view waveforms on a remote server, I recommend yne’s vcd: https://github.com/yne/vcd

As a student who had to submit homework… I once encountered a really annoying TA who said they couldn’t understand yne/vcd’s waveform diagrams ( ・∇・?)

Compile + Install

git clone https://github.com/yne/vcd.git; cd vcd
make

# install into your environment
sudo make install

# or directly use it
cd ../
vcd/vcd --help

Usage

yne/vcd uses stdin/stdout for input and output. You can directly pipe bash input into it and output to a file, or print directly to the terminal:

# input from file and output to a file
vcd/vcd < result.vcd > result.yml

The result will look like this:

fig2

Other Solutions

I’ve also found other solutions, each with different usage methods:

vcdrom (Web App)

vcdrom is a web application version of a VCD viewer.

Website: https://vc.drom.io Github repository: https://github.com/wavedrom/vcdrom

VaporView (VSCode Extension)

fig1

VaporView is a VSCode plugin for viewing VCD files.

Github repository: https://github.com/Lramseyer/vaporview

Write Your Own VCD Viewer?

The file format isn’t actually that complex; it mainly records the state changes of various test signals at discrete time points. If you’re interested in graphical programming, you could even try writing a simple VCD waveform viewer yourself using something like processing!

٩( ᐛ )و

iVerilog + GTKWave

The structure of a testbench will generally look like this:

You will certainly have a main module, let’s assume it’s main.v:

module main(
    ...
);
    ... your code here ...
endmodule

A testbench is essentially a test case for your code, just like a unit test for any software. It’s a runnable program (a circuit in this context).

You will definitely have these sections:

  1. Test input and output variables/wires/registers, occasionally some test data.
  2. You need to instantiate your main module (top-level entry) within the tester.
  3. You need a clock and some delays for your program, as defined below.

main_test.v

`timescale 1ns/ 1ns
`include "main.v"

module tester();
    // inputs for your main module
    reg clk;
    reg reset;
    reg start;
    reg din;

    // outputs for your main module
    // > outputs should always be "wire"
    wire count_out;
    wire[0:3] count_one;
    wire dout_valid;
    wire dout;

    // your constants data
    reg[0:7] data = 8'b01010010;

    // This part will
    // connect your main module into tester module
    main m(
        .reset(reset),
        .clk(clk),
        .start(start),
        .din(din),
        .count_out(count_out),
        .count_one(count_one),
        .dout_valid(dout_valid),
        .dout(dout)
    );

    // maybe some integer to count on
    integer i;

    // the part of only run once
    initial begin

        // this part is very important!
        // this define the vvp (runtime) to output
        // the wave file result main_test.vcd
        // for later with the signals inside tester module
        $dumpfile("main_test.vcd");
        $dumpvars(0, tester);

        clk = 0;
        reset = 0;
        start = 0;
        din = 0;

        // send a reset signal

        // this means waiting 2 unit of time
        // as well as defined in $timescale
        #2;
        reset = 1;
        #2;
        reset = 0;

        // ...

        for (i = 0; i < 8; i = i + 1) begin
            din = data[i];
            #2;
        end

        // ...

        // This is also very important!
        // To stop the vvp simulation,
        // you'll need this line
        $finish;
    end

    // generate clock signal for every 1 unit of time
    //
    // why not #2? because one cycle of clock
    // required two changes, high and low,
    // so the program above will wait every 2 unit of time
    always #1 clk = ~clk;

endmodule

Finally, execute this tester.

vvp is Icarus Verilog’s simulation environment, used to simulate your compiled Verilog.

iverilog -o main_test.vvp main_test.v
vvp main_test.vvp

After the simulation, a main_test.vcd file should appear.

  1. In your folder, open this file using GTKWave (File > Open Tab).
  2. Select your module in the SST (Signals & Scopes Tree).
  3. Select the signals you want to observe at the bottom and click “Insert”.
  4. Then you can see your execution results.

If you simulate the program again, just go to File > Reload Waveform or press the shortcut Ctrl+Shift+R to load the latest results.

Example: iVerilog + yne/vcd

If you are using Termux or SSHing into a remote machine, this plain text method is recommended.

Below is a simple counter example:

main.v

module main (
    input  wire       clk,   // Clock input
    input  wire       rst,   // Asynchronous reset input (active high)
    output reg  [1:0] count  // 2-bit output for the counter value
);

  always @(posedge clk or posedge rst) begin
    if (rst) begin
      count <= 2'b00;  // Reset count to 00
    end else begin
      count <= count + 2'b01;  // Increment count by 1
    end
  end

endmodule

main_test.v

`timescale 1ns / 1ns
`include "main.v"

module tester ();

  reg clk;
  reg rst;

  wire [1:0] count;

  main m (
      .clk  (clk),
      .rst  (rst),
      .count(count)
  );

  initial begin
    $dumpfile("result.vcd");
    $dumpvars(0, tester);

    // Initialize inputs
    clk = 0;
    rst = 0;

    #1;  // Wait for 2ns
    rst = 1;  // Assert reset
    #1;  // Hold reset for 2ns
    rst = 0;  // De-assert reset

    for (integer i = 0; i < 10; i = i + 1) begin
      #1;  // Wait for 10ns (5 clock cycles)
    end

    $finish;
  end

  always #1 clk = ~clk;

endmodule
iverilog -o main_test.vvp main_test.v
vvp main_test.vvp
vcd/vcd < result.vcd > result.yml

Below is result.yml:

unknown token : ivl_for_loop0
unknown token : dumpall
global:
  zoom: 9
  date: Mon Jun  9 05:22:45 2025
  total: 13
  skip: 0
  time:
    scale: 1.00
    unit: ns
    line        : "0                                                                                         10                                                                                        "
channels:
  tester:
    count [1:0] : "x        0        0        1        1        2        2        3        3        0        0        1        1        "
  m:
    clk         : "▁▁▁▁▁▁▁▁▁╱▔▔▔▔▔▔▔▔╲▁▁▁▁▁▁▁▁╱▔▔▔▔▔▔▔▔╲▁▁▁▁▁▁▁▁╱▔▔▔▔▔▔▔▔╲▁▁▁▁▁▁▁▁╱▔▔▔▔▔▔▔▔╲▁▁▁▁▁▁▁▁╱▔▔▔▔▔▔▔▔╲▁▁▁▁▁▁▁▁╱▔▔▔▔▔▔▔▔╲▁▁▁▁▁▁▁▁"
    rst         : "▁▁▁▁▁▁▁▁▁╱▔▔▔▔▔▔▔▔╲▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁"
    count [1:0] : "x        0        0        1        1        2        2        3        3        0        0        1        1        "
  :
    i [31:0]    : "x        x        0        1        2        3        4        5        6        7        8        9        A        "

Some TAs seem to have a bit of a “reading disability” with these types of diagrams ( ・∇・ ???) They even said I was just trying to be cool by showing them such diagrams (Me: ???

I won’t reveal who it was specifically, but you all know the type ( ◠‿◠ )b

Therefore, if you’re ever grading assignments in the future, it might be worth noting that some TAs might be a bit “opinionated” in their evaluations. (Because I genuinely had some unpleasant experiences back then, and it was quite disheartening at the time (/ _ ; ))

Conclusion

Congratulations! Now you can happily write Verilog on macOS, Linux, and even your Android phone or tablet.

Reference

AI Contents:

  • *: Refined by Gemini 2.5 Pro and Flash

Unless specified, all blog posts are licensed under CC BY-NC-ND 4.0 . Please credit "ljcucc" and this site's URL when reposting.