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 rtlandtests` 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