cocotbは、Pythonで記述できるテストベンチ作成ユーティリティだ。
ユニットテストをする際、簡単にテストパタンを作れたほうがいいし、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' から出ます