How to write your first Cocotb Testbench

Welcome to cocotb adventurer, I will be your guide on a journey through the realm of testing digital designs. Our destination: the land of efficient and effective verification. To embark on this quest, you will need to arm yourself with knowledge of the motivations behind cocotb, the tools to install it, and the courage to dive into code. With every step, I will be by your side, explaining the finer details of the code and showing you how to run the tests. By the end of this journey, you will have the knowledge and confidence to navigate the land of cocotb with ease. So, grab your shields of determination and your swords of curiosity, let’s begin our adventure.

I. Motivations

Cococtb, or Coroutine based COsimulation Testbench, is a Python framework for testing designs written in VHDL or Verilog/SystemVerilog. It can connect with nearly all simulators. Benefits of using cocotb include:

  • Access to the vast ecosystem of Python, the second most widely used language globally, rather than being limited to the little VHDL or Verilog communities.
  • One verification language to verify them all… and in darkness bind them. Meaning: use a single verification language for all designs, allowing for a common library of verification tools
  • Finding a python programmer is simplier than a VHDL/Verilog tester.
  • Just writing softwares with a real software language.
  • The opportunity to learn a language used in other domains, unlike VHDL or SystemVerilog.
  • Cost-effective verification process – tests can be run on open-source simulators, and then move to a multi-thousands dollars mono-threaded licensed simulator for final verification.
  • There are many other benefits. I am sure you can find one and share it in comment 😉

II. Installation and Project Setting

1. Installation of cocotb:

Follow the instruction (RTFM!) here is the best way to start.

2. Create your project tree. We will fill the three files in the next section :

mkdir first_cocotb_testbench
cd first_cocotb_testbench/
mkdir src testbench
touch Makefile src/dff.sv testbench/test_dff.py

3. Install a simulator like Icarus Verilog.

sudo apt-get install iverilog

III. Code Explanation

The code is available on my github here.

The DUT (Design Under Test)

For our first testbench using Cocotb, we will be testing a basic D Flip-Flop using the following code.

