FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

ReadyとValidを制御するためのSkid Buffer

f:id:msyksphinz:20160522024602j:plain

通常のデジタル回路設計において、特定のブロックとの信号をやり取りするのに、ReadyとValidを使うことができる。

Validは送信元がデータ送信を示すのに利用し、Readyは送信先のユニットがデータ受け入れ可能であることを示す。

ReadyとValidの制御については、以下のブログの記事が詳しい。僕もいつも参考にさせてもらっている。

ryuz.txt-nifty.com

ただし、この機構を使うと、すべてのパイプラインでreadyがスライスを切ることなく伝わってしまい、すべてのユニットに遅延が伝搬してしまう。

f:id:msyksphinz:20160522021701p:plain

f:id:msyksphinz:20160522021907p:plain

ユニット間でReadyとValidをいったん区切るためには、Skid Bufferというものを挿入する。これは、Readyがモジュールの逆方向に伝搬し、Readyの信号がパイプライン全体に広がって遅延を増大させることを防ぐ。

f:id:msyksphinz:20160522022307p:plain

Skid Bufferは、

  1. 信号の受け側からのReadyとValid
  2. 信号の送信側からのReadyとValid

の信号を受け、もし受け側が受け入れられない(Readyが0)の場合は、内部のストレージに信号をバックアップしておく機能を持っている。

このデザインについて、ALTERAの資料がVerilogのデザインとともに提供されている。

https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/manual/stx_cookbook.pdf

cookbookという圧縮ファイルをダウンロードすると、cookbokk/stogage/ready_skid.vを開くことができる。 このユニットの構造は大まかに以下のようになっている。

f:id:msyksphinz:20160522022924p:plain

受け側のReadyが0(受け入れられない)場合に送信要求が来た場合

受信側は受け入れられないので、そのままready_iにready_oが伝わり、SkidBufferにそもそもデータは受け入れられない。

    ready_i <= ready_o;

送信元からValidによりデータを受け入れたのだが、その後すぐに受け入れ側のReadyが落ちた場合は、次の送信側のValidをバックアップストレージに格納する。

if (internal_ready_i && valid_i) begin
    // must accept data from source
    if (ready_o || !internal_valid_o) begin
        // accept to main registers
        valid_o <= 1'b1;
        internal_valid_o <= 1'b1;
        dat_o <= dat_i;
    end
    else begin
        // accept to backup storage
        backup_valid <= 1'b1;
        backup_storage <= dat_i;
        ready_i <= 1'b0; // stop stop!
        internal_ready_i <= 1'b0;
    end
end           

上記のVerilogデザインでは、ready_o(受信側のReady信号)が1になっている場合はバックアップストレージを使用せずにそのまま1回スライスを挟んで受信側にデータを送信する。 そうでない場合は、一旦backup_storageにデータを格納し、backup_validを1に設定してready_oが再び立ち上がるのを待つ。

backup_storageにデータが格納されている状態でready_oが立ち上がった場合

ready_oが立ち上がったときに、backup_storageにデータが入っている場合はそれを吐き出す。

if (internal_valid_o & ready_o) begin
    // main data is leaving to the sink
    if (backup_valid) begin
        // dump the backup word to main storage
        backup_valid <= 1'b0;
        dat_o <= backup_storage;
        valid_o <= 1'b1;    
        internal_valid_o <= 1'b1;   
        if (ready_i && valid_i) begin
            $display ("ERROR: data lost in skid buffer");
        end
    end

そうでない場合は、ready_iを1に戻し、送信元からのデータ受け入れを再開する。

Veritak Simulatorにおけるファイルリストの活用法

f:id:msyksphinz:20160425012032p:plain

僕はプライベートでVerilog設計をするとき、シミュレータとしてVeritakを使っている。 国産のVerilogシミュレータで、フリーのVerilogシミュレータに比べてかなり高速、またデバッグ環境が充実している。

大手EDAベンダが販売しているNC-VerilogVCS、QuestaSimなどに比較すると速度も機能充実ども劣るが、個人使用する分には十分で、かなり重宝している。 最近はアップデートがなくなってしまいもう開発停止?になってしまっているのかもしれないが、品質としては現バージョンで十分満足している。

Veritakにおけるプロジェクトの扱い方

このVeritakというシミュレータは、プロジェクトの管理は基本的にGUIで行う仕組みになっている。

f:id:msyksphinz:20160425003508p:plain

