Vivado を使った Verilog のシミュレーションとデバッグの方法

公開日:
Vivado を使った Verilog のシミュレーションとデバッグの方法

Verilog を書いていざコンパイル…!と思っても意外とやり方が分からなかったりデバッグの方法がわからなくて困ることが多いと思います。
筆者の経験を踏まえて、簡単にコンパイル・シミュレーション・デバッグを行う方法をまとめていきたいと思います。

以下のソースコードを利用してコンパイルなどを試していきたいと思います。
実際に試してみたい方は以下のコマンドで clone してから始めてください。

$ git clone https://github.com/hashi0203/riscv-processor.git

また、使用した Vivado のバージョンは 2021.1 でした。(前のバージョンでも大きく違うことはないと思いますが…)

$ vivado -version
Vivado v2021.1 (64-bit)
SW Build 3247384 on Thu Jun 10 19:36:07 MDT 2021
IP Build 3246043 on Fri Jun 11 00:30:35 MDT 2021
Copyright 1986-2021 Xilinx, Inc. All Rights Reserved.

また、Verilog の書き方や気をつけるべきことについては以下の記事を参考にしてください。

1. コンパイル・シミュレーション

先程紹介した記事の 2. テストベンチ でも触れたのですが、まずはコンパイルの方法について説明します。

1.1. コマンドラインを使う場合

まずは、コマンドラインを使ってコンパイル・シミュレーションを行う方法を解説します。
コマンドラインで行うと、Makefile さえ作ればコマンド 1 つでサクッとシミュレーションができてとても便利です。
一方で、波形を見ることができないので、細かなデバッグには向かないかもしれません。

コンパイルのために用いるのは、xvlogxelabxsim の三つのコマンドです。
それぞれの役割は以下の通りです。

  • xvlog : Verilog のコードをコンパイルするのに使用
  • xelab : シミュレーションをするためのスナップショットファイル (.sim) を作成
  • xsim : スナップショットファイルを元にシミュレーションを実施

筆者は使用したことがありませんが、Verilog ではなく VHDL というハードウェア記述言語をコンパイルするときには xvlog の代わりに xvhdl というコマンドを使用すればいいようです。

それぞれのコマンドを実行してみると以下のようになります。
xvlog をするときはテストベンチ、その他の回路を実装したファイル (複数ある場合は順不同) の順で記述してください。
また、System Verilog をコンパイルする時は --sv オプションを忘れずにつけてください。
xelabxsimtest_core の部分はテストベンチのファイルの名前に合わせておくといいと思います。

$ cd /path/to/riscv-processor/src
$ xvlog --sv test_core.sv alu.sv core.sv decode.sv def.sv execute.sv fetch.sv memory.sv register.sv write.sv
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/test_core.sv" into library work
INFO: [VRFC 10-311] analyzing module test_core
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/alu.sv" into library work
INFO: [VRFC 10-311] analyzing module alu
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/core.sv" into library work
INFO: [VRFC 10-311] analyzing module core
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/decode.sv" into library work
INFO: [VRFC 10-311] analyzing module decode
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/def.sv" into library work
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/execute.sv" into library work
INFO: [VRFC 10-311] analyzing module execute
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/fetch.sv" into library work
INFO: [VRFC 10-311] analyzing module fetch
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/memory.sv" into library work
INFO: [VRFC 10-311] analyzing module memory
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/register.sv" into library work
INFO: [VRFC 10-311] analyzing module register
INFO: [VRFC 10-2263] Analyzing SystemVerilog file "/path/to/riscv-processor/src/write.sv" into library work
INFO: [VRFC 10-311] analyzing module write
$ xelab -debug typical test_core -s test_core.sim
Vivado Simulator v2021.1
Copyright 1986-1999, 2001-2021 Xilinx, Inc. All Rights Reserved.
Running: /gopt/Xilinx/Vivado/2021.1/bin/unwrapped/lnx64.o/xelab -debug typical test_core -s test_core.sim
Multi-threading is on. Using 126 slave threads.
Starting static elaboration
Pass Through NonSizing Optimizer
Completed static elaboration
Starting simulation data flow analysis
Completed simulation data flow analysis
Time Resolution for simulation is 100ps
Compiling package work.$unit_test_core_sv
Compiling module work.fetch
Compiling module work.decode
Compiling module work.alu
Compiling module work.memory
Compiling module work.execute
Compiling module work.write
Compiling module work.register
Compiling module work.core
Compiling module work.test_core
Built simulation snapshot test_core.sim
$ xsim --runall test_core.sim

****** xsim v2021.1 (64-bit)
  **** SW Build 3247384 on Thu Jun 10 19:36:07 MDT 2021
  **** IP Build 3246043 on Fri Jun 11 00:30:35 MDT 2021
    ** Copyright 1986-2021 Xilinx, Inc. All Rights Reserved.

