Verilatorの使い方。基本的なデジタル回路のシミュレーション方法が分かったら、次は波形をダンプする方法だ。
Verilatorを使ってRTLシミュレーションをすべてフリーのツールで完結させたい場合、波形をダンプするためには主に以下の2種類の方法がある。
vcd
をダンプしてGTKWaveで観察する。fst
をダンプしてGTKWaveで観察する。
VCDは"Value Change Dump"の略称で波形情報を含んでいる。これはバイナリ圧縮されていないので中身を目視で観察できる一方、ファイルサイズが大きい。
一方でfstは"Fast Signal Trace"の略称で同様に波形情報を格納している。これはバイナリ圧縮されておりファイルサイズがVCDと比較して小さい。
どちらを使っても良いが、ここでは両方の方法を解説したいと思う。個人的にはfstを使う方が好みだ。このご時世VCDファイルの中身を読むことなどあり得ない。
VCDをダンプする場合
コンパイル
Verilatorにて回路をコンパイルする際、以下のオプションを追加する。
--trace --trace-params --trace-structs --trace-underscore
最初の--trace
が必須で、以降のオプションは任意。これらのオプションはSystemVerilogの情報をどこまで出力するかを決定する。パラメータ、構造体の情報などは出力しておかないとデバッグ時に意外と面倒なことが多い。
$ verilator --cc --exe --trace --trace-params --trace-structs --trace-underscore \ counter_4bit.v -exe tb_counter_4bit.cpp
テストベンチで波形ダンプをONにする
次に行うべきはテストベンチ内で波形ダンプをONにする。有償シミュレータでも、テストベンチ内で波形ダンプのON/OFFを有効化する記述があるかと思う($fsdbDumpOn
や$fsdbDumpOff
など)。それと似たような記述をC++で記述したテストベンチ内で行う。
VCDをダンプする場合、前回示したtb_counter_4bit.cpp
に以下の記述を加える。
#include <verilated_vcd_c.h> # VCD出力用のインクルードファイルを追加 // Instantiate DUT Vcounter_4bit *dut = new Vcounter_4bit(); // -- ここから // Trace DUMP ON Verilated::traceEverOn(true); VerilatedVcdC* tfp = new VerilatedVcdC; dut->trace(tfp, 100); // Trace 100 levels of hierarchy tfp->open("simx.vcd"); // -- ここまで
これは波形ダンプを有効化し、波形ファイル名をsimx.vcd
としてファイルディスクリプタをオープンしている。これによりsimv.vcd
に波形が書き出される仕組みだ。
これだけでは波形は生成されず、dut->eval()
毎に波形ダンプ用の関数を呼び出す必要がある。
... // Reset Time while (time_counter < 100) { dut->eval(); tfp->dump(time_counter); // 波形ダンプ用の記述を追加 time_counter++; } ... int cycle = 0; while (time_counter < 500 && !Verilated::gotFinish()) { if ((time_counter % 5) == 0) { ... // Evaluate DUT dut->eval(); tfp->dump(time_counter); // 波形ダンプ用の記述を追加 ... dut->final(); tfp->close();
ここまででコンパイルして実行してみよう。VCDファイルが生成されるはずだ。
$ verilator --cc --exe --trace --trace-params --trace-structs --trace-underscore \ counter_4bit.v -exe tb_counter_4bit.cpp $ make -C obj_dir -f Vcounter_4bit.mk $ ./obj_dir/Vcounter_4bit $ ls -lt
-rw-rw-rw- 1 msyksphinz msyksphinz 321 May 1 11:53 counter_4bit.v -rw-rw-rw- 1 msyksphinz msyksphinz 248 May 2 11:33 Makefile -rw-rw-rw- 1 msyksphinz msyksphinz 1313 May 2 11:51 tb_counter_4bit.cpp drwxrwxrwx 1 msyksphinz msyksphinz 4096 May 2 11:52 obj_dir -rw-rw-rw- 1 msyksphinz msyksphinz 3196 May 2 11:52 simx.vcd
VCDファイルを開いてみよう。GTKwaveを使用する。
$ gtkwave simx.vcd &
波形を確認できた。
FSTをダンプする場合
コンパイル
FSTでダンプする場合はオプションを少し変更するだけだ。
--trace-fst --trace-params --trace-structs --trace-underscore
--trace
を--trace-fst
に変更する。これだけで問題ない。
$ verilator --cc --exe --trace-fst --trace-params --trace-structs --trace-underscore \ counter_4bit.v -exe tb_counter_4bit.cpp
テストベンチでFST波形ダンプをONにする
FST波形ダンプの場合もVCD波形ダンプとほとんど変わらない。セットアップ用のコードは以下のように変更する。
#include <verilated_fst_c.h> # FST出力用のインクルードファイルを追加 // Instantiate DUT Vcounter_4bit *dut = new Vcounter_4bit(); // -- ここから // Trace DUMP ON Verilated::traceEverOn(true); VerilatedFstC* tfp = new VerilatedFstC; dut->trace(tfp, 100); // Trace 100 levels of hierarchy tfp->open("simx.fst"); // -- ここまで
波形ダンプの方法はVCDの場合と全く変わらない。
... // Reset Time while (time_counter < 100) { dut->eval(); tfp->dump(time_counter); // 波形ダンプ用の記述を追加 time_counter++; } ... int cycle = 0; while (time_counter < 500 && !Verilated::gotFinish()) { if ((time_counter % 5) == 0) { ... // Evaluate DUT dut->eval(); tfp->dump(time_counter); // 波形ダンプ用の記述を追加 ... dut->final(); tfp->close();
ここまででコンパイルしてシミュレーションを実行する。
$ verilator --cc --exe --trace-fst --trace-params --trace-structs --trace-underscore \ counter_4bit.v -exe tb_counter_4bit.cpp $ make -C obj_dir -f Vcounter_4bit.mk $ ./obj_dir/Vcounter_4bit $ ls -lt
-rw-rw-rw- 1 msyksphinz msyksphinz 675 May 2 12:03 simx.fst drwxrwxrwx 1 msyksphinz msyksphinz 4096 May 2 12:03 obj_dir -rw-rw-rw- 1 msyksphinz msyksphinz 1313 May 2 12:02 tb_counter_4bit.cpp -rw-rw-rw- 1 msyksphinz msyksphinz 252 May 2 12:02 Makefile -rw-rw-rw- 1 msyksphinz msyksphinz 321 May 1 11:53 counter_4bit.v
simx.fst
をGTKwaveで開いてみよう。
$ gtkwave simx.fst &
波形が出力できていることが確認できた。