VeritakはGUIVerilogファイルをポチポチ選んでいき、プロジェクトを作成、プロジェクトをコンパイルしシミュレーション開始という手順を踏む。 しかし、これはマウスをたくさん動かす必要があり、あまりやりたくない方法ではある。 それよりも、最近のVCSやQuestaなどはファイルリストを読み込むことができる。これと同じようにして、ファイルリストにdefineやファイルのパスを記述しておけば自動的にプロジェクトとして利用できるようにするモードがある。

Veritakにおけるファイルリストの考え方について

Veritakは、ファイルリストやパラメータなどをveritak_src_files.txtというテキストファイルで管理している。これは、基本的にファイルリストと考え方は同じだ。

-Define
FILENAME="dhrystone.riscv.dat"
-include_dir
../../../common/include
c:/Xilinx/Vivado/2015.4/data/verilog/src/unisim_comp.v
../../../../hardware/mag_core/mag_top/sim/tb_mag_top.v
../../../../hardware/mag_core/mag_rnu/rtl/mag_rnu.v
...

define, include_dirの記述方法

defineを定義するためには、-Defineを記述した上で改行、define定数を定義する。include_dirも同様。改行を加える。

ファイルリストの記述方法

ファイルリストは単純にファイルを並べるだけ。これだけでVeritakファイルリストが作成できる。

Veritakのファイルリストのインポート方法

veritak_src_files.txtを作成したら、プロジェクトの編集画面で、[Import]をクリックすると、現在のディレクトリからveritak_src_files.txtを読み込んでプロジェクトファイルに反映される。

絶賛デバッグ中!

f:id:msyksphinz:20160425011209p:plain

ZedBoard向けUbuntu Linuxのビルド(VivadoによるPLの作成)

ZedBoardを使ってARM上でUbuntu Linuxを動作させたい。数年前に FPGAの部屋のmarseeさんによって既に実現されているのだが、同じ過程を辿ろうとすると、当該記事ではPlatform BuilderとPlanAheadが使われており、現在のVivadoの環境では使えないことが分かった。

FPGAの部屋 ZedBoard用のUbuntu Linuxをビルド1(PLをビルド)

さらにオリジナル記事はこちら。

fpgacpu.wordpress.com

そこで、Vivadoの環境でもUbuntu Linuxをビルドできる方法を調査していこう。一番ネックになるのは、おそらくPlatform StudioとPlanAheadで構築していた場所をどのようにしてVivadoに移植するか、というところだと思われる。

いろいろ調べた結果、PLデザインのベースになっているADIのリファレンスHDLおよびプロジェクトが、Vivadoに既に移植されており、これを使えばうまく行きそうな雰囲気がしてきた。

1. ADIのプロジェクトをgithubからダウンロードする

ADIのFPGAボード向けのプロジェクトは、既にgithub上に公開されている。これらをダウンロードして、Vivadoでビルドすれば良い。 今回は、Programmable Logic(PL)とLinuxのビルドを同一環境で実行したいため、全てVagrant上のUbuntuで環境を構築した。 ちなみに、Vagrantは基本的にCUIでの環境を提供しているが、GUIを使いたいとき(というか、Vivadoは基本的にGUIが無いとデザインが確認できないので)は、僕はMobaXtermというツールを使っている。

mobaxterm.mobatek.net

あまり人様の作ったツールに対してケチを付けるつもりは無いのだが、このMobaXtermというツールはお世辞にも実用に耐えれるかというとそうではない。 実際、重要な作業中に何度かXを落とされてしまっているので、仕事では使わないようにしている。趣味でも、最低限のレイアウトの確認とかでXを立ち上げるときは使うけど、それ以外ではCUIで事を済ませるようにしている。

話は戻って、まずはADIのプロジェクトをダウンロードする。

github.com

tree -L 1
.
├── library
├── LICENSE
├── Makefile
├── projects
└── README.md

2 directories, 3 files

このリポジトリは、良く見てみると複数のブランチが存在し、ちゃんと自分の環境に合ったブランチを指定しないとライブラリの生成でコける。僕もこの環境に合わせるために、ADIがサポートしている最新であるVivado 2015.2.1をダウンロードしてインストールする必要があった。

git branch -a
* hdl_2015_r2
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/axi_dmac_sg
  remotes/origin/dev
  remotes/origin/dev_ad7616
  remotes/origin/hdl_2014_r1
  remotes/origin/hdl_2014_r2
  remotes/origin/hdl_2015_r1
  remotes/origin/hdl_2015_r2
  remotes/origin/master

