ZYBO用U-Bootを、ブート時にuEnv.txtを読むようにして、かつBOOT.binから起動できるようにする

# はじめに

長らく古い U-boot を使っていたのですが、最新(2016年1月現在)のU-bootではFPGAのロードなんかにも対応しているということで、導入してみました。しかし、次の点が少し不便だったので手を加えました。

 - ZynqのBOOT.binにu-boot(ELFファイル)を入れるとデバイスツリーが無いという理由でu-bootの起動に失敗する。
 - Linuxのブート方法がFIT(Flat Image Tree)しかない。
 - 古い U-boot では有効だった uEnv.txt によるブート方法の変更が出来なくなっていた。

この記事ではこれらを修正した U-bootのビルド方法を説明します。

# U-Boot のビルドの手順

## U-boot のダウンロード

````
shell% git clone git://git.denx.de/u-boot.git
shell% cd u-boot
````

## ZYBO用のブランチを作る

今回はソースコードに手を加えるので、専用のブランチを切って作業します。

````
shell% git checkout -b zynq-zybo
````

## ビルドするu-boot(ELFファイル)にデバイスツリーを含めるように定義ファイルを修正する

デフォルトのままだと、u-boot(ELFファイル)にu-boot用のデバイスツリーが含まれません。これだとBOOT.binに含まれたu-bootが起動できないので、u-bootにデバイスツリーが含まれるように定義ファイルを修正します。
具体的には、configs/zynq_zybo_defconfig の最後に次の行を追加します。

````def_config:configs/zynq_zybo_defconfig
  :
 中略
  :
CONFIG_OF_EMBED=y
````

## ブート時にuEnv.txtの内容でブートするようにソースコードを修正する

include/configs/zynq-common.h で定義されている CONFIG_EXTRA_ENV_SETTINGS を次のように修正します。

````C:include/configs/zynq-common.h
#define CONFIG_EXTRA_ENV_SETTINGS	\
	"fit_image=fit.itb\0"		\
	"load_addr=0x2000000\0"		\
	"fit_size=0x800000\0"		\
	"flash_off=0x100000\0"		\
	"nor_flash_off=0xE2100000\0"	\
	"fdt_high=0x20000000\0"		\
	"initrd_high=0x20000000\0"	\
	"norboot=echo Copying FIT from NOR flash to RAM... && " \
		"cp.b ${nor_flash_off} ${load_addr} ${fit_size} && " \
		"bootm ${load_addr}\0" \
	"sdboot=echo Copying FIT from SD to RAM... && " \
		"load mmc 0 ${load_addr} ${fit_image} && " \
		"bootm ${load_addr}\0" \
	"jtagboot=echo TFTPing FIT to RAM... && " \
		"tftpboot ${load_addr} ${fit_image} && " \
		"bootm ${load_addr}\0" \
	"usbboot=if usb start; then " \
			"echo Copying FIT from USB to RAM... && " \
			"load usb 0 ${load_addr} ${fit_image} && " \
			"bootm ${load_addr}\0" \
		"fi\0" \
	"bootenv=uEnv.txt\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
````

同様に CONFIG_BOOTCOMMAND も次のように修正します。

````C:include/configs/zynq-common.h
#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"
````

ついでに zImage でブートするコマンド bootz を使えるようにします。何故か Zynq 用の U-Boot は bootz コマンドが使えないようになっています。

````C:include/configs/zynq-common.h
#define CONFIG_CMD_BOOTZ
````


## U-Bootをコンパイルする

クロスコンパイルする場合は、CROSS_COMPILE環境変数を設定してコンパイルします。

````
shell% make zynq_zybo_defconfig
shell% make CROSS_COMPILE=arm-linux-gnueabihf-  u-boot
````
これで u-bootと言う名前のELFファイルが出来ます。

# BOOT.binの作成

## FSBL.elf と FPGAのビットストリームファイルを準備

FPGAのビットストリームファイルとFSBL(First Stage Boot Loader)を用意します。
具体的なやり方は次の記事を参考にしてください。

 - [Synthesijerで作ったモジュールをMessagePack-RPCで制御する(ZYNQ論理合成編)](http://qiita.com/ikwzm/items/0254f1d49602095b5cde)
 - [Vivadoで論理合成と配置配線を実行するTclスクリプト](http://qiita.com/ikwzm/items/36e5911c155c8303b705)
 - [Vivado SDK でZynq FSBL(First Stage Boot Loader)をビルドするTclスクリプト](http://qiita.com/ikwzm/items/e64f11844dd23bc6b90b)

## u-boot.bifを準備

Vivado の bootgen コマンドで BOOT.bin を作るにはBIFファイルが必要です。次のような u-boot.bif を用意します。

````BIF:u-boot.bif
the_ROM_image:
{
  [bootloader]fsbl.elf
  design_1_wrapper.bit
  u-boot
}
````
the_ROM_image の\[bootlader\]には前節で用意したFSBL.elfを指定します。
次の行はFPGAのビットストリームファイルを指定します。
最後にビルドしたu-boot(ELFファイル)を指定します。

## bootgenコマンドを実行してBOOT.bin を作る

````
shell% bootgen -image u-boot.bif -w on -o BOOT.bin
````

# uEnv.txtの例

ここではSDカードからLinuxをブートする場合の uEnv.txt の例を示します。

SDカードの第一パーティションをFAT(VFAT)でファイルシステムを作り、BOOT.bin(FSBLとFPGAビットストリームとu-bootが入っている)、uImage(Linuxカーネルイメージ)、devicetree.dtb(デバイスツリーファイル)、uEnv.txt(u-bootでLinuxをブートするための定義ファイル)を用意します。
SDカードの第二パーティションにはLinuxのルートディレクトリがあるものとします。

uEnv.txtの内容は次の通りです。

````text:uEnv.txt
uenvcmd=fatload mmc 0 0x03000000 uImage && fatload mmc 0 0x02A00000 devicetree.dtb && bootm 0x03000000 - 0x02A00000
````