FPGA+SoC+Linuxのブートシーケンス(ZYNQ+Vivado編)

## はじめに


[前回](http://qiita.com/ikwzm/items/b4ccae34cf554c80d1b4)は[「 FPGA+SoC+Linuxのブートシーケンスの概要」](http://qiita.com/ikwzm/items/b4ccae34cf554c80d1b4)を説明しましたが、ここでは、Xilinx社のZYNQ に Vivado SDK で作った  FSBL と U-Boot を使って Linux をブートする際のシーケンスとデザインフローを説明します。

また、ZYNQの場合は、FSBLを使う方法の他に U-Boot-splを使う方法もあります。U-Boot-splを使う方法は[「FPGA+SoC+Linuxのブートシーケンス(ZYNQ+U-Boot-SPL編)」](http://qiita.com/ikwzm/items/efacec8d8e0744e17d45)にあります。あわせてご覧ください。

## ブートシーケンス


下図にZYNQでLinuxを動作させる時のブートシーケンスを示します。


![図1 ZYNQ の Linux の起動までのブートシーケンス](https://qiita-image-store.s3.amazonaws.com/0/24981/aa3e7109-0fb1-4851-fd7e-50d70841ff05.jpeg)

図1 ZYNQ の Linux の起動までのブートシーケンス

<br />

### 


#### ・ステージ 0 (BootROM)


ZYNQのステージ0ブートローダーはSDCardの第一パーティションにFATファイルシステムがある場合、そこからBOOT.binを探します。もし見つかったらBOOT.binを作る際に[bootloader]オプションを付けたブロックを内部RAMに転送して制御を移します。


#### ・ステージ 1 (FSBL)


ステージ1ブートローダー(FSBL)をVivado SDKで作った場合、FSBLにはProgrammable Logicを作った際に指定した各種設定が含まれます。そのため、FSBLがほとんど全ての設定を行います。

各種設定の後、BOOT.bin に含まれるPL部のビットストリームをPLにプログラムして起動します。その後、BOOT.binに含まれるステージ2ブートローダーをSDRAMに転送して制御を移します。

ちなみに、BOOT.binにステージ2ブートローダーの替わりにベアメタルプログラムを入れておけばそのまま実行できます。おそらくそれを前提として、FSBLでほとんど全ての設定を完了するようにしているのでしょう。


#### ・ステージ 2 (U-Boot)


ステージ2ブートローダー(U-Boot)はファイルシステムを使えたり、スクリプトや環境変数などが使えるので、その設定にしたがって、Linuxのカーネルイメージ、Device Tree、場合によってはルートファイルシステムをSDRAMにロードします。その後、Linuxカーネルイメージに制御を移します。

U-Bootが読み込めるDevice Tree は１つだけなので、あらかじめPS側のDevice Tree とPL側のDevice Tree とをマージしておく必要があります。






## デザインフロー


下図にZYNQでLinuxを動作させる時のデザインフローを示します。


![図2 ZYNQでLinuxを動作させるためのデザインフロー](https://qiita-image-store.s3.amazonaws.com/0/24981/4ad4e777-da8c-6950-cd7b-f1c5b3d53719.jpeg)

図2 ZYNQでLinuxを動作させるためのデザインフロー

<br />

### 1. VivadoでFPGAビットストリームファイル(.bit)を生成する


VHDLやVerilog-HDLなどで記述された回路をVivadoを使って論理合成と配置配線を行いFPGAビットストリームファイルを生成します。ここでは詳細な説明は省略します。


### 2. Vivadoでハードウェア情報ファイル(.hdf)をexportする


1.のFPGAビットストリームファイルを生成したプロジェクトから、ハードウェア情報ファイル(.hdf)をexportします。exportの手順に関しては[「Vivadoでハードウェア情報をエクスポートするTclスクリプト」](http://qiita.com/ikwzm/items/057d67d4a9e0828663cf)を参照してください。


### 3. Vivado SDK でハードウェア情報ファイル(.hdf)からFSBL(fsbl.elf)を作る


Vivado SDK を使って、ハードウェア情報ファイル(.hdf)からFSBL(fsbl.elf)をビルドします。FSBLのビルドに関しては[「Vivado SDK でZynq FSBL(First Stage Boot Loader)をビルドするTclスクリプト」](http://qiita.com/ikwzm/items/e64f11844dd23bc6b90b)を参照してください。


### 4. Vivado SDKでハードウェア情報ファイル(.hdf)からDevice Tree を作る


Vivado SDKを使って、ハードウェア情報ファイル(.hdf)からDevice Tree を作ります。ただし、ここで作られた Device Tree はあくまでも参考程度にしかなりません。このDevice Tree を使ってLinuxを起動しようとすると失敗することが多いです。

ここで作ったDevice Tree から、FPGAに実装したモジュールのレジスタアドレスの記述と割り込み関連の記述だけをメモしておきます。

また、モジュールのレジスタアドレスと割り込み番号が判るのであれば、ここでDevice Treeを作る必要はありません。


### 5. U-Bootのソースからu-boot.elfを作る


U-Bootの最新のリポジトリからU-Bootをビルドします。ただし、U-Bootに若干の修正を加える必要があります。詳細は[「ZYBO用U-Bootを、ブート時にuEnv.txtを読むようにして、かつBOOT.binから起動できるようにする」](http://qiita.com/ikwzm/items/1734676d787e2693df47)を参照してください。

なお、U-bootのビルド時にU-Boot Second Program Loader(u-boot-spl)も出来ますが、ここではステージ１ブートローダーにFSBLを使うのでu-boot-splは使用しません。


### 6. bootgen を使ってBOOT.binを作る


1.で作ったFPGAビットストリームファイル、3.で作ったFSBL、5.で作ったu-bootからBOOT.binを作ります。具体的には次のようなBIFファイルを用意して、bootgenコマンドを実行します。


```BIF:u-boot.bif
the_ROM_image:
{
  [bootloader]fsbl.elf
  design_1_wrapper.bit
  u-boot
}
```



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


the_ROM_imageの[bootloader]には3.で作ったFSBL.elfを指定します。次の行は1.で作ったFPGAのビットストリームファイルを指定します。最後に5.でビルドしたu-boot(ELFファイル)を指定します。


### 7. Linux Kernel Source から zImageとDevice Tree Blob(.dtb)を作る


Linux  Kernelのソースコードを入手してビルドします。最近のLinux はZYNQ用のカーネルもビルドできるようになっています。

ビルドすると、Linux Kernelのイメージと同時に Linux Kernel を起動するための Device Tree Blob(.dtb)も生成されます。

### 8. Linux Kernel Sourceから作ったDevice Tree とVivado SDKで作ったDevice Treeをマージする


7.で出来たDevice Tree Blob(.dtb) には、FPGAに実装したモジュールに関する情報が含まれていません。FPGAに実装したモジュールがLinuxとは無関係に動作するのであれば特にDevice Tree Blob に手を入れる必要はありませんが、FPGAに実装したモジュールをLinuxから制御する場合はレジスタのアドレスや割り込み番号などを Device Tree Blob に追加する必要があります。

残念ながら、今のところ、この部分を自動化することは出来ません。エディタなどを使ってマージする必要があります。

まず、7.で生成されたDevice Tree Blobを Device Tree Compiler(dtc)を使ってDevice Tree のソースコード(.dts)に戻します。


```Shell
shell% dtc -I dtb -O dts -o devicetree.dts $(LINUX_SRC)/arch/arm/boot/dts/zynq-zybo.dtb
```


その後修正を加えたDevice TreeのソースコードをDevice Tree Compier(dtc)を使ってDevice Tree Blob(devicetree.dtb)に変換します。


```Shell
shell% dtc -I dts -O dtb -o devicetree.dtb devicetree.dts
```



### 9. uEnv.txtを用意する


u-bootがブート時に実行するスクリプトを記述したファイルを用意します。ここではuEnv.txtを使いますが、u-bootには他にも専用のスクリプトファイルがあるので、そちらを使ってもかまいません。

ここではSDカードからLinuxをブートする場合の uEnv.txt の例を示します。SDカードの第一パーティションをFAT(VFAT)でファイルシステムを作り、6.で作ったBOOT.bin、7.で作ったzImage(Linuxカーネルイメージ)、8.で作ったdevicetree.dtb(デバイスツリーファイル)と、このuEnv.txtを置きます。SDカードの第二パーティションにはLinuxのルートディレクトリがあるものとします。


```u-boot:uEnv.txt
uenvcmd=fatload mmc 0 0x03000000 zImage && fatload mmc 0 0x02A00000 devicetree.dtb && bootz 0x03000000 - 0x02A00000
```





### 10. SD Card の第１パーティション(FAT File System)にBOOT.bin、zImage、Device Tree Blob(.dtb)、uEnv.txtを置く


SD Card の第１パーティションにFATで File System を作って以下のファイルを置きます。

 - 6.で作ったBOOT.bin
 - 7.で作ったLinux Kernelのイメージ(zImage)
 - 8.で作ったDevice Tree Blob(.dtb)
 - 9.で作ったuEnv.txt





## 参考


* [FPGA+SoC+Linuxのブートシーケンス(概要編)](http://qiita.com/ikwzm/items/b4ccae34cf554c80d1b4)
* [FPGA+SoC+Linuxのブートシーケンス(ZYNQ+U-Boot-SPL編)](http://qiita.com/ikwzm/items/efacec8d8e0744e17d45)
* [FPGA+SoC+Linuxのブートシーケンス(Altera SoC+EDS編)](http://qiita.com/ikwzm/items/c22d15803ed98f0e52ab)
* [ZYBO用U-Bootを、ブート時にuEnv.txtを読むようにして、かつBOOT.binから起動できるようにする](http://qiita.com/ikwzm/items/1734676d787e2693df47)
* [Vivado SDK でZynq FSBL(First Stage Boot Loader)をビルドするTclスクリプト](http://qiita.com/ikwzm/items/e64f11844dd23bc6b90b)
* [Vivadoでハードウェア情報をエクスポートするTclスクリプト](http://qiita.com/ikwzm/items/057d67d4a9e0828663cf)