Synchronous FIFO Tester Using Cocotb

Muhammed Kocaoğlu
5 min readNov 19, 2022

--

Recently I installed cocotb on my computer and tried the examples provided in the cocotb package under examples folder. The examples include adder, matrix multiplier simple_dff, etc. These are very helpful in understanding the python code written to test the SystemVerilog or VHDL modules. Cocotb is free and open source. The good thing about Cocotb is the language we are using which is python. The community is growing and more libraries to verify HDL designs will be available. So I thought it would be helpful for me if I learn Cocotb.

If you want to install Cocotb on your computer there are a few prerequisites you must install. The prerequisites are given on the Cocotb Github page. Adam Taylor also has a blog where you can learn the prerequisites and how to install them on your computer which is very helpful.

In this blog, we will be looking at the python code I have written to verify a synchronous FIFO. This is going to be a very basic test.

Firstly, we will be writing data to the FIFO until we see the full flag. Then we will read the data until we see the empty flag.

The data written to FIFO is also going to be written to Queue.

The FIFO ports are given below.

module sync_fifo_rtl # (
parameter DATA_WIDTH = 8,
parameter DATA_ADDR = 4 // 2**4-1:0 15:0 depth is 16
)(
input logic clk,
input logic srstn,
input logic [DATA_WIDTH-1:0] din,
input logic wr_en,
input logic rd_en,
output logic [DATA_WIDTH-1:0] dout,
output logic full,
output logic empty,
output logic [DATA_ADDR:0] data_count
);

Let’s start writing the Python code. The libraries we need to import are given below.

import queue
from cocotb.clock import Clock
import cocotb
from cocotb.triggers import Timer

queue library is going to be used to store data which is also written to the FIFO HDL model. Then we will read it back when we read data from the FIFO and compare whether they match.

The clock is imported to generate the clock signal.

cocotb is imported to use start_soon Coroutine.

timer is imported in order to wait in the tester code. We will see the usage below.

The function below is going to be used to test the FIFO. @cocotb.test() is used to access your HDL top-level design. You can access the port signals using dut.port_name syntax and its values using dut.port_name.value syntax.

@cocotb.test()
async def test_fifo(dut):

With the below code, I gave initial values to inputs and created a queue with the name q1.

    q1 = queue.Queue()

dut.wr_en.value = 0;
dut.rd_en.value = 0;
dut.din.value = 0;

We can create the clock using the code below.

cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())   

Apply active low reset to the system.

    dut.srstn.value = 0
await Timer(20, units="ns")
dut.srstn.value = 1
await Timer(20, units="ns")

We generated the clock signal and applied reset to the system.

Let’s write data to the FIFO until it is full. We can do that using the code below.

    i=0
await Timer(5, units="ns")
while dut.full.value == 0:
q1.put(i) # this will additem from 0 to 20 to the queue
dut.din.value = i;
dut.wr_en.value = 1;
await Timer(10, units="ns")
i = i + 1;

dut.wr_en.value = 0;

await Timer(100, units="ns")

The FIFO is now full. We expect that the data written to the FIFO HDL model is also written to the queue we defined.

We can now test whether the FIFO I wrote using SystemVerilog is working properly or not. We can do that using the code snippet below.

    golden_dout=0

while dut.empty.value == 0:
dut.rd_en.value = 1
golden_dout = q1.get()
await Timer(10, units="ns")
print("FIFO Dout Value=",int(dut.dout.value), "Queue Value=",int(golden_dout))
assert dut.dout.value == golden_dout, "test failed"

dut.rd_en.value = 0

await Timer(500, units="ns")

The complete python code and Synchronous FIFO code are also given in my Github account.

Create Make File

The make file is required in order to test the example. I gave the content of the make file below. This is a modified make file of examples in the Cocotb package.

TOPLEVEL_LANG ?= verilog

PWD=$(shell pwd)

export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH)

ifeq ($(TOPLEVEL_LANG),verilog)
VERILOG_SOURCES = $(PWD)/sync_fifo_rtl.sv
else ifeq ($(TOPLEVEL_LANG),vhdl)
VHDL_SOURCES = $(PWD)/sync_fifo_rtl.vhd
else
$(error A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG))
endif

TOPLEVEL := sync_fifo_rtl
MODULE := test_fifo

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

Lastly, open the anaconda terminal to run the example and see the result. First go to the directory where you put your make file, python code, and HDL file. You can change the directory from C to D disk using the command below.

cd /d d:\    % change from C to D
cd D:\Desktop\cocotb\cocotb\examples\mixed_signal\tests % go to the directory you want

Once you are in the directory of your project, now you can run the make command on the anaconda terminal.

I am using the ModelSim simulator, you can change it to the simulator you are using. I also want to see the Waveform so I made GUI=1.

In this example, we are using Verilog.

make SIM=modelsim clean
make SIM=modelsim GUI=1 TOPLEVEL_LANG=verilog

The Waveform is given below. The clock and reset signals are generated properly. Write and read operations are also successful.

References:

https://github.com/cocotb/cocotb

https://www.adiuvoengineering.com/post/microzed-chronicles-getting-started-with-cocotb

https://www.adiuvoengineering.com/post/microzed-chronicles-cocotb-and-axi

--

--