企業や大学などにおいても基本無料でHDLをシミュレーションして回路を合成可能なAMD(Xilinx)のVivadoはよく使われていると思います。
ですが、ただでさえソフトウェア開発に比べやることの多いRTLでの回路記述~実行までの手順中に、GUI環境の操作に慣れることに必死で肝心のRTLで回路を作成することに集中できないという事態に陥り、初学者のやる気をいきなり挫いてきます。かくいう私も過去に挫折させられた一人です。
如何せんグラフィカルな開発環境はバージョンの違いや、ちょっとした環境の違いでデザインや操作感が変わることが多く、手順の再現性が低いので個人的にかなり敬遠してしまいます。
そうはいっても、商用を中心としたEDAベンダーのツールは有料のものが多く、個人で利用しにくいので、無料で使うことができ、FPGAなどを持っていれば動作させることもできるツールというのは重宝され、利用機会が多いのも確かです。
そんなわけで開発効率の改善と私を含め初心者のハードウェア開発の敷居下げのために、Vivadoを用いたCUIベース+GUI波形描画のVerilogを用いたハードウェアの開発環境を構築する手順について良い機会だと思い調べました。
前置きが長くなりましたが、調査したことを備忘録的にまとめておきたいと思います。
Vivado導入と環境
まず、Vivadoをパソコンにインストールすることから始めます。
開発環境
ここでは、Ubuntu(Linux)環境で開発環境を構築したいので、WSL2を用いたUbuntu環境で開発環境を整えました。
OS | Ubuntu22.04.2 LTS(WSL2) |
---|---|
Vivado | Vivado:Version2023.1(64-bit)最小構成 |
Vivadoのダウンロード
この手順だけはGUI開くことのできる環境が必要です。リモートのEWS(engineering workstation)環境や手元のLinux環境でX Window System(x11)もしくは、RDP(リモートデスクトップ)などのグラフィカルな表示ができる設定になっているか確認しておいてください。
「xeyes」コマンドなどで有効になっているか確認するなどしてください。
インストール手順については、ここに詳しく載っているのでこれに従えば完了です。
Vivadoは以下のページからダウンロード可能です。ダウンロードにはアカウントの作成が必要となります。
以下のコマンドでインストールできたか確認できます。
コマンド:
$ vivado -version
※エラーが発生した場合→補足
開発用ディレクトリの構成
必ずしもこの構成である必要はないですが、説明の都合上、ある程度ファイルの役割によって分けたディレクトリ構成をとして固めておきます。
ディレクトリの構成:
ProjectDirectory
├── rtl
├── sdc
├── sim
├── tb
├── tp
└── vivado
rtl |
|
---|---|
sdc |
|
sim |
|
tb |
|
tp |
|
vivado |
|
いちおうFPGAで動作確認したシンプル構成のお試しのコードをGitHubに置いているので、以下の手順においてこれを試しに使ってもかまいません。
RTL記述回路のSimulation手順
以下コマンドでコンパイルしていきますが、ある程度やることが固まったらmakefileやシェルスクリプトにまとめておけば、コマンド一発でエラーチェックから接続、動作シミュレーションまで自動化できます。
Vivadoで最初にプロジェクトを作ることから始めると、いきなりよくわからない設定を聞かれたりしますが、ここではその必要はありません。
xvlog:Verilog Compile
RTLで記述した回路のエラーチェックを行います。xvhdlでVHDLで記述した回路もコンパイル可能らしいですが、今回は扱いません。
コマンド例:
xvlog -sv \
--uvm_version 1.2 \
-L uvm \
--include {インクルードパス} \
--sourcelibdir {ソースコードディレクトリパス} \
{ソースコード} \
{テストベンチコード}
-sv | 入力ファイルにSystemVerilogが含まれる場合 |
---|---|
UVMのバージョンを指定(default 1.2) | |
-L | ライブラリ指定 |
`includeを使用してインクルードされるファイルを検索するディレクトリ指定 | |
--sourcelibdir | ソースコードのあるディレクトリ指定 |
ここでは使っていませんが、UVMという検証用の技術などを今後利用していきたいと考えているので、UVMに関連するオプションを入れています。オプションに関しては主にこちらなどを参考にさせていただきました。「xvlog --help」とすると、指定できるオプションの詳細が表示されます。
※{ソースコード}などの記述は、任意のものに置き換えてください。
以上を実行すると、xsim.dirなどのディレクトリや、xvlog.logなどのログなどが生成されます。
xelab:elaboration
記述自体のエラーチェックは終わったので、接続を完成します。
コマンド例:
xelab -debug typical \
--include {インクルードディレクトリ} \
--uvm_version 1.2 \
-L uvm \
{デザインのトップ} \
-s {任意名称}.sim
-debug typical | デバッグ情報の追加 以下のから、選択できますが迷ったら名前の通り代表的なものを含む「typical」でとりあえずOK
|
---|---|
UVMのバージョンを指定(default 1.2) | |
-L | ライブラリ指定 |
`includeを使用してインクルードされるファイルを検索するディレクトリ指定 | |
テストベンチのトップファイルの名称指定(拡張子不要) | |
-s hoge.sim | 生成する.simファイル名指定 |
オプションに関しては主にこちらなどを参考にさせていただきました。かなりの量がありますが「xelab --help」でオプションの詳細を確認することができます。
xelab.logというログや、xsim.dir内に「.sim」ディレクトリが生成されます。
xsim:simulation
ここで、シミュレーションを含めコマンド上で自動化する方法と、観察する信号線の選択および、シミュレーションは手動で行う方法との2つ紹介します。
シミュレーションを含めコマンドで自動化する
上で生成した「.sim」ファイルを使って、波形ファイル「.wdb」を生成します。
コマンド例:
xsim --wdb {任意の名前}.wdb \
{xelabで生成した.simファイル名}.sim \
-tclbatch sim.tcl
上記で波形シミュレーションの手順を記載した「sim.tcl」というファイルを使っています。以下は基本的なtclファイルの記述です。ファイルは任意の名前で大丈夫です。
sim.tcl:
# すべての信号を観測対象に追加
add_wave /[list */*]
# シミュレーション開始
run all
# シミュレーションを閉じる
exit
これで、シミュレーションまで終了しました。波形シミュレーションの結果を表示したい場合には、以下のコマンドで先ほど生成した「.wdb」ファイルを開いて波形を確認することができます。
コマンド:
xsim -gui {作成した.wdbファイル名}.wdb
観察したい信号を右クリックして「Add to Wave Window」で信号を追加することで、波形を表示することができます。
シミュレーションからGUIで行う
上では「.sim」ファイルを用いてシミュレーションを行ってから、「.wdb」ファイルを開きましたが、シミュレーションを行う前の「.sim」ファイルを直接開いて、観察したい信号線を手動で指定してから、シミュレーションを行うことができます。
コマンド例:
xsim -gui {xelabで生成した.simファイル名}.sim
右クリック「Add to Wave Window」で観察したい信号線を選択して、波形ウィンドウに追加してから、「Run All」を実行すれば、波形シミュレーションを実行可能です。
CompileとSimulationの自動化
以上のコードをmakefileを使って自動化してみます。
おためしコード:
ディレクトリおよび、ファイルの構成は以下のようになっています。
初期ファイル構成例:
.
├── rtl
│ ├── chattering.v
│ ├── counter.v
│ ├── ltop.v
│ ├── onehot.v
│ └── top.v
├── sdc
│ └── Arty-A7-100-Master.xdc
├── sim
│ ├── makefile
│ ├── sim.sh
│ └── sim.tcl
├── tb
│ ├── tbench.sv
│ └── tp.sv
├── tp
│ └── test_1.sv
└── vivado
makefile:
TB := tbench
# rtl
FILE := $(shell find ../rtl/* | grep -e '\.v' -e '\.sv' | grep -v 'tp.sv' | xargs echo)
# test bench
TBFILE := $(shell find ../tb/* | grep -e '\.v' -e '\.sv' | grep -v 'tp.sv' | xargs echo)
# include directory
INCPATH := ../tb
all: xvlog xelab xsim wave
xvlog:
xvlog -sv \
--uvm_version 1.2 \
-L uvm \
--include $(INCPATH) \
--sourcelibdir ../rtl/ \
$(FILE) \
$(TBFILE)
xelab:
xelab -debug typical \
--include $(INCPATH) \
--uvm_version 1.2 \
-L uvm \
$(TB) \
-s $(TB).sim
xsim:
xsim --wdb sim.wdb \
$(TB).sim \
-tclbatch sim.tcl
wave:
xsim -gui sim.wdb
xsim_wave:
xsim -gui $(TB).sim
.PHONY: clean
clean:
find ./* | grep -v -E 'makefile|sim.sh|xvlog.log|xelab.log|sim.tcl|xsim.log' | xargs rm -rf & rm -rf .Xil
/sim以下に入ってmakeコマンド一発でRTL記述のエラーチェックから、波形シミュレーションを行って、波形を確認するためのウィンドウを開くことができるよう自動化してみました。
作成した回路をFPGAに実装
ここまでで回路動作を確かめるまでは行ったので、あとは、ここからFPGAに回路として焼き付けるデータ(ビットストリーム)を生成して動かすだけです。
本記事の本題は上の内容までですが、折角なので簡単に基本的な手順と内容をまとめておきます。
制約ファイル(.xdc)の探し方と用意
FPGAボードの周囲についている入出力や、LEDやボタンなど周辺機器(ペリフェラル)と、ここまでで自分で記述した回路の入出力を対応付けていくためのファイルです。一般的にSDC(Synopsys Design Constraints)などと言われることが多いですが、VivadoではXDC(Xilinx Design Constraints)となっています。
用意と言っても、制約ファイル?どこにそんなものがあるんだ!と過去の私を振り返ると思っていたので、探しかたを説明しておきます。
私の手元にあるボードを例に挙げると、DigilentのArty A7というボードなのでここ(digilent-xdc)から取得してきます。「Digilent Arty A7」などのキーワードで検索をかけるとこのページ(Digilent Reference)があり、その中をよく見ると対象のxdcファイルへのgithubリンクがありました。
個人ではなかなか手を出さないであろうAlveoなどの上位機種のボードですら、制約ファイルは探せばダウンロードしてこれるので、よほど特別なボードでない限り、インターネットで検索をかければダウンロードしてこれるはずです。
制約ファイルの書き換え
制約ファイルにはたくさんの記述がありますが、ここから自分に必要な記述を選んでほんの少しだけ手を加えて使用します。
今回でいうと、クロック信号(clk)、リセット信号(resetn)、ボタン入力(btn)、LED出力(led)を使おうと思っているので、以下の記述を自分のトップモジュールの入出力端子名に書き換えてそれ以外はコメントアウトしておきます。
xdcファイル一部抜粋:
## Clock signal
set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_35 Sch=gclk[100]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports { clk }];
## LEDs
set_property -dict { PACKAGE_PIN H5 IOSTANDARD LVCMOS33 } [get_ports { led[0] }]; #IO_L24N_T3_35 Sch=led[4]
set_property -dict { PACKAGE_PIN J5 IOSTANDARD LVCMOS33 } [get_ports { led[1] }]; #IO_25_35 Sch=led[5]
set_property -dict { PACKAGE_PIN T9 IOSTANDARD LVCMOS33 } [get_ports { led[2] }]; #IO_L24P_T3_A01_D17_14 Sch=led[6]
set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { led[3] }]; #IO_L24N_T3_A00_D16_14 Sch=led[7]
## Buttons
set_property -dict { PACKAGE_PIN D9 IOSTANDARD LVCMOS33 } [get_ports { btn }]; #IO_L6N_T0_VREF_16 Sch=btn[0]
set_property -dict { PACKAGE_PIN C2 IOSTANDARD LVCMOS33 } [get_ports { resetn }]; #IO_L16P_T2_35 Sch=ck_rst
今回であれば216行程度あった元のファイルのうち、コメントアウトを外して有効にするのはたった8行です。[get_ports {hoge}]の{hoge}内を自分のtopモジュールの入出力信号の名前に変更しています。
xdcファイルの詳しい記述方法は、他のサイトなどを参考にしてください。私もまだ経験が浅いので勉強中です。
プロジェクトの作成
ここでやっと、Vivadoのプロジェクトを作成することにします。今回の例でいうとvivadoディレクトリなど、プロジェクトを生成した際に自動的に生成されるファイルやディレクトリをまとめて置けるディレクトリなどに移動して、「vivado」コマンドでvivadoを立ち上げます。
「Create Project」を選択してプロジェクトを作成します。
プロジェクト名は任意のものでかまいません。「Create project Subdirectory」もサブディレクトを作りたければチェックを入れてもかまいません。
Project Typeでは、今回シミュレーションまで行った回路記述の動作確認をしたいので、「RTL Project」を選択します。ここではいったん、「Do not specify sources at this time」にチェックを入れて、コードを追加せず、空のプロジェクトを作っています。
次に、実装に用いる適切なFPGAまたは、ボードを選択する必要があります。
以下は選択の例です。持ち合わせの評価ボードだと、ここで「FPGA part: XC7A100TCSG324-1」と書いているので、Serchの欄にこれを入力して選択しています。
初期状態では選択肢に存在しないボードもあるので、この場合はボードやFPGAを追加しなければいけません。追加の方法についてはここでは省きます。
FPGAを選択したら「Next」→「Finish」でプロジェクトを作成します。
プロジェクトを作成できたら、左上にある「Add Sources」でプロジェクトに先ほどまでで用意したファイルを追加していきます。
「Add or create design sources」で回路記述のソースコード(/rtl)を追加して、「Add or create constraints」で制約ファイル(/sdc)を追加して、「Add or create simulation sources」でテストベンチ(/tb)を追加します。
上の画像のように、各ファイルが認識され、それぞれに自動的に配置されていればOKです。includeされるファイルを追加する場合には、上のバーの「Tools」から、「Settinggs」→「General」の設定欄から、「Verilog option」の選択「…」から、「Verilog Include Files Serch Paths」でディレクトリパスを指定する必要があります。
今回の例では、シミュレーションはすでに行っているため、includeの記述のあるテストベンチは追加しなくても問題ないです。
ビットストリームの生成
ここまでで、すでにシミュレーションまで終えたHDL言語で記述した自作の回路とボードの制約ファイルがVivadoプロジェクトに追加され、揃ったので、いよいよ実際にFPGAに実装するためのデータを生成します。
トップ画面の左下にある「Generate Bitstream」を選択します。「Yes」→「OK」と進んで、ビットストリームの生成を開始します。
以上のようになれば完了です。「トップモジュール名.bit」という拡張子のビットストリームファイルが「/プロジェクト名.runs/impl_1/」以下に生成されているはずです。
FPGAに回路を実装する
上で、FPGAに書き込むことのできるビットストリームを生成したので、実際にFPGAボードに実装して動かしてみます。
FPGAボードをコンピュータにUSBケーブルなどで接続します。次に、先ほどの「Generate Bitstream」の下にある「Open Hardware Manager」の「Open Target」をクリックして「Auto Connect」を選択してFPGAとの接続を確立します。
WSL上でVivadoを起動している場合は、USBで接続したボードを認識させるのにひと手間が必要です。
WSLでUSB接続を行う:
接続された状態↓
ビットストリームファイルを選択する画面が出てくるので、先ほど生成したビットストリームファイルを選択して、「Program」に進むとFPGAに回路が書き込まれ、動作を確認することができるようになります。
補足:エラー対処
環境構築の過程で出現したエラーについてまとめておきます。
「vivado -version」コマンドで以下エラー:
/tools/Xilinx/Vivado/2023.1/bin/rdiArgs.sh: line 37: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory
/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
terminate called after throwing an instance of 'std::runtime_error'
what(): locale::facet::_S_create_c_locale name not valid
/tools/Xilinx/Vivado/2023.1/bin/rdiArgs.sh: line 352: 6592 Aborted "$RDI_PROG" "$@"
sudo locale-gen en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
「libtinfo.so.5」が見つからないエラー:
error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory
sudo apt install libtinfo5
コマンドラインで実行する方が、手順の再現性が高いため波形の描画など、必要最低限のところ以外は今回の手順で実行していきたいと思います。
参考:
・https://blog.n-hassy.info/2021/08/verilog-simulation-debug/
・https://vengineer.hatenablog.com/entry/2022/06/17/090000
・https://vengineer.hatenablog.com/entry/2022/06/10/090000
・https://fpga.kice.tokyo/fpga/vivado-simulator
以上、ありがとうございました。