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.