UltraZed 向け Debian GNU/Linux (v2018.2版) で FPGA をコンフィギュレーション例

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

次の記事で UltraZed 向け Debian GNU/Linux (v2018.2版) の構築をしました。

 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(イントロ編)」@Qiita](https://qiita.com/ikwzm/items/5b657ebd8895945c4eae)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(Boot Loader編)」@Qiita](https://qiita.com/ikwzm/items/a0dfef6355cf839ebc08)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(Linux Kernel編)」@Qiita](https://qiita.com/ikwzm/items/d5417b39c983f8e56af9)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(Debian9 Root File System編)」@Qiita](https://qiita.com/ikwzm/items/a0e88798ed29b30fa003)

また、次の記事で、上の記事で構築したシステムのイメージを紹介しました。

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


この記事では、上の記事で構築したシステムを使って、Linux から FPGA をコンフィギュレーションする例を示します。この記事で紹介する FPGA のデザインやプログラムは次の URL に公開しています。

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

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

今回 FPGA にコンフィギュレーションする回路は、次の記事で説明したものを使用します。

 * [「UltraZed 向け Debian GNU/Linux (v2017.3版) の構築(Sample FPGA Design編)」@Qiita](https://qiita.com/ikwzm/items/61316ecec2fc68be4c96)

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

![Fig.1 ZynqMP Sample Design](https://qiita-image-store.s3.amazonaws.com/0/24981/1270d3cc-8228-891b-46f2-84e5a715f4c1.png "Fig.1 ZynqMP Sample Design")

Fig.1 ZynqMP Sample Design

UltraZed 向け Debian GNU/Linux (v2018.2版) で FPGA にロードするためには、Bitstream File を Binary File に変換する必要があります。Binary File への変換は、次のような bif file を用意して、Vivado-SDK v2018.2 の bootgen コマンドを使います。

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

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

Binary File の作り方の詳細は以下の URL を参照してください。

 * http://www.wiki.xilinx.com/Solution+ZynqMP+PL+Programming


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

### UltraZed の準備

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

 * [「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-0-UltraZed](https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-0-UltraZed)

ここでは v2018.2 版 を examples/gpio としてダウンロードします。

```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-0-UltraZed gpio
fpga@debian-fpga:~/examples$ cd gpio
fpga@debian-fpga:~/examples/gpio$ git checkout v2018.2
```

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

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

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

```console
fpga@debian-fpga:~/examples/gpio$ sudo cp design_1_wrapper.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 = "design_1_wrapper.bin";
		};
	};
};
```

target-path が "/fpga-full" であることに注意してください。これは Linux 起動時に読み込んだ Device Tree で指定した fpga-region のシンボル名です。

次の手順で 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/gpio$ dtc -I dts -O dtb -o fpga-load.dtb fpga-load.dts
fpga@debian-fpga:~/examples/gpio$ sudo mkdir /config/device-tree/overlays/fpga
fpga@debian-fpga:~/examples/gpio$ sudo cp fpga-load.dtb /config/device-tree/overlays/fpga/dtbo
[ 1462.560122] fpga_manager fpga0: writing design_1_wrapper.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>;
			};
		};
	};
};
```

ここでは clocks で ZynqMP の PL CLOCK[0] を指定しています。```&clk``` はクロック制御ドライバのシンボル名、0x47 は PL CLOCK[0] のインデックス番号です。また、この Device Tree が挿入されたとき PL CLOCK[0] は 周波数 100MHz のクロックを出力するように設定しています。

なお、[「UltraZed 向け Debian GNU/Linux (v2017.3版) で FPGA をコンフィギュレーション」](https://qiita.com/ikwzm/items/c423e6dfded5d3542a1e) で説明した fclk0-zynqmp.dts とは clocks の記述が違うことに注意してください。v2017.3版では ```<&clkc 0x47>``` でしたが、v2018.3版では ```<&clk 0x47>``` となっています。

次の手順で 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/gpio$ dtc -I dts -O dtb -o fclk0-zynqmp.dtb fclk0-zynqmp.dts
fpga@debian-fpga:~/examples/gpio$ sudo mkdir /config/device-tree/overlays/fclk0
fpga@debian-fpga:~/examples/gpio$ sudo cp fclk0-zynqmp.dtb /config/device-tree/overlays/fclk0/dtbo
[ 1830.238976] fclkcfg amba:fclk0: driver installed.
[ 1830.243617] fclkcfg amba:fclk0: device name    : fclk0
[ 1830.248737] fclkcfg amba:fclk0: clock  name    : pl0
[ 1830.253678] fclkcfg amba:fclk0: clock  rate    : 99999999
[ 1830.259085] fclkcfg amba:fclk0: clock  enabled : 1
[ 1830.263833] fclkcfg amba:fclk0: remove rate    : 1000000
[ 1830.269125] fclkcfg amba:fclk0: remove enable  : 0
```

