Back to Documentation home

How To Hello World

How To "Hello World"

Below are the simplest of "Hello World" type examples for using each of the tools in the MSD Design and Verification Suite. This document assumes you are already familiar with SystemVerilog and/or VHDL. These tutorials will give you the general idea of how each particular tool is used.

Icarus Verilog (iverilog)

Create a SystemVerilog (no different from Verilog in this case) file named hello.sv with this inside:

module top;
  initial begin
    $display("Hello, World!");
  end
endmodule

Compile and run this example with this command:

iverilog hello.sv -o hello
./hello

You should see this printed:

Hello, World!

Verilator

Verilator compiles SystemVerilog code into C++ (or SystemC, but for now we'll focus on plain C++). Create a SystemVerilog (no different from Verilog in this case) file named hello.sv with this inside:

module top;
  initial begin
    $display("Hello, World!");
    $finish();
  end
endmodule

Note the need for a $finish that you wouldn't normally need in a simple SystemVerilog example like this. We will also need a top-level C++ file to run this simulation. Create a file named sim_main.cpp with this inside:

#include "Vhello.h"
#include "verilated.h"

int main(int argc, char** argv, char** env) {
    Verilated::commandArgs(argc, argv);
    Vhello* top = new Vhello;
    while (!Verilated::gotFinish()) {
        top->eval();
    }
    delete top;
    exit(0);
}

Generate C++ from our SystemVerilog with this command:

verilator --cc hello.sv --exe sim_main.cpp

Now we have a directory named obj_dir. Compile the C++ with this command:

make -j -C obj_dir -f Vhello.mk Vhello

And now run it:

obj_dir/Vhello

You should see this printed:

Hello, World!
- hello.sv:4: Verilog $finish

If you compare this with the Icarus Verilog example you can see that Verilator is a little more complicated to use than Icarus, but you can get much faster simulation times with Verilator.

GHDL

Create a VHDL file named hello.vhdl with this inside:

entity hello_world is
end entity;

architecture sim of hello_world is
begin
  process is
  begin
    report "Hello, World!";
    wait;
  end process;
end architecture;

GHDL has a 2-step compile process (analysis and elaboration) and then a third command to run the resulting simulation . These are the three commands (pay attention to when we use "hello" and when we use "hello_world":

ghdl -a hello.vhdl
ghdl -e hello_world
ghdl -r hello_world

You should see this printed:

hello.vhdl:8:5:@0ms:(report note): Hello, World!

CoCoTB

CoCoTB (also known as, cocotb) is a Python testbench framework. A simple "Hello World" example would just be plain python code and not very helpful. We will instead create a simple counter in SystemVerilog (don't worry, VHDL is coming too) and then use cocotb to create a testbench for it.

First put this code for a simple 8-bit counter in counter.sv:

`timescale 1ns/1ns

module counter (
  input clk, reset,
  output logic [7:0] count
);
  always @(posedge clk)
    if (reset) begin
      count <= '0 ;
    end
    else begin
      count <= count + 1;
    end
endmodule

Then put this testbench code for the counter in counter_testbench.py:

import cocotb
from cocotb.triggers import Timer
from cocotb.result import TestFailure

CLOCK_PERIOD = 10

@cocotb.coroutine
def run_clock(dut):
    for cycle in range(10):
        dut.clk <= 0
        yield Timer(CLOCK_PERIOD / 2, units='ns')
        dut.clk <= 1
        yield Timer(CLOCK_PERIOD / 2, units='ns')

@cocotb.test()
def my_first_test(dut):
    # start the clock:
    cocotb.fork(run_clock(dut))

    # hold reset high for a few clocks:
    dut.reset <= 1
    yield Timer(CLOCK_PERIOD * 3, units='ns')
    dut.reset <= 0

    # do some counting
    expected_count = 5
    yield Timer(CLOCK_PERIOD * expected_count, units='ns')

    # check results
    count = dut.count.value.integer
    if count != expected_count:
        raise TestFailure(f'expected count of {expected_count}, '
                          f'got count of {count}')
    dut._log.info('Hello, World!  Our test passed!')

We then create a file named Makefile with this in it:

PYTHON_BIN = python3
export PYTHON_BIN
ifeq ($(SIM),ghdl)
  VHDL_SOURCES = $(PWD)/counter.vhdl
else
  VERILOG_SOURCES = $(PWD)/counter.sv
endif
TOPLEVEL = counter
MODULE = counter_testbench
include $(shell cocotb-config --makefiles)/Makefile.inc
include $(shell cocotb-config --makefiles)/Makefile.sim

Now, to run our simulation of the counter with our testbench, simply run:

make

This will compile and run the simulation using cocotb's default simulator, Icarus Verilog. There will be a lot of output, but you should be able to find the "Hello, World! Our test passed!" message from our testbench in there.

Normally cocotb sets the timescale of the simulation by passing a command-line argument to the simulator. Icarus Verilog does not support such a command-line argument right now. That's why our counter.sv has a `timescale directive at the top.

We should note a couple really nice things about cocotb while we are here. First of all, when you change just the python code for the testbench and re-run make (go ahead and try it), notice that it doesn't have to do any slow recompiling of any SystemVerilog or VPI code at all. The second great thing about cocotb is that it's very easy to run the same testbench and design on a different simulator. To use verilator instead of Icarus, simply do this:

make SIM=verilator

If you are looking closely at the Makefile above, you can see that we added support for a VHDL version of our counter too. We can generate that VHDL counter from our SystemVerilog code using Icarus Verilog like so:

iverilog -t vhdl -g2005-sv counter.sv -o counter.vhdl

And then we can simulate the resulting VHDL code with this command:

make SIM=ghdl

With this simple example you can start to see how powerful cocotb is, and how powerful all of these open source tools working together can be.

GTKWave

No design and verification suite would be complete without a waveform viewer. Building off of the cocotb example where we simulated a counter, we can add a waveform dump and view it with GTKWave.

If simulating with Icarus, you can add a few lines to bottom of counter.sv to create a VCD file. The full counter.sv will look like this:

`timescale 1ns/1ns

module counter (
  input clk, reset,
  output logic [7:0] count
);
  always @(posedge clk)
    if (reset) begin
      count <= '0 ;
    end
    else begin
      count <= count + 1;
    end

`ifdef COCOTB_SIM
  initial begin
    $dumpfile ("counter.vcd");
    $dumpvars (0, counter);
    #1;
  end
  `endif
endmodule

Then run the simulation and open the VCD file with GTKWave:

make
gtkwave counter.vcd

After GTKWave starts up, click on "counter" in the upper left window pane. The signals will appear in the lower left window pane. Double click on each signal to display them. You should see something like this:

Screenshot of GTKWave showing our counter's signals

Back to Documentation home