ちなみに、2015_r2ブランチのページには「Vivado 2015.2.1」と書いてあるので、きちんと「2015.2.1」をインストールする必要があるので注意すること。 (2015.2だと、ライブラリのビルド時にバージョンが合わないとエラーが出る)。

git clone https://github.com/analogdevicesinc/hdl.git
cd hdl
git checkout hdl_2015_r2

2. ライブラリのビルド

ADIのページには、まずはライブラリをビルドを必要があるらしい。

ADI Reference Designs HDL User Guide [Analog Devices Wiki]

f:id:msyksphinz:20160225224403p:plain

どうやら、これらのIPのビルドをしなければならない。ZedBoardの場合はADV7511?そして必要なIPはこのあたりだろうか?

f:id:msyksphinz:20160225224513p:plain

このために、それぞれGUIを立ち上げてtclを走らせるのは面倒だ。tclを作成して、ライブラリ作成を自動化する。

  • build_zed.tcl
cd ./hdl/library/axi_clkgen/
source axi_clkgen_ip.tcl
close_project

cd ../../../hdl/library/axi_hdmi_tx
source axi_hdmi_tx_ip.tcl
close_project

cd ../../../hdl/library/axi_hdmi_rx
source axi_hdmi_rx_ip.tcl
close_project

cd ../../../hdl/library/axi_spdif_tx
source axi_spdif_tx_ip.tcl
close_project

cd ../../../hdl/library/axi_i2s_adi/
source axi_i2s_adi_ip.tcl
close_project

cd ../../../hdl/library/util_i2c_mixer/
source util_i2c_mixer_ip.tcl
close_project

以下で必要なライブラリをビルドする。

vivado -mode batch -source ./build_zed.tcl

3. ZynqのPL部をビルドする。

これは、上記のリポジトリのprojectディレクトリに格納されている。 ZedBoardのプロジェクトは以下に格納されている。また、Makefileも格納されているので、すぐにビルドできる。

cd ./hdl/projects/adv7511/zed
make

無事にコンパイルできたようだ。

$ make
#-----------------------------------------------------------
# Vivado v2015.2.1 (64-bit)
# SW Build 1302555 on Wed Aug  5 13:06:02 MDT 2015
# IP Build 1291990 on Mon Jul 27 03:18:52 MDT 2015
# Start of session at: Thu Feb 25 13:03:41 2016
# Process ID: 8865
# Log file: /home/vagrant/download/hdl/projects/adv7511/zed/vivado.log
# Journal file: /home/vagrant/download/hdl/projects/adv7511/zed/vivado.jou
#-----------------------------------------------------------
start_gui
source system_project.tcl
# source ../../scripts/adi_env.tcl
## set ad_hdl_dir  "../../.."
...
  A total of 55 instances were transformed.
  IOBUF => IOBUF (IBUF, OBUFT): 38 instances
  RAM32M => RAM32M (RAMD32, RAMD32, RAMD32, RAMD32, RAMD32, RAMD32, RAMS32, RAMS32): 16 instances
  RAM32X1D => RAM32X1D (RAMD32, RAMD32): 1 instances

open_run: Time (s): cpu = 00:00:12 ; elapsed = 00:00:12 . Memory (MB): peak = 6805.539 ; gain = 206.344 ; free physical = 1153 ; free virtual = 2115
INFO: [Timing 38-91] UpdateTimingParams: Speed grade: -1, Delay Type: min_max.
update_compile_order -fileset sim_1
exit
INFO: [Common 17-206] Exiting Vivado at Thu Feb 25 13:15:08 2016...

4. ビルド結果を確認する。

ビルド結果は、hdl/projects/adv7511/zed/adv7511_zed.runs/impl_1 に格納されている。どうやら、一番新しいdcpは、system_top_routed.dcpのようだ。オープンして結果を見てみる。

vivado system_top_routed.dcp

うまくいっているみたいだ?

f:id:msyksphinz:20160225225922p:plain

f:id:msyksphinz:20160225225958p:plain

関連記事

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

cocotbでテストパタンを記述する(どんな記述ができる?)

前回に引き続き、cocotbによりテストパタンを書いている。 AXIの挙動を真似るのは大変だ。でも、このあたりがうまく攻略できるようになると、例えばISSPythonのIFを供えれば、ISSとの協調シミュレーションも可能になるのではないか。 あとは、なるべくランダムな要素も入れたいので、Pythonをうまく活用してパタンのカバレッジを上げていきたい。