source xsim.dir/test_core.sim/xsim_script.tcl
# xsim {test_core.sim} -autoloadwcfg -runall
Time resolution is 100 ps
run -all
############### start of checking module core ###############
clocks       : 18613
pc           :    43
instructions : total 14587, normal   3809, exception   709, others 10069
prediction   : total  1344, succeed  1130, fail        214
register     :
    r00:    0,    r01:   43,    r02: 2032,    r03:    0,
    r04:    0,    r05:    0,    r06:    0,    r07:    0,
    r08: 2048,    r09:    0,    r10:   89,    r11:    0,
    r12:    0,    r13:    0,    r14:    0,    r15:   89
################# end of checking module core ###############
$finish called at time : 372260 ns : File "/path/to/riscv-processor/src/test_core.sv" Line 70
exit
INFO: [Common 17-206] Exiting xsim at Sun Aug  1 14:22:45 2021...

実行の結果は xsim の出力結果を確認すればいいです。
しっかりエラーなく出力されていれば大丈夫だと思います。

この 3 つのコマンドを実行すれば、コマンドラインを使ったコンパイル・シミュレーションは完了ですが、3 つのコマンドを毎回打ったり、ファイル名を変更したり、ファイルを追加したときに面倒なので、少し工夫して Makefile を作って、make コマンドだけで実行できるようにしてみましょう!

結論から言うと、Makefile は以下のようになります。

MAIN_MODULE=core
ALL_SRC=$(shell ls *.sv)
TEST_SRC=test_$(MAIN_MODULE).sv
CIRC_SRC=$(filter-out $(TEST_SRC), $(ALL_SRC))

all:
	xvlog --sv $(TEST_SRC) $(CIRC_SRC)
	xelab -debug typical test_$(MAIN_MODULE) -s test_$(MAIN_MODULE).sim
	xsim --runall test_$(MAIN_MODULE).sim

まず、ls コマンドを使って全ての .sv ファイルを ALL_SRC に格納しておきます。
ここで、注意としては先程述べたように xvlog をするときに一番初めにテストベンチが来るようにしないといけないので、filter-out を使用して ALL_SRC からテストベンチを抜いたファイルを CIRC_SRC としておきます。
後は、実行したいコマンドを順に並べていけば OK です。

後は以下のコマンドを打つだけでシミュレーションまでできてしまいます。

$ make

少し凝ったMakefile の作り方については以下の記事が参考になると思います。

1.2. GUI を使う場合

波形を見るためには GUI を使用しないといけません。
波形を見ることによってさらに細かなデバッグができて、とても便利です。

今回は 2 つの方法を紹介しますので、好きな方を使って試してみてください。

1.2.1. コマンドから開く方法

コマンドを使って GUI を開いて波形を見る方法をまずは説明します。
初めに先程のコマンドラインを使う方法と同じように、xvlogxelab を実行しておきます。
最後に xsim をするときに GUI で開くようにオプションをつけてあげれば OK です。

$ cd /path/to/riscv-processor/src
$ xvlog --sv test_core.sv alu.sv core.sv decode.sv def.sv execute.sv fetch.sv memory.sv register.sv write.sv
$ xelab -debug typical test_core -s test_core.sim
$ xsim test_core.sim -gui
****** xsim v2021.1 (64-bit)
  **** SW Build 3247384 on Thu Jun 10 19:36:07 MDT 2021
  **** IP Build 3246043 on Fri Jun 11 00:30:35 MDT 2021
    ** Copyright 1986-2021 Xilinx, Inc. All Rights Reserved.

start_gui

少し待つと以下のような Vivado の画面が開くと思います。

xsim で GUI を開いた時の画面
xsim で GUI を開いた時の画面

さらに、真ん中の Objects のタブで、波形を見たい信号を選んで Add to Wave Window をすると、その信号が Wave Window に加えられて右側に表示されます。
Wave Window が表示されてからはドラッグ&ドロップでも追加できます。

Add to Wave Window をすると右に Wave Window が表示される
Add to Wave Window をすると右に Wave Window が表示される

次に、上のバーの中の ▶ (Run All) を押してシミュレーションを実行してください。
すると、Wave Window があったところに test_core.sv が表示されると思いますが、閉じてしまって大丈夫です。

これで、Wave Window を開くと緑の波形が見えていると思います。
このままでは見にくいので、Wave Window のタブの右上の方の Float のボタンを押して大きくして、バーの中の虫眼鏡にマイナスのマークが書いてあるボタンを押して縮小することで、かなり見やすくなると思います。

ここで、それぞれの波形の値を見てみると 16 進数で表示されていることがわかります。
これでは少し見にくいこともあるので、変更したい波形の名前を右クリックして、Radix > Signed Decimal などにすることで、符号付きの 10 進数表示に変更したりすることができます。

Wave Window の表示
Wave Window の表示

さらに、信号を追加して波形をみたい場合は先程と同様に Objects から信号を追加して、 ▶ (Run All) の左隣にある|◀︎ (Restart) を押してリセットをしてから ▶ (Run All) をするようにしてください。

この方法では、コマンド 1 つで起動できて便利なのですが、ソースコードを変更するたびに Vivado を開き直したり、信号を追加したりしないといけない (と筆者は思っている) ため少し面倒かもしれません。

1.2.2. GUI で設定する方法

