##はじめに
こんどこそ、ハードウェアリソースへのアクセス。
第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(base_overlay.leds)
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)の解説が詳しい
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
。
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で制御する
- 割り込みもあるが、とりあえずおいておく