PYNQ を使って Python で手軽に FPGA を活用 (3)

みなさんこんにちは。このコースでは、Python で FPGA を手軽に利用できる PYNQ とその活用方法を紹介していきます。ゴールは、Jupyter Notebook の環境を使って Python から手軽に FPGA を利用できるようになることです。

前回は、PYNQ の紹介とセットアップ方法を紹介しました。第 3 回となる今回は、PYNQ を使った FPGA の操作をいくつかの例を交えて紹介します。

PYNQ が提供する環境

PYNQ では、ZYNQ の PS (arm プロセッサ) の上で動作する Jupyter Notebook を使って Python でプログラミングができます。

PYNQでは、Jupyter Notebook で普通にPythonプログラミングができる

Numpy、Matplotlib、Scipy や Pandas など Python でデータ処理や数値計算を行なうモジュールが使用できるほか、PIL で画像を扱ったり IPython.display で音声や動画を扱ったりできます。

PYNQ の Jupyter Notebook 上で、音声や動画を扱うこともできる

ただし、PYNQ Z-1 の PS は 650MHz で動作する arm Cortex A-9 で、Python プログラムの実行速度はそれなりです。参考程度の評価結果ですが、PYNQ 上の Jupyter Notebook で31番目の フィボナッチ 数を求めるのに5.1秒程度かかりました (手元の 1.9GHz の Intel Core i7-8665U を搭載したノートパソコンでの実行時間は、0.28 秒程度でした)。

Jupyter Notebook は、ターミナルとしても利用できます。普段使いのファイル操作プログラムやコンパイラ (gcc) が使えます。vi を使ってファイルを編集することもできます。

Jupyter Notebook でターミナルを開いてコマンド入力ができる。gccやviも使える。

PYNQ で FPGA を扱う流れ

せっかく ZYNQ の上で Python プログラムを書くのですから、ZYNQ の PL (プログラマブル・ロジック領域) を活用しましょう。サンプルとして提供されている Buttons and LEDs demonstration を分解して、PL 上のロジックの操作方法を追ってみましょう。

冒頭の、

from time import sleep
from pynq.overlays.base import BaseOverlay

は、Python のモジュールロードですね。2 番目は PYNQ 特有のモジュールで、PL のコンフィギュレーション情報を操作するためのモジュールです。

base = BaseOverlay("base.bit")

インポートした BaseOverlay を使って PL にロジックをセットします。引数に指定した base.bitは、Vivado で作成した PL 用のビットストリームファイルの名前です。返り値の base がハードウェア情報を保持していて、PL 上に構成したハードウェアを参照するためのアクセスハンドラとして利用できます。

Delay1 = 0.3
Delay2 = 0.1
color = 0
rgbled_position = [4,5]

Python の変数やリストは通常通り使用することができます。プロセッサ上の Python 処理系でプログラムを動かしているので、当たり前といえば当たり前なのですが。

for led in base.leds:
    led.on()   

次にでてくるのは、forループです。引数の base.leds で、base.bit でPLに構成したハードウェアが持つ情報にアクセスしています。これは具体的にはボード上の LED に相当します。つまり、このループは、すべての LED に対して on() メソッドを呼び出す (LED を点灯する) コードです。

while (base.buttons[3].read()==0):
    if (base.buttons[0].read()==1):
        color = (color+1) % 8
        for led in rgbled_position:
            base.rgbleds[led].write(color)
            base.rgbleds[led].write(color)
        sleep(Delay1)

    elif (base.buttons[1].read()==1):
        for led in base.leds:
            led.off()
        sleep(Delay2)
        for led in base.leds:
            led.toggle()
            sleep(Delay2)

    elif (base.buttons[2].read()==1):
        for led in reversed(base.leds):
            led.off()
        sleep(Delay2)
        for led in reversed(base.leds):
            led.toggle()
            sleep(Delay2)                 

for だけではなく while ループも使えます。while の条件 base.buttons[3].read()==0 では、PL を介してボタンの情報を読み出し 0 かどうかをチェックします。base.buttons[3].read() はボタンが押されると 1 になります。つまり、この while ループはボタンが押されるまでループボディを繰り返す処理、ということになります。ループボディの条件分岐も同様に、ボタンの状態に応じて処理を分岐させます。

print('End of this demo ...')
for led in base.leds:
    led.off()
for led in rgbled_position:
    base.rgbleds[led].off()

最後は終了のメッセージを表示して、すべての LED (base.leds) と3色 LED (base.rgbleds) を消灯しています。
このように PYNQ では、Python プログラムの手軽さを活用して、PL 上のハードウェアを操作できます。PYNQ で用意されているモジュールを習熟する必要はありますが、それ以外は普通の Python プログラムと何ら変わりません。BaseOverlay で PL を利用したいハードウェア構成に変更して、 base という変数でハードウェアへのアクセスが抽象化されているので簡単にアクセスできます。

