cocotb
cocotb is a coroutine-based cosimulation library that enables writing Verilog and VHDL testbenches in Python. It provides a robust framework for verifying hardware designs by interacting directly with commercial and open-source HDL simulators. The current version is 2.0.1, with major releases typically aligning with significant architectural changes, moving towards more Pythonic async/await patterns.
Common errors
-
ModuleNotFoundError: No module named 'cocotb'
cause The Python environment running the test does not have `cocotb` installed or the `PYTHONPATH` is not correctly configured to find the test modules.fixRun `pip install cocotb` in your active Python environment. If using a `Makefile`, ensure `PYTHONPATH` is correctly set to include the directory containing your Python test file(s). -
ERROR: No tests discovered
cause The `cocotb` test runner could not find any functions decorated with `@cocotb.test()` in the specified test modules, or the Python test modules were not correctly loaded.fixEnsure your test functions are decorated with `@cocotb.test()`. Verify the `Makefile`'s `MODULE` variable points to the correct Python module (e.g., `MODULE=test_my_design`) and `PYTHONPATH` includes the directory containing `test_my_design.py`. -
ERROR: [simulator] command not found
cause The HDL simulator specified in the `SIM` variable of your `Makefile` (e.g., `ghdl`, `iverilog`, `vsim`) is not installed or not accessible in your system's PATH.fixInstall the required HDL simulator (e.g., Icarus Verilog for Verilog, GHDL for VHDL). Ensure its executable is added to your system's PATH environment variable. -
AttributeError: module 'cocotb.test' has no attribute 'TestFactory'
cause You are attempting to import `TestFactory` from `cocotb.test` in `cocotb` version 2.0.0 or later.fixChange your import statement from `from cocotb.test import TestFactory` to `from cocotb.regression import TestFactory`.
Warnings
- breaking cocotb 2.0 introduced significant breaking changes, migrating towards native Python `async`/`await` syntax. The `@cocotb.coroutine` decorator was removed (use `async def` with `@cocotb.test()`), `cocotb.test.TestFactory` moved to `cocotb.regression.TestFactory`, and `cocotb.result` module was removed.
- gotcha cocotb itself is a Python library, but it requires an external HDL simulator (e.g., Icarus Verilog, GHDL, QuestaSim, VCS) to run any tests. This simulator must be installed and accessible in your system's PATH.
- gotcha Running `cocotb` tests typically relies on a `Makefile` to configure the simulation environment (e.g., `TOPLEVEL`, `TOPLEVEL_LANG`, `SIM`, `VERILOG_SOURCES`/`VHDL_SOURCES`, `PYTHONPATH`) and invoke the simulator. Incorrect `Makefile` configuration is a common source of issues.
- gotcha All `cocotb` triggers (e.g., `Timer`, `RisingEdge`) and asynchronous cocotb functions must be `await`ed within an `async` test function. Forgetting `await` will not pause the simulation and can lead to unexpected behavior or tests that complete instantly.
Install
-
pip install cocotb
Imports
- test
import cocotb
- Timer
from cocotb.triggers import Timer
- Clock
from cocotb.clock import Clock
- TestFactory
from cocotb.test import TestFactory
from cocotb.regression import TestFactory
Quickstart
import cocotb
from cocotb.triggers import Timer, RisingEdge
from cocotb.clock import Clock
import os
@cocotb.test()
async def my_first_test(dut):
"""Attempt to verify D flip-flop functionality"""
cocotb.log.info("Starting my_first_test")
# Access signals on the DUT (Design Under Test)
# These signal names (clk, i, q) correspond to ports in an HDL design
clk = dut.clk
i = dut.i
q = dut.q
# Set initial values
i.value = 0
await Timer(1, units="ns")
# Create a clock
clock = Clock(clk, 10, units="ns") # 10 ns period, 100MHz
cocotb.start_soon(clock.start())
# Assert reset if available (common in DFF designs)
if hasattr(dut, 'reset_n'): # Check if 'reset_n' signal exists on DUT
dut.reset_n.value = 0
await RisingEdge(clk)
await RisingEdge(clk) # Ensure reset holds for a bit
dut.reset_n.value = 1
await RisingEdge(clk)
else:
await RisingEdge(clk) # Just advance clock if no reset
# Test case 1: input 0, expect output 0 (after clock edge)
i.value = 0
await RisingEdge(clk)
cocotb.log.info(f"Input: {i.value}, Output: {q.value}")
assert q.value == 0, f"Expected q=0, got {q.value}"
# Test case 2: input 1, expect output 0 (output lags input by one clock cycle)
i.value = 1
await RisingEdge(clk)
cocotb.log.info(f"Input: {i.value}, Output: {q.value}")
assert q.value == 0, f"Expected q=0, got {q.value}"
# Test case 3: input 1, expect output 1 (on the next clock edge)
await RisingEdge(clk)
cocotb.log.info(f"Input: {i.value}, Output: {q.value}")
assert q.value == 1, f"Expected q=1, got {q.value}"
cocotb.log.info("Test finished")