次は、GUI を使って一つずつ設定していく方法を見ていきます。

まずは以下のコマンドを使うか GUI で探すなどして Vivado を起動してください。

$ vivado &

コマンドが認識されない場合はパスが通っていないと思うので、パスを確認してみてください。
うまくいけば下のようなページが表示されると思います。

Vivado のトップページ
Vivado のトップページ

次に、Quick Start の中の Create Project or 左上の File > Project > New... を押してください。

Create a New Vivado Project というウィンドウが出てくると思うので、Next を押して進みます。
次は Project Name を設定する画面になると思うので、ソースコードの入ったディレクトリ (/path/to/riscv-processor) を Project location に入れましょう。(別に違うディレクトリでも大丈夫だと思いますが…)
Project name はなんでもいいので、デフォルトの project_1 のままにしておきます。
プロジェクトファイルがディレクトリの中で散らばると嫌なので、Create project subdirectory にもチェックを入れておきます。

Vivado プロジェクト名設定
Vivado プロジェクト名設定

Next を押すと、Project Type の画面が出てくると思うので、デフォルトの RTL Project のまま進みましょう。

その後の Add Sources が出てくると思うので、Add Files を押して、テストベンチも含めて全てのソースファイル (.sv) を追加してください。
シフトキーを押しながら一番上のファイルと一番下のファイルをクリックすると、間のファイルが全て選択されるので楽です。

Add Sources の結果
Add Sources の結果

その後は全て Next を押して進み、最後に New Project Summary が出てくると思うので Finish を押せば完成です。

プロジェクトができたら、次はシミュレーションをしていきます。

右のバーから Flow Navigator > PROJECT MANAGER > SIMULATION > Run Simulation > Run Behavioral Simulation を選択してクリックしてください。
二度目以降ならポップアップが表示されたりするかもしれませんが、全て YesSave で大丈夫だと思います。

後は 1.2.1. コマンドから開く方法 で行ったのと同じように、波形を確認したい信号を Wave Window に追加し、▶︎ (Run All) をして波形を確認してください。

コードを変更した時は再度 Run Behavioral Simulation をすればコードの変更を反映させることができます。
このとき、追加した信号の情報も保持しておいてくれるので再度信号を追加し直す手間もありません。

設定は少し面倒でしたが、一度やってしまうと後は先程の方法よりも楽に波形を確認できるかもしれません。

2. デバッグ

ようやく、コンパイル・シミュレーションが終了しました。
初めは少し手間取るかもしれませんが、一度慣れてしまえば後はすぐにできるようになると思います!

次はデバッグの方法です。
筆者のおすすめの方法を紹介しますが、他にもいい方法はあると思うので、参考程度に見てください。

大まかな流れは以下のようになります。

  1. コマンドラインで実行して簡単なバグやミスを取り除く
  2. GUI で波形を見る
  3. 思い通りに動いていない波形を探して、ミスの原因を探る
  4. コードを修正したら 1 に戻る

まず、簡単なバグやミスについてはコンパイルエラーやウォーニングを吐いてくれるので、1.1. コマンドラインを使う場合 を参考に、コマンドラインで実行しながらちゃちゃっと直してください。
コンパイルエラーは xvlog を実行したときの出力に注目してください。

どう直せばいいかわからなければ、ググってみると大体の場合解決方法が出てくると思います。
それでも治らない場合はコメントをいただければ最善を尽くします。

ちなみに、定義されていない信号が幅 1 の wireとして解釈されるというバグ仕様なので、これを起こさせないために、Verilog のソースコードの一番最初の行と一番最後の行に以下の行を追加しておくことをお勧めします。

`default_nettype none // 最初の行

// モジュールの宣言などの処理

`default_nettype wire // 最後の行

コンパイルエラーがなくなったところでうまく動けばいいのですが、そううまく行かないのがハードウェア記述言語の辛いところおもしろいところなので、次は 1.2. GUI を使う場合 を参考に波形を見ていきましょう。

波形を見るときに注意したほうがいいと思っていることは以下の 2 つです。

  • 前から見ること
  • 赤色の信号 (X になっているところ) に注目すること

この 2 つを意識すれば大体のバグがなくなる気がします。(言い過ぎ…)

前から見るのは当たり前っちゃ当たり前ですが、前があっていないのに後ろが合うわけがないので、初めから順に正しい挙動をしているかどうか確認していってください。

また、バグっている可能性が高いところとして、赤色の信号になっているところが挙げられます。
同じクロックで 2 回代入されている reg であったり、初期化されていない reg の値を参照していたりする場合が多いと思いますが、何らかの原因で未定義の信号になってしまっているので、その原因を突き止めましょう。

後はひたすらにらめっこをして 1 つずつバグの原因を取り除いていってください。

3. まとめ

今回は Vivado を使って Verilog をコンパイルして、シミュレーションを行い、波形を見る方法について解説しました。
最後にデバッグのこつ (?) も少し伝えられていたらいいなと思います。

低レイヤーになればなるほど人間にとってはわかりにくい部分になってくると思うので大変ですが、その分自由に高速化したりすることもできてやりがいがあると思っています!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA