一時的なメモです。masterにmergeされてPYNQ v2.4に対応したりすると手順が変わると思います。
準備
ホストPCのOSはUbuntu 16.04です。XilinxのSDSoC 2018.2, PetaLinux 2018.2をインストールします。Ultra96にはPYNQのimage_v2.3を用います。
Ultra96-PYNQのビルド
「Ultra96-PYNQを自分でビルドする」に沿ってUltra96-PYNQのimage_v2.3をビルドします。文中のv2.4をv2.3に読み替えて下さい。途中、sensors96b.bspの作成が必要です。image_v2.3のREADME.mdのUltra96 BSPの作成方法の通り実行します。
一通りビルドが完了したら、BOOT.BINを作り直します。Ultra96用のVTAは、AXI HPCでキャッシュコヒーレンシ転送をする前提になっており、Broadcasting Inner Shareableの設定をブート時にする必要があります。次のregs.init
ファイルを作成し、PYNQ/sdbuild/Makefile
を変更します。
.set. 0xFF41A040 = 0x3;
$$(BOOT_ROOT_$1)/BOOT.BIN : $$(BOOT_DEPENDS_$1) $$(BOOT_BITSTREAM_$1) | $$(BOOT_ROOT_$1)
- cd $$(BOOT_ROOT_$1) && petalinux-package --boot --fpga $$(BITSTREAM_ABS_$1) --u-boot -p $$(PL_PROJ_$1) --force
+ cd $$(BOOT_ROOT_$1) && petalinux-package --boot --fpga $$(BITSTREAM_ABS_$1) --u-boot -p $$(PL_PROJ_$1) --force --bif-attribute init --bif-attribute-value /path/to/regs.init
cp -f $$(PL_PROJ_$1)/images/linux/BOOT.BIN $$(BOOT_ROOT_$1)
$$(BOOT_ROOT_$1)/image.ub : $$(BUILD_ROOT_$1)/image.its $$(BUILD_ROOT_$1)/system.dtb $$(BUILD_ROOT_$1)/$$(KERNEL_$$(ARCH_$1)) | $$(BOOT_ROOT_$1)
BOOT.BINを更新します。
$ make output/boot/Ultra96/BOOT.BIN BOARDDIR=/path/to/Ultra96-PYNQ
できたBOOT.BINをSDカードに上書きし、Ultra96を起動します。
参考:Outer Share を Inner Share にする裏技?
Ultra96用TVMの取得
開発者のThierry MoreauさんがforkしているtvmのリポジトリをホストPCにcloneします。
$ git clone --recursive https://github.com/tmoreau89/tvm.git
ブランチvta_dev
をチェックアウトし、ビルド用のディレクトリにconfig.cmake
をコピーします。
$ git checkout vta_dev
$ mkdir build
$ cp cmake/config.cmake build
build/config.cmake
中のset(USE_LLVM OFF)
をset(USE_LLVM ON)
に変更します。
# - ON: enable llvm with cmake's find search
# - OFF: disbale llvm
# - /path/to/llvm-config: enable specific LLVM when multiple llvm-dev is available.
-set(USE_LLVM OFF)
+set(USE_LLVM ON)
#---------------------------------------------
# Contrib libraries
LLVM 4.0をインストールします。
sudo apt install llvm-4.0
ビルドします。
$ cd build
$ cmake ..
$ make -j4
Pythonスクリプトの実行に必要なライブラリをインストールします。
$ pip install --user numpy decorator attrs
Ultra96用VTAのbitstream作成
$ cp vta/config/ultra96_sample.json vta_config.json
として、Ultra96用の設定を使います。このconfigではVivado HLSのシミュレーションが通りませんので、vta/hardware/xilinx/Makefile
を次のように変更してsimをスキップします。
HSI = hsi
# HLS mode
-MODE = all
+MODE = skip_sim
# Debug flag
DEBUG = False
# SLURM
bitstreamを作成します。Xilinxのツールにパスを通しておきます。
$ source /opt/sdsoc.2018.2/Vivado/2018.2/settings64.sh
$ cd vta/hardware/xilinx
$ make
bitstreamは、vta/build/hardware/xilinx/vivado/ultra96_1x16x16_a8w8o8s32_15_15_18_17_333MHz_2ns_gii1_aii2/export/vta.bit
にできます。
Ultra96実機でのコンパイル
Ultra96実機でruntimeをコンパイルします。VTA Installation Guideの通りです。
$ mkdir <mountpoint>
$ sshfs xilinx@192.168.3.1:/home/xilinx <mountpoint>
$ cd <mountpoint>
$ git clone --recursive https://github.com/tmoreau89/tvm.git
$ cd ~
$ sudo umount <mountpoint>
$ ssh xilinx@192.168.3.1
% cd /home/xilinx/tvm
% git checkout -f vta_dev
% mkdir build
% cp cmake/config.cmake build/
% cp vta/config/ultra96_sample.json build/vta_config.json
% cd build
% cmake ..
% make runtime vta -j2
RPCサーバを起動します。
% cd /home/xilinx/tvm
% sudo ./apps/pynq_rpc/start_rpc_server.sh # pw is 'xilinx'
INFO:RPCServer:bind to 0.0.0.0:9091
実行
ホストPCからbitstreamを転送します。vta/tests/python/pynq/test_program_rpc.py
の中でビットストリームを直接指定します。
-program_rpc_bitstream()
+program_rpc_bitstream("/path/to/tvm/vta/build/hardware/xilinx/vivado/ultra96_1x16x16_a8w8o8s32_15_15_18_17_333MHz_2ns_gii1_aii2/export/vta.bit")
reconfig_rpc_runtime()
環境変数を設定します。
$ export TVM_HOME=/path/to/tvm
$ export PYTHONPATH=$TVM_HOME/vta/python:$TVM_HOME/python:$TVM_HOME/topi/python:$TVM_HOME/nnvm/python
$ export VTA_ULTRA96_RPC_HOST=192.168.3.1
$ export VTA_ULTRA96_RPC_PORT=9091
実行します。
$ python <tvm root>/vta/tests/python/pynq/test_program_rpc.py
準備完了です。2D convolutionのテストベンチを実行します。
$ python <tvm root>/vta/tests/python/integration/test_benchmark_topi_conv2d.py
('resnet-18.C2', Conv2DWorkload(batch=1, height=56, width=56, in_filter=64, out_filter=64, hkernel=3, wkernel=3, hpad=1, wpad=1, hstride=1, wstride=1))
VTA TEST PASSED: Time cost = 0.00229248 sec/op, 100.856 GOPS
('resnet-18.C4', Conv2DWorkload(batch=1, height=56, width=56, in_filter=64, out_filter=128, hkernel=3, wkernel=3, hpad=1, wpad=1, hstride=2, wstride=2))
VTA TEST PASSED: Time cost = 0.00102993 sec/op, 112.246 GOPS
('resnet-18.C5', Conv2DWorkload(batch=1, height=56, width=56, in_filter=64, out_filter=128, hkernel=1, wkernel=1, hpad=0, wpad=0, hstride=2, wstride=2))
VTA TEST PASSED: Time cost = 0.000634823 sec/op, 20.2341 GOPS
('resnet-18.C6', Conv2DWorkload(batch=1, height=28, width=28, in_filter=128, out_filter=128, hkernel=3, wkernel=3, hpad=1, wpad=1, hstride=1, wstride=1))
VTA TEST PASSED: Time cost = 0.00198324 sec/op, 116.583 GOPS
('resnet-18.C7', Conv2DWorkload(batch=1, height=28, width=28, in_filter=128, out_filter=256, hkernel=3, wkernel=3, hpad=1, wpad=1, hstride=2, wstride=2))
VTA TEST PASSED: Time cost = 0.0011103 sec/op, 104.121 GOPS
('resnet-18.C8', Conv2DWorkload(batch=1, height=28, width=28, in_filter=128, out_filter=256, hkernel=1, wkernel=1, hpad=0, wpad=0, hstride=2, wstride=2))
VTA TEST PASSED: Time cost = 0.000512048 sec/op, 25.0856 GOPS
('resnet-18.C9', Conv2DWorkload(batch=1, height=14, width=14, in_filter=256, out_filter=256, hkernel=3, wkernel=3, hpad=1, wpad=1, hstride=1, wstride=1))
VTA TEST PASSED: Time cost = 0.0016426 sec/op, 140.759 GOPS
('resnet-18.C10', Conv2DWorkload(batch=1, height=14, width=14, in_filter=256, out_filter=512, hkernel=3, wkernel=3, hpad=1, wpad=1, hstride=2, wstride=2))
VTA TEST PASSED: Time cost = 0.00101667 sec/op, 113.71 GOPS
('resnet-18.C11', Conv2DWorkload(batch=1, height=14, width=14, in_filter=256, out_filter=512, hkernel=1, wkernel=1, hpad=0, wpad=0, hstride=2, wstride=2))
VTA TEST PASSED: Time cost = 0.000585341 sec/op, 21.9446 GOPS
('resnet-18.C12', Conv2DWorkload(batch=1, height=7, width=7, in_filter=512, out_filter=512, hkernel=3, wkernel=3, hpad=1, wpad=1, hstride=1, wstride=1))
VTA TEST PASSED: Time cost = 0.00174309 sec/op, 132.644 GOPS