最終更新: 2021-05-03
Verilator は サイクルベース方式で高速な無償の Verilog HDL シミュレータです。本記事では、WSL 上 Ubuntu-20.04 の標準パッケージだけを使用して、GUI でシミュレーション波形を観測する手順を紹介します。Ubuntu-20.04 標準パッケージ版 V4.0 と最新版 の Verilator V4.2 との差異も解説します。
Verilator は RISC-V の開発に使用され、SiFive 社より既に製品が発売されています。しかし、Verilator は未だ開発途上のソフトウェアな為、バージョンによっては機能に制限が生じます。
目次:
- 前提条件:
- 公式ドキュメントと Ubuntu-20.04 版の違い:
- 最上位が C++ の場合のテストベンチの変更:
- 最上位が C++ の場合のコンパイル用スクリプト:
- 最上位が SystemC の場合のテストベンチの変更:
- 最上位が SystemC の場合のコンパイル用スクリプト:
- 最上位を Verilog HDL で記述する場合のテストベンチ (比較用):
- 最上位が Verilog HDL の場合の Icarus Verilog 用コンパイル スクリプト:
- 波形の観測:
前提条件:
- Windows 10 の WSL に Ubuntu-20.04 をインストール済なこと
- 【こちらの別記事】で例示したサンプル回路を使用すること
- 前記とは別の【こちらの別記事】で例示したテストベンチを作成済なこと
公式ドキュメントと Ubuntu-20.04 版の違い:
2021年5月現在、Verilator の公式サイトのドキュメントは V4.202 版です。一方、Ubuntu-20.04 の標準レポジトリからインストールした物は V4.028 版です。この部分で、公式ドキュメントと若干の差異が有ります。
波形をダンプするに当たっては、次の 公式ドキュメント FAQ の How do I generate waveforms (traces) in C++?
及び How do I generate waveforms (traces) in SystemC?
のセクションを参照します。
このドキュメントの V4.202版と Ubuntu-20.04 の V4.028 版の違いについて、次項以降で具体的に説明します。尚、現在使用している Verilator のバージョンを確認したい場合は、次の通りコマンドを打ちます。
verilator --version
最上位が C++ の場合のテストベンチの変更:
A. Pass the --trace option to Verilator, and in your top level C code, call Verilated::traceEverOn(true).により、Verilog HDL 内の
$dumpfile
と $dumpvars
が動作するというのは Ubuntu-20.04 の版 V4.028 では使えない様です。そこで、次の説明に従う事にします。
B. Or, for finer-grained control, or C++ files with multiple Verilated modules you may also create the trace purely from C++.
cat から EOF までを ubuntu のターミナル上にコピー&ペーストすると、cframe.cpp というファイルが出来ます。
cat << "EOF" > cframe.cpp
#pragma onece
#include <iostream>
#include "verilated.h"
#include "verilated_fst_c.h"
#include "obj_dir/Vbench.h"
#define PERIOD 1000
int main(int argc, char* argv[]) {
/* Command Line Arguments */
// Verilated::commandArgs(argc, argv);
/* Testbench */
Vbench* u_bench = new Vbench();
/* FST Dump */
Verilated::traceEverOn(true);
VerilatedFstC* tfp = new VerilatedFstC;
u_bench->trace(tfp, 3); // Trace 3 levels of hierarchy
tfp->open("u_bench.dump");
/* Clock and DumpTrigger */
while (!Verilated::gotFinish()) {
u_bench->clk = 1;
u_bench->eval();
u_bench->dumptrig = 0;
// tfp->dump(u_bench->count * PERIOD - PERIOD/2);
// u_bench->eval();
u_bench->clk = 0;
u_bench->eval();
u_bench->dumptrig = 1;
tfp->dump(u_bench->count * PERIOD);
u_bench->eval();
}
/* Closing Operation */
tfp->close();
delete tfp;
u_bench->final();
// Never comes here.
delete u_bench;
return 0;
}
EOF
2021-05-02: delete 追加。tfp->dump の位置修正。count を下位から持ち上げ。2021-05-03: delete tfp; の位置を有効に変更。
while
文の中で、クロック信号 clk
の立ち上がり直前で波形をダンプしています。タイムスタンプにクロックサイクルカウント count
の1000倍を使用しています。1クロックサイクルに1回の波形ダンプを観測出来れば良い方にはこれで問題無いのですが、クロック信号の立下り直前の状態も見たい方は、while
文中のコメント2行の //
外してください。
最上位が C++ の場合のコンパイル用スクリプト:
cat << "EOF" > veri.sh
#!/bin/dash -x
if [ -e obj_dir/Vbench ]; then
/bin/rm obj_dir/Vbench
fi
if [ -e u_bench.dump ]; then
/bin/rm u_bench.dump
fi
verilator -Wall -Wno-SYNCASYNCNET --trace-fst --cc bench.v dut.v --exe cframe.cpp
if [ $? -eq 0 ]; then
cd obj_dir
export CXX='g++'
export CXXFLAGS='-Og -g -gdwarf-2 -fPIC'
export LINK='g++'
export LDFLAGS=''
make -e -j --output-sync -f Vbench.mk
if [ $? -eq 0 ]; then
(cd ..; time ./obj_dir/Vbench)
fi
cd ..
fi
EOF
次のコマンドでコンパイルとシミュレーションの実行が出来ます。
chmod +x veri.sh
./veri.sh
シミュレーションのみの再実行、または ROM コード (main.v
) のみの変更の場合には、次のコマンドを実行します。
./obj_dir/Vbench
最上位が SystemC の場合のテストベンチの変更:
SystemC の場合にもやはり版数の違いによる問題が有る様で、Ubuntu-20.04 では FST ダンプは行えず、VCD ダンプのみとなります。
C++ の場合との違いは、VerilatedVcdSc
インスタンス (末尾が Sc ) を使用すると、明示しなくとも sc_start
間のダンプが自動的に行われる事です。尚、VerilatedVcdC
インスタンス (末尾が C ) を使用して明示的にダンプする事も可能です。
SystemC でテストベンチを記述した場合は、次の通り書き換えます。
cat << "EOF" > scframe.cpp
#pragma onece
#include <iostream>
#include <systemc.h>
#include "verilated_sc.h"
#include "verilated_vcd_sc.h"
#include "obj_dir/Vbench.h"
#define PERIOD 1000
int sc_main(int argc, char* argv[]) {
/* Command Line Arguments */
// Verilated::commandArgs(argc, argv);
/* Signal Definition */
sc_signal<bool> clk;
sc_signal<bool> dumptrig;
sc_signal<uint64_t> count;
/* Testbench */
Vbench* u_bench = new Vbench("bench");
/* Port Connection */
u_bench->clk(clk);
u_bench->dumptrig(dumptrig);
u_bench->count(count);
/* VCD Dump */
#ifdef VCD_CXX
VerilatedVcdC* tfp = new VerilatedVcdC;
#else
VerilatedVcdSc* tfp = new VerilatedVcdSc;
#endif
Verilated::traceEverOn(true);
u_bench->trace(tfp, 3); // Trace 3 levels of hierarchy
tfp->open("u_bench.dump");
/* Clock and DumpTrigger */
sc_start();
while (!Verilated::gotFinish()) {
clk = 1;
sc_start(PERIOD/2-1, SC_NS);
dumptrig = 0;
#ifdef VCD_CXX
tfp->dump(u_bench->count * PERIOD - PERIOD/2);
#endif
sc_start(1, SC_NS);
clk = 0;
sc_start(PERIOD/2-1, SC_NS);
dumptrig = 1;
#ifdef VCD_CXX
tfp->dump(u_bench->count * PERIOD);
#endif
sc_start(1, SC_NS);
}
/* Closing Operation */
tfp->close();
delete tfp;
u_bench->final();
// Never comes here.
delete u_bench;
return 0;
}
EOF
2021-05-02: delete 追加。tfp->dump を追加。count を下位から持ち上げ。2021-05-03: "verilated.h" -> "verilated_sc.h"。while の前に sc_start(); 追加。delete tfp; の位置を有効に変更。VcdC から VcdSc に変更。
最上位が SystemC の場合のコンパイル用スクリプト:
cat << "EOF" > scveri.sh
#!/bin/dash -x
if [ -e obj_dir/Vbench ]; then
/bin/rm obj_dir/Vbench
fi
if [ -e u_bench.dump ]; then
/bin/rm u_bench.dump
fi
verilator -Wall -Wno-SYNCASYNCNET --trace --sc bench.v dut.v --exe scframe.cpp
if [ $? -eq 0 ]; then
cd obj_dir
export CXX='g++'
export CXXFLAGS='-Og -g -gdwarf-2 -fPIC'
export LINK='g++'
export LDFLAGS=''
make -e -j --output-sync -f Vbench.mk
if [ $? -eq 0 ]; then
(cd ..; time ./obj_dir/Vbench)
fi
cd ..
fi
EOF
次のコマンドでコンパイルとシミュレーションの実行が出来ます。
chmod +x scveri.sh
./scveri.sh
シミュレーションのみの再実行、または ROM コード (main.v
) のみの変更の場合には、次のコマンドを実行します。
./obj_dir/Vbench
最上位を Verilog HDL で記述する場合のテストベンチ (比較用):
上記と同じ処理を Verilog HDL で記述する場合は次の通りとなります。
尚、Verilator には $dumplimit
に対応する機能は有りません。
cat << "EOF" > frame.v
`default_nettype none
`timescale 1ns/1ps
`define PERIOD 1000
module frame();
/* Command Line Arguments */
// parameter argv = 1'bz;
/* Signal Definition */
reg clk;
reg dumptrig;
/* Testbench and Port Connection */
bench u_bench(
.clk(clk),
.dumptrig(dumptrig),
.count()
);
/* VCD or FST Dump */
initial begin
$dumpfile("u_bench.dump");
$dumplimit(10000000); // 10M bytes
$dumpvars(3, u_bench); // Trace 3 levels of hierarchy
end
/* Clock and DumpTrigger */
always begin
clk = 1;
#(`PERIOD/2-1);
dumptrig = 0;
#1;
clk = 0;
#(`PERIOD/2-1);
dumptrig = 1;
#1;
end
endmodule
EOF
2021-05-02: count を下位から持ち上げ。
最上位が Verilog HDL の場合の Icarus Verilog 用コンパイル スクリプト:
cat << "EOF" > ivlog.sh
#!/bin/dash -x
if [ -e a.out ]; then
/bin/rm a.out
fi
if [ -e u_bench.dump ]; then
/bin/rm u_bench.dump
fi
export IVERILOG_DUMPER=fst
iverilog frame.v bench.v dut.v
if [ $? -eq 0 ]; then
time vvp ./a.out -fst
fi
EOF
次のコマンドでコンパイルとシミュレーションの実行が出来ます。
chmod +x ivlog.sh
./ivlog.sh
シミュレーションのみの再実行、または ROM コード (main.v
) のみの変更の場合には、次のコマンドを実行します。
vvp ./a.out -fst
尚、Icarus Verilog の波形ダンプファイルを常に FST 形式 (バイナリ) で保存したい場合には、.bashrc
に次の1行を追加します。これにより、./a.out
だけで常に FST 形式 (バイナリ) となります。環境変数を設定しない場合には、デフォルトの波形ファイルは VCD 形式 (アスキー形式) となります。
export IVERILOG_DUMPER=fst
波形の観測:
ダンプした波形は、GTKWave を使用して GUI 表示します。前準備として、X Window サーバを起動しておきます。次のコマンドで GTKWave を起動します。
gtkwave u_bench.dump