1. 待ち時間をランダムに設定する

例えば、リクエストが発行されてから、応答するまでの待ち時間をランダムに設定するためには、PythonのRandom関数を使って応答すれば良い。

        wait_time = random.randint(0, 10)
        for i in range(0, wait_time):
            yield FallingEdge(self.dut.CPU_CLK)
            if self.dut.IF_MARVALID == 0:
                raise TestFailure("[NG] IF_MARVILD goes down")

2. 別のモデルを挿入することは可能なのか?

例えば、AXIのマスタはテスト対象そのものだが、スレーブに別のVerilogファイルを含めることは可能だろうか? あるいは、もう一つ上の階層でVerilogで囲んでやる必要があるかもしれない。この辺の調査はあまりできていない。 少なくとも、現在はパタンの環境として、

class tb_mag_ifu(object):
    def __init__(self, dut, dubug=True):
        self.dut = dut
...
@cocotb.test()
def basic_test(dut):
    """basic_test"""
    tb = tb_mag_ifu(dut)

まあこの程度しか指定していない。ここでmag_ifuがテスト対象な訳だが、これ以外に例えばAXIメモリなどのモデルを挿入することはできるのだろうか。まだ良く分からない。

3. バグ?関数を定義すると挙動が変わる場合がある

例えば、この二つのコードは同一の意味だと思っているのだがどうだろうか?

    @cocotb.coroutine
    def gen_and_check(self, id):
...

        wait_time = random.randint(0, 10)
        for i in range(0, wait_time):
            yield FallingEdge(self.dut.CPU_CLK)
            if self.dut.IF_MARVALID == 0:
                raise TestFailure("[NG] IF_MARVILD goes down")
        yield RisingEdge(self.dut.CPU_CLK)
        self.dut.IF_MARREADY <= 1
        yield RisingEdge(self.dut.CPU_CLK)
...
    def wait_until_marready(self):
        wait_time = random.randint(0, 10)
        for i in range(0, wait_time):
            yield FallingEdge(self.dut.CPU_CLK)
            if self.dut.IF_MARVALID == 0:
                raise TestFailure("[NG] IF_MARVILD goes down")
        yield RisingEdge(self.dut.CPU_CLK)
        self.dut.IF_MARREADY <= 1
        yield RisingEdge(self.dut.CPU_CLK)

    @cocotb.coroutine
    def gen_and_check(self, id):
...
    wait_until_marready()

なんだか良く分からないが、上記の2つでパタンの挙動に違いがでる。というか下記の関数を定義した場合には、パタンが正常に終了してくれなかった。 何だろう?ちょっと調べてみたほうが良さそうだ。

...

cocotbでAXIインタフェースのチェックパタンを書こう

久し振りにVerilogで回路を書き始めている。ユニットずつ実装をしているのだが、まずはAXI経由でメモリにアクセスする回路を書かなければ。 その際、バッファが溢れないようにAXIインタフェースをちゃんと制御できるか、ちゃんと設計結果を検証することは、なかなかに大変だ。 その際、いろんなパタンを、簡単な言語で記述できるとうれしい。そのための言語としてはやはりSystemVerilogが有名だ。SystemVerilogを使えば、Verilog-HDLよりもより簡単にテストパタンを記述できる。

しかし、今回はもっと別のアプローチを取ってみよう。そのために採用したのは、cocotbを用いたPythonでのテストパタン記述だ。 Pythonをインタフェースとしたことで、VPIを経由したインタフェースでVerilogとの協調検証を行うことができる。

cocotbについては、去年もちょっと試していたが、本格的な導入は未経験だった。

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

1. cocotb + icarus Verilogでテストパタンを実行する

f:id:msyksphinz:20160219011823p:plain

上記のような環境を構築する。実際には、テスト対象に対して、Pythonとテストシナリオを記述していくだけだ。テストシナリオを書くと、テストパタンは自動的に構築される。

import cocotb
from cocotb.triggers import Timer, RisingEdge
from cocotb.result import TestFailure
from cocotb.clock import Clock

import random

class tb_mag_ifu(object):
    def __init__(self, dut, dubug=True):
        self.dut = dut

...

