FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(ZYBOとDE0-Nano-SoCのデュアルブートに対応)

## 注意(2017年9月20日追記)

この記事は Linux Kernel 4.4 時点に投稿したものであり、古い内容が含まれています。Linux Kernel 4.10 以降、FPGA 関連は大きく進歩しました。詳しくは[「Linux Kernel 4.10 でのFPGAのサポート事情」](http://qiita.com/ikwzm/items/2ff7d5429da8ace7c0bd)を参照してください。

## はじめに

以下の記事で Device Tree Overlay と FPGA Manager に対応した FPGA+SoC+Linux の動作環境を紹介しました。

* [「FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(ブートイメージの提供)」@Qiita](http://qiita.com/ikwzm/items/cdeb3c9c352776eac683)

さらに、以下の記事でこのシステムを ZYBO と DE0-Nano-SoC に対応した実例を紹介しました。

* [「FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(ZYBO-Examples)」@Qiita](http://qiita.com/ikwzm/items/61ea3c5a037b83006f17)
* [「FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(DE0-Nano-SoC-Examples)」@Qiita](http://qiita.com/ikwzm/items/ca6229d4e32fb421b0a9)

上で紹介した実例では ZYBO と DE0-Nano-SoC で別々の SD-Card を用意していましたが、この記事では、1枚の SD-Card で ZYBO と DE0-Nano-SoC の２種類のボードで起動する方法を紹介します。

もともと、このデュアルブート方法は、次の資料に触発されたもので、おおいに参考にさせてもらいました。この場を借りてお礼を申し上げます。

* 石原ひでみ. 『第五章 ついに起動！デュアル・ブート SD カードの作り方』「FPGA マガジン No.12」57頁

## 追記

* 2017/1/14 に Linux Kernel のバージョンを v4.8.17 に更新しました。
* 2017/1/31 に udmabuf のバージョンを v0.6.0 に更新しました。

## 概要

* Hardware
  + ZYBO : Xilinx Zynq-7000 ARM/FPGA SoC Trainer Board by Digilent
  + DE0-Nano-SoC : Altera SoC FPGA Development Kit by terasic
* U-Boot v2016.03 (customized)
  + Build for ZYBO and DE0-Nano-SoC
  + Customized boot by uEnv.txt
  + Customized boot by boot.scr
* Linux Kernel Version v4.4.7 or v4.8.17
  + Available in both Xilinx-Zynq-7000 and Altera-SoC in a single image
  + Enable Device Tree Overlay
  + Enable FPGA Manager
* Debian8(jessie) Root File System
  + Installed build-essential
  + Installed device-tree-compiler
  + Installed ruby ruby-msgpack ruby-serialport
  + Installed u-boot-tools
* FPGA Device Drivers
  + dtbocfg     (Device Tree Blob Overlay Configuration File System)
  + fpgacfg     (FPGA Configuration Interface for Linux FPGA Manager Framework)
  + fclkcfg     (FPGA Clock Configuration Device Driver)
  + udmabuf     (User space mappable DMA Buffer)
  + zptty       (Pseudo TeleTYpewriter for FPGA Device)
  + fpga-bridge (FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices)

## インストール

### ダウンロード

github から次のようにダウンロードしてください。
現時点の最新バージョンは v0.2.0 です。チェックアウトしてください。
なお、いくつかのイメージファイルはかなり大きいので、Git LFS(Large File Storage)を使っています。
お使いの環境に git-lfs がインストールされている必要があります。

```
shell$ git clone git://github.com/ikwzm/FPGA-SoC-Linux
shell$ cd FPGA-SoC-Linux
shell$ git checkout v0.3.3
shell$ git lfs pull
```

### Build boot files

ZYBO 用のディレクトリと DE0-Nano-SoC 用のディレクトリから必要なファイルをデュアル・ブート用のディレクトリ(target/zynq-zybo-de0-nano-soc)にコピーします。
target/zynq-zybo-de0-nano-soc/Makefile を用意していますので活用してください。

```
shell$ cd target/zynq-zybo-de0-nano-soc/
shell$ make
mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "linux boot script" -d boot/boot.script boot/boot.scr
Image Name:   linux boot script
Created:      Fri Jan 13 17:05:07 2017
Image Type:   ARM Linux Script (uncompressed)
Data Size:    1324 Bytes = 1.29 kB = 0.00 MB
Load Address: 00000000
Entry Point:  00000000
Contents:
   Image 0: 1316 Bytes = 1.29 kB = 0.00 MB
cp ../zynq-zybo/boot/zImage-4.8.17-armv7-fpga boot/zImage-4.8.17-armv7-fpga
cp ../zynq-zybo/boot/boot.bin boot/boot.bin
cp ../zynq-zybo/boot/design_1_wrapper.bit boot/design_1_wrapper.bit
cp ../zynq-zybo/boot/devicetree-4.8.17-zynq-zybo.dtb boot/devicetree-4.8.17-zynq-zybo.dtb
cp ../zynq-zybo/boot/devicetree-4.8.17-zynq-zybo.dts boot/devicetree-4.8.17-zynq-zybo.dts
cp ../zynq-zybo/boot/u-boot.img boot/u-boot.img
cp ../de0-nano-soc//boot/devicetree-4.8.17-socfpga.dtb boot/devicetree-4.8.17-socfpga.dtb
cp ../de0-nano-soc//boot/devicetree-4.8.17-socfpga.dts boot/devicetree-4.8.17-socfpga.dts
cp ../de0-nano-soc//boot/DE0_NANO_SOC.rbf boot/DE0_NANO_SOC.rbf
cp ../de0-nano-soc//u-boot/u-boot-spl.sfp u-boot/u-boot-spl.sfp
cp ../de0-nano-soc//u-boot/u-boot.img u-boot/u-boot.img
```

### ファイルの説明

デュアル・ブート用のディレクトリ(tareget/zynq-zybo-de0-nano-soc)には以下のようなファイルがあります。

 * tareget/zynq-zybo-de0-nano-soc/
   + boot/
     - boot.bin                                                  : Stage 1 Boot Loader      (for ZYBO U-boot-spl)
     - u-boot.img                                                : Stage 2 Boot Loader      (for ZYBO U-boot image)
     - uEnv.txt                                                  : U-Boot environment variables for set kernel version
     - boot.script                                               : U-Boot boot script       (source)
     - boot.scr                                                  : U-Boot boot script       (binary)
     - design_1_wrapper.bit                                      : FPGA configuration file  (for ZYBO)
     - DE0_NANO_SOC.rbf                                          : FPGA configuration file  (for DE0-Nano-SoC)
     - zImage-4.8.17-armv7-fpga                                  : Linux Kernel Image
     - devicetree-4.8.17-zynq-zybo.dtb                           : Linux Device Tree Blob   (for ZYBO)
     - devicetree-4.8.17-zynq-zybo.dts                           : Linux Device Tree Source (for ZYBO)
     - devicetree-4.8.17-socfpga.dtb                             : Linux Device Tree Blob   (for DE0-Nano-SoC)
     - devicetree-4.8.17-socfpga.dts                             : Linux Device Tree Source (for DE0-Nano-SoC)
   + u-boot/
     - u-boot-spl.sfp                                            : Stage 1 Boot Loader      (for DE0-Nano-SoC U-boot-spl)
     - u-boot.img                                                : Stage 2 Boot Loader      (for DE0-Nano-SoC U-boot image)
 * debian8-rootfs-vanilla.tgz                                    : Debian8 Root File System (use Git LFS)
 * linux-image-4.8.17-armv7-fpga_4.8.17-armv7-fpga-1_armhf.deb   : Linux Image Package      (use Git LFS)
 * linux-headers-4.8.17-armv7-fpga_4.8.17-armv7-fpga-1_armhf.deb : Linux Headers Package    (use Git LFS)
 * fpga-soc-linux-drivers-4.8.17-armv7-fpga_0.0.3-1_armhf.deb    : Device Drivers Package   (use Git LFS)


### SD-Card のフォーマット

SD-Card のパーティション１を FAT File System でファイルシステムを作ります。
SD-Card のパーティション２を ext3 File System でファイルシステムを作ります。
SD-Card のパーティション３を 特殊パーティション(パーティションタイプ=0xa2)にします。ファイルシステムは作りません。
フォーマット方法の詳細は『第五章 ついに起動！デュアル・ブート SD カードの作り方』「FPGA マガジン No.12」を参照してください。

### SD-Card への書き込み

SD-Card のパーティション３(下の例では/dev/sdc3)に target/zynq-zybo-de0-nano-soc/u-boot/ 下のファイルイメージを dd を使って書き込みます。
SD-Card のパーティション１(下の例では/dev/sdc1)に target/zynq-zybo-de0-nano-soc/boot/ 下のファイルをコピーします。
SD-Card のパーティション２(下の例では/dev/sdc2)に debian8-rootfs-vanilla.tgz の中身を展開します。
また、展開したルートファイルシステムの home/fpga に fpga-soc-linux-drivers パッケージをあらかじめコピーしておくと良いでしょう。後から network 経由でコピーしてもかまいません。

````
shell# mount /dev/sdc1 /mnt/usb1
shell# mount /dev/sdc2 /mnt/usb2
shell# cp target/zynq-zybo-de0-nano-soc/boot/*                          /mnt/usb1
shell# dd if=target/zynq-zybo-de0-nano-soc/u-boot/u-boot-spl.sfp of=/dev/sdc3 bs=64k seek=0
shell# dd if=target/zynq-zybo-de0-nano-soc/u-boot/u-boot.img     of=/dev/sdc3 bs=64k seek=4
shell# tar xfz debian8-rootfs-vanilla.tgz -C                            /mnt/usb2
shell# cp linux-image-4.8.17-armv7-fpga_4.8.17-armv7-fpga-1_armhf.deb   /mnt/usb2/home/fpga
shell# cp linux-headers-4.8.17-armv7-fpga_4.8.17-armv7-fpga-1_armhf.deb /mnt/usb2/home/fpga
shell# cp fpga-soc-linux-drivers-4.8.17-armv7-fpga_0.0.3-1_armhf.deb    /mnt/usb2/home/fpga
shell# umount mnt/usb1
shell# umount mnt/usb2
````

### デバイスドライバパッケージのインストール

省略します。[「FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(ブートイメージの提供)」](http://qiita.com/ikwzm/items/cdeb3c9c352776eac683) を参照してください。


## デュアル・ブートのしくみ

１枚の SD-Card で ZYNQ と DE0-Nano-SoC の両方をブートするしくみに関しては『第五章 ついに起動！デュアル・ブート SD カードの作り方』を参照してください。

ここでは、U-Boot がスクリプトによって ZYBO と DE0-Nano-SoC でブート手順を切り替える手法の説明をします。

### U-Boot の改良

[https://github.com/ikwzm/FPGA-SoC-Linux](https://github.com/ikwzm/FPGA-SoC-Linux)で用意した U-Boot には、ソースコードに少し細工がしてあります。

ZYBO 用の U-Boot のソースコードには次のようなパッチをあてています。

```
diff --git configs/zynq_zybo_defconfig configs/zynq_zybo_defconfig
index 7c23fec..8fc954e 100644
--- configs/zynq_zybo_defconfig
+++ configs/zynq_zybo_defconfig
@@ -22,3 +22,5 @@ CONFIG_DEBUG_UART_ZYNQ=y
 CONFIG_DEBUG_UART_BASE=0xe0001000
 CONFIG_DEBUG_UART_CLOCK=50000000
 CONFIG_ZYNQ_QSPI=y
+# CONFIG_OF_SEPARATE is not set
+CONFIG_OF_EMBED=y
diff --git include/configs/zynq-common.h include/configs/zynq-common.h
index 982905d..fd50c69 100644
--- include/configs/zynq-common.h
+++ include/configs/zynq-common.h
@@ -227,10 +227,35 @@
 	"usbboot=if usb start; then " \
 			"echo Copying FIT from USB to RAM... && " \
 			"load usb 0 ${load_addr} ${fit_image} && " \
-			"bootm ${load_addr}; fi\0" \
-		DFU_ALT_INFO
-
-#define CONFIG_BOOTCOMMAND		"run $modeboot"
+			"bootm ${load_addr}\0" \
+		"fi\0" \
+	"bootenv=uEnv.txt\0" \
+        "config=" CONFIG_DEFAULT_DEVICE_TREE "\0" \
+	"loadbootenv=load mmc 0 ${load_addr} ${bootenv}\0" \
+	"importbootenv=echo Importing environment from mmc ...;" \
+		"env import -t $load_addr $filesize\0" \
+        "loadbootscript=load mmc 0 ${load_addr} boot.scr\0" \
+        "bootscript=echo Running bootscript from mmc ...;" \
+                "source ${load_addr}\0" \
+	DFU_ALT_INFO
+
+#define CONFIG_BOOTCOMMAND \
+	"if mmc rescan; then " \
+		"echo SD/MMC found on device...;" \
+		"if run loadbootenv; then " \
+			"echo Loaded environment from ${bootenv};" \
+			"run importbootenv;" \
+		"fi;" \
+		"if test -n $uenvcmd; then " \
+			"echo Running uenvcmd ...;" \
+			"run uenvcmd;" \
+		"fi;" \
+		"if run loadbootscript; then " \
+			"run bootscript; " \
+		"fi; " \
+	"fi;" \
+        "run $modeboot"
+#define CONFIG_CMD_BOOTZ
 #define CONFIG_BOOTDELAY		3 /* -1 to Disable autoboot */
 #define CONFIG_SYS_LOAD_ADDR		0 /* default? */
```

DE0-Nano-SoC 用の U-Boot のソースコードには次のようなパッチをあてています。

```
diff --git include/configs/socfpga_de0_nano_soc.h include/configs/socfpga_de0_nano_soc.h
index cbc7396..1b68bdb 100644
--- include/configs/socfpga_de0_nano_soc.h
+++ include/configs/socfpga_de0_nano_soc.h
@@ -37,7 +37,22 @@
 #define CONFIG_BOOTDELAY	3
 #define CONFIG_BOOTFILE		"fitImage"
 #define CONFIG_BOOTARGS		"console=ttyS0," __stringify(CONFIG_BAUDRATE)
-#define CONFIG_BOOTCOMMAND	"run mmcload; run mmcboot"
+#define CONFIG_BOOTCOMMAND     \
+	"if mmc rescan; then " \
+		"echo SD/MMC found on device...;" \
+		"if run loadbootenv; then " \
+			"echo Loaded environment from ${bootenv};" \
+			"run importbootenv;" \
+		"fi;" \
+		"if test -n $uenvcmd; then " \
+			"echo Running uenvcmd ...;" \
+			"run uenvcmd;" \
+		"fi;" \
+		"if run loadbootscript; then " \
+			"run bootscript; " \
+		"fi; " \
+	"fi;" \
+        "run mmcload; run mmcboot"
 #define CONFIG_LOADADDR		0x01000000
 #define CONFIG_SYS_LOAD_ADDR	CONFIG_LOADADDR
 
@@ -65,6 +80,15 @@
 	"mmcload=mmc rescan;" \
 		"load mmc 0:1 ${loadaddr} ${bootimage};" \
 		"load mmc 0:1 ${fdt_addr} ${fdtimage}\0" \
+        "fpgadata=0x02000000\0" \
+	"bootenv=uEnv.txt\0" \
+	"config=" CONFIG_DEFAULT_DEVICE_TREE "\0" \
+	"loadbootenv=load mmc 0 ${loadaddr} ${bootenv}\0" \
+	"importbootenv=echo Importing environment from mmc ...;" \
+		"env import -t $loadaddr $filesize\0" \
+        "loadbootscript=load mmc 0 ${loadaddr} boot.scr\0" \
+        "bootscript=echo Running bootscript from mmc ...;" \
+                "source ${loadaddr}\0" \
 
 /* The rest of the configuration is shared */
 #include <configs/socfpga_common.h>
```

いずれのパッチも次のような修正をしています。

1. config 変数に U-Boot ビルド時のボード名を設定する
2. ブート時に uEnv.txt を読んで環境変数を設定する
3. ブート時に uEnv.txt に uenvcmd 変数が設定されていた場合はこれを実行する
4. ブート時に uenvcmd が無くて boot.scr があれば、boot.scr を実行する

### uEnv.txt

uEnv.txt では Linux Kernel のバージョン番号を設定しています。

```:uEnv.txt
kernel_version=4.8.17
```

### boot.scr

boot.scr は次のようなスクリプト(boot.script)を U-Boot 用にコンパイルしたものです。

```:boot.script
if test $config = "zynq-zybo"; then
    echo "Configuration for " $config
    boot_image=zImage-$kernel_version-armv7-fpga
    fdt_image=devicetree-$kernel_version-zynq-zybo.dtb
    fpga_image=design_1_wrapper.bit
    if  fatload mmc 0 0x03000000 $fpga_image; then
        fpga loadb  0 0x03000000 $filesize
        mw.l 0xF8000008 0xDF0D
        mw.l 0xF8000170 0x00100A00
        mw.l 0xF8000004 0x767B
    fi
    fatload mmc 0 0x03000000 $boot_image
    fatload mmc 0 0x02A00000 $fdt_image
    setenv bootargs console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait uio_pdrv_genirq.of_id=generic-uio
    if test $kernel_version = "4.8.17"; then
        setenv bootargs $bootargs sdhci.debug_quirks=64
    fi
    bootz 0x03000000 - 0x02A00000
fi
if test $config = "socfpga_cyclone5_de0_nano_soc"; then
    echo "Configuration for " $config
    boot_image=zImage-$kernel_version-armv7-fpga
    fdt_image=devicetree-$kernel_version-socfpga.dtb
    fpga_image=DE0_NANO_SOC.rbf
    if  fatload mmc 0 $fpgadata $fpga_image; then
        fpga load 0   $fpgadata $filesize
        bridge enable
    fi
    fatload mmc 0 $loadaddr $boot_image
    fatload mmc 0 $fdt_addr $fdt_image
    setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait uio_pdrv_genirq.of_id=generic-uio
    bootz $loadaddr - $fdt_addr
fi
```

U-Boot のビルド時に config 変数にボードの種類が設定されていて、この config 変数の値によってブートの手順を切り替えています。

uEnv.txt で kernel_verion 変数に Linux カーネルのバージョンを設定して、そのバージョンに対応したカーネルのイメージとデバイスツリーをロードするようにしています。

なお、Linux Kernel v4.8.17 には sdhci ドライバに問題があって、そのままでは ZYNQ では動きません。その回避策として bootargs に sdhci.debug_quirks=64 を追加しています。詳細は[「Zynq + Linux Kernel 4.5(以降) で "mmc0: Timeout waiting for hardware interrupt" が起きる場合の対処方法」](http://qiita.com/ikwzm/items/9d64d9583f8e3eb5c94a) を参照してください。

## 参考

* [https://github.com/ikwzm/FPGA-SoC-Linux](https://github.com/ikwzm/FPGA-SoC-Linux)
* [「FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(ブートイメージの提供)」@Qiita](http://qiita.com/ikwzm/items/cdeb3c9c352776eac683)
* [「FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(ZYBO-Examples)」@Qiita](http://qiita.com/ikwzm/items/61ea3c5a037b83006f17)
* [「FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(DE0-Nano-SoC-Examples)」@Qiita](http://qiita.com/ikwzm/items/ca6229d4e32fb421b0a9)
* [「Zynq + Linux Kernel 4.5(以降) で "mmc0: Timeout waiting for hardware interrupt" が起きる場合の対処方法」@Qiita](http://qiita.com/ikwzm/items/9d64d9583f8e3eb5c94a)

* 石原ひでみ. 『第五章 ついに起動！デュアル・ブート SD カードの作り方』「FPGA マガジン No.12」57頁