// dff.sv
`timescale 1us/1ns
module dff (
    output logic q,
    input logic clk, d
);
always @(posedge clk) begin
    q <= d;
end
endmodule

If you are unfamiliar with the code, take a moment to read my article on D Flip-Flops and explore Verilog/SystemVerilog tutorials on Google or YouTube. This is a fundamental module that you should design early on. Don’t forget to return here afterwards.

The Makefile

Here is the complete Makefile that we will analyze next:

# Makefile
export PYTHONPATH := $(PWD)/testbench:$(PYTHONPATH)

TOPLEVEL_LANG = verilog
VERILOG_SOURCES = $(PWD)/src/dff.sv
TOPLEVEL = dff
MODULE = test_dff

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

Let’s examine each line one by one:

1. This line sets the Python path for the cocotb framework to locate the testbench

The “PWD” command retrieves the path of the makefile

By appending “/testbench” to the path, we get the path of the testbench directory:

export PYTHONPATH := $(PWD)/testbench:$(PYTHONPATH)

2. Specifies the language used to design the DUT, which is written in Verilog :

TOPLEVEL_LANG = verilog

3. Provides the location of the verilog source for the DFF to cocotb :

VERILOG_SOURCES = $(PWD)/src/dff.sv

4. Identifies the top module in the Verilog code; the module identifier is “dff”. Line 1 of dff.sv: “module dff (output …)”

TOPLEVEL = dff

5. Points to the Python file containing the tests for the DFF. “test_dff” = “test_dff.py” without “.py” extension

MODULE = test_dff

6. inludes the pre-written makefiles to handle every language and simulator cocotb supports

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

The cocotb testbench

Now let’s examine the Python file. I assume readers have little to no prior knowledge of Python. So I will do my best but a full Python tutorial is not the purpose here. If you don’t understand something (decorators, async etc), the following explanation will give you keywords to search for on google or youtube.

Here is the complete code without comments that we will examine line by line:

# test_dff.py
import random
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import FallingEdge

@cocotb.test()
async def test_dff_simple(dut):
    """ Test that d propagates to q """
    clock = Clock(dut.clk, 10, units="us") 
    cocotb.start_soon(clock.start())  

    for i in range(10):
        val = random.randint(0, 1)
        dut.d.value = val  
        await FallingEdge(dut.clk)
        assert dut.q.value == val, f"output q was incorrect on the {i}th cycle. Got {dut.q.value} expected {val}"

In the beginning, we have the “Import” section.

# "random" is a standard python package installed with python
import random
# "cocotb" is NOT a standard package
# We installed it with "pip install cocotb"
import cocotb
# importing Clock and FallingEdge reduces code verbosity.
# writing "Clock()" is faster than "cocotb.clock.Clock()"
from cocotb.clock import Clock
from cocotb.triggers import FallingEdge

Next, we have the declaration section where we create instances of the necessary elements:

@cocotb.test()

The “@cocotb.test()” is a Python decorator that informs the Cocotb framework that the function immediately below it is a Cocotb test. If you are wondering where the “dut” parameter is passed to the test function, understanding this line and the concept of Python decorators will provide the answer.

async def test_dff_simple(dut):

The keyword “async” in Python signifies that the function “test_ddd_simple” is asynchronous. All Cocotb test functions must be asynchronous. The test function has one parameter “dut” which is passed by the decorator mentioned earlier.

""" Test that d propagates to q """

A comment surrounded by triple quotes under a function definition is referred to as a docstring in Python. The Cocotb framework will display this in the test log when the function is call

clock = Clock(dut.clk, 10, units="us")

The declaration of your clock. The first parameter is the dut clock signal. The second is the clock period. The third is the period unit. This line create a 10us period clock on port clk.

Here is the logic of the test. The test consist in generating random bit and check that they are copied by the output q on the next cycle.

    # Start the clock and the simulation
    cocotb.start_soon(clock.start())
    # we are going to test 10 random value
    for i in range(10):
        # generate a random value that equal 0 or 1
        val = random.randint(0, 1)
        # Assign the random value val to the input port d
        dut.d.value = val  
        # Wait for a falling edge on the clock signal
        await FallingEdge(dut.clk)
        # The Test ! check if q has the expected value
        assert dut.q.value == val, f"output q was incorrect on the {i}th cycle. Got {dut.q.value} expected {val}"

‘await’ is a reserved word in python. As its name suggested it wait for an event to occur, here, the falling edge of the clock. If you want to know more about it, search for ‘async’ and ‘await’ simultaniously.

‘assert’ is reserved word in python. If the condition that follow (here dut.q.value == val) is true, nothing append, the test passed. If the condition is false, it raise an exception with the message that follow in the error report.

IV. Running the test

To run the simulation just enter the following command on the project root (the directory where the makefile is) :

make SIM=icarus 

SIM=icarus specifies the simulator icarus (iverilog).

You should get a beautiful report with a passed !

Now try to implement an error in the DFF, like this for example :

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

And try to run it again to see how the error look like.

Conclusion

Our time together in the world of Cocotb has come to an end… for now. With the knowledge and confidence you have acquired, I am confident that you will vanquish even the mightiest of design bugs with a flick of your Python code. However, should you ever need me, don’t hesitate to summon me via the comment spell I tought you.

If you like this article and if you want more Cocotb tutorial, please tell me in the comment. Don’t forget to share and bookmark. Thank you for reading !

Leave a Reply

Your email address will not be published. Required fields are marked *

How to write your first Cocotb Testbench

dark blue dot

Summary

Share it !

Get my Ebook ?

ebook_hero-home

Jumpstart you FPGA journey by

• Understanding the place of FPGA in industry
• Learn about internal composition of an FPGA
• A simple beginner friendly project
• An overview of the FPGA workflow
ebook_banner_11