Vitis HLS と Vivado で作った回路を PYNQ を使って FPGA 上で動かすまで ー③Vivado で回路を組むー

公開日:
最終更新日:
Vivado で回路を組む

高位合成を使って FPGA 上に回路実装をしていくお話の第三弾になります.
4月になって忙しく,筆が進まなかったのですが,某さんに急かされたので頑張って書いていきます!
他の回は以下にあるのでご覧ください.

  1. コードを書く
  2. シミュレーションをする
  3. Vivado で回路を組む (この記事)
  4. PYNQ を使って実機で動かす

1. 今回行うこと

全体の流れは以下のようになっています.

  1. C++ を使って Vitis HLS 用に実行したいコードを書く
  2. Vitis HLS を使って,シミュレーションをしてデバッグする
  3. Vitis HLS を使って,Verilog にコンパイルする
  4. コンパイルしてできた IP を使って,Vivado で回路を組む (これが難しい…)
  5. Vivado を使ってできた回路から bitstream を作成する (いわゆるジェネビ)
  6. 生成された bitstream を PYNQ にコピーする
  7. PYNQ で回路にデータを流して結果を得る

今回は 4. コンパイルしてできた IP を使って,Vivado で回路を組む5. Vivado を使ってできた回路から bitstream を作成する の手順を解説していきたいと思います.

ソースコードは以下に公開しているので適宜参照してください.

2. コンパイルしてできた IP を使って,Vivado で回路を組む

2.1. Vivado のプロジェクト作成

まず,Vivado を起動してプロジェクトを作成しましょう.

起動方法は GUI で Vivado を探すか,terminal 等で $ vivado & とすればいいと思います.
terminal でコマンドが認識されない場合はパスが通っていないと思うので,パスを確認してみてください.
うまくいけば下のようなページが表示されると思います.

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

次に,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 のままにしておきます.

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

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

その後の Add SourcesAdd Constraints はスルーして Next を押して進んでいきます.

次は,Default Part の画面になると思うので,自分が使っているパーツ or ボードを選択しましょう.
今回は Zynq UltraScale+ MPSoC ZCU104 評価キット なので,Boards タブに移って,Zynq UltraScale+ MPSoC ZCU104 Evaluation Board を選択し,Next を押します.

Vivado ボード選択
Vivado ボード選択

最後に,New Project Summary が出てくると思うので,Finish を押せば完成です.

2.2. 回路の作成

プロジェクトを作り終えたら以下のような画面が表示されていると思います.
一度閉じてしまった人は Vivado を起動した後,右上の File > Project > Open... を選び,/path/to/test/project_1.xpr を選べばプロジェクトを開くことができます.

Vivado のプロジェクトページ
Vivado のプロジェクトページ

2.2.1. 自作 IP の追加

まず,前回高位合成して作った IP を読み込んでもらうために,IP Catalog を編集します.
クリックすると右上のパネルに IP Catalog というタブができると思います.
次に,Vivado Repository とかが並んでいるあたりでどこでもいいので右クリックして,出てきた中から Add Repository... を選びます.
出てきたウィンドウで前回作った IP の入ったフォルダを選びます.
筆者の場合は /path/to/test/solution1/impl/ip になります.

IP カタログの編集画面
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 を押しましょう.
ブロックの設定をうまく変更してくれます.(が,今回はなぜか少し後で変更が必要です.)

Zynq IP
Zynq IP

次にブロックをダブルクリックして設定を少し変更します.
PS-PL Configuration > PS-PL Interfaces の中の

  • Master Interface > AXI HPM1 FPD のチェックを外し,
  • Slave Interface > AXI HP > AXI HP0 FPD にチェックを入れます.

ここでは余分にできたポートを削除して,必要なポートを追加する作業をしています.

次に,Processor System ResetAXI TimerAXI InterconnectConcat という IP を追加します.

さらに,AXI Interconnectをもう一つ追加して,ダブルクリックし,Top Level Settings > Number of Master Interfaces を 2 から 1 に変更しておきます.
これも余分なインターフェースを削除するということを行なっています.
(余分だとわかったのは後述する Run Connect Automation の後にポートが余っていて怒られたからなのですが,今回は先に対処しておきます.)

