Quickstart Guide
In the following sections,
we are walking you through creating and running a small but complete cocotb testbench
for a fictional Design Under Test (DUT) called my_design
.
Please install the prerequisites
and cocotb itself (pip install cocotb
) now.
Run cocotb-config --version
in a terminal window to check that cocotb is correctly installed.
The code for the following example is available as
examples/doc_examples/quickstart
in the cocotb sources.
You can also download the files here:
my_design.sv
,
test_my_design.py
,
test_runner.py
,
Makefile
.
Creating a Test
A typical cocotb testbench requires no additional HDL code. The DUT is instantiated as the toplevel in the simulator without any HDL wrapper code.
The test is written in Python.
In cocotb, you can access all internals of your design,
e.g. signals, ports, parameters, etc. through an object that is passed to each test.
In the following we’ll call this object dut
.
Let’s create a test file test_my_design.py
containing the following:
# test_my_design.py (simple)
import cocotb
from cocotb.triggers import Timer
@cocotb.test()
async def my_first_test(dut):
"""Try accessing the design."""
for cycle in range(10):
dut.clk.value = 0
await Timer(1, units="ns")
dut.clk.value = 1
await Timer(1, units="ns")
dut._log.info("my_signal_1 is %s", dut.my_signal_1.value)
assert dut.my_signal_2.value[0] == 0, "my_signal_2[0] is not 0!"
This will first drive 10 periods of a square wave clock onto a port clk
of the toplevel.
After this, the clock stops,
the value of my_signal_1
is printed,
and the value of index 0
of my_signal_2
is checked to be 0
.
Things to note:
Use the
@cocotb.test()
decorator to mark the test function to be run.Use
.value = value
to assign a value to a signal.Use
.value
to get a signal’s current value.
The test shown is running sequentially, from start to end.
Each await
expression suspends execution of the test until
whatever event the test is waiting for occurs and the simulator returns
control back to cocotb (see Simulator Triggers).
It’s most likely that you will want to do several things “at the same time” however -
think multiple always
blocks in Verilog or process
statements in VHDL.
In cocotb, you might move the clock generation part of the example above into its own
async
function and start()
it (“start it in the background”)
from the test:
# test_my_design.py (extended)
import cocotb
from cocotb.triggers import FallingEdge, Timer
async def generate_clock(dut):
"""Generate clock pulses."""
for cycle in range(10):
dut.clk.value = 0
await Timer(1, units="ns")
dut.clk.value = 1
await Timer(1, units="ns")
@cocotb.test()
async def my_second_test(dut):
"""Try accessing the design."""
await cocotb.start(generate_clock(dut)) # run the clock "in the background"
await Timer(5, units="ns") # wait a bit
await FallingEdge(dut.clk) # wait for falling edge/"negedge"
dut._log.info("my_signal_1 is %s", dut.my_signal_1.value)
assert dut.my_signal_2.value[0] == 0, "my_signal_2[0] is not 0!"
Note that the generate_clock()
function is not marked with @cocotb.test()
since this is not a test on its own, just a helper function.
See the sections Concurrent and sequential execution and Coroutines and Tasks for more information on such concurrent processes.
Note
Since generating a clock is such a common task, cocotb provides a helper for it -
cocotb.clock.Clock
.
No need to write your own clock generator!
You would start Clock
with
cocotb.start_soon(Clock(dut.clk, 1, units="ns").start())
near the top of your test,
after importing it with from cocotb.clock import Clock
.
Creating a Makefile
In order to run a test,
you create a Makefile
that contains information about your project
(i.e. the specific DUT and test).
In the Makefile
shown below we specify:
the default simulator to use (
SIM
),the default language of the toplevel module or entity (
TOPLEVEL_LANG
,verilog
in our case),the design source files (
VERILOG_SOURCES
andVHDL_SOURCES
),the toplevel module or entity to instantiate (
TOPLEVEL
,my_design
in our case),and a Python module that contains our cocotb tests (
MODULE
. The file containing the test without the .py extension,test_my_design
in our case).
# Makefile
# defaults
SIM ?= icarus
TOPLEVEL_LANG ?= verilog
VERILOG_SOURCES += $(PWD)/my_design.sv
# use VHDL_SOURCES for VHDL files
# TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
TOPLEVEL = my_design
# MODULE is the basename of the Python test file
MODULE = test_my_design
# include cocotb's make rules to take care of the simulator setup
include $(shell cocotb-config --makefiles)/Makefile.sim
Running a Test
When you now type
make
Icarus Verilog will be used to simulate the Verilog implementation of the DUT because we defined these as the default values.
If you want to simulate the DUT with Siemens Questa instead, all you would need to change is the command line:
make SIM=questa
Using runners
Warning
Python runners and associated APIs are an experimental feature and subject to change.
An alternative to the Makefile
is to use the Python Test Runners, or “runner” for short.
Let’s define a runner for test_my_design_runner
:
# test_runner.py
import os
from pathlib import Path
from cocotb.runner import get_runner
def test_my_design_runner():
sim = os.getenv("SIM", "icarus")
proj_path = Path(__file__).resolve().parent
sources = [proj_path / "my_design.sv"]
runner = get_runner(sim)
runner.build(
sources=sources,
hdl_toplevel="my_design",
)
runner.test(hdl_toplevel="my_design", test_module="test_my_design,")
if __name__ == "__main__":
test_my_design_runner()
Running a test involves three steps:
first the runner is instantiated with
get_runner
based on the default simulator to use,then the HDL is built using the design sources and the toplevel with
runner.build
,finally, the module containing tests to run are passed to
runner.test
The runner can be used with pytest:
pytest
Or it can be run directly:
python test_runner.py
See the section Building HDL and Running Tests for more details on how to use runners.
This concludes our quick introduction to cocotb. You can now look through our Tutorials or check out the Writing Testbenches chapter for more details on the above.