高位合成によるFPGA回路設計を読んで、MNISTを試してみた


## はじめに
前々から、FPGAの高位合成や画像認識に興味はあったのですが、これはという書籍もなく、色々なサンプルに手を出しては、途中で挫折するということを繰り返していました。(主にpetalinuxのせいな気がする)
そんな中、これは！という書籍が発売されているのを知り、早速購入してみました。

https://www.morikita.co.jp/books/mid/078741

帯に書いてあった"高位合成の初歩から丁寧に学べる待望の入門書"という売り文句は間違ってなく、大変勉強になったんですが・・・、Chapter5のVitis AIでDPUを実装し、MNISTを実行する、というところで散々ハマったので、その過程を書き記してみます。
私が回路・基板設計がメインのハードウェアエンジニアで、たまにHDLもかじったことがありますという程度のスキルレベルで、Linuxをいじったり、C++でコーディングするハードルが高いということもあり、これらの罠回避に難航したということもあるかもしれません。
ただ、MNISTを実行するだけのために、これだけの労力を費やさないとならないというのは、こういうハードルや罠の多さが、今ひとつ普及しない要因になってるんじゃ・・・とかも思ったりします。(クラウドだったら、Google Colabでサッと試せるし)

## 実行環境

|項目|バージョン|
|------|-------------|
|OS    |Ubuntu 18.04 (Virtualbox)|
|ボード |Ultra96v2  |
|Vivado|2020.1|
|Vitis|2020.1|
|Petalinux|2020.1 (2019.2)|
|Vitis AI| 1.2|

## 罠1: Bootgenでエラー発生
5-3-2 DPU動作環境の構築で、VitisプラットフォームにDPUを組み込むところがあるんですが、森北出版さんから提供されているVitisカスタムプラットフォームを使っても、Bootgenでエラーが発生して、bootimageが生成できませんでした。エラーメッセージを見た感じ、各ファイルの格納場所の指定を変更する必要がありそう・・・というのは分かるんですが、どこでそれを変更するのかが分からん、と。

```
[WARNING]: [fsbl_config] a53_x64 | a53_x32 | r5_single | r5_dual is no more supported. Use 'destination_cpu' attribute for bootloader partition
[ERROR]  : Cannot read file - /home/tanaka/work/HLS_for_SW/Appendix/petalinux/boot/pmufw.elf
ERROR: [v++ 82-1008] Cannot generate bootimage

```
<br>
おそらくVitisプラットフォームの何処かで指定しているんだろうと思いつつも、見当がつかないので、Appendixを見ながら、Vitisプラットフォームを一から作ってみることにしました。で、一から作ってみたところ、bifファイルで指定することを把握。下記のように自分の環境に合わせ変更して、再ビルドすることで、Bootgen成功。

```linux.bif
the_ROM_image:
{
       [fsbl_config] a53_x64
       [bootloader] /home/madrid/ultra96v2_work/petalinux/boot/zynqmp_fsbl.elf
       [pmufw_image] /home/madrid/ultra96v2_work/petalinux/boot/pmufw.elf
       [destination_device=pl] <bitstream>
       [destination_cpu=a53-0, exception_level=el-3, trustzone] /home/madrid/ultra96v2_work/petalinux/boot/bl31.elf
       [destination_cpu=a53-0, exception_level=el-2] /home/madrid/ultra96v2_work/petalinux/boot/u-boot.elf
}
```

## 罠2: MNISTの学習でエラー発生
5-4-2 MNISTの学習で、Keras形式のMNIST学習モデルを生成するスクリプトを実施したところ、Illegal instruction(core dumped)エラーが発生しました。どうもTensorflowをimportするところでエラーが発生しているっぽいんだけど、ググってみたら、以下のような情報を発見。

https://ameblo.jp/akenji/entry-12388557689.html

<br>

確かに、うちのPCのCPU Core-i7 3770だから、、、ダメっぽいですね。というか、もうこんなんどうしようもないやん、、、はい、PCを新調して解決しました。

https://www.tensorflow.org/install/source?hl=ja
> 注: TensorFlow 1.6 以降、バイナリは AVX 命令を使用するので、古い CPU では動作しないことがあります。