次に,AXI Interrupt ControllerAXI 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 に変更する.
DMA の設定
DMA の設定

自作 IP は先に追加してもいいが後ほど追加することにして,自動ではつないでくれない線をつないで,回路を組み立てていきます.
ポートあたりにマウスを置くと鉛筆のようなマークに変わると思うので,クリックして少しドラッグすると線が伸びると思うので,後は目的のポートを探して,もう一度クリックすれば線がつながります.
間違えて繋げてしまった場合は右クリックして Delete を選択して,削除しておいてください.
具体的には以下の三つの線を繋いでください.

  • Concat > doutInterrput Controller > intr
  • Interrupt Controller > interrupt > irqZynq UltraScale + MPSoC > pl_ps_irq0
  • Concat > in1Timer > interrupt

ここまで作ったら一度保存しておくことをお勧めします
というのも,Vivado のプロジェクトで一度 bitstream を生成したりした後に IP などを編集して,もう一度 bitstream を生成したりすると,謎のキャッシュの影響でバグってしまって苦しめられるということがまれにあるようなので,できれば IP を高位合成しなおしたりしたときには新しくプロジェクトを作り直す方がよいからです.
何度も一から回路を作るのは面倒なので,この段階までを保存しておいて,コピーして使い回すというのが現実的に最良な方法ではないかと思っています.
具体的には project_1.cacheproject_1.genproject_1.hwproject_1.ip_user_filesproject_1.simproject_1.srcsproject_1.xpr の7つ (古いバージョンの Vivado では project_1.gen 以外の6つ) を別のフォルダにコピーしておきましょう.

ここから再び本題に戻りますが,次はようやく自作 IP を追加します.
関数名で登録されているはずなので,今回は Gaussian という名前になっているはずです.
この自作 IP のポートを繋いでいきます.

  • DMA > S_AXIS_S2MM自作 IP > out_strm
  • 自作 IP > in_strmDMA > M_AXIS_MM2S
  • 自作 IP > InterruptConcat > In0

また,クロック周波数を上げたり,下げたりしたいという場合は Zynq UltraScale + MPSoC をダブルクリックして,Clock Configuration > Output Clocks > Low Power Domain Clocks > PL Fabric Clocks > PL0 で変更することができます.
デフォルトでは 100 MHz になっているはずです.
クロック周波数はちょうど設定した値になるわけではなく,可能な値のうち設定したものを超えない最小のものが選ばれるのではないかと思っています.
クロック周波数を上げると高速に処理できるようになりますが,上げすぎるとタイミングを満たさなくなって,正しい答えを返さなくなってしまうことがあるので,気をつけましょう.

この段階では下のような回路になっていると思います. (回路図は GitHub にも載せています.)

Vivado の回路図 (途中)
Vivado の回路図 (途中)

次に Run Connect Automation を2回行います.
1度目は All Automation にチェックを入れてから OK を押してください.
これで自動で残りの線を繋げてくれます.
これで下のような回路が出来上がると思います.

Vivado の回路図 (完成版)
Vivado の回路図 (完成版)

次に ☑︎ の Validate Design をして,正しい回路ができているか確認してください.
Validation successful. There are no errors or critical warnings in this design. のようなメッセージが出ていれば成功です.
多分大丈夫だと思いますが,エラーが出たら,エラーメッセージをググって調べてみてください…

最後にこの回路の Wrapper を作って完成です.
左上の Sources タブに移り,Design Sources の中から design_1 の場所を右クリックし,出てきた選択肢の中から,Create HDL Wrapper... をクリックしてください.

HDL Wrapper の作り方
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 をやり直してください.

Vivado で合成されたデザイン
Vivado で合成されたデザイン

これで,実機に読み込ませる回路が完成しました.

4. まとめ

ようやく実機を使う手前まで来ました 👏
Vivado はかなり奥が深く(?)分からないことだらけで,いつも困ってしまいますが,何か上手い方法はないものですかね…
次回もお楽しみに.

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA