Ultra96でTVMのVTAを動かしてみる 

一時的なメモです。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を自分でビルドする](https://qiita.com/okuhata/items/44b279d266191273885d)」に沿ってUltra96-PYNQのimage_v2.3をビルドします。文中のv2.4をv2.3に読み替えて下さい。途中、sensors96b.bspの作成が必要です。[image_v2.3のREADME.md](https://github.com/Avnet/Ultra96-PYNQ/blob/image_v2.3/README.md)の[Ultra96 BSPの作成方法](https://github.com/Avnet/Ultra96-PYNQ/blob/image_v2.3/README.md#note-if-you-already-have-a-pre-made-ultra96-bsp-that-meets-the-requirements-you-can-skip-creating-one-and-skip-to-the-next-note)の通り実行します。

一通りビルドが完了したら、BOOT.BINを作り直します。Ultra96用のVTAは、AXI HPCでキャッシュコヒーレンシ転送をする前提になっており、Broadcasting Inner Shareableの設定をブート時にする必要があります。次の`regs.init`ファイルを作成し、`PYNQ/sdbuild/Makefile`を変更します。

```test:regs.init
.set. 0xFF41A040 = 0x3;
```

```diff:PYNQ/sdbuild/Makefile
 $$(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を更新します。

```sh
$ make output/boot/Ultra96/BOOT.BIN BOARDDIR=/path/to/Ultra96-PYNQ
```

できたBOOT.BINをSDカードに上書きし、Ultra96を起動します。

参考：[Outer Share を Inner Share にする裏技？](https://qiita.com/ikwzm/items/82f98bfa76922073e610#outer-share-%E3%82%92-inner-share-%E3%81%AB%E3%81%99%E3%82%8B%E8%A3%8F%E6%8A%80)

# Ultra96用TVMの取得

[開発者のThierry Moreauさんがforkしているtvmのリポジトリ](https://github.com/tmoreau89/tvm/tree/vta-dev)をホストPCにcloneします。

```sh
$ git clone --recursive https://github.com/tmoreau89/tvm.git
```

ブランチ`vta_dev`をチェックアウトし、ビルド用のディレクトリに`config.cmake`をコピーします。

```sh
$ git checkout vta_dev
$ mkdir build
$ cp cmake/config.cmake build
```

`build/config.cmake`中の`set(USE_LLVM OFF)`を`set(USE_LLVM ON)`に変更します。

```diff:build/config.cmake
 # - 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をインストールします。

```sh
sudo apt install llvm-4.0
```

ビルドします。

```sh
$ cd build
$ cmake ..
$ make -j4
```

Pythonスクリプトの実行に必要なライブラリをインストールします。

```sh
$ pip install --user numpy decorator attrs
```

# Ultra96用VTAのbitstream作成

```sh
$ cp vta/config/ultra96_sample.json vta_config.json
```

として、Ultra96用の設定を使います。このconfigではVivado HLSのシミュレーションが通りませんので、`vta/hardware/xilinx/Makefile`を次のように変更してsimをスキップします。

```diff:vta/hardware/xilinx/Makefile
 HSI = hsi
 
 # HLS mode
-MODE = all
+MODE = skip_sim
 # Debug flag
 DEBUG = False
 # SLURM
```

bitstreamを作成します。Xilinxのツールにパスを通しておきます。

```sh
$ 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](https://docs.tvm.ai/vta/install.html)の通りです。

```sh:ホストPC
$ 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
```

```sh:Ultra96
% 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サーバを起動します。

```sh:Ultra96
% 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`の中でビットストリームを直接指定します。

```diff: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()
```

環境変数を設定します。

```sh:ホストPC
$ 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
```

実行します。

```sh:ホストPC
$ python <tvm root>/vta/tests/python/pynq/test_program_rpc.py
```

準備完了です。2D convolutionのテストベンチを実行します。

```sh:ホストPC
$ 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
```