高位合成を使って FPGA 上に回路実装をしていくお話の第三弾になります.
4月になって忙しく,筆が進まなかったのですが,某さんに急かされたので頑張って書いていきます!
他の回は以下にあるのでご覧ください.
1. 今回行うこと
全体の流れは以下のようになっています.
- C++ を使って Vitis HLS 用に実行したいコードを書く
- Vitis HLS を使って,シミュレーションをしてデバッグする
- Vitis HLS を使って,Verilog にコンパイルする
- コンパイルしてできた IP を使って,Vivado で回路を組む (これが難しい…)
- Vivado を使ってできた回路から bitstream を作成する (いわゆるジェネビ)
- 生成された bitstream を PYNQ にコピーする
- PYNQ で回路にデータを流して結果を得る
今回は 4. コンパイルしてできた IP を使って,Vivado で回路を組む
,5. Vivado を使ってできた回路から bitstream を作成する
の手順を解説していきたいと思います.
ソースコードは以下に公開しているので適宜参照してください.
2. コンパイルしてできた IP を使って,Vivado で回路を組む
2.1. Vivado のプロジェクト作成
まず,Vivado を起動してプロジェクトを作成しましょう.
起動方法は GUI で Vivado を探すか,terminal 等で $ vivado &
とすればいいと思います.
terminal でコマンドが認識されない場合はパスが通っていないと思うので,パスを確認してみてください.
うまくいけば下のようなページが表示されると思います.
次に,Quick Start
の中の Create Project
or 左上の File > Project > New...
を押してください.
Create a New Vivado Project
というウィンドウが出てくると思うので,Next
を押して進みます.
次は Project Name
を設定する画面になると思うので,前々回作った Vitis HLS のプロジェクトのディレクトリ (/path/to/test) を Project location
に入れましょう.Project name
はなんでもいいので,デフォルトの project_1
のままにしておきます.
Next
を押すと,Project Type
の画面が出てくると思うので,デフォルトの RTL Project
のまま進みましょう.
その後の Add Sources
と Add Constraints
はスルーして Next
を押して進んでいきます.
次は,Default Part
の画面になると思うので,自分が使っているパーツ or ボードを選択しましょう.
今回は Zynq UltraScale+ MPSoC ZCU104 評価キット なので,Boards
タブに移って,Zynq UltraScale+ MPSoC ZCU104 Evaluation Board
を選択し,Next
を押します.
最後に,New Project Summary
が出てくると思うので,Finish
を押せば完成です.
2.2. 回路の作成
プロジェクトを作り終えたら以下のような画面が表示されていると思います.
一度閉じてしまった人は Vivado を起動した後,右上の File > Project > Open...
を選び,/path/to/test/project_1.xpr
を選べばプロジェクトを開くことができます.
2.2.1. 自作 IP の追加
まず,前回高位合成して作った IP を読み込んでもらうために,IP Catalog
を編集します.
クリックすると右上のパネルに IP Catalog
というタブができると思います.
次に,Vivado Repository
とかが並んでいるあたりでどこでもいいので右クリックして,出てきた中から Add Repository...
を選びます.
出てきたウィンドウで前回作った IP の入ったフォルダを選びます.
筆者の場合は /path/to/test/solution1/impl/ip
になります.
正しく追加できたら, 1 repository was added to the project. For more information related to disabled IPs, please refer to IP Catalog.
のようなメッセージが出てくると思うので, OK
を押して終了します.
2.2.2. Block Design の作成
IP が追加できたら,Block Design を作成していきます.
まず,左のFlow Navigator
の中から IP INTEGRATOR > Create Block Design
を選択します.Create Block Design
というウィンドウが出てくると思うので,そのまま OK
を押しましょう.Design name
は適当に編集しても構いませんが,デフォルトの design_1
でいいと思います.
作成できると右上のパネルに Diagram
のタブが現れると思います.
2.2.3. Diagram の作成
ここからが本番です.
と言っても,今回はこの記事通りに繋げば動くはずなのでいいのですが,自分でいろいろカスタマイズしていく時に回路の作り方がわからなくて困るかもしれません.
正直筆者もどのように回路を組めば確実に動くのかまで理解しきれてはいないので,個別の場合には対応できないかもしれませんが,とりあえずたくさんググって,似た回路を作っている人を探してそれを真似していくのがいいと思います.
今回作っていく回路のイメージは PYNQ を使って DMA を通して回路にデータを送り込み,再び DMA を通してデータを送り返してもらうというものです.
まずは IP を追加していきましょう.
IP を追加する時は ➕ を押すか,Diagram
内の白い部分を右クリックして Add IP...
を選択します.
すると IP の一覧が出てくると思うので,追加したい IP を検索してクリックしてください.
まずは自分の使っているパーツ or ボードを入れましょう.
今回入れるのは Zynq UltraScale + MPSoC
なので,zynq
と検索すれば出てきて,追加できるはずです.
もし出てこない時はおそらくボードの設定が間違っているので,上のタブの中から Window > Project Summary
を探してクリックし,Project part
の項目が正しいか確認し,間違っていればそこをクリックして正しいものに更新し,Apply
を押してください.
追加できたらこのようになって,Run Block Automation
とあるので,クリックしてそのまま OK
を押しましょう.
ブロックの設定をうまく変更してくれます.(が,今回はなぜか少し後で変更が必要です.)
次にブロックをダブルクリックして設定を少し変更します.PS-PL Configuration > PS-PL Interfaces
の中の
Master Interface > AXI HPM1 FPD
のチェックを外し,Slave Interface > AXI HP > AXI HP0 FPD
にチェックを入れます.
ここでは余分にできたポートを削除して,必要なポートを追加する作業をしています.
次に,Processor System Reset
,AXI Timer
,AXI Interconnect
,Concat
という IP を追加します.
さらに,AXI Interconnect
をもう一つ追加して,ダブルクリックし,Top Level Settings > Number of Master Interfaces
を 2 から 1 に変更しておきます.
これも余分なインターフェースを削除するということを行なっています.
(余分だとわかったのは後述する Run Connect Automation
の後にポートが余っていて怒られたからなのですが,今回は先に対処しておきます.)
次に,AXI Interrupt Controller
,AXI Direct Memory Access
も追加します.
DMA の方をダブルクリックし,
Enable Scatter Gather Engine
のチェックを外し,Width of Buffer Length Register
を 14 から 26 に変更し,- 一度で送るデータの長さを格納する場所で,一度に送るデータの数を増やしたい場合はこれを増やしておく必要がある
Enable Read Channel > Max Burst Size
を 16 から 256 に変更し,- バースト転送するデータの数の最大値を指定する
Enable Write Channel > Max Burst Size
を 16 から 256 に変更する.
自作 IP は先に追加してもいいが後ほど追加することにして,自動ではつないでくれない線をつないで,回路を組み立てていきます.
ポートあたりにマウスを置くと鉛筆のようなマークに変わると思うので,クリックして少しドラッグすると線が伸びると思うので,後は目的のポートを探して,もう一度クリックすれば線がつながります.
間違えて繋げてしまった場合は右クリックして Delete
を選択して,削除しておいてください.
具体的には以下の三つの線を繋いでください.
Concat > dout
とInterrput Controller > intr
Interrupt Controller > interrupt > irq
とZynq UltraScale + MPSoC > pl_ps_irq0
Concat > in1
とTimer > interrupt
ここまで作ったら一度保存しておくことをお勧めします.
というのも,Vivado のプロジェクトで一度 bitstream を生成したりした後に IP などを編集して,もう一度 bitstream を生成したりすると,謎のキャッシュの影響でバグってしまって苦しめられるということがまれにあるようなので,できれば IP を高位合成しなおしたりしたときには新しくプロジェクトを作り直す方がよいからです.
何度も一から回路を作るのは面倒なので,この段階までを保存しておいて,コピーして使い回すというのが現実的に最良な方法ではないかと思っています.
具体的には project_1.cache
,project_1.gen
,project_1.hw
,project_1.ip_user_files
,project_1.sim
,project_1.srcs
,project_1.xpr
の7つ (古いバージョンの Vivado では project_1.gen
以外の6つ) を別のフォルダにコピーしておきましょう.
ここから再び本題に戻りますが,次はようやく自作 IP を追加します.
関数名で登録されているはずなので,今回は Gaussian
という名前になっているはずです.
この自作 IP のポートを繋いでいきます.
DMA > S_AXIS_S2MM
と自作 IP > out_strm
自作 IP > in_strm
とDMA > M_AXIS_MM2S
自作 IP > Interrupt
とConcat > In0
また,クロック周波数を上げたり,下げたりしたいという場合は Zynq UltraScale + MPSoC
をダブルクリックして,Clock Configuration > Output Clocks > Low Power Domain Clocks > PL Fabric Clocks > PL0
で変更することができます.
デフォルトでは 100 MHz になっているはずです.
クロック周波数はちょうど設定した値になるわけではなく,可能な値のうち設定したものを超えない最小のものが選ばれるのではないかと思っています.
クロック周波数を上げると高速に処理できるようになりますが,上げすぎるとタイミングを満たさなくなって,正しい答えを返さなくなってしまうことがあるので,気をつけましょう.
この段階では下のような回路になっていると思います. (回路図は GitHub にも載せています.)
次に Run Connect Automation
を2回行います.
1度目は All Automation
にチェックを入れてから OK
を押してください.
これで自動で残りの線を繋げてくれます.
これで下のような回路が出来上がると思います.
次に ☑︎ の Validate Design
をして,正しい回路ができているか確認してください.Validation successful. There are no errors or critical warnings in this design.
のようなメッセージが出ていれば成功です.
多分大丈夫だと思いますが,エラーが出たら,エラーメッセージをググって調べてみてください…
最後にこの回路の Wrapper を作って完成です.
左上の Sources
タブに移り,Design Sources
の中から design_1
の場所を右クリックし,出てきた選択肢の中から,Create HDL Wrapper...
をクリックしてください.
出てきたウィンドウはそのまま OK
を押して進んでください.design_1
の右側がオレンジから青に変わっていれば成功です.
これで,回路作成は完了です.
長い道のりでした…
3. Vivado を使ってできた回路から bitstream を作成する
次は bitstream を作っていきます.
といっても,ここでする作業はほとんどなく基本的には待っていれば完成します.
Vivado を開いていると思うので,左側の Flow Navigator
の中から PROGRAM AND DEBUG > Generate Bitstream
を選択してください.implementation results
がないと言われると思いますが,自動で作ってくれると言っているので,Yes
を押して進みましょう.
次は Launch Runs
というウィンドウが出てくると思います.job
の数などを適当に設定して(分からなければデフォルトで大丈夫です.),OK
を押すとGenerate Bitstream
が始まります.
時間がかかるので,しばし待ちましょう.
Bitstream Generation successfully completed.
と出れば完成です.Open Implemented Design
(デフォルト)のまま OK
を押して,できた回路を確認してみましょう.
注目すべきなのは,下の Timing
タブの中の Design Timing Summary > Setup > Worst Negative Slack (WNS)
です.
これが正の値になっていれば,タイミングを満たしていますが,負になっていれば基本的には実機で動かした時に正しい答えを返してくれません(と言いながらも,正しい答えを返してくれることも多いです).
この時は,Zynq
の周波数を落として再度 Generate Bitstream
をやり直してください.
これで,実機に読み込ませる回路が完成しました.
4. まとめ
ようやく実機を使う手前まで来ました
Vivado はかなり奥が深く(?)分からないことだらけで,いつも困ってしまいますが,何か上手い方法はないものですかね…
次回もお楽しみに.