GHDLとVunitでVHDLのテスト環境を構築する

# はじめに
FPGAはハードウェアでありながら回路機能を書き換えることができるため
ソフトウェアと同様に開発中にも仕様変更や機能追加がしばしば発生します
しかしながらFPGAベンダが提供しているツールには
複数のテストを自動実行する環境が基本的に用意されていません [^1]

そこでオープンソースソフトウェアによる
ハードウェア記述言語VHDLのテスト環境の構築方法と
テストの実行方法をまとめます

テストの書き方については別の記事でまとめます

# GHDLとは
* オープンソースのVHDLシミュレータ
* Windows, Linux, Mac OS X 対応

# Vunitとは
* オープンソースのVHDL/System Verilogテスティングフレームワーク
* Windows, Linux, Mac OS X 対応
* シミュレータがGHDLの場合はVHDLのみ対応

# 開発環境
* Windows10 Pro 64bit
* Python 3.7.0
* vunit-hdl 4.0.8
* colorama 0.4.1
* GHDL 0.35

GHDLが諸々のツールを揃えた[dockerイメージ](https://hub.docker.com/r/ghdl/ext)を公開していますが
~~dockerの使い方がよくわからないので~~ [^2]
**2019/07/27追記**
[Docker版を書きました](https://qiita.com/ryo_i6/items/bbcaab0a0fc5706b7c7e)

この記事ではローカルに環境を構築します



# 環境構築
Pythonのインストールは省略

## Vunitのインストール
Vunitはpipでインストールします

```:cmd
pip install vunit_hdl
```
このとき、テスト結果を色別表示するために
colormaをインストールするか確認されるのでインストールします

## GHDLのインストール
[GHDL](https://github.com/ghdl/ghdl/releases)をダウンロードしてユーザー環境変数にPathを追加します

```:ユーザ環境変数Path
foo\ghdl\bin
```

Pathを追加後、Windowsを再起動して環境変数を反映させます

# テストの実行
## 全テストの実行

[Vunitのリポジトリ](https://github.com/VUnit/vunit)をダウンロードします
ディレクトリを移動してuser_guideのサンプルを実行すると結果が表示されます
-v オプションをつけない場合、成功したテストの出力(Hello world!など)は表示されません

```:cmd
cd bar\vunit-master\examples\vhdl\user_guide
python run.py -v
```

```:実行結果
Compiling into vunit_lib: ..\..\..\..\..\AppData\Local\Programs\Python\Python37-32\lib\site-packages\vunit\vhdl\string_ops\src\string_ops.vhd                       passed
Compiling into vunit_lib: ..\..\..\..\..\AppData\Local\Programs\Python\Python37-32\lib\site-packages\vunit\vhdl\path\src\path.vhd                                   passed
...
中略
...
Compiling into vunit_lib: ..\..\..\..\..\AppData\Local\Programs\Python\Python37-32\lib\site-packages\vunit\vhdl\check\src\check_deprecated_pkg.vhd                  passed
Compiling into vunit_lib: ..\..\..\..\..\AppData\Local\Programs\Python\Python37-32\lib\site-packages\vunit\vhdl\vunit_context.vhd                                   passed
Compiling into lib:       tb_example_many.vhd                                                                                                                       passed
Compiling into lib:       tb_example.vhd                                                                                                                            passed
Compiling into vunit_lib: ..\..\..\..\..\AppData\Local\Programs\Python\Python37-32\lib\site-packages\vunit\vhdl\check\src\check.vhd                                 passed
Compile passed

Running test: lib.tb_example.all
Running test: lib.tb_example_many.test_pass
Running test: lib.tb_example_many.test_fail
Running 3 tests

Starting lib.tb_example.all
Output file: vunit_out\test_output\lib.tb_example.all_7b5933c73ddb812488c059080644e9fd58c418d9\output.txt
bar\vunit-master\examples\vhdl\user_guide\tb_example.vhd:19:5:@0ms:(report note): Hello world!
simulation stopped @0ms with status 0
pass (P=1 S=0 F=0 T=3) lib.tb_example.all (0.4 seconds)

Starting lib.tb_example_many.test_pass
Output file: vunit_out\test_output\lib.tb_example_many.test_pass_aff64431373db20d8bbba18c28096f449861ccbe\output.txt
bar\vunit-master\examples\vhdl\user_guide\tb_example_many.vhd:23:9:@0ms:(report note): This will pass
simulation stopped @0ms with status 0
pass (P=2 S=0 F=0 T=3) lib.tb_example_many.test_pass (0.3 seconds)

Starting lib.tb_example_many.test_fail
Output file: vunit_out\test_output\lib.tb_example_many.test_fail_d8956871e3b3d178e412e37587673fe9df648faf\output.txt
bar\vunit-master\examples\vhdl\user_guide\tb_example_many.vhd:26:9:@0ms:(assertion error): It fails
foo\ghdl\bin\ghdl.exe:error: assertion failed
  from: process lib.tb_example_many(tb).main at tb_example_many.vhd:26
foo\ghdl\bin\ghdl.exe:error: simulation failed
fail (P=2 S=0 F=1 T=3) lib.tb_example_many.test_fail (0.4 seconds)

==== Summary =========================================
pass lib.tb_example.all            (0.4 seconds)
pass lib.tb_example_many.test_pass (0.3 seconds)
fail lib.tb_example_many.test_fail (0.4 seconds)
======================================================
pass 2 of 3
fail 1 of 3
======================================================
Total time was 1.0 seconds
Elapsed time was 1.0 seconds
======================================================
Some failed!
```
## 指定したテストの実行

実行するテストを指定することもできます
一度コンパイルされたものは変更がない限り再コンパイルされません

```:cmd
python run.py lib.tb_example_many*
```

```:実行結果
Re-compile not needed

Starting lib.tb_example_many.test_pass
Output file: vunit_out\test_output\lib.tb_example_many.test_pass_aff64431373db20d8bbba18c28096f449861ccbe\output.txt
pass (P=1 S=0 F=0 T=2) lib.tb_example_many.test_pass (0.3 seconds)

Starting lib.tb_example_many.test_fail
Output file: vunit_out\test_output\lib.tb_example_many.test_fail_d8956871e3b3d178e412e37587673fe9df648faf\output.txt
bar\vunit-master\examples\vhdl\user_guide\tb_example_many.vhd:26:9:@0ms:(assertion error): It fails
foo\ghdl\bin\ghdl.exe:error: assertion failed
  from: process lib.tb_example_many(tb).main at tb_example_many.vhd:26
foo\ghdl\bin\ghdl.exe:error: simulation failed
fail (P=1 S=0 F=1 T=2) lib.tb_example_many.test_fail (0.3 seconds)

==== Summary =========================================
pass lib.tb_example_many.test_pass (0.3 seconds)
fail lib.tb_example_many.test_fail (0.3 seconds)
======================================================
pass 1 of 2
fail 1 of 2
======================================================
Total time was 0.5 seconds
Elapsed time was 0.5 seconds
======================================================
Some failed!
```

# 注意事項 (FPGAの場合)
* VunitによるテストはVHDLの検証なので、動作周波数や回路規模は論理合成の結果を確認する必要があります
* VHDLは記述方法によって動作周波数や回路規模が大きく変わるため、テストが書きにくい場合があります [^3]

# 参考
[GHDL公式](http://ghdl.free.fr/)
[Vunit公式](https://vunit.github.io/index.html#)
[VHDL/Verilogのテスティングフレームワーク"VUnit"](http://msyksphinz.hatenablog.com/entry/2017/10/05/020000)

[^1]: あったら教えてください
[^2]: 使えるようになったらまとめます
[^3]: 要件さえ満たせていれば、テストのしやすさを優先したほうがよいとおもいます