Getting started with Open Source Design Verification with Icarus Verilog and cocotb.

Getting started with Open Source Design Verification with Icarus Verilog and cocotb.

As someone who was always more interested in the analog side of things, I never bothered to learn anything more than basic digital concepts. I joined Texas Instruments in a team that did Mixed-Signal ICs. As my exposure grew, I was intrigued by the world of Mixed Signal IC design and you can't really do Mixed Signal ICs without digital.

As I delved deeper into the world of digital, learning a Hardware Description Language became imperative, I had never bothered to learn more that basic VHDL during my undergrad and i found System Verilog counter intuitive. I had a good hold over python and while I am all for learning new things, I wanted to get some results quickly to motivate me to immerse myself in this pursuit.

The best thing for a beginner would probably be to go to a website like EDAplayground. But I am a huge fan of running things on my local machine so that is what we will do here.

The first couple of things that I needed to install were Python and Icarus Verilog. Apart from this I also setup a virtual environment.

sudo apt-get install make python3 python3-pip
sudo apt install iverilog
python3 -m venv myenv

Then I simply activated the environment and started installing cocotb and it's dependancies.

source myenv/bin/activate
pip3 install pytest cocotb cocotb-bus cocotb-coverage

The next step is to try the sample code from cocotb GitHUb repo to test a D Flip Flop. We create two files, dff.sv and test_dff.py

// dff.sv

`timescale 1us/1ns

module dff (
    output logic q,
    input logic clk, d
);

always @(posedge clk) begin
    q <= d;
end

endmodule
# test_dff.py

import random

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.types import LogicArray

@cocotb.test()
async def dff_simple_test(dut):
    """Test that d propagates to q"""

    # Assert initial output is unknown
    assert LogicArray(dut.q.value) == LogicArray("X")
    # Set initial input value to prevent it from floating
    dut.d.value = 0

    clock = Clock(dut.clk, 10, units="us")  # Create a 10us period clock on port clk
    # Start the clock. Start it low to avoid issues on the first RisingEdge
    cocotb.start_soon(clock.start(start_high=False))

    # Synchronize with the clock. This will regisiter the initial `d` value
    await RisingEdge(dut.clk)
    expected_val = 0  # Matches initial input value
    for i in range(10):
        val = random.randint(0, 1)
        dut.d.value = val  # Assign the random value val to the input port d
        await RisingEdge(dut.clk)
        assert dut.q.value == expected_val, f"output q was incorrect on the {i}th cycle"
        expected_val = val # Save random value for next RisingEdge

    # Check the final input on the next clock
    await RisingEdge(dut.clk)
    assert dut.q.value == expected_val, "output q was incorrect on the last cycle"

We also need a makefile to set the simulator for the HDL file.

# Makefile

TOPLEVEL_LANG = verilog
VERILOG_SOURCES = $(shell pwd)/dff.sv
TOPLEVEL = dff
MODULE = test_dff

include $(shell cocotb-config --makefiles)/Makefile.sim

The setup is now complete, we execute the simulation with Icarus Verilog

make SIM=icarus

If all is as expected, this is what you ought to see after execution.

I'm going to try to understand more about cocotb and verification and the next blog would probably have more details.