@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 = tb_mag_ifu(dut)
    cocotb.fork(clock_gen(dut.CPU_CLK))
    tb.axi_reset()
    tb.reset_br_signal()
    tb.set_pc_init(0xbfc00000)
    yield RisingEdge(dut.CPU_CLK)
    yield tb.reset()

    for i in range(10):
        yield tb.gen_and_check()

上記のPythonのテストシナリオにより、tb.gen_and_check()を10回呼んでいる。この中に実際のAXIのテストパタンが書かれており、AXIの動作をチェックしているという訳だ。

1.1. AXIテストの本体(書きかけ)

実際のgen_and_check()は、現在は書きかけなのだが、大体こんな感じだ。

    def gen_and_check(self):
        yield RisingEdge(self.dut.CPU_CLK)
        yield RisingEdge(self.dut.CPU_CLK)
        if self.dut.IF_MARVALID == 1:
            self.dut.log.info("Detect MARVALID")
        else:
            self.dut.log.info("Wait MARVALID")
        wait_time = random.randint(0, 10)
        for i in range(0, wait_time):
            yield RisingEdge(self.dut.CPU_CLK)
            if self.dut.IF_MARVALID == 0:
                raise TestFailure("[NG] IF_MARVILD goes down")
        self.dut.IF_MARREADY <= 1

IF_MARVALIDが立ち上がる(テスト対象がアドレスチャネルを有効化する)と、それからランダムなタイミングでIF_MARREADYをアサートする。ここでRandomを使えたり、for文で簡単にテスト動作を記述できるのがcocotbの強みだ。 そこから、さらに別のIDとか、VALIDのタイミングなどがチェックできるし、Pythonのライブラリを使ってFIFOを擬似的に作ったりすることができる。 ただし、そこまではまだ構築できていない。

2. 実行結果

makeによりテストパタンを実行すると、以下のような結果が出力された。

        TESTCASE= TOPLEVEL=mag_ifu TOPLEVEL_LANG=verilog \
        vvp -M /home/vagrant/magnetor-1/hardware/cocotb/build/libs/x86_64 -m gpivpi sim_build/sim.vvp  -I /home/vagrant/magnetor-1/hardware/mag_core/mag_ifu/cocotb/../../../common/include/
     -.--ns INFO     cocotb.gpi                                GpiCommon.cpp:91   in gpi_print_registered_impl       VPI registered
     0.00ns INFO     cocotb.gpi                                  gpi_embed.c:248  in embed_sim_init                  Running on Icarus Verilog version 0.9.7
     0.00ns INFO     cocotb.gpi                                  gpi_embed.c:249  in embed_sim_init                  Python interpreter initialised and cocotb loaded!
     0.00ns INFO     cocotb                                      __init__.py:115  in _initialise_testbench           Running tests with Cocotb v1.0 from /home/vagrant/magnetor-1/hardware/cocotb
     0.00ns INFO     cocotb                                      __init__.py:131  in _initialise_testbench           Seeding Python random module with 1455812561
     0.00ns INFO     cocotb.regression                         regression.py:161  in initialise                      Found test tb_mag_ifu.basic_test
     0.00ns INFO     cocotb.regression                         regression.py:262  in execute                         Running test 1/1: basic_test
     0.00ns INFO     ..routine.basic_test.0x2b690cd41910       decorators.py:189  in send                            Starting test: "basic_test"
                                                                                                                               Description: basic_test
/home/vagrant/magnetor-1/hardware/cocotb/cocotb/handle.py:165: UserWarning: Use of log attribute is deprecated
  warnings.warn("Use of %s attribute is deprecated" % name)
     0.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:13   in axi_reset                       Resetting AXI
VCD info: dumpfile dump.vcd opened for output.
     5.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:31   in reset                           Resetting DUT
    15.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:40   in reset                           Out of reset
    35.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:47   in gen_and_check                   Detect MARVALID
   105.00ns WARNING  ..tine.gen_and_check.0x2b690cd86490       decorators.py:117  in send                            [NG] IF_MARVILD should go down
   105.00ns ERROR    cocotb.regression                         regression.py:246  in handle_result                   Test Failed: basic_test (result was TestFailure)
   105.00ns ERROR    cocotb.regression                         regression.py:167  in tear_down                       Failed 1 out of 1 tests (0 skipped)
   105.00ns INFO     cocotb.regression                         regression.py:176  in tear_down                       Shutting down...

