Simulation and functional verification
How to write a test that can actually catch a bug, with cocotb.
On this page
Write the test from the specificationWhat a test should coverA minimal testStaying in step with the simulatorEvery check in one tableWhat a pass meansRules the lab applies to testsVerification asks whether the code does what the specification says. In this lab a test is a Python file, tb/test_top.py, run with cocotb on the Icarus Verilog simulator.
Write the test from the specification#
Work out each expected value from the described behaviour, in Python. Never copy an expression from the design into the test. A test that repeats the design's mistake cannot catch it.
What a test should cover#
- Reset. Each registered output takes its specified reset value.
- The main behaviour, with several different inputs, not one.
- Edges. The smallest and largest inputs, zero, and the case that overflows or wraps.
- Control. State governed by an enable holds while that enable is low. Reset in the middle of operation returns state to its reset values.
- Repeatability. When inputs are random, set a fixed seed so a failure repeats: one line
export COCOTB_RANDOM_SEED = 1234in the Makefile. The lab keeps the seed of every Simulate run; see Seeds, proof limits, equivalence and the SystemVerilog subset.
A minimal test#
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import FallingEdge
@cocotb.test()
async def test_accumulate(dut):
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
dut.rst_n.value = 0
dut.en.value = 0
await FallingEdge(dut.clk)
dut.rst_n.value = 1
dut.a.value, dut.b.value, dut.en.value = 3, 4, 1
await FallingEdge(dut.clk)
await FallingEdge(dut.clk)
got = dut.acc.value.to_signed()
assert got == 12, f"3 x 4: expected 12, saw {got}"Staying in step with the simulator#
- Change inputs on the falling clock edge.
- Read outputs on a later falling edge, so the design has settled and the test never races it.
- Give every
asserta message with the inputs, the expected value and the value seen.
In cocotb 2 the clock argument is unit, not units, and a signed read is value.to_signed().
Every check in one table#
The project's V&V tab lists every check the lab has for the project (lint, simulation, formal, coverage, synthesis, equivalence, DRC, LVS, antenna, timing, IR drop) with its state, run id and report link, and exports the list as JSON. See V&V summary and checks.
What a pass means#
A pass shows that this test and this design agree. It does not show that the test is thorough. Ask yourself what behaviour the test never exercises, and add it.
Rules the lab applies to tests#
A test may import only cocotb, cocotb.clock, cocotb.triggers and random. It may not read files, start processes or use the network.
Something unclear or missing? Send feedbackSign in and use Feedback to tell us.