## 罠3: Petalinuxのバージョン間違えた
実行環境のところに、2020.1 (2019.2)と書いたんですが、最初あまり考えず、元からインストールしてあった2019.2を使用していました。まあ、自分の思慮不足、不注意と言ってしまえば、それまでなんですが、ちょっとバージョンが違っただけでここまで挙動変わるのは・・・初心者には結構つらい。まさかPetalinuxのバージョンが問題になっているとは気づかず、様々なトラブルシューティングをする羽目に陥っていました。

### ライブラリ入ってない問題
DPUのバイナリ作成も完了し、VARTのサンプルプログラムと合わせて、Ultra96v2ボードにコピーしてしてみたところ、複数のライブラリが入ってないというエラーが発生。下記は無理やり適当なバイナリをscpでもってきてrpmでインストールしてみたが、バージョンが古く動かなかった時のログ。
```
root@ultra96v2_base:~/MNIST# ./MNIST dpu_mnist_0.elf 
./MNIST: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.26' not found (required by /usr/lib/libxir.so.1)
```

### dnf使えない問題
不足しているライブラリをインストールするために、dnfコマンドを使おうとしたが、リポジトリが登録されておらず使えない。どこのリポジトリをどうやって登録すればいいんだ？？とハマったあげく、Xilinxから提供されている？リポジトリを登録したら、何とかライブラリをインストールできるように。

http://petalinux.xilinx.com/sswreleases/rel-v2020.3/generic/rpm/aarch64/

```
root@ultra96v2_base:/etc/yum.repos.d# dnf -v repolist
DNF version: 2.7.5
cachedir: /var/cache/dnf/aarch64/thud
petalinux_v2020.3-generic                                                      1.9 MB/s | 3.6 MB     00:01    
not found deltainfo for: petalinux_v2020.3-generic
not found updateinfo for: petalinux_v2020.3-generic
Xilinx-v2020.3: using metadata from Fri Apr  2 22:24:00 2021.
Last metadata expiration check: 0:00:05 ago on Fri Mar 18 16:58:24 2022.

Repo-id      : Xilinx-v2020.3
Repo-name    : petalinux_v2020.3-generic
Repo-revision: 1617402240
Repo-updated : Fri Apr  2 22:24:00 2021
Repo-pkgs    : 10215
Repo-size    : 3.7 G
Repo-baseurl : http://petalinux.xilinx.com/sswreleases/rel-v2020.3/generic/rpm/aarch64/
Repo-expire  : 172800 second(s) (last: Fri Mar 18 16:58:24 2022)
Repo-filename: /etc/yum.repos.d/CentOS-Base.repo
Total packages: 10215
```

### DPUから応答がない問題
ようやくライブラリのインストールが終わって、VARTサンプルプログラムを実行したところ、execute_asyncした後、DPUから応答がない。エラーログでググっても、それらしい解決方法も見当たらないし、結構お手上げでした。ただ、この辺で、そもそもなんでライブラリのインストールが必要だったんだ？書籍ではそんな手順の記載はなかったし、どこかで何かの手順に問題があったんじゃ？と思い、改めて見直したところ、書籍ではpetalinux 2020.1を使用していることに気づく。もしかして、これが原因？？と思い、petalinux 2020.1を入れて、改めてVitisプラットフォームの作成を行うことにしました。

```
root@ultra96v2_base:~/MNIST# ./MNIST dpu_mnist_0.elf 
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0318 19:33:13.888545  2321 main.cc:195] create running for subgraph: mnist_0
out_dims : 1, 1, 1, 10
in_dims  : 1, 28, 28, 1
F0318 19:33:23.977075  2321 xrt_cu.cpp:165] Check failed: is_done cu timeout! core_idx 0  handle=0x55cd4e5760 ENV_PARAM(XLNX_DPU_TIMEOUT) 10000 state 1 ERT_CMD_STATE_COMPLETED 4 ms 10010  bo=4is_done 0 
*** Check failure stack trace: ***
Aborted
root@ultra96v2_base:~/MNIST#
```