failになってしまった!MARVALIDとMARREADYが立ち上がったあと、一度間隔をあけるためにMARVALIDを落とすような仕様にしているのだが、それがうまく動作していないようだ? まあ、それ以外にも、前回と違うIDを出力してくるはずだとか、チェックのやりようはたくさんあるので、その辺を追加していこう。

ちなみに、

今回はIcarus Verilogを利用したのだが、GTKWaveがおそろしく使いにくいので、もっと使いやすい環境に移行したい。

おまけ. Vivado Simulatorの環境を構築することは可能なのか?

調査してみたが、どうやらシミュレータにVPIの環境が備わっている必要があるようだ。現在、Vivado SimulatorはDPIしか装備しておらず、うまくいかない。。。 この辺は、もしかしたらDPIを記述すれば、自力で対応させることができるかもしれない?

CMakeでVerilogシミュレーション環境を構築する

Verilogで久し振りに回路設計をしているのだが、ビルドの標準環境としてCMakeを使いたい。 CMakeでVerilogを構築する手法については、以下の日記でトライしてみた。これを今度は実用してみよう。

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

1. Vivado Simulatorの環境を構築するためのCMakeLists.txt

CMakeLists.txtには、Vivado Simulatorを実行するための記述をする。 Vivado Simulatorって、基本的に以下の3ステップが必要だという理解だ。

  1. xvlog ファイルリストからシミュレーション環境を構築する
  2. xelab シミュレーション環境をelaborateする。シミュレーションのためのスナップショットを作成する
  3. xsim スナップショットをシミュレーションする

これらをCMakeLists.txtで記述すると、以下のようになる。

add_custom_target (my_design ALL
  COMMAND xsim run_tb_my_design --R
  DEPENDS my_design_xelab
  )

add_custom_target (my_design_xelab
  COMMAND xelab -s run_tb_my_design work.tb_my_design
  DEPENDS work
  )

add_custom_command (OUTPUT work
  COMMAND xvlog -f filelist.f -i ../../../../hardware/common/include/
  DEPENDS filelist.f
  )

add_custom_command (OUTPUT filelist.f
  COMMAND cpp -P -DHW_DIR=../../../../hardware tb_my_design.f > filelist.f
  DEPENDS tb_my_design.f ../rtl/my_design.f
  )

add_custom_target (distclean ALL
  COMMAND rm -rf xsim.dir *.log *.jou *.pb *.wdb
  )

このときに気をつけなければならないのは、xvlogはインクルードファイルが格納されているディレクトリの場所は--include(-i)で指定しなければならないところだ。 filelistに格納して+incdir+DIRで指定できるのかと思っていたが、違うようだ。気をつけなければ。

とりあえずこれでVivado Simulatorでシミュレーションできる環境が構築できる。次は、cocotbでテストパタンを構築する方法を探索しよう。

CMakeによりVerilogのビルドを管理するための調査(2. CTestによるテスト追加)

前回までで、どうにかこうにかCMakeでVivado Simulatorのビルド環境を構築した。

ここまで出来たら、実はCTestによるテストの追加は容易なのだ。

CMakeLists.txtに以下を追加してみた。

# CTest
enable_testing()

add_test (NAME basic_test
          COMMAND /cygdrive/c/Xilinx/Vivado/2015.4/bin/xsim --R top_sim)

今回はxsimのコマンドを追加し、オプション無しだが、引数として追加するか、外部から制御ファイルを与えることで複数のテストも追加できるだろう。 さらに言うならば、今回はテストの成功と失敗の判定をしていないが、スクリプトなどで一回囲んで、ちゃんとPass/Failを出力するようにすべきだろう。

CTestで実行してみる

ここまで出来たら後のリグレッションは非常に単純だ。CTestを実行すれば良い。

$ ctest
Test project /home/masayuki/work/xvlog_test
    Start 1: basic_test
1/1 Test #1: basic_test .......................   Passed    2.55 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) =   2.56 sec

自動的にテストを実行してくれた!あとはCMakeLists.txtに自分の好きなテストを追加すればやり放題だ。

つまり、CMakeとCTestによるテストパタンの管理は、「CMakeLists.txtを使って適切にシミュレータのビルド環境さえ構築できれば」非常に容易であることが分かった。 ビルド環境の構築には、やはりソフトウェアのプロジェクトで無い分複雑性が増すが、それもadd_custom_command()などを使えば比較的容易に記述できる。

うまく使えば、協力なリグレッションツールになるのでは無いだろうか。