ZynqMP で Linux Kernel を 4.19 にしたら PL からのメモリアクセスが遅くなった件


はじめに
--------------------------------------------------

筆者は次の記事で紹介したような Xilinx 製 SoC(通称 ZynqMP) を搭載した評価ボードに Debian/Linux をビルドして公開しています。

* [「UltraZed/Ultra96/Ultra96-V2 向け Debian GNU/Linux (v2019.1版) ブートイメージの提供」@Qiita](https://qiita.com/ikwzm/items/0c6f110aa19e368af03d)
* [「Ultra96 向け Debian GNU/Linux (v2018.2版) ブートイメージの提供」@Qiita](https://qiita.com/ikwzm/items/975ab6997905700dd2e0)

あるアプリケーション(PL回路を含む)を実行したところ、v2019.1版は v2018.2版よりも倍近く処理時間がかかってしまいました。PL 側の回路と動作週波数が同じであるにもかかわらず OS をかえただけでこんなに性能がおちるのは異常です。

そこで Vivado の Integrated Logic Analyzer（ILA）を使って ZynqMP の PS-PL AXI I/F の波形を確認してみました。

この記事では、その波形を紹介します。あくまでも今回の記事は波形の紹介だけであって、原因はまだわかっていません。

症状
--------------------------------------------------

## 比較対象

次の二つの ZynqMP-FPGA-Linux で比較しました。

* [ZynqMP-FPGA-Linux v2019.1.0](https://github.com/ikwzm/ZynqMP-FPGA-Linux)
  - ATF: arm-trusted-firmware (xilinx-v2019.1)
  - U-Boot: u-boot-xlinx (xilinx-v2019.1)
  - Kernel: Linux Kernel 4.19.0 (linux-xlnx v2019.1)
  - Root File System: Debian10 (buster)
* [ZynqMP-FPGA-Linux v2018.2.0](https://github.com/ikwzm/ZynqMP-FPGA-Linux)
  - ATF: arm-trusted-firmware (xilinx-v2018.2)
  - U-Boot: u-boot-xlinx (xilinx-v2018.2)
  - Kernel: Linux Kernel 4.14.0 (linux-xlnx v2018.2)
  - Root File System: Debian9 (stretch)

## 比較環境

ZynqMP-FPGA-Linux 以外の環境は次のとおりです。

* ハードウェア: Ultra96
* アプリケーション: [QCONV-STRIP-Ultra96](https://github.com/ikwzm/QCONV-STRIP-Ultra96)
* Vivado: v2018.3

## 比較結果

ZynqMP-FPGA-Linux v2018.2 でアプリケーションを走らせると 694 [usec] でした。

```console
fpga@debian-fpga:~/examples/QCONV-STRIP-Ultra96$ rake test_2_000
./unit_test -iw 160 -ih 160 -ic 64 -oc 32 -kw 1 -kh 1 -th 1 random
FPGA exec time (160x160x64x32 1x1): 694 [usec]
[qconv_strip] test success!!!
```

ところが、ZynqMP-FPGA-Linux v2019.1 でアプリケーションを走らせると 1408 [usec] になってしまいました。

```console
fpga@debian-fpga:~/examples/QCONV-STRIP-Ultra96$ rake test_2_000
./unit_test -iw 160 -ih 160 -ic 64 -oc 32 -kw 1 -kh 1 -th 1 random
FPGA exec time (160x160x64x32 1x1): 1408 [usec]
[qconv_strip] test success!!!
```

およそ倍近く処理時間がかかっています。

準備
--------------------------------------------------

## HPC について

対象のアプリケーションは HPC に接続することにより CPU とキャッシュのコヒーレンシの維持をハードウェアで行っています。HPC に関しては以下の記事を参考にしてください。

 * [「UltraZed 向け Debian GNU/Linux で AXI HPC port を使う (基礎編)」@Qiita](https://qiita.com/ikwzm/items/82f98bfa76922073e610)
 * [「UltraZed 向け Debian GNU/Linux で AXI HPC port を使う (実践編1)」@Qiita](https://qiita.com/ikwzm/items/79170301e215b21b14aa)

## Vivado Integrated Logic Analyzer（ILA） の設定

ILA の Debug 対象信号を ZynqMP の S_AXI_HPC0_FPD にします。

![Fig.1 ILA の設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/cafed096-5be0-8aaa-8acd-d66038e3d37c.png "Fig.1 ILA の設定")

Fig.1 ILA の設定

System ILA の 設定で Sample Data Depth を 1024 から 8192 に変更します。

トリガーを、ある物理アドレス(0x7010_6XXX)へのリードアクセスに設定します。

## Linux Kernel の bootargs に cpuidle.off=1 を追加

Linux Kernel で動作するアプリケーションを ILA でデバッグするためには、Linux Kernel の起動パラメータに cpuidle.off=1 を追加しておく必要があります。この設定をしておかないと、ILA の Open Target で Ultra96 に JTAG 接続した時、Linux Kernel が誤動作(Panic や フリーズ)します。

ZynqMP-FPGA-Linux では uEnv.txt の bootargs に "cpuidle.off=1" を追加しておきます。

```text:uEnv.txt
linux_kernel_image=image-4.19.0-xlnx-v2019.1-zynqmp-fpga
linux_fdt_image=devicetree-4.19.0-xlnx-v2019.1-zynqmp-fpga-ultra96.dtb
linux_boot_args=console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait uio_pdrv_genirq.of_id=generic-uio cpuidle.off=1

linux_img_load_cmd=fatload mmc ${sdbootdev} ${kernel_addr} ${linux_kernel_image}
linux_fdt_load_cmd=fatload mmc ${sdbootdev} ${fdt_addr}    ${linux_fdt_image}
linux_load_cmd=run linux_img_load_cmd && run linux_fdt_load_cmd
linux_boot_cmd=setenv bootargs ${linux_boot_args} && booti ${kernel_addr} - ${fdt_addr}

uenvcmd=run linux_load_cmd && run linux_boot_cmd

bootmenu_0=Boot Default=boot
```

波形の観測結果
--------------------------------------------------

## ZynqMP-FPGA-Linux v2018.2 での波形

![Fig.2 ZynqMP-FPGA-Linux v2018.2 での波形](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/aeb29fd8-8a6f-8603-3d73-9b674ba42af9.png "Fig.2 ZynqMP-FPGA-Linux v2018.2 での波形")


## ZynqMP-FPGA-Linux v2019.1 での波形

![Fig.3 ZynqMP-FPGA-Linux v2019.1 での波形](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/ce30c281-1616-7e7c-4f1a-57ecde4ad466.png "Fig.3 ZynqMP-FPGA-Linux v2019.1 での波形")


考察
--------------------------------------------------

v2019.1 での波形で明かにおかしいと思われる点は、下図で示すように、リードアクセスとライトアクセスが同時に発生した時、ライトデータ転送中はリードデータが転送されていない点です。下図の赤枠で示したように、ライトデータ転送中(WVALID=1 and WREADY=1)は、PS からリードデータが出力されていません(RVALID=0になっている)。しかもライトデータ転送終了後も、しばらくリードデータが出力されていません。そのため、このリードアクセスは 3531 クロックもかかってしまっています。

![Fig.4 ZynqMP-FPGA-Linux v2019.1 でのリードアクセス波形](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/d18de806-149f-93f0-cbd7-9422c56e5a08.png "Fig.4 ZynqMP-FPGA-Linux v2019.1 でのリードアクセス波形")

一方 v2018.2 では、同じリードアクセスの波形をみると、ライトデータ転送とリードデータ転送が並列に実行されているため、このリードアクセスは 546 クロックで終っています。

![Fig.5 ZynqMP-FPGA-Linux v2018.2 でのリードアクセス波形](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/6030a4c0-9f3d-d46f-8ddc-55b4edf0561e.png "Fig.5 ZynqMP-FPGA-Linux v2018.2 でのリードアクセス波形")

追記(2018年9月4日)
--------------------------------------------------

Linux Kernel を 4.14.0(linux-xlnx v2018.2) にしたまま boot.bin を v2019.1 でビルドしたものに変更して試してみたところ、性能は落ちませんでした。ということは FSBL(First Stage Boot Loader), PMU_FW(Platform Management Unit Firmware), ATF(ARM Trusted Firmware) は関係無いようです。ZynqMP の interconnect 関連(CCI400等)のレジスタは ATF か PMU_FW しかアクセス出来無いので、この設定が違うことは無さそうです。

追記(2018年9月7日)
--------------------------------------------------

PL 側の回路でリードとライトの AXI ID が異なるように変更して試してみたが、結果は変らず遲いままでした。


追記(2018年9月7日)
--------------------------------------------------

boot.bin を outer shareable しないものに変更したら、速くなりました。outer shareable に関しては次の記事を参考にしてください。

 * [「UltraZed 向け Debian GNU/Linux で AXI HPC port を使う (基礎編)」@Qiita](https://qiita.com/ikwzm/items/82f98bfa76922073e610)
 * [「UltraZed 向け Debian GNU/Linux で AXI HPC port を使う (実践編1)」@Qiita](https://qiita.com/ikwzm/items/79170301e215b21b14aa)


追記(2018年9月9日)
--------------------------------------------------

PL 側からのアクセスの前に手動でキャッシュをフラッシュしたら速くなりました。ただし、キャッシュのフラッシュ時間を含めるとトータルで速くなるかは微妙です。この例では、キャッシュを手動でフラッシュした方がトータルで 1008 [usec] となり、ハードウェアでキャッシュコヒーレンシを維持した場合(1408 [usec])よりも速くなりました。

```console
fpga@debian-fpga:~/examples/QCONV-STRIP-Ultra96$ ./unit_test -iw 160 -ih 160 -ic 64 -oc 32 -kw 1 -kh 1 --sync random
Cache sync for device time : 432 [usec]
Cache sync for cpu time : 0 [usec]
FPGA exec time (160x160x64x32 1x1): 576 [usec]
[qconv_strip] test success!!!
```

Fig.6 に 手動でキャッシュをフラッシュした場合のリードアクセス波形を示します。


![Fig.6 ZynqMP-FPGA-Linux v2019.1 + Cache Sync での波形](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/2f3ffd37-652f-c3a1-3d65-df747a5727b7.png "Fig.6 ZynqMP-FPGA-Linux v2019.1  + Cache Sync での波形")


この記事での説明はここまでですが、今後、時間をみつけて原因を究明していくつもりです。