PYNQでAlveo向けにVitisで作ったRTLカーネルを使ってみる

Pocket

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で動作確認できるのはとっても便利そうです。