LoginSignup
24
23

More than 5 years have passed since last update.

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

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が出たばかりでもあり、今後に期待です。

24
23
0

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
24
23