DPU + vadd on Ultra96v2 (Vitis 2020.2)

以前の[Vitis-AI v1.4 on Ultra96v2](https://qiita.com/lp6m/items/4117a3bab185afedfd5f)ではAvnetが用意してくれているスクリプトを使用してVitisプラットフォームを作成し、XilinxのDPU-TRDプロジェクトを使用してDPUの動作環境を生成しました。

今回は前回記事の[Build Vitis platform for Ultra96v2 (Vitis 2020.2)](https://qiita.com/lp6m/items/a66765e733234499ca28)で作成したVitis Platformを使用して、DPUとvector addカーネルを動作させてみたいと思います。

以前の[Vitis-AI v1.4 on Ultra96v2](https://qiita.com/lp6m/items/4117a3bab185afedfd5f)記事では[Xilinxが公開しているDPU-TRD](https://github.com/Xilinx/Vitis-AI/blob/v1.4/dsa/DPU-TRD/prj/Vitis/README.md)のCUIフローをベースにDPUが動作する環境を構築しましたが、HWシステムの構築ではvitisに含まれるv++を直接実行しており、GUIで開けるVitisのプロジェクトは作成されていませんでした。CUIベースで新しくカーネルを追加したり、HWシステムの設定を変更するのは難しいと感じたため、GUIフローで作業を行なってみようと思います。
本記事は[XilinxのDPU-TRD GUI Flow](https://github.com/Xilinx/Vitis-AI/blob/v1.4/dsa/DPU-TRD/prj/Vitis/README.md#6-gui-flow)をベースにしています。DPUを追加するところは全く同じです。

また、DPUと他のHWカーネルを同時に実装することができるかも同時に試します。今回追加するカーネルはvadd(vector add)で、Vitis HLS向けの高位合成チュートリアルでも使用されている単純なカーネルです。

# Environment
- Host: Ubuntu 18.04
- Target: Ultra96v2
- Vitis/Vivado/Petalinux 2020.2

Vitis-AIをダウンロードしておきます。DPU IPやDPUを使用するVitisライブラリ、DPUを実行するためのランタイムが含まれています。

```sh
git clone https://github.com/Xilinx/Vitis-AI
cd Vitis-AI
git checkout refs/tags/v1.4
```

[前回記事](https://qiita.com/lp6m/items/a66765e733234499ca28)のUltra96向けVitisプラットフォームが作成されていることを前提にしています。

# Vitisプロジェクト

## プロジェクトの作成
Vitisを起動します。

```sh
mkdir dpu_vitis_prj && cd dpu_vitis_prj
source /tools/Xilinx/Vitis/2020.2/settings64.sh
vitis
```
Window->Preferences->Library Repositoriesに、Vitis-AIリポジトリのの`dsa/DPU-TRD`ディレクトリを追加します。
![Screenshot from 2021-12-06 02-58-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/7872807a-040b-12d9-0a78-f2d8c240e076.png)


`File->New->Application Project`からプロジェクトを作成します。ベースとなるプラットフォームは前回記事で作成したプラットフォームを選択します。
![Screenshot from 2021-12-06 03-01-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/d9c30aff-ead4-0265-6615-4ea86527ffbf.png)
テンプレートの選択画面では`DPU Kernel`を選択します。
![Screenshot from 2021-12-06 03-04-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/39b89326-173d-ab6e-711f-94e33bdb47bd.png)
プロジェクトが作成されました。
![Screenshot from 2021-12-06 03-06-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/ec3de0bd-b987-9d8c-07d7-5bb0c45bdfcf.png)

## DPUコンフィグレーションの変更
Explorerウインドウのdpu_trd_kernelsからsrc->prj->Vitis->dpu_conf.vhを開いてDPUのコンフィグレーションを変更します。
![142996031-ba03f57e-e2ae-45c7-8b2f-ea7a562c965c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/9da0b6cf-f4e7-8b15-677f-63b99029c70d.png)

```diff
26c26
< `define B1600
---
> `define B4096 
81c81
< `define RAM_USAGE_HIGH
---
> `define RAM_USAGE_LOW
103c103
< `define DWCV_DISABLE
---
> `define DWCV_ENABLE
114c114
< `define POOL_AVG_DISABLE
---
> `define POOL_AVG_ENABLE
```
## DPUのクロックとポートの設定
次に`dpu_trd->src->config_gui.cfg`を開いてDPUに接続するクロックとポートの設定を変更します。今回ベースにしているプラットフォームプロジェクトのクロックは以下の通りで、150MHz/300MHzを利用したいので以下のように設定しました。また今回はDPUを1つだけ搭載するので元のポート情報の後半を削除しました。

![142999491-8d652cf3-aead-4a7e-9b60-0b2aa3894e6c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/ed7dd457-0451-c395-f90e-b374879747aa.png)
![142999448-0bde8e51-4397-4cce-af2b-9b0966273f3f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/1a8ac6c5-98a4-3293-3b51-84ed8421c59a.png)

**【2022.03.16追記】**  
**上記手法ではDPUのクロック指定が正しくできていません。**  
[Vitis-AIのチュートリアル](https://github.com/Xilinx/Vitis-AI/blob/1.4/dsa/DPU-TRD/prj/Vitis/README.md)には以下のように書かれています。
>  The gui flow only support default configuration. if want to other configuration, please use commandline flow.

GUIFlowで一度ビルドすると、`dpu_trd_hw_link/Hardware/dpu-link.cfg`というファイルが生成されます。このファイルの`[clock], [connectivity]`を変更します。

```
[connectivity]
nk=DPUCZDX8G:1:DPUCZDX8G_1

[clock]
id=1:DPUCZDX8G_1.aclk
id=3:DPUCZDX8G_1.ap_clk_2
```
これでコマンドラインから`make`を実行し直すことでクロックが正しく設定されます。


## Vector Addの追加
次に、独自カーネルのテストとしてvadd(vector_add)を追加するための作業を行います。
まず、File->New->Application Projectでアプリケーションプロジェクトを作成。名前はvaddとしました。このとき`Select a system project`に`dpu_trd_system`を選択することでDPUプロジェクトにvaddのカーネルとアプリケーションを追加できます。
![Screenshot from 2021-12-06 09-29-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/f3cd1f71-c8b0-2bb0-2fbb-6818f969cf05.png)
アプリケーションテンプレートは`Vector Add`を選択します。
![Screenshot from 2021-12-06 09-29-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/bdfe3d37-9a15-c3bd-f38b-5c98b281dc62.png)
`dpu_trd_system`というsystem projectの中に`dpu, vadd`の２つのカーネル・2つのアプリケーションが存在する状態が確認できます。
![Screenshot from 2021-12-06 09-29-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/5c079b0c-18c7-bd6b-29d6-379bf36a093a.png)

## Hardware Link Configの変更

最後にdpu_trd_system_hw_link.prjを開く。どのアクセラレーションカーネルをいくつ搭載するかの画面がある。
Active build configurationをHardwareに変更。
今回はsoftmaxコアは不要なのでsfm_xrt_topは削除、DPUCZDX8Gの個数を2個から1個に変更、さらに右上のイナズマアイコンのAdd Hardware Functionsからkrnl_vaddを追加した。
![Screenshot from 2021-12-06 09-34-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/ff392c54-81ec-6a16-8de7-c3975bf834b8.png)

## Vitisプロジェクトのビルド
`Explorer`から`dpu_trd_system`を選択し、全てのビルドを実行します。高位合成、Vivadoプロジェクトの自動作成、Vivado上での論理合成、配置配線、ビットストリーム生成、sysroot環境を使用したアプリケーションのクロスコンパイル等が全て自動的に実行されます。
![Screenshot from 2021-12-06 09-38-38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/5f82fe2a-63d5-abca-cda3-9e15a0a09805.png)

生成されたVivadoブロックデザインを開いてみます。`./dpu_trd_system_hw_link/Hardware/dpu.build/link/vivado/vpl/prj/prj.xpr`が生成されたVivadoプロジェクトです。先程設定したとおり、DPUとvaddコアが1つずつ搭載されているのが確認できます。
![Screenshot from 2021-12-06 09-40-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/9721192b-27e2-1add-1582-81132328e328.png)
**【2022.03.16追記】**  
**ここではDPUのクロック指定が正しくできていません。** DPUに100/200MHzのクロックが設定されています。

ちなみに、dpuのアプリケーションのビルドは失敗してしまいました。DPUカーネルを作成するときにテンプレートから自動生成されたresnest50モデルを動作させるアプリケーションです。
![Screenshot from 2021-12-06 09-42-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/e0ffc78e-d4e3-2677-651b-68dad580bcd5.png)

おそらく前回記事でやったように[sysrootへのVARTインストール](https://qiita.com/lp6m/items/4117a3bab185afedfd5f#compile-the-ai-applications)をVitisのプラットフォームを作成する前に行なっておく必要があるのだと思います。ただ、現時点ではVitisを使用しなくてもCUI上でsysrootを用いたクロスコンパイルが可能なので無視しています。Vitis上でもコンパイルしたいので、解決すれば追記します。

# Create SD Card
Vitisにより生成されたSDカードイメージをSDに書き込みます。

```sh
find ./ | grep sd_card
./dpu_trd_system/Hardware/package/sd_card.img
./dpu_trd_system/Hardware/package/sd_card
./dpu_trd_system/Hardware/package/sd_card/boot.scr
./dpu_trd_system/Hardware/package/sd_card/dpu_trd
./dpu_trd_system/Hardware/package/sd_card/image.ub
./dpu_trd_system/Hardware/package/sd_card/vadd
./dpu_trd_system/Hardware/package/sd_card/system.dtb
./dpu_trd_system/Hardware/package/sd_card/BOOT.BIN
./dpu_trd_system/Hardware/package/sd_card/bl31.elf
./dpu_trd_system/Hardware/package/sd_card/pmufw.elf
./dpu_trd_system/Hardware/package/sd_card/u-boot.elf
./dpu_trd_system/Hardware/package/sd_card/fsbl.elf
./dpu_trd_system/Hardware/package/sd_card/dpu.xclbin
```
本当ならば`sd_card.img`をSDに書き込むだけでブートに必要なファイルやrootfsが全て書き込まれるはずなのですが。。なぜかそれだけだとブートできなかったので、以下の方法でSDカードを作成しました。

```
cd ./dpu_trd_system/Hardware/package/
cp BOOT.BIN /media/lp6m/BOOT/
cp image.ub /media/lp6m/BOOT/
cp boot.scr /media/lp6m/BOOT/
```

rootfsはSWプラットフォームを作成したときの`rootfs.tar.gz`を使用しました。

```sh
sudo tar xvf rootfs.tar.gz -C /media/lp6m/rootfs
```

アプリケーションとFPGAコンフィグレーションファイル、VART(Vitis-AI runtime)などをホームディレクトリにコピーします。

```sh
# vadd application
sudo cp vadd /media/lp6m/rootfs/home/root
# FPGA configuration file
sudo cp dpu.xclbin /media/lp6m/rootfs/home/root
# VART
cd <Vitis-AI repository dir>
sudo cp -r ./setup/mpsoc/VART /media/lp6m/rootfs/home/root
sync
```
以上でSDカードの作成は完了です。

# 動作確認
SDカードをUltra96に挿入、キーボード、USB-LAN、DP-HDMIアダプタ、電源つないで電源投入。実機の端末から`ifconfig`でIPアドレスを確認します。ユーザ名root, パスワードrootでssh接続が可能です。以降は実機上にssh接続して作業を行います。

まずVARTをインストールします。

```sh
root@u96v2-sbc-base-2020-2:~# cd VART/
root@u96v2-sbc-base-2020-2:~/VART# ./target_vart_setup_2020.2.sh 
Verifying...                          ################################# [100%]
Preparing...                          ################################# [100%]
%prein(libunilog-1.4.0-r56.aarch64): scriptlet start
%prein(libunilog-1.4.0-r56.aarch64): execv(/bin/sh) pid 1347
%prein(libunilog-1.4.0-r56.aarch64): waitpid(1347) rc 1347 status 0
Updating / installing...
   1:libunilog-1.4.0-r56              ################################# [100%]
%post(libunilog-1.4.0-r56.aarch64): scriptlet start
...
```

DPUの情報確認

```sh
root@u96v2-sbc-base-2020-2:~/VART# cd /home/root
root@u96v2-sbc-base-2020-2:~# cp dpu.xclbin /usr/lib/
root@u96v2-sbc-base-2020-2:~# dexplorer -w
[DPU IP Spec]
IP  Timestamp            : 2021-06-07 19:15:00
DPU Core Count           : 1

[DPU Core Configuration List]
DPU Core                 : #0
DPU Enabled              : Yes
DPU Arch                 : B1600
DPU Target Version       : v1.4.1
DPU Freqency             : 300 MHz
Ram Usage                : High
DepthwiseConv            : Disabled
DepthwiseConv+Relu6      : Disabled
Conv+Leakyrelu           : Enabled
Conv+Relu6               : Enabled
Channel Augmentation     : Enabled
Average Pool             : Disabled
```

ちゃんとコンフィグしたとおりになっています。

vaddの動作確認

```sh
root@u96v2-sbc-base-2020-2:~# cd /home/root
root@u96v2-sbc-base-2020-2:~# ./vadd dpu.xclbin
Loading: 'dpu.xclbin'
TEST PASSED
```
vaddも動作しています！

# まとめ
自分で作成したVitisプラットフォームを使用して、DPUと自分のカーネル(vadd)を搭載したシステムを構築することができました。
前回のVItisプラットフォームの作成（特にPetalinuxでwifiを使うための設定）で非常に時間がかかってしまいましたが、一度プラットフォームを作ってしまえばVitis上でシステムを構築するのはとても簡単に感じました。

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="und" dir="ltr"><a href="https://t.co/JIIPGWAw6b">pic.twitter.com/JIIPGWAw6b</a></p>&mdash; lp6m (@lp6m1) <a href="https://twitter.com/lp6m1/status/1467165811513360388?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>