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 つでサクッとシミュレーションができてとても便利です。
一方で、波形を見ることができないので、細かなデバッグには向かないかもしれません。
コンパイルのために用いるのは、xvlog
、xelab
、xsim
の三つのコマンドです。
それぞれの役割は以下の通りです。
xvlog
:Verilog
のコードをコンパイルするのに使用xelab
: シミュレーションをするためのスナップショットファイル (.sim
) を作成xsim
: スナップショットファイルを元にシミュレーションを実施
筆者は使用したことがありませんが、Verilog
ではなく VHDL
というハードウェア記述言語をコンパイルするときには xvlog
の代わりに xvhdl
というコマンドを使用すればいいようです。
それぞれのコマンドを実行してみると以下のようになります。xvlog
をするときはテストベンチ、その他の回路を実装したファイル (複数ある場合は順不同) の順で記述してください。
また、System Verilog
をコンパイルする時は --sv
オプションを忘れずにつけてください。xelab
や xsim
の test_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 を開いて波形を見る方法をまずは説明します。
初めに先程のコマンドラインを使う方法と同じように、xvlog
、 xelab
を実行しておきます。
最後に 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
の画面が開くと思います。
さらに、真ん中の Objects
のタブで、波形を見たい信号を選んで Add to Wave Window
をすると、その信号が Wave Window
に加えられて右側に表示されます。Wave Window
が表示されてからはドラッグ&ドロップでも追加できます。
次に、上のバーの中の ▶ (Run All) を押してシミュレーションを実行してください。
すると、Wave Window があったところに test_core.sv が表示されると思いますが、閉じてしまって大丈夫です。
これで、Wave Window を開くと緑の波形が見えていると思います。
このままでは見にくいので、Wave Window のタブの右上の方の Float
のボタンを押して大きくして、バーの中の虫眼鏡にマイナスのマークが書いてあるボタンを押して縮小することで、かなり見やすくなると思います。
ここで、それぞれの波形の値を見てみると 16 進数で表示されていることがわかります。
これでは少し見にくいこともあるので、変更したい波形の名前を右クリックして、Radix > Signed Decimal
などにすることで、符号付きの 10 進数表示に変更したりすることができます。
さらに、信号を追加して波形をみたい場合は先程と同様に Objects
から信号を追加して、 ▶ (Run All) の左隣にある|◀︎
(Restart) を押してリセットをしてから ▶ (Run All) をするようにしてください。
この方法では、コマンド 1 つで起動できて便利なのですが、ソースコードを変更するたびに Vivado
を開き直したり、信号を追加したりしないといけない (と筆者は思っている) ため少し面倒かもしれません。
1.2.2. GUI で設定する方法
次は、GUI を使って一つずつ設定していく方法を見ていきます。
まずは以下のコマンドを使うか GUI で探すなどして 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
にもチェックを入れておきます。
Next
を押すと、Project Type
の画面が出てくると思うので、デフォルトの RTL Project
のまま進みましょう。
その後の Add Sources
が出てくると思うので、Add Files
を押して、テストベンチも含めて全てのソースファイル (.sv
) を追加してください。
シフトキーを押しながら一番上のファイルと一番下のファイルをクリックすると、間のファイルが全て選択されるので楽です。
その後は全て Next
を押して進み、最後に New Project Summary
が出てくると思うので Finish
を押せば完成です。
プロジェクトができたら、次はシミュレーションをしていきます。
右のバーから Flow Navigator
> PROJECT MANAGER
> SIMULATION
> Run Simulation
> Run Behavioral Simulation
を選択してクリックしてください。
二度目以降ならポップアップが表示されたりするかもしれませんが、全て Yes
か Save
で大丈夫だと思います。
後は 1.2.1. コマンドから開く方法 で行ったのと同じように、波形を確認したい信号を Wave Window
に追加し、▶︎ (Run All) をして波形を確認してください。
コードを変更した時は再度 Run Behavioral Simulation
をすればコードの変更を反映させることができます。
このとき、追加した信号の情報も保持しておいてくれるので再度信号を追加し直す手間もありません。
設定は少し面倒でしたが、一度やってしまうと後は先程の方法よりも楽に波形を確認できるかもしれません。
2. デバッグ
ようやく、コンパイル・シミュレーションが終了しました。
初めは少し手間取るかもしれませんが、一度慣れてしまえば後はすぐにできるようになると思います!
次はデバッグの方法です。
筆者のおすすめの方法を紹介しますが、他にもいい方法はあると思うので、参考程度に見てください。
大まかな流れは以下のようになります。
- コマンドラインで実行して簡単なバグやミスを取り除く
- GUI で波形を見る
- 思い通りに動いていない波形を探して、ミスの原因を探る
- コードを修正したら 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
をコンパイルして、シミュレーションを行い、波形を見る方法について解説しました。
最後にデバッグのこつ (?) も少し伝えられていたらいいなと思います。
低レイヤーになればなるほど人間にとってはわかりにくい部分になってくると思うので大変ですが、その分自由に高速化したりすることもできてやりがいがあると思っています!