Uio の準備
-------------------------------------------------------------

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

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

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

			uio1: uio@80000000 {
				compatible = "generic-uio";
				reg = <0x0 0x80000000 0x0 0x10000>;
	                };
			uio2: uio@80010000 {
				compatible = "generic-uio";
				reg = <0x0 0x80010000 0x0 0x10000>;
	                };
			uio3: uio@80020000 {
				compatible = "generic-uio";
				reg = <0x0 0x80020000 0x0 0x10000>;
	                };
                };
	} ;
} ;
```

Zynq の場合と異なり、注意点があります。

  1. target-path に ```"/amba_pl@0"``` を指定します。
  2. address-cells と size-cells がともに 2 であること。これは ZynqMp のアドレスのビット幅が 64bit であるためです。従って reg には <アドレスの上位32bit アドレスの下位32bit サイズの上位32bit サイズの下位32bit> という形で指定します。


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

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

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

```console
fpga@debian-fpga:~/examples/gpio$ dtc -I dts -O dtb -o uio.dtb uio.dts
fpga@debian-fpga:~/examples/gpio$ sudo mkdir /config/device-tree/overlays/uio
fpga@debian-fpga:~/examples/gpio$ sudo cp uio.dtb /config/device-tree/overlays/uio/dtbo
fpga@debian-fpga:~/examples/gpio$ ls -la /dev/uio*
crw------- 1 root root 245, 0 Dec 31 10:20 /dev/uio0
crw------- 1 root root 245, 1 Dec 31 10:20 /dev/uio1
crw------- 1 root root 245, 2 Dec 31 10:20 /dev/uio2
crw------- 1 root root 245, 2 Dec 31 10:20 /dev/uio3
```

LED を点滅させてみる
-------------------------------------------------------------

次のような Python のプログラムを用意しました。

```python:led_on.py
from uio     import Uio
import numpy as np
import time
import signal

class  LED:
    def __init__(self):
        self.uio  = Uio('uio2')
        self.regs = self.uio.regs()
        self.regs.write_word(4, 0)
        self.pattern = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02]
        self.index   = 0
        
    def run(self):
        self.regs.write_word(0, self.pattern[self.index])
        if self.index < len(self.pattern)-1:
            self.index = self.index + 1
        else:
            self.index = 0;

if __name__ == '__main__':
    led  = LED()

    for i in range(100):
        led.run()
        time.sleep(0.1)
```

ここで import している uio.py は次の記事で紹介したものです。

 * [「Python と Numpy で UIO を制御」@Qiita](https://qiita.com/ikwzm/items/d7559858d099c86c350c)

なお、[「UltraZed 向け Debian GNU/Linux (v2017.3版) で FPGA をコンフィギュレーション」](https://qiita.com/ikwzm/items/c423e6dfded5d3542a1e) で説明した led_on.py とは指定した uio のデバイスドライバ名が違うことに注意してください。v2017.3 では ```Uio('uio1')``` でしたが v2018.3 では ```Uio('uio2')``` になっています。

上のプログラムを走らせてみて数秒 LED が左右に点滅すれば成功です。

```console
fpga@debian-fpga:~/examples/gpio$ sudo python3 led_on.py
```

後始末
-------------------------------------------------------------

遊び終わったら、Device Tree Overlay で追加したデバイスツリーを、後から追加した順に削除します。

```console
fpga@debian-fpga:~/examples/gpio$ sudo rmdir /config/device-tree/overlays/uio
fpga@debian-fpga:~/examples/gpio$ sudo rmdir /config/device-tree/overlays/fclk0
[ 2149.037235] fclkcfg amba:fclk0: change rate    : 992064
[ 2149.042497] fclkcfg amba:fclk0: change enable  : 0
[ 2149.047353] fclkcfg amba:fclk0: driver unloaded
fpga@debian-fpga:~/examples/gpio$ sudo rmdir /config/device-tree/overlays/fpga
```

注意
-------------------------------------------------------------

### Binary File Format について

次で示す記事で v2017.3版 での方法を示しました。

* [「UltraZed 向け Debian GNU/Linux (v2017.3版) で Vivado-HLS を使って合成した回路を動かす」 @Qiita](https://qiita.com/ikwzm/items/c423e6dfded5d3542a1e)

v2018.2 版では Binary File のフォーマットが異なっていることに注意してください。v2017.3 では fpga-bit-to-bin.py という Python のスクリプトを使って変換した Binary File を使いましたが、v2018.2 からは Vivado-SDK の bootgen で変換した Binary File を使う必要があります。

なお、v2018.2 版で 2017.3 版と同様の fpga-bit-to-bin.py を使って変換した Binary File を扱うには、次のように fpga_manager のフラグに 0x20 を設定します。

```console
fpga@debian-fpga:~/examples/negative$ sudo echo 20 > /sys/class/fpga_manager/fpga0/flags
```

参考
-------------------------------------------------------------

 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(イントロ編)」@Qiita](https://qiita.com/ikwzm/items/5b657ebd8895945c4eae)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(Boot Loader編)」@Qiita](https://qiita.com/ikwzm/items/a0dfef6355cf839ebc08)
 * [「UltraZed 向け Debian GNU/Linux (v2017.3版) の構築(Sample FPGA Design編)」@Qiita](https://qiita.com/ikwzm/items/61316ecec2fc68be4c96)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(Linux Kernel編)」@Qiita](https://qiita.com/ikwzm/items/d5417b39c983f8e56af9)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) の構築(Debian9 Root File System編)」@Qiita](https://qiita.com/ikwzm/items/a0e88798ed29b30fa003)
 * [「UltraZed 向け Debian GNU/Linux (v2018.2版) ブートイメージの提供」@Qiita](https://qiita.com/ikwzm/items/6e0506fc161d1da58e9c)
 * [「UltraZed 向け Debian GNU/Linux (v2017.3版) で Vivado-HLS を使って合成した回路を動かす」 @Qiita](https://qiita.com/ikwzm/items/c423e6dfded5d3542a1e)
 * [「Python と Numpy で UIO を制御」@Qiita](https://qiita.com/ikwzm/items/d7559858d099c86c350c)
 * [http://www.wiki.xilinx.com/Solution+ZynqMP+PL+Programming](http://www.wiki.xilinx.com/Solution+ZynqMP+PL+Programming)
 * [https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-0-UltraZed](https://github.com/ikwzm/ZynqMP-FPGA-Linux-Example-0-UltraZed)
 * [https://github.com/ikwzm/fclkcfg](https://github.com/ikwzm/fclkcfg)