PYNQ で画像処理

FPGA を使う醍醐味の一つは I/O と処理を密に結合させられることです。New Style HDMI input and Pixel Formatting は、HDMI で受け取った画像にソフトウェア処理を施して HDMI で出力する例です。

from pynq.overlays.base import BaseOverlay
from pynq.lib.video import *

base = BaseOverlay("base.bit")
hdmi_in = base.video.hdmi_in
hdmi_out = base.video.hdmi_out

PL 上に構成された HDMI 入出力ポートに対するハンドラ hdmi_inhdmi_out を Python プログラム中で読み書きすることで、HDMI 入力された画像を受けとって処理をし、HDMI 出力ポートに出力できます。

取り込んだ画像にソフトウェアでフィルタを適用することでグレースケール化などの処理を適用し、HDMI ポートから出力するといったアプリケーションを手軽に実現できます。

HDMI から入力された画像(Windowsパソコンの画面)を取り込み、白黒化して出力している。この画像は同時にHDMIポートからも出力されている。

PYNQ でディープラーニング

PYNQ でディープラーニングを楽しむこともできます。ここでは、GitHub で公開されている PYNQ 向けのディープラーニングの実装事例を紹介しましょう。Jupyter Notebook でターミナルを開き、pip コマンドを使ってセットアップできます。

root@pynq:/home/xilinx# pip3 install git+https://github.com/Xilinx/BNN-PYNQ.git #pipコマンドでインストール
Collecting git+https://github.com/Xilinx/BNN-PYNQ.git
  Cloning https://github.com/Xilinx/BNN-PYNQ.git to /tmp/pip-hgmjqg6_-build
Installing collected packages: bnn-pynq
  Running setup.py install for bnn-pynq ... done
Successfully installed bnn-pynq-0.1
root@pynq:/home/xilinx# ls jupyter_notebooks/     # jupyter_notebooksの下にbnnフォルダがセットアップされた
 base   common            logictools        Untitled.ipynb
 bnn    getting_started   Untitled1.ipynb  'Welcome to Pynq.ipynb'
root@pynq:/home/xilinx#

ここでは、以下のノートブックがセットアップされます。

  • CNV-BNN_Cifar10.ipynb
  • CNV-BNN_Road-Signs.ipynb
  • CNV-BNN_SVHN.ipynb
  • CNV-QNN_Cifar10.ipynb
  • CNV-QNN_Cifar10_Testset.ipynb
  • CNV-QNN_Cifar10_Webcam.ipynb
  • LFC-BNN_Chars_Webcam.ipynb
  • LFC-BNN_MNIST_Webcam.ipynb
  • LFC-QNN_MNIST.ipynb

そのうちの一つ、 CNV-QNN_Cifar10.ipynb を試してみましょう。このノートブックは、6つの畳み込み層、3つのプーリング層、3つの全結合層からなる量子化ニューラルネットワークの実装例で、CIFAR-10 の画像認識をします。ビット数の異なる3つのパタンでの認識精度と実行時間を評価しています。

鹿の画像を推論させて、Deer であることが得られるか?という QNN のサンプル

ZYNQ の PL を活用することで PS 上のソフトウェア実行より推論が高速化されることを確かめることができます。PYNQ を使うことで、推論に必要な入力を Python プログラムを使って簡単に用意できること、複数のパタンの比較や結果の可視化が容易であることがわかります。

Python を使って、複数のパタンの比較や比較結果の可視化が容易にできるのも PYNQ の魅力

知っておこう。PYNQ での PL 再構成にかかる時間

PYNQ では、Python プログラムで PL の上のロジックを構成できますが、このための時間は 0 ではありません。構成のための時間 (書き換え時間) を測定するプログラムが Downloading Overlays に用意されています。

手元で実行した結果は次の通りでした。このグラフの縦軸が測定した書き換え時間です。単位はミリ秒です。ひとつだけはずれ値がありますが、書き換え時間はおおむね200ミリ秒程度のようです。

PL を書き換えるのにかかる時間はおよそ 200m 秒

まとめ

今回は、いくつかの例を紹介しながら、(1) PYNQ でできること、(2) Python プログラムからどのように PL 上のロジックを扱うのか、を紹介しました。手元に PYNQ Z-1 あるいは PYNQ を利用できる ZYNQ ボードを用意できる人は、是非試してみてください。
さて、次回は、PYNQ の上で自分で設計したロジックを動かしてみることにしましょう。

わさらぼ・みよしたけふみ

タイトルとURLをコピーしました