Edited at

第4回 PYNQのOVERLAYを使ってみた


はじめに

こんどこそ、ハードウェアリソースへのアクセス。

第1回 Setup PYNQ-Z1

第2回 はじめてのJupyter

第3回 pythonとPYNQ PL資源へのアクセス

第4回 PYNQ OVERLAY

第5回 PYNQのOverlayを作ってみた

第6回 カスタムIPを作ってPYNQ overlayに組み込む

第7回 PYNQのHDMI出力overlayを作る

番外編 python ジャンプスタート


loading Overlay

こんどこそ。

overlayはbitstreamのことで、ブート時には"base"と呼ばれるオーバレイがPL(Programmable Logic)に読み込まれる。その後新しいオーバレイを読み込むこともできる。オーバーレイはFPGA回路データのbitstream,vivadoのTcl, Python APIを含む。

PYNQのOverlay classはbitstreamファイル名をつかってインスタンスをつくることできる。

from pynq import Overlay

overlay = Overlay("base.bit")


base overlay

boot時に読み込まれるオーバレイ。これがデフォルトね。

from pynq.overlays.base import BaseOverlay

base_overlay = BaseOverlay("base.bit")

読み込みが完了すればpythonからアクセス可能になる。4〜5秒かかる感じ。help(base_overlay)にて、各種機能のhelpが得られる。


こんな感じでhelpを読む

help(base_overlay.leds)



LEDへのアクセスはこんな感じ

base_overlay.leds[0].toggle()



partial reconfiguration

部分的な構成変更も可能。この場合ビットストリームとともに「.hwh」ファイルも必要とのこと。とりあえず後回しでいいかな。

partial reconfiguration


PYNQ-Z1のオーバーレイ


base overlay

サードパーティ製のオーバーレイもあるが、まずbaseをみる。


ハードウエア対応


  • HDMI (Input and Output) --> {Jupyter dashboard}/base/video

  • Microphone in --> {Jupyter dashboard}/base/audio

  • Audio out

  • User LEDs, Switches, Pushbuttons -->{Jupyter dashboard}/base/board/board_btns_leds.ipynb

  • 2x Pmod PYNQ MicroBlaze

  • Arduino PYNQ MicroBlaze

  • 3x Trace Analyzer (PMODA, PMODB, ARDUINO)

Audio/videoとLED,SWはサポートするクラスがある。PmodやArduinoインターフェースは、MicroBlase(softマクロ、マイコン)をプログラムしてコントローラにしてアクセスする構成か。


PYNQ-Z1のvideoのテスト

