VOOZH about

URL: https://qiita.com/tethys_seesaa/items/99504dede7657f1619ce

⇱ VerilogのテストにPython製フレームワーク「cocotb」を使う。 #FPGA - Qiita


👁 Image
25

Go to list of users who liked

24

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

More than 5 years have passed since last update.

@tethys_seesaa

VerilogのテストにPython製フレームワーク「cocotb」を使う。

25
Posted at

はじめに。

cocotbは、Pythonを使用したHDLテストフレームワークです。
しばらく成り行きを見守っていたのですが、バージョン1.0がリリースされたので試してみることにしました。

cocotbの特徴。

cocotbは、Potential Ventures社が開発しているHDL向けの軽量なテストフレームワークで、Pythonで記述されています。BSDライセンスにてGitHubに公開されています。
https://github.com/potentialventures/cocotb

ドキュメントはこちらになります。
http://cocotb.readthedocs.org/en/latest/index.html

FPGA開発における速やかな検証環境の立ち上げ意識しており、SystemVerilogで書かれたUVMよりも非常に軽く、またPythonで書かれているため、簡素で見やすい記述になっています。

対応シミュレーターは、Icarus Verilog, VCS, Riviera-PRO, Questa(ModelSim), Incisiveに対応しています。
特にIcarusとVCSは親和性が良さそうです。

また、xUnit用のファイル出力機能を備えており、CIツールであるJenkinsと連携しやすいのも特徴と言えます。

DUT(HDL)とのアクセスにはPythonのジェネレータとコルーチン、デコレータ技術が使われています。コルーチンでシミュレーターをポーズさせ、cocotbの処理を入れて、シミュレーターを再開させる仕組みです。
Pythonを使っている人にも慣れないところがありますが、cocotbでは使いどころがわかればあまり意識をする必要はありません。

今回はサンプルとドキュメントを手かがりに、Verilogの順序回路を検証する環境をcocotbを使って記述してみました。OSは、CentOS6.6です。

準備

GitHubよりダウンロードします。

git clone https://github.com/potentialventures/cocotb

今回はexampleディレクトリに新たにワークディレクトリを作成し、そこにrtlおよびtestsディレクトリを作成し、それぞれDUT、テストを保存しました。

シミュレータにはIcarus Verilogを使用します。makefilesディレクトリにある「Makefile.sim」のシミュレーター選択部分に「icarus」を入れます。

# Default to Icarus if no simulator is defined
SIM ?= icarus

DUT

8bitの順序回路になります。
中で、波形取得のためにvcdファイルを出力させるようにしています。

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

テスト

ファイル1個で済ますため、テストベンチ環境(DUTとのインスタンス)と簡単なドライバ・チェッカを入れました。
内容としてはランダムな値を入力し、1サイクル後DUTからの出力値をチェックするシナリオです。

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()

cocotbでは「dut」が予約語で、これがDUTのトップ階層と対応します。
ここでは簡単にするため、classのコンストラクタは今のところdutのインスタンスしか記述されていませんが、他の検証モジュールやユーティリティを初期化等の処理を入れていきます。

テストシナリオは、最後に書かれているようにcocotb.test()をデコレートして記述します。

シミュレーションを行うに当たって、主な処理する部分はcocotb.coroutineをデコレートします。上記記述では「clock_gen」や「gen_and_check」辺りがそれになります。

シミュレーション実行スクリプト

testsディレクトリにMakefileを用意しました。
(TOPLEVEL)にDUTトップ階層を指定し、(MODULE)にPythonスクリプト名を入力します。

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

実行結果・波形

以下のように、初期化フェイズからリセット発行、出力値チェック、シミュレーション終了まで流すことが出来ました。

 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]

 (中略)

 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...

波形は以下のようになりました。RST_Nで初期化されて、その後ランダムな値が入力されることがわかります。

👁 waves.png

おわりに。

Pythonの知識がある程度あると、比較的簡単に検証環境を構築できる印象です。
しかし、自分の理解不足なところもあるかもしれませんが、ドキュメントはあまり充実しているとは言えません。また、Tutorialの最初にあるEndian Swapperは敷居が高すぎるように感じました。
とはいえ、SystemVerilogではデバッグしづらいテスト部分がPythonだとかなり分かり易く、また今回のrandomのようにPythonのライブラリが使えるのは、コストや利用者の多さから考えて利点が大きいと思っています。
まだ1.0が出たばかりでもあり、今後に期待です。

25

Go to list of users who liked

24
0

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25

Go to list of users who liked

24