FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

cocotbを試す(1.導入)

cocotbは、Pythonで記述できるテストベンチ作成ユーティリティだ。

qiita.com

github.com

ユニットテストをする際、簡単にテストパタンを作れたほうがいいし、Verilogをそのまま書くのも面倒なので、Pythonで記述できるcocotbを使ってみることにした(まあPythonはあまり使ったことが無いのだけれど...)

  • まずはcocotbをインストールする前に、各種のツールをインストールした。gtkwave,icarus-verilog,python-develだ。 そして、cocotbをgithubからクローンする。cocotb自体にはMakefileが付いているので、とりあえずbuildしておく(INSTALLに書いていないので、必要かどうかは不明)。
# Install pre-requisites (waveform viewer optional)
sudo yum install -y iverilog python-devel gtkwave

# Checkout git repositories
git clone https://github.com/potentialventures/cocotb.git
cd cocotb/
make

いくつかテストパタンがある。./examples/adder/などを見てみる。

./cocotb/examples/adder$ tree .
.
├── hdl
│   └── adder.v
├── model
│   ├── __init__.py
│   ├── adder_model.py
│   └── adder_model.pyc
└── tests
    ├── Makefile
    ├── dump.vcd
    ├── results.xml
    ├── sim_build
    │   └── sim.vvp
    ├── test_adder.py
    └── test_adder.pyc

4 directories, 10 files
  • hdl : デザインが含まれている。テストするデザイン。
  • model : これはゴールデンモデルが入っているらしい。adderならば、Pythonで記述されたadderのゴールデンモデルだ。
  • tests : デザインとゴールデンモデルを接続し、テストパタンを動作させるための環境だ。

まずはhdlから見ていこう。adder.vは通常のデザインファイルだ。

$ cat hdl/adder.v 
// Adder DUT
module adder (input [3:0] A,
              input [3:0] B,
              output reg [4:0] X);
  always @(A or B) begin
    X = A + B;
  end

  // Dump waves
  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(1, adder);
  end

endmodule

非常に単純だ。次はPythonで記述されたゴールデンモデル。

$ cat model/adder_model.py
def adder_model(a, b):
    """ model of adder """
    return a + b

単純だ。次に、テストパタンだ。

$ cat tests/test_adder.py
# Simple tests for an adder module
import cocotb
from cocotb.triggers import Timer
from cocotb.result import TestFailure
from adder_model import adder_model
import random


@cocotb.test()
def adder_basic_test(dut):
    """Test for 5 + 10"""
    yield Timer(2)
    A = 5
    B = 10

    dut.A = A
    dut.B = B

    yield Timer(2)

    if int(dut.X) != adder_model(A, B):
        raise TestFailure(
            "Adder result is incorrect: %s != 15" % str(dut.X))
    else:  # these last two lines are not strictly necessary
        dut.log.info("Ok!")


@cocotb.test()
def adder_randomised_test(dut):
    """Test for adding 2 random numbers multiple times"""
    yield Timer(2)

    for i in range(10):
        A = random.randint(0, 15)
        B = random.randint(0, 15)

        dut.A = A
        dut.B = B

        yield Timer(2)

        if int(dut.X) != adder_model(A, B):
            raise TestFailure(
                "Randomised test failed with: %s + %s = %s" %
                (int(dut.A), int(dut.B), int(dut.X)))
        else:  # these last two lines are not strictly necessary
            dut.log.info("Ok!")

この例では、通常のテストと、ランダムテストを実施しているみたいだ。 見て分かるとおり、デザインの生成した答えと、ゴールデンモデルが生成した答えが一致しているかを確認している。

        if int(dut.X) != adder_model(A, B):
            raise TestFailure(
                "Randomised test failed with: %s + %s = %s" %
                (int(dut.A), int(dut.B), int(dut.X)))

あとはtestディレクトリでmakeを実行することで、テストが行なわれる。 ちゃんと通常テストと、ランダムテストを実行している。

PYTHONPATH=/home/masayuki/work/cocotb/build/libs/x86_64:/home/masayuki/work/cocotb:/home/masayuki/work/cocotb/examples/adder/tests:/home/masayuki/work/cocotb/examples/adder/tests/../model:/home/masayuki/work/cocotb/examples/adder/tests/../model: LD_LIBRARY_PATH=/home/masayuki/work/cocotb/build/libs/x86_64::/usr/lib:/usr/lib:/usr/lib:/usr/lib MODULE=test_adder \
        TESTCASE= TOPLEVEL=adder \
        vvp -M /home/masayuki/work/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.10.0 (devel)
     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 /home/masayuki/work/cocotb
     0.00ns INFO     cocotb.gpi                                  __init__.py:119  in _initialise_testbench           Seeding Python random module with 1435501859
     0.00ns INFO     cocotb.regression                         regression.py:153  in initialise                      Found test test_adder.adder_basic_test
     0.00ns INFO     cocotb.regression                         regression.py:153  in initialise                      Found test test_adder.adder_randomised_test
     0.00ns INFO     cocotb.regression                         regression.py:254  in execute                         Running test 1/2: adder_basic_test
     0.00ns INFO     ..e.adder_basic_test.0x2aba91ba8490       decorators.py:186  in send                            Starting test: "adder_basic_test"
                                                                                                                               Description: Test for 5 + 10
VCD info: dumpfile dump.vcd opened for output.
     0.00ns INFO     cocotb.adder                              test_adder.py:25   in adder_basic_test                Ok!
     0.00ns INFO     cocotb.regression                         regression.py:201  in handle_result                   Test Passed: adder_basic_test
     0.00ns INFO     cocotb.regression                         regression.py:254  in execute                         Running test 2/2: adder_randomised_test
     0.00ns INFO     ..er_randomised_test.0x2aba91ba8890       decorators.py:186  in send                            Starting test: "adder_randomised_test"
                                                                                                                               Description: Test for adding 2 random numbers multiple times
     0.00ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.01ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.01ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.01ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.01ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.01ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.02ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.02ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.02ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.02ns INFO     cocotb.adder                              test_adder.py:47   in adder_randomised_test           Ok!
     0.02ns INFO     cocotb.regression                         regression.py:201  in handle_result                   Test Passed: adder_randomised_test
     0.02ns INFO     cocotb.regression                         regression.py:162  in tear_down                       Passed 2 tests (0 skipped)
     0.02ns INFO     cocotb.regression                         regression.py:168  in tear_down                       Shutting down...
make[1]: ディレクトリ `/home/masayuki/work/cocotb/examples/adder/tests' から出ます