Vitisは、ホスト-FPGA間をOpenCL的に利用できるようにするXilinxの開発環境です。ホストとFPGAの間のデータ転送や制御の仕組みを自分で考えなくていいので楽なのですが、それでもちょっとした回路を動かす時にホスト側のC++コードを書くのがちょっと面倒。
と思っていたら、PYNQでAlveoにxclbinをロードして動かすのが簡単だということを知りました ( 高位合成で加速するアクセラレータ開発 (1) ~ ACRi ルームで体験してみよう )。で、RTLカーネルだったらどうなるのかな?とテストしてみたところ、同じようにRTLから作ったxclbinもPYNQから呼び出せることがわかりました。これは便利。
作業はPYNQが使えるようになっているACRiのAlveoサーバを利用します。
ハードウェア側の準備
RTL作る手順は普段どおり。
- Vitis起動して適当なワークスペースを作る
- Create Application Projectでプロジェクトを作成する
- プロジェクト名は好きにつけて構いません
- Select a platform repositryで使うFPGAを間違えずに選ぶ
- TemplateではEmpty Applicationを選択
- プロジェクトができたら、メニューの”Xilinx”->RTL Kernel Wizard…をクリック
- RTLカーネルウィザードでシンプルなサンプルを作る
- Scalars(スカラな引数)は0個にする(デフォルトは1になってる)
- それ以外はそのまま
- Summaryで表示されるプロトタイプが
void rtl_kernel_wizard_0(global void *axi00_ptr0);
になるはず
- RTLカーネルを終了するとVivadoが起動する。Genelrate RTL Kernel Wizardをクリック
- 指定されたメモリ領域をintの配列と見立てて、全要素に+1するサンプルが生成される
- ここではデフォルトのままSource-only kernelで十分なので、そのままOK
- すぐ終わる。Vivado終了させてしまっていいので、でてきたダイアログはOK
- xclbinを作る
- Active build configurationでHardwareを選択
- Hardware Functionsのとこの稲妻マークみたいなボタンをクリックして、合成対象に
rtl_kernel_wizard_0
を追加する - Explorerでツリーの上の方(たとえばルート)を選択すると、ツールバーの金槌マークがクリックできるようになる。クリックしてビルド。
- binary_container_1.xclbinができあがる
- ワークスペース/プロジェクト名/Hardwareの下にある、はず。
JupyterLabでFPGAを制御
JupyterLabを起動します。
$ /opt/pynq-notebooks/start.sh
ここからはJupyterLab上での作業
- 左のメニューっぽいブロックの上にあるツールーバーの「フォルダに+がついたマーク」のボタンをクリックして作業ディレクトリを作成して、その下に入る。
- 同じくツールバーの「上矢印ボタン」をクリックしてxclbinをアップロードする。
- Lancherが開いているはずなので、Python3をクリック。Lancherがひらいてなければツールバーの+ボタンをクリックするといい
- Untitled.ipynbのウィンドウが開くはず
次のようなコードを書く。
import pynq
import numpy as np
ol = pynq.Overlay('binary_container_1.xclbin')
print(ol.__dict__)
kernel = ol.rtl_kernel_wizard_0_1
in1 = pynq.allocate((1024,), 'u4')
in2 = pynq.allocate((1024,), 'u4')
in1[:] = np.random.randint(low=0, high=100, size=(1024,), dtype='u4')
in2[:] = in1[:]
print("in1", in1)
print("in2", in2)
in1.sync_to_device()
kernel.call(in1)
in1.sync_from_device()
print("in1", in1)
print("in2", in2)
np.array_equal(in1, in2 + 1)
ol.__dict__
の結果、大量の情報が出力された後で
in1 [86 77 22 ... 7 68 41]
in2 [86 77 22 ... 7 68 41]
in1 [87 78 23 ... 8 69 42]
in2 [86 77 22 ... 7 68 41]
True
という意図通り(FPGAとやりとりした方の領域だけ+1されてる)ことが確認できました。
ちなみに、ol.__dict__
の結果を見ると
'ip_dict': {'rtl_kernel_wizard_0_1': {'phys_addr': 20971520, 'addr_range': 4096, 'type': 'mycompany.com:kernel:rtl_kernel_wizard_0:1.0', ...
というようにRTLカーネルの情報がロードされている様子が見て取れます。
まとめ
ちょっとしたロジックをAlveoの上で動かすときにC++コードを書かずに、PYNQを使ってPythonで動作確認できるのはとっても便利そうです。