cocotb is an HDL testing framework that uses Python. I've been watching for a while, but since version 1.0 has been released, I decided to give it a try.
cocotb is a lightweight testing framework for HDL developed by Potential Ventures, written in Python. It is published on GitHub under the BSD license. https://github.com/potentialventures/cocotb
The documentation is here. http://cocotb.readthedocs.org/en/latest/index.html
Aware of the rapid launch of a verification environment in FPGA development, it is much lighter than UVM written in SystemVerilog, and written in Python. Therefore, the description is simple and easy to read.
Compatible simulators are Icarus Verilog, VCS, Riviera-PRO, [Questa (ModelSim)](http://www.mentorg.co.jp/products/fv/ It supports questa /), Incisive. Especially Icarus and VCS seem to have a good affinity.
In addition, it has a file output function for xUnit, and it can be said that it is easy to link with the CI tool Jenkins.
Python generators, coroutines and decorator technology are used to access DUT (HDL). It is a mechanism to pause the simulator with a coroutine, insert the processing of cocotb, and restart the simulator. Some people who use Python are not familiar with it, but with cocotb, if you know how to use it, you don't need to be very conscious of it.
This time, using samples and documents as clues, I used cocotb to describe an environment for verifying Verilog's sequential circuit. The OS is CentOS 6.6.
Download from GitHub.
git clone https://github.com/potentialventures/cocotb
This time, I created a new working directory in the ʻexample directory, created the
rtland
tests` directories there, and saved the DUT and tests, respectively.
Icarus Verilog is used for the simulator. Put "icarus" in the simulator selection part of "Makefile.sim" in the makefiles
directory.
# Default to Icarus if no simulator is defined
SIM ?= icarus
DUT It is an 8-bit sequential circuit. Inside, I am trying to output a vcd file for waveform acquisition.
dff.v
module dff(
input RST_N,
input CLK,
input [7:0] D,
output reg [7:0] Q
);
always @(negedge RST_N, posedge CLK)
if(~RST_N)
Q <= 8'h0;
else
Q <= D;
initial begin
$dumpfile("dump.vcd");
$dumpvars(1, dff);
end
endmodule
I put in a testbench environment (instance with DUT) and a simple driver checker because I only need one file. This is a scenario in which a random value is input and the output value from the DUT is checked after one cycle.
tests.py
import cocotb
from cocotb.triggers import Timer, RisingEdge
from cocotb.result import TestFailure
from cocotb.clock import Clock
import random
class DffTB(object):
def __init__(self, dut, dubug=True):
self.dut = dut
@cocotb.coroutine
def reset(self, duration=10000):
self.dut.log.info("Resetting DUT")
self.dut.RST_N <= 0
self.dut.D <= 0
yield Timer(duration)
yield RisingEdge(self.dut.CLK)
self.dut.RST_N <= 1
self.dut.log.info("Out of reset")
@cocotb.coroutine
def gen_and_check(self):
D = random.randint(0, 255)
self.dut.D = D;
yield RisingEdge(self.dut.CLK)
yield Timer(1)
if int(self.dut.Q) != D :
raise TestFailure(
"[NG] Compre error. D==%s Q==%s" % (D, int(self.dut.Q)))
else :
self.dut.log.info("[OK]")
@cocotb.coroutine
def clock_gen(signal):
while True:
signal <= 0
yield Timer(5000)
signal <= 1
yield Timer(5000)
@cocotb.test()
def basic_test(dut):
"""basic_test"""
tb = DffTB(dut)
cocotb.fork(clock_gen(dut.CLK))
yield RisingEdge(dut.CLK)
yield tb.reset()
for i in range(30):
yield tb.gen_and_check()
In cocotb, "dut" is a reserved word, which corresponds to the top level of DUT. For the sake of simplicity, only the instance of dut is described in the constructor of class so far, but we will add processing such as initialization of other verification modules and utilities.
The test scenario is written by decorating cocotb.test () as written at the end.
In performing the simulation, the main processing part is decorating coroutb.coroutine. In the above description, it is around "clock_gen" and "gen_and_check".
I have prepared a Makefile in the tests
directory.
Specify the DUT top hierarchy in (TOPLEVEL) and enter the Python script name in (MODULE).
Makefile
TOPLEVEL := dff
TOPLEVEL_LANG ?= verilog
PWD=$(shell pwd)
COCOTB=$(PWD)/../../..
ifeq ($(OS),Msys)
WPWD=$(shell sh -c 'pwd -W')
PYTHONPATH := $(WPWD)/../model;$(PYTHONPATH)
else
WPWD=$(shell pwd)
PYTHONPATH := $(WPWD)/../model:$(PYTHONPATH)
endif
export PYTHONPATH
VERILOG_SOURCES = $(WPWD)/../rtl/dff.v
GPI_IMPL := vpi
export TOPLEVEL_LANG
MODULE ?= tests
include $(COCOTB)/makefiles/Makefile.inc
include $(COCOTB)/makefiles/Makefile.sim
As shown below, it was possible to flow from the initialization phase to reset issuance, output value check, and simulation end.
TESTCASE= TOPLEVEL=dff \
vvp -M /tmp/cocotb/build/libs/x86_64 -m gpivpi sim_build/sim.vvp
-.--ns INFO cocotb.gpi GpiCommon.cpp:47 in gpi_print_registered_impl VPI registered
0.00ns INFO cocotb.gpi gpi_embed.c:229 in embed_sim_init Running on Icarus Verilog version 0.9.6
0.00ns INFO cocotb.gpi gpi_embed.c:230 in embed_sim_init Python interpreter initialised and cocotb loaded!
0.00ns INFO cocotb.gpi __init__.py:103 in _initialise_testbench Running tests with Cocotb v1.0 from /tmp/cocotb
0.00ns INFO cocotb.gpi __init__.py:119 in _initialise_testbench Seeding Python random module with 1430897996
0.00ns INFO cocotb.regression regression.py:153 in initialise Found test tests.basic_test
0.00ns INFO cocotb.regression regression.py:254 in execute Running test 1/1: basic_test
0.00ns INFO ..routine.basic_test.0x7f2a3156ffd0 decorators.py:186 in send Starting test: "basic_test"
Description: basic_test
VCD info: dumpfile dump.vcd opened for output.
5.00ns INFO cocotb.dff tests.py:14 in reset Resetting DUT
15.00ns INFO cocotb.dff tests.py:20 in reset Out of reset
25.00ns INFO cocotb.dff tests.py:32 in gen_and_check [OK]
35.00ns INFO cocotb.dff tests.py:32 in gen_and_check [OK]
(Omission)
315.00ns INFO cocotb.dff tests.py:32 in gen_and_check [OK]
315.00ns INFO cocotb.regression regression.py:201 in handle_result Test Passed: basic_test
315.00ns INFO cocotb.regression regression.py:162 in tear_down Passed 1 tests (0 skipped)
315.00ns INFO cocotb.regression regression.py:168 in tear_down Shutting down...
The waveform is as follows. You can see that it is initialized with RST_N and then a random value is entered.
With some knowledge of Python, I get the impression that it is relatively easy to build a verification environment. However, although I may not understand it, the documentation is not very complete. Also, the Endian Swapper at the beginning of the Tutorial felt like the threshold was too high. However, in SystemVerilog, it is fairly easy to understand that the test part that is difficult to debug is Python, and I think that using a Python library like random this time has a big advantage in terms of cost and many users. I will. 1.0 has just come out, and I have high expectations for it in the future.
Recommended Posts