## 罠4: U-Bootが正常に起動しない
petalinux 2020.1でVitisプラットフォームを再作成し、DPUを組み込むところまで終わって、ようやくUltra96v2に書き込んでみたところ、U-bootが正常に起動しませんでした。これだ！と考えた原因をつぶしこんでみたら、そもそもBootせず、状況がより悪化するという事態に、若干心が折れそうになったんですが・・・、全く同じ現象が発生し、解決方法まで書いてくれている方を発見(本当に助かりました)。
Bootパーティションに、boot.scrも追加でコピーしたら、正常に起動するようになりました。
- BOOT.BIN
- image.ub
- dpu.xclbin
- boot.scr <--これを追加

https://qiita.com/yoshiyasu1111/items/9518bad2d90f61a0a021

https://support.xilinx.com/s/question/0D52E00006hpMz8SAE/petalinux-20201-uboot-wrong-image-format?language=ja

```
Release 2020.1   Mar 25 2022  -  16:03:34
NOTICE:  ATF running on XCZU3EG/silicon v4/RTL5.1 at 0xfffea000
NOTICE:  BL31: v2.2(release):xilinx_rebase_v2.2_2020.1
NOTICE:  BL31: Built : 15:43:28, Mar 25 2022


U-Boot 2020.01 (Mar 25 2022 - 15:59:59 +0000)

Model: Avnet Ultra96 Rev1
Board: Xilinx ZynqMP
DRAM:  2 GiB
PMUFW:  v1.1
EL Level:       EL2
Chip ID:        zu3eg
NAND:  0 MiB
MMC:   mmc@ff160000: 0, mmc@ff170000: 1
In:    serial@ff010000
Out:   serial@ff010000
Err:   serial@ff010000
Bootmode: SD_MODE
Reset reason:   EXTERNAL
Net:   No ethernet found.
Hit any key to stop autoboot:  0
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
## Executing script at 20000000
Wrong image format for "source" command
SCRIPT FAILED: continuing...
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Card did not respond to voltage select!
Warning: SPI speed fallback to 100 kHz
unrecognized JEDEC id bytes: ff, ff, ff
Failed to initialize SPI flash at 0:0 (error -2)
SCRIPT FAILED: continuing...


no devices available
SCRIPT FAILED: continuing...
starting USB...
Bus dwc3@fe300000: Register 2000440 NbrPorts 2
Starting the controller
USB XHCI 1.00
scanning bus dwc3@fe300000 for devices... 4 USB Device(s) found
       scanning usb for storage devices... 0 Storage Device(s) found

Device 0: unknown device

Device 1: unknown device
scanning bus for devices...

Device 0: unknown device
No ethernet found.
missing environment variable: pxeuuid
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm-zynqmp-zynqmp
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm-zynqmp
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default
No ethernet found.
Config file not found
No ethernet found.
No ethernet found.
```

## MNISTの実行
罠4の回避後、MNISTが動作することをようやく確認できました。MNISTのサンプルを試してみるだけで、2～3週間かかったので、うまく動いたときの感動は、Google colabでのMNISTとは雲泥の差ですよ、良い意味でも悪い意味でも。
次は、YOLOv3を試してみようと思いますが、また新たな罠にハマらないことを願うばかりです。

```
root@ultra96v2_v2020_1:~/MNIST# ./MNIST dpu_mnist_0.elf 
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0325 16:33:50.311563   741 main.cc:203] create running for subgraph: mnist_0
out_dims : 1, 1, 1, 10
in_dims  : 1, 28, 28, 1
Image : one_9819.jpg, label = 1
Image : five_6491.jpg, label = 5
Image : eight_9150.jpg, label = 8
Image : nine_9386.jpg, label = 9
Image : six_8240.jpg, label = 6
Image : two_1256.jpg, label = 2
Image : three_9925.jpg, label = 3
Image : zero_9095.jpg, label = 0
Image : four_8500.jpg, label = 4
Image : seven_9262.jpg, label = 7
Elapsed Time per frame: 532[us]
root@ultra96v2_v2020_1:~/MNIST#
```