UltraZed 向け Debian GNU/Linux で AXI HPC port を使う (実践編1)

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

UltraZed に載っている Zynq UltraScale+ MPSoC は PL(Programmable Logic) から PS(Processing System) へのアクセスを行う AXI Port が複数あります。そのうち HPC0 と HPC1 はハードウェアで APU のキャッシュをスヌープすることによりコヒーレンシを維持して転送することが出来ます。しかし、ハードウェアは用意されていても実際に使うとなるとけっこうハードルが高いです。

この記事では UltraZed 向け Debian GNU/Linux で AXI HPC Port を使ってキャッシュコヒーレンシ転送する方法を説明します。

また次の記事で AXI HPC port によるキャッシュコヒーレンシ転送の基礎知識を説明しているので参照してください。

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

キャッシュコヒーレンシ転送の基本
-------------------------------------------------------------

Zynq UltraScale+ MPSoC でキャッシュコヒーレンシ転送を行うにはいくつかの条件が必要です。Xilinx の Wiki に詳しい説明があります。次の URL を参照してください。

* [Zynq UltraScale MPSoC Cache Coherency](http://www.wiki.xilinx.com/Zynq+UltraScale+MPSoC+Cache+Coherency)


上記の説明によれば、Linux 上で HPC を使ってキャッシュコヒーレンシ転送を行うには次の条件が必要だそうです。

1 AXI HPC0/HPC1 を使う。
2 AxCACHE 信号の上位2ビットを非ゼロにする。
3 AxPROT 信号を正しく設定する。
4 MPSoC の内部レジスタを変更して内部共有可能トランザクションをブロードキャストできるようにする。
5 コヒーレントなデバイスが dma-coherent プロパティを持つようにする。

Sample FPGA Design
-------------------------------------------------------------

### リポジトリ

この記事で使うデザインは以下の URL にあります。

* https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-3-UltraZed

このリポジトリには次のようなファイルが含まれています。

* negative2.bin     - FPGAのビットストリームファイル
* fclk0-zynqmp.dts  - PL Clock 0 設定用 device-tree source
* fpga-load.dts     - FPGA ビットストリームロード用 device-tree source
* negative2.dts     - uio/udmabuf 設定用 device-tree source
* negative.c        - negative の Vivado-HLS C source
* negative.py       - negative テスト用 Python コード(sync_to_cpu/sync_to_device あり)
* negative2.py      - negative テスト用 Python コード(sync_to_cpu/sync_to_device なし)
* udmabuf.py        - udmabuf 用 Python ライブラリ
* uio.py            - uio 用 Python ライブラリ
* boot/             
  - boot.bif        - boot.bin 生成用 BIF ファイル
  - boot.bin        - boot.bin (regs.init 入り)
  - regs.init       - MPSoC 内部レジスタ設定ファイル

### デザインの説明

ここでは[「UltraZed 向け Debian GNU/Linux (v2018.2版) で Vivado-HLS を使って合成した回路を動かす」](https://qiita.com/ikwzm/items/725ef2c4306e7bf021f0) で紹介したデザインを変更して使っています。具体的には次のように変更しています。

#### HPC に接続

デザインのブロック図は次のようになっています。

![Fig.1 Diagram](https://qiita-image-store.s3.amazonaws.com/0/24981/c88da652-3e32-d887-1482-ad0bc8ecb04b.png "Fig.1 Diagram")

Fig.1 Diagram

オリジナルと異なっているのは、条件1 にもとづいて Zynq UltraScale+ MPSoC の HPC に接続されていることです。Zynq UltraScale+ MPSoC を 選択して Customize Block を行い、PS-PL Configuration ＞ PS-PL Interfaces ＞ Slave Interface ＞ AXI HP を開き、AXI HPC0 FPD をチェックします。


![Fig.2 Zynq UltraScale+ MPSoC Re-customize IP](https://qiita-image-store.s3.amazonaws.com/0/24981/2fc986a3-2f9c-4d31-5f38-0ef9aec844e8.png "Fig.2 Zynq UltraScale+ MPSoC Re-customize IP")

Fig.2 Zynq UltraScale+ MPSoC Re-customize IP

#### AxCACHE/AxPROT を変更

さらに条件2と条件3にもとづいて AxCACHE と AxPROT の値を変更します。具体的には negative_0 を選択して Customize Block を行い、PROT value を "010" に、CACHE value を "1111" に変更します。

![Fig.3 Negative Re-customize IP](https://qiita-image-store.s3.amazonaws.com/0/24981/1cdca2a6-fa77-d83d-6dfa-012a984d426e.png "Fig.3 Negative Re-customize IP")

Fig.3 Negative Re-customize IP

### ビットストリームファイル

project/ の下にビルド用のスクリプトを用意しています。
なお、ビルド済みのビットストリームファイル(negative2.bit)をリポジトリに用意しているので、ビルドが面倒な方はこちらを使ってください。

### バイナリーファイル

UltraZed 向け Debian GNU/Linux (v2018.2版) で FPGA にロードするためには、ビットストリームファイルをバイナリーファイルに変換する必要があります。バイナリーファイルへの変換は、次のような bif file を用意して、Vivado-SDK v2018.2 の bootgen コマンドを使います。

```text:negative2.bif
all:
{
	[destination_device = pl] negative2.bit
}
```

```console
vivado% bootgen -image negative2.bif -arch zynqmp -w -o negative2.bin
```

なお、変換済みのバイナリーファイル(negative2.bin)をリポジトリに用意しているので、変換が面倒な方はこちらを使ってください。

### boot.bin の説明

Linux 上で HPC を使ってキャッシュコヒーレンシ転送を行うための条件として、「MPSoC の内部レジスタを変更して内部共有可能トランザクションをブロードキャストできるようにする」というのがあります。もう少し具体的に言うと「APUがリセット中に、LPD_SLCRモジュールの lpd_apu レジスタの brdc_inner ビットを書き込む」というものです。

この「APUがリセット中に」というのが曲者で、つまり FSBL や U-Boot や Linux からでは設定出来ないということです。なぜなら[「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(Boot Loader編)」](https://qiita.com/ikwzm/items/a0dfef6355cf839ebc08) で説明したように、FSBL、U-Boot、Linux は APU で動くからです。つまり FSBL の起動前、すなわちステージ０(PMU が内部ROMに格納されているブートローダーを実行している状態)でレジスタを設定する必要があります。

幸い、ステージ０でレジスタを設定できる仕掛けが用意されています。まずは以下のようなファイルを用意しておきます。

```text:boot/regs.init
.set. 0xFF41A040 = 0x3;
```

boot.bin 生成時に使用する bif file に上記のファイルを init プロパティの値として追加します。

```text:boot/boot.bif
the_ROM_image:
{
        [fsbl_config] a53_x64
        [init]                                                    regs.init
        [bootloader]                                              zynqmp_fsbl.elf
        [pmufw_image]                                             zynqmp_pmufw.elf
        [destination_cpu=a53-0, exception_level=el-3, trustzone]  bl31.elf
        [destination_cpu=a53-0, exception_level=el-2]             u-boot.elf
}
```

このようにして生成した boot.bin は、ブート時にステージ０で PMU がレジスタを設定するようになります。

なお、このように生成した boot.bin をリポジトリの boot/ に用意しています。


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

### UltraZed の準備

次の記事を参考に UltraZed に Debian GNU/Linux をインストールしてください。

 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) ブートイメージの提供」@Qiita](https://qiita.com/ikwzm/items/6e0506fc161d1da58e9c)

### fpga でログイン

Linux が起動したら ユーザー名 fpga でログインします。パスワードは fpga になっています。

```console
debian-fpga login: fpga
Password:
fpga@debian-fpga:~$
```

### リポジトリのダウンロード

サンプルとなる FPGA デザインやプログラムを下記の URL よりダウンロードします。

 * [https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-3-UltraZed](https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-3-UltraZed)

ここでは examples/negative2 としてダウンロードします。

```console
fpga@debian-fpga:~$ mkdir examples
fpga@debian-fpga:~$ cd examples
fpga@debian-fpga:~/examples$ git clone https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-3-UltraZed negative2
fpga@debian-fpga:~/examples$ cd negative2
```

### boot.bin の変更

リポジトリの boot/boot.bin を boot パーティションにコピーします。

```console
fpga@debian-fpga:~/examples/negative2$ sudo cp boot/boot.bin /mnt/boot
```

なお、boot.bin を変更するには注意が必要です。失敗すると二度と起動できなくなる恐れがあります。念のため boot.bin のバックアップをとる等の施策をとってください。

### リブート

boot.bin を変更したらリブートします。

FPGA のコンフィギュレーション
-------------------------------------------------------------

### Binary File を /lib/firmware へコピー

FPGA Region では /lib/firmware にコンフィギュレーションするファイルを置いておく必要があります。

```console
fpga@debian-fpga:~/examples/negative2$ sudo cp negative2.bin /lib/firmware
```

### Device Tree Overlay による FPGA のコンフィギュレーション

FPGA Region では Device Tree を使って FPGA をコンフィギュレーションします。そこで次のような Device Tree Overlay 用のソースファイルを用意します。

```dts:fpga-load.dts
/dts-v1/;
/ {
	fragment@0 {
		target-path = "/fpga-full";
		__overlay__ {
			firmware-name = "negative2.bin";
		};
	};
};
```

次の手順で Device Tree Overlay による FPGA のコンフィギュレーションを行います。

 1. Device Tree Overlay 用ソースファイル(ここでは fpga-load.dts)を dtc(Device Tree Compiler) を使って dtb (ここでは fpga-load.dtb) に変換します。
 2. /config/device-tree/overlays 下に Device Tree Overlay 用のディレクトリ(ここでは fpga) を作ります。
 3. 2.で作ったディレクトリ下の dtbo に 1. で作った dtb を書き込みます。

これで次のようなカーネルメッセージが出れば、FPGA のコンフィギュレーションは成功です。
 
```console
fpga@debian-fpga:~/examples/negative2$ dtc -I dts -O dtb -o fpga-load.dtb fpga-load.dts
fpga@debian-fpga:~/examples/negative2$ sudo mkdir /config/device-tree/overlays/fpga
fpga@debian-fpga:~/examples/negative2$ sudo cp fpga-load.dtb /config/device-tree/overlays/fpga/dtbo
[   56.218477] fpga_manager fpga0: writing negative2.bin to Xilinx ZynqMP FPGA Manager
```

FPGA のクロックの設定
-------------------------------------------------------------

FPGA をコンフィギュレーションするだけでは FPGA 側の回路は動きません。なぜなら FPGA へ供給するクロックの設定が終わっていないからです。

ここでは fclkcfg を使ってクロックを設定します。fclkcfg は筆者が作ったデバイスドライバで、次の URL に公開しています。

 * [https://github.com/ikwzm/fclkcfg](https://github.com/ikwzm/fclkcfg)

[「UltraZed 向け Debian GNU/Linux ブートイメージの提供」](https://qiita.com/ikwzm/items/c807f0af1b76da9a1585)をインストールしてあれば、すでに fclkcfg が組み込まれています。

クロックを設定するには、次のような Device Tree Overlay 用のソースファイルを用意します。

```dts:fclk0-zynqmp.dts
/dts-v1/;/plugin/;
/ {
	fragment@0 {
		target-path = "/amba";
		__overlay__ {
			fclk0 {
				compatible    = "ikwzm,fclkcfg-0.10.a";
				clocks        = <&clk 0x47>;
				insert-rate   = "100000000";
				insert-enable = <1>;
				remove-rate   = "1000000";
				remove-enable = <0>;
			};
		};
	};
};
```

次の手順で Device Tree Overlay を使ってクロックを設定します。

 1. Device Tree Overlay 用ソースファイル(ここでは fclk0-zynqmp.dts)を dtc(Device Tree Compiler) を使って dtb (ここでは fclk0-zynqmp.dtb) に変換します。
 2. /config/device-tree/overlays 下に Device Tree Overlay 用のディレクトリ(ここでは fclk0) を作ります。
 3. 2.で作ったディレクトリ下の dtbo に 1. で作った dtb を書き込みます。

これで次のようなカーネルメッセージが出れば、FPGA クロックの設定は成功です。

```console
fpga@debian-fpga:~/examples/negative2$ dtc -I dts -O dtb -o fclk0-zynqmp.dtb fclk0-zynqmp.dts
fpga@debian-fpga:~/examples/negative2$ sudo mkdir /config/device-tree/overlays/fclk0
fpga@debian-fpga:~/examples/negative2$ sudo cp fclk0-zynqmp.dtb /config/device-tree/overlays/fclk0/dtbo
[  111.238976] fclkcfg amba:fclk0: driver installed.
[  111.243617] fclkcfg amba:fclk0: device name    : fclk0
[  111.248737] fclkcfg amba:fclk0: clock  name    : pl0
[  111.253678] fclkcfg amba:fclk0: clock  rate    : 99999999
[  111.259085] fclkcfg amba:fclk0: clock  enabled : 1
[  111.263833] fclkcfg amba:fclk0: remove rate    : 1000000
[  111.269125] fclkcfg amba:fclk0: remove enable  : 0
```

Uio と Udmabuf の準備
-------------------------------------------------------------

FPGA にコンフィギュレーションした回路にアクセスするには uio を使います。また、データのやりとりは udmabuf を使います。そのために次のような Device Tree Overlay 用のソースファイルを用意します。

```dts:negative2.dts
/dts-v1/;/plugin/;
/ {
	fragment@0 {
		target-path = "/amba_pl@0";
		#address-cells = <2>;
		#size-cells = <2>;

		__overlay__ {
			#address-cells = <2>;
			#size-cells = <2>;

			negative-uio {
				compatible = "generic-uio";
				reg = <0x0 0x80010000 0x0 0x10000>;
				interrupt-parent = <&gic>;
				interrupts = <0 89 4>;
			};

			negative-udmabuf4 {
				compatible  = "ikwzm,udmabuf-0.10.a";
				device-name = "udmabuf4";
				size = <0x00100000>;
			};

			negative-udmabuf5 {
				compatible = "ikwzm,udmabuf-0.10.a";
				device-name = "udmabuf5";
				size = <0x00100000>;
			};
		};
	};
} ;
```

次の手順で Device Tree Overlay を使って uio と udmabuf を設定します。

 1. Device Tree Overlay 用ソースファイル(ここでは negative2.dts)を dtc(Device Tree Compiler) を使って dtb (ここでは negative2.dtb) に変換します。
 2. /config/device-tree/overlays 下に Device Tree Overlay 用のディレクトリ(ここでは negative2) を作ります。
 3. 2.で作ったディレクトリ下の dtbo に 1. で作った dtb を書き込みます。

これで次のように /dev/uio1、/dev/udmabuf4、/dev/udmabuf5 が出来れば成功です。

```console
fpga@debian-fpga:~/examples/negative2$ dtc -I dts -O dtb -o negative2.dtb negative2.dts
fpga@debian-fpga:~/examples/negative2$ sudo mkdir /config/device-tree/overlays/negative2
fpga@debian-fpga:~/examples/negative2$ sudo cp negative2.dtb /config/device-tree/overlays/negative2/dtbo
[  164.123998] udmabuf amba_pl@0:negative-udmabuf4: driver probe start.
[  164.131871] udmabuf udmabuf4: driver installed
[  164.136254] udmabuf udmabuf4: major number   = 244
[  164.141021] udmabuf udmabuf4: minor number   = 0
[  164.145619] udmabuf udmabuf4: phys address   = 0x0000000070400000
[  164.151689] udmabuf udmabuf4: buffer size    = 1048576
[  164.156811] udmabuf udmabuf4: dma coherent   = 0
[  164.161412] udmabuf amba_pl@0:negative-udmabuf4: driver installed.
[  164.167998] udmabuf amba_pl@0:negative-udmabuf5: driver probe start.
[  164.175758] udmabuf udmabuf5: driver installed
[  164.180142] udmabuf udmabuf5: major number   = 244
[  164.184917] udmabuf udmabuf5: minor number   = 1
[  164.189505] udmabuf udmabuf5: phys address   = 0x0000000070500000
[  164.195576] udmabuf udmabuf5: buffer size    = 1048576
[  164.200698] udmabuf udmabuf5: dma coherent   = 0
[  164.205298] udmabuf amba_pl@0:negative-udmabuf5: driver installed.
fpga@debian-fpga:~/examples/negative2$ ls -la /dev/uio*
crw------- 1 root root 245, 0 Jan  8 18:07 /dev/uio0
crw------- 1 root root 245, 0 Jan  8 18:07 /dev/uio1
fpga@debian-fpga:~/examples/negative2$ ls -la /dev/udmabuf*
crw------- 1 root root 244, 0 Jan  8 18:07 /dev/udmabuf4
crw------- 1 root root 244, 1 Jan  8 18:07 /dev/udmabuf5
```

実際に走らせてみよう
-------------------------------------------------------------

### negative2.py

FPGA にロードした回路を動かすために次のようなサンプルプログラムを用意しています。このプログラムは[「UltraZed 向け Debian GNU/Linux (v2018.2版) で Vivado-HLS を使って合成した回路を動かす」](https://qiita.com/ikwzm/items/725ef2c4306e7bf021f0) で紹介した negative.py の udmabuf の sync_for_device()、sync_for_cpu() を使っているところをコメントアウトしたものです。negative.py の詳細な説明は記事を参照してください。

```python:negative2.py
from udmabuf import Udmabuf
from uio     import Uio
import numpy as np
import time

if __name__ == '__main__':
    uio1       = Uio('uio1')
    regs       = uio1.regs()
    udmabuf4   = Udmabuf('udmabuf4')
    udmabuf5   = Udmabuf('udmabuf5')
    test_dtype = np.uint32
    test_size  = min(int(udmabuf4.buf_size/(np.dtype(test_dtype).itemsize)),
                     int(udmabuf5.buf_size/(np.dtype(test_dtype).itemsize)))
  
    udmabuf4_array    = udmabuf4.memmap(dtype=test_dtype, shape=(test_size))
    udmabuf4_array[:] = np.random.randint(-21474836478,2147483647,(test_size))
    udmabuf4.set_sync_to_device(0, test_size*(np.dtype(test_dtype).itemsize))

    udmabuf5_array    = udmabuf5.memmap(dtype=test_dtype, shape=(test_size))
    udmabuf5_array[:] = np.random.randint(-21474836478,2147483647,(test_size))
    udmabuf5.set_sync_to_cpu(   0, test_size*(np.dtype(test_dtype).itemsize))

    total_setup_time   = 0
    total_cleanup_time = 0
    total_xfer_time    = 0
    total_xfer_size    = 0
    count              = 0

    for i in range (0,9):

        start_time  = time.time()
        # udmabuf4.sync_for_device()
        # udmabuf5.sync_for_device()
        regs.write_word(0x18, udmabuf4.phys_addr & 0xFFFFFFFF)
        regs.write_word(0x20, udmabuf5.phys_addr & 0xFFFFFFFF)
        regs.write_word(0x28, test_size)
        regs.write_word(0x04, 0x000000001)
        regs.write_word(0x08, 0x000000001)
        regs.write_word(0x0C, 0x000000001)
        uio1.irq_on()
        phase0_time = time.time()
        regs.write_word(0x00, 0x000000001)
        uio1.wait_irq()

        phase1_time = time.time()
        regs.write_word(0x0C, 0x000000001)
        # udmabuf4.sync_for_cpu()
        # udmabuf5.sync_for_cpu()

        end_time     = time.time()
        setup_time   = phase0_time - start_time
        xfer_time    = phase1_time - phase0_time
        cleanup_time = end_time    - phase1_time
        total_time   = end_time    - start_time

        total_setup_time   = total_setup_time   + setup_time
        total_cleanup_time = total_cleanup_time + cleanup_time
        total_xfer_time    = total_xfer_time    + xfer_time
        total_xfer_size    = total_xfer_size    + test_size
        count              = count              + 1
        print ("total:{0:.3f}[msec] setup:{1:.3f}[msec] xfer:{2:.3f}[msec] cleanup:{3:.3f}[msec]".format(round(total_time*1000.0,3), round(setup_time*1000.0,3), round(xfer_time*1000.0,3), round(cleanup_time*1000.0,3)))


    print ("average_setup_time  :{0:.3f}".format(round((total_setup_time  /count)*1000.0,3)) + "[msec]")
    print ("average_cleanup_time:{0:.3f}".format(round((total_cleanup_time/count)*1000.0,3)) + "[msec]")
    print ("average_xfer_time   :{0:.3f}".format(round((total_xfer_time   /count)*1000.0,3)) + "[msec]")
    print ("throughput          :{0:.3f}".format(round(((total_xfer_size/total_xfer_time)/(1000*1000)),3)) + "[MByte/sec]")

    udmabuf4_negative_array = np.negative(udmabuf4_array)
    if np.array_equal(udmabuf4_negative_array, udmabuf5_array):
         print("np.negative(udmabuf4) == udmabuf5 : OK")
    else:
         print("np.negative(udmabuf4) == udmabuf5 : NG")
         count = 0
         for i in range(test_size):
             if udmabuf4_negative_array[i] != udmabuf5_array[i] :
                 count = count + 1
                 if count < 16:
                     print("udmabuf4_negative_array[0x{0:08X}] = 0x{1:08X} udmabuf5_array[0x{0:08X}] = 0x{2:08X}".format(i, udmabuf4_negative_array[i], udmabuf5_array[i]))
         print("NG Count:{0}".format(count))
```

### 実行結果

#### negative2.py

うまくいくと次のような結果が得られます。

```console
fpga@debian-fpga:~/examples/negative2$ python3 negative2.py
total:0.182[msec] setup:0.130[msec] xfer:0.041[msec] cleanup:0.012[msec]
total:0.107[msec] setup:0.077[msec] xfer:0.020[msec] cleanup:0.010[msec]
total:0.106[msec] setup:0.076[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.105[msec] setup:0.075[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.104[msec] setup:0.075[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.105[msec] setup:0.075[msec] xfer:0.019[msec] cleanup:0.011[msec]
total:0.104[msec] setup:0.075[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.387[msec] setup:0.077[msec] xfer:0.298[msec] cleanup:0.012[msec]
total:0.268[msec] setup:0.075[msec] xfer:0.181[msec] cleanup:0.012[msec]
average_setup_time  :0.082[msec]
average_cleanup_time:0.011[msec]
average_xfer_time   :0.071[msec]
throughput          :3713.172[MByte/sec]
np.negative(udmabuf4) == udmabuf5 : OK
````
udmabuf の sync_for_device()、sync_for_cpu() を使っていない(つまりソフトウェアでキャッシュのフラッシュと無効化をしていない)にも関わらず正常に動作していることがわかります。

#### negative.py

キャッシュのフラッシュと無効化をソフトウェアで行っている negative.py を実行すると次のような結果が得られます。

```console
fpga@debian-fpga:~/examples/negative2$ python3 negative.py
total:1.411[msec] setup:0.813[msec] xfer:0.042[msec] cleanup:0.555[msec]
total:1.218[msec] setup:0.673[msec] xfer:0.020[msec] cleanup:0.525[msec]
total:1.172[msec] setup:0.667[msec] xfer:0.020[msec] cleanup:0.485[msec]
total:1.173[msec] setup:0.667[msec] xfer:0.020[msec] cleanup:0.486[msec]
total:1.190[msec] setup:0.667[msec] xfer:0.020[msec] cleanup:0.503[msec]
total:1.184[msec] setup:0.677[msec] xfer:0.020[msec] cleanup:0.487[msec]
total:9.057[msec] setup:0.666[msec] xfer:7.903[msec] cleanup:0.489[msec]
total:1.169[msec] setup:0.664[msec] xfer:0.020[msec] cleanup:0.485[msec]
total:1.201[msec] setup:0.696[msec] xfer:0.020[msec] cleanup:0.485[msec]
average_setup_time  :0.688[msec]
average_cleanup_time:0.500[msec]
average_xfer_time   :0.898[msec]
throughput          :291.837[MByte/sec]
np.negative(udmabuf4) == udmabuf5 : OK
```

negative2.py の結果に比べて、setup と cleanup にけっこうな時間がとられているのがわかります。それだけキャッシュのフラッシュと無効化には時間がかかっていることがわかります。

#### おまけ

boot.bin を変更しない(すなわちMPSoC の内部レジスタを変更して内部共有可能トランザクションをブロードキャストできるようにしない)状態で negative2.py を実行すると次のように計算値と期待値が一致しません。

```console
fpga@debian-fpga:~/examples/negative2$ python3 negative2.py
total:0.222[msec] setup:0.168[msec] xfer:0.043[msec] cleanup:0.011[msec]
total:0.119[msec] setup:0.090[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.118[msec] setup:0.088[msec] xfer:0.019[msec] cleanup:0.011[msec]
total:0.118[msec] setup:0.088[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.118[msec] setup:0.088[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.117[msec] setup:0.087[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.117[msec] setup:0.088[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.117[msec] setup:0.088[msec] xfer:0.019[msec] cleanup:0.010[msec]
total:0.159[msec] setup:0.129[msec] xfer:0.019[msec] cleanup:0.010[msec]
average_setup_time  :0.102[msec]
average_cleanup_time:0.011[msec]
average_xfer_time   :0.022[msec]
throughput          :12009.229[MByte/sec]
np.negative(udmabuf4) == udmabuf5 : NG
udmabuf4_negative_array[0x00001160] = 0x2BF44900 udmabuf5_array[0x00001160] = 0x4AAC10A6
udmabuf4_negative_array[0x00001161] = 0xE19DF117 udmabuf5_array[0x00001161] = 0xDE4038DE
udmabuf4_negative_array[0x00001162] = 0x6B655459 udmabuf5_array[0x00001162] = 0x4745277B
udmabuf4_negative_array[0x00001163] = 0xCB40962A udmabuf5_array[0x00001163] = 0x3A6ED662
udmabuf4_negative_array[0x00001164] = 0x7932EB61 udmabuf5_array[0x00001164] = 0x65701BBC
udmabuf4_negative_array[0x00001165] = 0x8B8146FD udmabuf5_array[0x00001165] = 0x5A550861
udmabuf4_negative_array[0x00001166] = 0x99AC4841 udmabuf5_array[0x00001166] = 0x265323B0
udmabuf4_negative_array[0x00001167] = 0xA46B6593 udmabuf5_array[0x00001167] = 0x5513097F
udmabuf4_negative_array[0x00001168] = 0x726FF784 udmabuf5_array[0x00001168] = 0x6FEBBC08
udmabuf4_negative_array[0x00001169] = 0xD834BBCE udmabuf5_array[0x00001169] = 0x2ECBBFB8
udmabuf4_negative_array[0x0000116A] = 0x70580503 udmabuf5_array[0x0000116A] = 0x70B08FFC
udmabuf4_negative_array[0x0000116B] = 0xA3704FC6 udmabuf5_array[0x0000116B] = 0x0CED699C
udmabuf4_negative_array[0x0000116C] = 0x1922A3D6 udmabuf5_array[0x0000116C] = 0x41D123D5
udmabuf4_negative_array[0x0000116D] = 0x554F7E03 udmabuf5_array[0x0000116D] = 0xEF6F420B
udmabuf4_negative_array[0x0000116E] = 0x06A95EE2 udmabuf5_array[0x0000116E] = 0x754C3622
NG Count:63968
```

これはキャッシュのコヒーレンシ維持が出来ていないために、PL 側の回路が間違った値を読んでいるか PL が書いた値を CPU が正常に読めていないために起ったものです。

参考
-------------------------------------------------------------
 * [「Zynq UltraScale MPSoC Cache Coherency」@www.wiki.xilinx.com](http://www.wiki.xilinx.com/Zynq+UltraScale+MPSoC+Cache+Coherency)
 * [https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-3-UltraZed](https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-3-UltraZed)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) ブートイメージの提供」@Qiita](https://qiita.com/ikwzm/items/6e0506fc161d1da58e9c)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) で Vivado-HLS を使って合成した回路を動かす」](https://qiita.com/ikwzm/items/725ef2c4306e7bf021f0)
 * [「UltraZed 向け Debian GNU/Linux で AXI HPC port を使う (基礎編)」 @Qiita](https://qiita.com/ikwzm/items/82f98bfa76922073e610)