HDMI入力にMACのディスプレイ出力、出力にHDMIディスプレイをつないでデモをやってみる(http://{IP ADDRESS}:9090/notebooks/base/video/hdmi_introduction.ipynb)

bitストリームのダウンロードは、すこし時間がかかるがすんなり通過。

from pynq.overlays.base import BaseOverlay  # BaseOverlayをimport

from pynq.lib.video import * # video関係もまとめて

base = BaseOverlay("base.bit") # baseをロード
hdmi_in = base.video.hdmi_in # hdmi入出力をcreate
hdmi_out = base.video.hdmi_out


パススルーテスト

hdmi_in.configure()  # Configure the pipeline to use the specified pixel format.

# configure(self, mode, pixelformat=None)
# Configure the pipeline to use the specified pixel format and size.
# mode : VideoMode
hdmi_out.configure(hdmi_in.mode)

hdmi_in.start() # Start the pipeline
hdmi_out.start()

hdmi_in.tie(hdmi_out)  # Mirror the video input on to an output channel

ひと仕事おわったら、ちゃんとクローズするようにとのこと。context managerをつかえば、自動でおかたづけできるとのこと。(どうするのかは書いてない)

hdmi_out.close()

hdmi_in.close()

しかし、これをやったとしても、2例目以降を実行しようとするとJupyterに"kernel restarting"のダイアログがでる。なぜだろう。


映像のキャプチャ


オープンしてパススルー

hdmi_in.configure(PIXEL_RGB)  # RGBフォーマット

hdmi_out.configure(hdmi_in.mode, PIXEL_RGB)

hdmi_in.start()
hdmi_out.start()

hdmi_in.tie(hdmi_out)



キャプチャ

import PIL.Image  # Pillow : Python Imaging Library


frame = hdmi_in.readframe() # Read a video frame; See AxiVDMA.S2MMChannel.readframe for details
image = PIL.Image.fromarray(frame)
image

Imaging Libraryね。PIL, Pillow, OpenCV, matplotlibの関係はどうなっているのかな。

nkmk(@nkmk_me twitter)さんのnote.nkmk.me Pythonで画像処理: Pillow, NumPy, OpenCVの違いと使い分けに回答があった。



  • Pillow(PIL): 画像処理ライブラリ

  • NumPy: 数値計算ライブラリ

  • OpenCV: コンピュータビジョンライブラリ


help(hdmi_in.readframe)するとAxiVDMA.S2MMChannel.readframe()を引けとでた。ヘルプはどこだろう。また見知らぬ略語だ。


  • AxiVDMA : AXI Video Direct Memory Access

  • S2MM : Stream to Memory Map

なるほど。まるで70年代のアセンブラのラベル。オートコンプリートつかってないのかなぁ。

みつけた pynq.lib.video Module


Audio

同様にサンプルをやってみる。マイクは内蔵なので、スピーカをつなぐ。ちゃんと音もでるし、録音したものをnotebookで再生できる。

レベルは調整したほうがいいかな。

MICはPulse Density Modulation(PDM)で符号化された出力なので、PDM->PCM変換が必要。ΔΣ変調がかかった状態なので、LPFをかければもとの信号を取り出せる。

なお、help(base.audio.record()によれば、ワードのサンプリングレートは192000Hz(192kHz)。1ワード16bitなので192x16ksample/sec(1bit)。

PDM MICについてはFreeplanetsPDMマイクからの信号よみとり(IP)の解説が詳しい


PDM->PCM

from pynq.overlays.base import BaseOverlay

base = BaseOverlay("base.bit")
pAudio = base.audio
pAudio.record(3)
#pAudio.load("Recording_1.pdm")

# ここから変換
import numpy as np

# まずバッファを32bitから16bitに変換(astype(np.int16))し
# 1bit x 16 sample を 8 bit x 16 sampleに変換
af_uint8 = np.unpackbits(pAudio.buffer.astype(np.int16)
.byteswap(True).view(np.uint8))

# PDM->PCM
from scipy import signal # downsampleのためにscipyモジュールをインポート
# signal.decimateは8次チェビシェフ type I のIIRフィルタを用いる。
af_dec = signal.decimate(af_uint8,8,zero_phase=True) # 1/8
af_dec = signal.decimate(af_dec,6,zero_phase=True) # 1/6 (1/48)
af_dec = signal.decimate(af_dec,2,zero_phase=True) # 1/2 (1/96)
# 16 x 192000 [sample/sec] /96 -> 32 [ksample/sec]
af_dec = (af_dec[10:-10]-af_dec[10:-10].mean()) # DC offsetを削除する

del af_uint8

from IPython.display import Audio as IPAudio
IPAudio(af_dec, rate=32000)



LEDs, Switches, Pushbuttons

{Jupyter dashboard}/base/board/board_btns_leds.ipynb をやってみる。これは簡単。

from time import sleep

from pynq.overlays.base import BaseOverlay

base = BaseOverlay("base.bit")

for led in base.leds:
led.off()
sleep(0.5)
base.leds[0].on()
while (base.buttons[3].read()==0):
if (base.buttons[0].read()==1):
base.leds[1].toggle();
sleep(0.1);
for led in base.leds:
led.off()

せっかくなのでもう少し低いレベルまでほってみる。

PYNQ libraries AxiGPIO

ふむふむGPIOは、32pinを2チャンネルもてて、LED,Switch,Button,RGBLEDクラスはこれを拡張してカスタマイズしたもの[led|switch|button|rgbleds]_gpio


AxiGPIOの資料より

from pynq import Overlay

from pynq.lib import AxiGPIO
ol = Overlay("base.bit")

led_ip = ol.ip_dict['gpio_leds']
switches_ip = ol.ip_dict['gpio_switches']
leds = AxiGPIO(led_ip).channel1
switches = AxiGPIO(switches_ip).channel1



まとめ

手順は、baseをダウンロードし、しかるべきオブジェクトを介して操作する。


  • audioはPDMフォーマットであるから、PCMとの変換が必要

  • ledやSwitchは単純にread/writeするだけ

  • pmodやシールドはMicroBleaseで制御する

  • 割り込みもあるが、とりあえずおいておく