VivadoとVitisを利用して、DPUのIPを合成したプロジェクトを作りました。
KR260でPYNQ上で作成したDPUを使い、Vitis AIの物体検出(YOLOv3)をしています。
その上でKR260にて、GPIO(PWM)も一緒に動かすまでの流れを紹介します。
DPUのIPを合成して、PYNQで動かしてみた
VivadoとVitisを利用して、DPUのIPを合成したプロジェクトを作りました。
KR260でPYNQ上で作成したDPUを使っています。
Vitis AIの物体検出(YOLOv3)をしています。
DPUと一緒にGPIO(PWM)も一緒に動かすまでの流れを紹介します。
まとめたテスト動画は下記となります。
作成フロー
DPUのIPを含んだプロジェクトは色々な作成方法があります。
今回のはあくまで一例です。DPUと下記フローで作成していきます。
筆者は2023.1の環境で作成しています。同様に実施する方は適宜修正をしてください。
①VivadoでDPUに必要なMPSoC・クロック・リセットを合成する。
②VitisでDPUを合成する。
③VivadoでGPIOやPWMなどDPU以外のIPを合成する
VivadoでDPUのプロジェクトを作る
Vivadoを開いて、プロジェクトを作成していきます。
最初にDPUに必要なMPSoC、クロックやリセットのIPを配置していきます。
Create Project
「Create Project」→適当な名前つけて進めていきます。
Project Typeでは、デフォルトのRTLにしています。
Default Partでは、Boardを選択してKR260を選びます。
Create Block Design
Projectファイルが出来上がります。
Create Block Designを選択して、ブロックのデザインを進めます。
Diagramの「+」箇所から、Zynq UltraScale+ MPSoCを選びます。
その後Run Block Automationの設定を行います。(デフォルトでOKです)
Diagramの「+」箇所からclockで検索して、Clocking WizardのIPを選びます。
IP選択後はクロックを100,150,200,300,400,600の6出力にして、ResetをActiveLowにしときます。
ResetのIPを用意します。Processor System Resetです。クロックと同じ5個分用意します。
Run Block Automationの設定で各6つのリセットのIPに、各5つのクロックのソースを選択します。
自動接続の後、更新マークの「Regenerate Layout」をするとIPが綺麗に整理されます。
その後クロックのlockedと各リセットのdcm_lockedを繋げます。
今回はVivadoの後にVitisでも編集します。
Setting→Generalの箇所で、「Project is an extensible Vitis platform」のチェックを入れます。
※一番最初にプロジェクトを作る際に設定してもOKです。
その後はWindowのタブからPlatform Setupを選択します。
クロックは全てEnabledにチェックを入れて、IDを0から振っていきます。
デフォルトは300MHzとしています。
Zynq UltraScale+ MPSoCのIPの設定で「PS-PL Configuration」の項目を開きます。
AXI HPHM0 LPDにチェックをいれます。
IPの検索でInterと入力して、AXI Interrupt Controllerを配置します。
下にあるInterrupt Actionを「Single」にしておきます。
その後Run Block Automationで繋げて、interの出力とMPSoCの入力を接続します。
Platform SetupでIntrにチェックを入れます。
またAXI Portも設定しておきます。SPTagまで名前を付けておきます。
これでVivadoでのDPU周りの設定が終了です。
これでDPUを作成に必要なクロック・リセットの作成が完了しました。
Generate Bitstreamをクリックして合成します。
この時点でのFPGAの使用率(Utilization)としては下記状況でした。
まだDPUやGPIO,PWMを合成していないため、全く使っていない状況です。
Export Platform
合成後はExport PlatformでVitis用へのPlatformを作成します。
「Hardware and Hardware emulation」を選択します。
include bitstreamの箇所にチェックを入れます。
プラットフォームの名前を付けて、XSAファイルを出力します。
デフォルトだとプロジェクトフォルダ内に.xsaファイルが出来ます。
Vitisでプラットフォームの登録する際に使います。
VitisでDPUを組み込む
VitisでDPUに必要なイメージ・IPをダウンロードしておきます。
ZYNQMP common image
下記ページから「ZYNQMP common image」をダウンロードします。
ZYNQMPとはFPGAとCPUを組み合わせたプラットフォームです。
ダウンロード後は解凍して、インストールを実行しておきます。
1 2 3 |
tar -xzvf xilinx-zynqmp-common-v2023.1_05080224.tar.gz cd xilinx-zynqmp-common-v2023.1/ ./sdk.sh -d . |
Vitis AIのRuntime
Vitis AIのRuntime パッケージもダウンロードして、ZynqMPの中に解凍します。
1 2 3 |
wget https://www.xilinx.com/bin/public/openDownload?filename=vitis_ai_2023.1-r3.5.0.tar.gz -O vitis_ai_2023.1-r3.5.0.tar.gz rm -r xilinx-zynqmp-common-v2023.1/sysroots/cortexa72-cortexa53-xilinx-linux/usr/share/cmake/XRT/ tar -xzvf vitis_ai_2023.1-r3.5.0.tar.gz -C xilinx-zynqmp-common-v2023.1/sysroots/cortexa72-cortexa53-xilinx-linux/ |
DPUCZDX8G
DPUCZDX8GはDPUのIPです。
今回使うKR260用のものをダウンロード・解凍します。
1 2 |
wget https://www.xilinx.com/bin/public/openDownload?filename=DPUCZDX8G.tar.gz -O DPUCZDX8G.tar.gz tar -xzvf DPUCZDX8G.tar.gz |
Vitisを起動してDPUのIPを登録します。
WindowsのタブからPreferencesを選択します。
Library Repositoriesの箇所からDPUのIPを登録します。
AddからDPUCZDX8Gの位置を指定します。
VitisでPlatformを作る
Vitisを起動して、Create Platform Projectから新規プラットフォームを作成します。
Vivadoで作成した,xsaファイルを選択して、LinuxをOSとして選びます。
また一番下にあるGenerate…のチェック項目は外しておきます。
platform.sprを選択して、上にあるビルドのアイコンをクリックします。
直ぐにPlatformのビルドが完了するはずです。
Vitisでプロジェクトを作る
Vitisでプロジェクトを作成していきます。
New→Application Projectを選択します。
先ほど作ったPlatformがあるので、選択します。
Applicationの名前を決めます。
Sysrootのパスは、用意したZYNQMPのcortexa72...を選択します。
DPUのIPを事前に登録したので、DPU Kernelが選択できます。
ここからアプリケーションの設定をしていきます。
最初に右上にあるActive build ...の項目を「Hardware」に選択します。
DPUのIPの数を設定します。..._hw_link.prjの箇所から設定できます。
今回は1個あれば十分なのでDPUCZDX8Gを2→1にしています。
DPUの設定をします。kernels→src→prj→Vitisの箇所にdpu_conf.vhがあります。
デフォルト設定から、URAMやDWCVなどの設定を変更しました。
下記のPYNQ-DPUの設定を参考にしました。DPUのサイズはB4096を使用しています。
https://qiita.com/basaro_k/items/dc439ffbc3ea3aed5eb2
https://github.com/Xilinx/DPU-PYNQ/blob/master/boards/README.md#dpu-ip
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
`define B4096 `define URAM_ENABLE //config URAM `ifdef URAM_ENABLE `define def_UBANK_IMG_N 6 `define def_UBANK_WGT_N 17 `define def_UBANK_BIAS 1 `elsif URAM_DISABLE `define def_UBANK_IMG_N 0 `define def_UBANK_WGT_N 0 `define def_UBANK_BIAS 0 `endif `define DRAM_DISABLE //config DRAM `ifdef DRAM_ENABLE `define def_DBANK_IMG_N 1 `define def_DBANK_WGT_N 1 `define def_DBANK_BIAS 1 `elsif DRAM_DISABLE `define def_DBANK_IMG_N 0 `define def_DBANK_WGT_N 0 `define def_DBANK_BIAS 0 `endif `define RAM_USAGE_LOW `define CHANNEL_AUGMENTATION_ENABLE `define DWCV_ENABLE `define ALU_PARALLEL_DEFAULT `define CONV_RELU_LEAKYRELU_RELU6 `define ALU_RELU_RELU6 `define DSP48_USAGE_HIGH `define LOWPOWER_DISABLE `define MPSOC |
DPUへのクロック・AXIの設定も修正します。
Assistant箇所のHW_Linkの下にあるHardware→dpuを右クリックします。
V++のsettingsを修正します。デフォルトでは2個分の設定がされています。
そのため不要な2個目の箇所は#をつけて省略しています。
1 2 3 4 5 6 7 8 9 10 11 12 |
[clock] freqHz=300000000:DPUCZDX8G_1.aclk freqHz=600000000:DPUCZDX8G_1.ap_clk_2 #freqHz=300000000:DPUCZDX8G_2.aclk #freqHz=600000000:DPUCZDX8G_2.ap_clk_2 [connectivity] sp=DPUCZDX8G_1.M_AXI_GP0:HPC0 sp=DPUCZDX8G_1.M_AXI_HP0:HP0 sp=DPUCZDX8G_1.M_AXI_HP2:HP1 #sp=DPUCZDX8G_2.M_AXI_GP0:HPC0 #sp=DPUCZDX8G_2.M_AXI_HP0:HP2 #sp=DPUCZDX8G_2.M_AXI_HP2:HP3 |
最後にビルドします。..._hw_link[pl]の箇所を選択して、上にあるビルドのアイコンを選択します。
おそらくPCのスペック次第ですが数十分~数時間かかります。
DPUのビルドは多くのメモリを使います。
CPU2個でのビルドで、16Gのメモリぐらい必要としました。
もしメモリが足りない方は、下記のように仮想メモリを増やすなどの対応をお願いします。
1 2 3 |
sudo dd if=/dev/zero of=/var/swap bs=1M count=16384 sudo mkswap /var/swap sudo swapon /var/swap |
Vitisでのビルド完成後は、DPUが組み込まれたモデルをVivadoで確認します。
下記例のようにVitisのプロジェクト(***_hw_link)のHardwareの奥深くにあります。
~/***_hw_link/Hardware/dpu.build/link/vivado/vpl/prj
Vivadoでプロジェクトを開くと下記のようにDPUのIPが確認できます。
DPUを入れるとFPGAの使用率(Utilization)は一気に上がります。
VivadoでGPIOやPWMのIPを追加する
Vivadoで、DPU以外に必要なGPIOやPWMのIPを追加していきます。
今回はGPIOのIPを2個、PWMを4個追加しました。
KR260のPMODコネクタからモータドライバ、LED、SW、センサを制御します。
GPIO、PWMのIPの使い方は下記記事で紹介しています。
Create HDL Wrapper
全てのIPが設定できたら、Sources箇所で右クリックをして、Create HDL Wrapper をします。
XDCファイルを作成する
ピンアサインをしていきます。
Constrainsの箇所で右クリックしてAdd Sorucesから設定します。
GPIOやTimerなど外部出力するピンがある場合はXDCファイルも設定しておきます。
今回はKR260のPMODに合わせた形で記載しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#PMOD1 motor-driver1 set_property PACKAGE_PIN H12 [get_ports PWM_0] set_property PACKAGE_PIN E10 [get_ports PWM_1] set_property PACKAGE_PIN D10 [get_ports PWM_2] set_property PACKAGE_PIN C11 [get_ports PWM_3] set_property PACKAGE_PIN B10 [get_ports gpio_rtl_0_tri_o[0]] #PMOD2 motor-driver2 set_property PACKAGE_PIN J11 [get_ports gpio_rtl_0_tri_o[5]] set_property PACKAGE_PIN J10 [get_ports gpio_rtl_0_tri_o[6]] set_property PACKAGE_PIN H11 [get_ports gpio_rtl_0_tri_o[1]] #PMOD3 infrared sensor set_property PACKAGE_PIN AE12 [get_ports gpio_rtl_1_tri_i[0]] set_property PACKAGE_PIN AF12 [get_ports gpio_rtl_1_tri_i[1]] #PMOD4 debug(led-sw) set_property PACKAGE_PIN AC12 [get_ports gpio_rtl_0_tri_o[2]] set_property PACKAGE_PIN AD12 [get_ports gpio_rtl_0_tri_o[3]] set_property PACKAGE_PIN AE10 [get_ports gpio_rtl_0_tri_o[4]] set_property PACKAGE_PIN AF10 [get_ports gpio_rtl_1_tri_i[2]] set_property IOSTANDARD LVCMOS33 [get_ports PWM_0] set_property IOSTANDARD LVCMOS33 [get_ports PWM_1] set_property IOSTANDARD LVCMOS33 [get_ports PWM_2] set_property IOSTANDARD LVCMOS33 [get_ports PWM_3] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_0_tri_o[0]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_0_tri_o[1]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_0_tri_o[2]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_0_tri_o[3]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_0_tri_o[4]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_0_tri_o[5]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_0_tri_o[6]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_1_tri_i[0]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_1_tri_i[1]] set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_1_tri_i[2]] |
Generate Bitstreamから合成します。エラーなければ作成できます。
DPUやGPIO,PWM含めた、最終的なFPGAの使用率(Utilization)は下記になりました。
KR260+PYNQで作ったDPUを動かす
KR260のPYNQ上で動かすには、下記3つのファイル「.bit」「.xclbin」「.hwh」が必要です。
Vitisで作成したプロジェクト(***_hw_link)内にあります。
KR260に送れるようにコピーとリネームをします。筆者は「dpu.***」の名前にしました。
- dpu.bit → ~/***_hw_link/Hardware/dpu.build/link/vivado/vpl/prj/prj.runs/impl_1
- dpu.xclbin → ~/***_hw_link/Hardware
- dpu.hwh → ~/***_hw_link/Hardware/dpu.build/link/vivado/vpl/prj/prj.gen/sources_1/bd/design_1/hw_handoff
KR260に必要なファイルを転送します。
1 2 3 4 |
scp -r pynq-original-dpu-model ubuntu@192.168.11.7:/home/ubuntu/ dpu.bit 100% 7615KB 6.9MB/s 00:01 dpu.hwh 100% 886KB 64.8MB/s 00:00 dpu.xclbin 100% 7667KB 67.2MB/s 00:00 |
参考までにKR260でのデフォルトの「dpu.bit」「dpu.xclbin」「dpu.hwh」は下記にあります。
/usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq_dpu/
ファイル場所を指定しないと、dpuoverlayで下記場所のものが使われます。
1 2 3 4 |
root@kria:/home/ubuntu# cd /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq_dpu/ root@kria:/usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq_dpu# ls dpu.bit dpu.hwh dpu.py dpu.xclbin.link notebooks old_dpu.hwh __pycache__ dpu.bit.link dpu.hwh.link dpu.xclbin __init__.py old_dpu.bit old_dpu.xclbin tests |
Yoloで物体検出をして、GPIO・PWMでLチカする
あとはプログラムを実行するのみです。
実行したJupyter notebookは下記GitHubに置いています。
PYNQ-DPUのサンプルプログラムを修正したものです。
メインに変更した箇所としては下記です。
最初にDpuoverlayで読み込んだものを、普通のoverlayにも適用しています。
1 2 3 4 5 6 |
from pynq_dpu import DpuOverlay overlay = DpuOverlay("/root/jupyter_notebooks/pynq-original-dpu-model/dpu.bit") from pynq import Overlay from pynq.lib import AxiGPIO ol = overlay |
指定の物体(Bus、Ball)が検出できれば、特定のGPIO・PWMを出力させています
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# Decode output from YOLOv3 boxes, scores, classes = evaluate(yolo_outputs, image_size, class_names, anchors) if display: _ = draw_boxes(input_image, boxes, scores, classes) print("Number of detected objects: {}".format(len(boxes))) print("Scores: {}".format(scores)) print("Details of detected objects: {}".format(classes)) # Bus(5)-Red_led(GPIO+PWM) if 5 in classes: gpio_out.write(0x10,mask) set_motor_A_pwm(10, 'reverse') time.sleep(3) set_motor_A_pwm(50, 'reverse') time.sleep(3) set_motor_A_pwm(99, 'reverse') time.sleep(3) set_motor_A_pwm(0, 'coast') gpio_out.write(0x0,mask) # Sports ball(32)-Green_led(GPIO+PWM) if 32 in classes: gpio_out.write(0x9,mask) set_motor_B_pwm(10, 'forward') time.sleep(3) set_motor_B_pwm(50, 'forward') time.sleep(3) set_motor_B_pwm(99, 'forward') time.sleep(3) set_motor_B_pwm(0, 'coast') gpio_out.write(0x0,mask) |
冒頭でも紹介しましたが、実際のテスト動画は下記です。
Vitis AIの物体検出(YOLOv3)をしています。
DPUと一緒にGPIO(PWM)も一緒に動かすまでの流れを紹介します。
参照先
・KV260にVitis AIを組み込む(AIEDGEコンテスト対応版)
https://qiita.com/basaro_k/items/dc439ffbc3ea3aed5eb2
毎回KV260/KR260にVitis AIを組み込むときに見させていただいてます。
・KV260向けにVitisプラットフォームを作成してDPUを動かす その1 (Vitis 2022.1 + Vitis-AI v2.5)
https://qiita.com/lp6m/items/df1b87b11f8275ee6210
同じくDPU触るとき、毎回見させていただいています。
まとめ
KR260用にDPUとGPIOとPWMのIPを合成しました。
物体検出(Object Detect)する中で、GPIO,PWMも出力出来ました。
今回のKR260で実施した内容は、下記記事で紹介したテストの一部です。
コメント