FPGA+SoC+Linuxのブートシーケンス(Altera SoC+EDS編)

## はじめに


[前回](http://qiita.com/ikwzm/items/b4ccae34cf554c80d1b4)は [「FPGA+SoC+Linuxのブートシーケンスの概要」](http://qiita.com/ikwzm/items/b4ccae34cf554c80d1b4)を説明しましたが、ここでは、Altera社の Altera SoCに Altera SoC Embedded Design Suite(Altera SoC EDS)で作ったブートローダを使ってLinuxをブートする際のシーケンスを説明します。


## ブートシーケンス


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


![図1 Altera SoC の Linux の起動までのブートシーケンス](https://qiita-image-store.s3.amazonaws.com/0/24981/3a11ee21-61df-ac66-57c9-0cdd2455411d.jpeg)

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

<br />



### 


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


Altera SoC のステージ0ブートローダー(BootROM)は、SD Cardの特殊パーティション(パーティションタイプ=0xa2)にあるpreloader imageを内部RAMに転送して制御を移します。


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


ステージ1ブートローダー(preloader)は、Hard Processor System(HPS)の各種設定(HPS I/O pin、Clock、Interconnect、Timer、UART、SDRAM)を行った後、ステージ2ブートローダーをSDRAMに転送して制御を移します。

Altera SoC EDSでは、この preloader はU-bootのSPL(Secondary Program Loader)をカスタマイズして使っています。ただし元になっているU-Bootのソースコードは、本家ではなく、Altera SoC EDS に含まれているものです。


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


ステージ2ブートローダー(U-boot)はFPGA Fabricのロードと起動を行った後、Linuxのカーネルイメージ、Device Tree、場合によってはルートファイルシステムをSDRAMにロードし、Linuxカーネルイメージに制御を移します。

上記のシーケンスはU-boot専用のスクリプトで記述します。U-boot自体はそのスクリプトを実行しているだけです。

このU-Bootは Altera SoC EDSに含まれているもので、FPGA Fabric のロードが出来るようにカスタマイズされています。また、本家の最新の U-Boot にも FPGA にロードする機能が追加されています。

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




## デザインフロー


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


![図2 Altera SoCでLinuxを動作させるためのデザインフロー](https://qiita-image-store.s3.amazonaws.com/0/24981/5be849dd-d0e9-086c-2274-b79270518912.jpeg)


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

<br />



### 1. Qsys&Quartus でFPGA Fablicの論理合成と配置配線を行う


Qsys&Quartusを使って論理合成と配置配線を行いFPGA Hardware SRAM Object File(.sof)を生成します。

QsysでHDLを生成する際、.sopcinfoファイルも生成されます。

配置配線時にpreloaderを作るための各種情報を格納したディレクトリ(Handoff Folder)も同時に生成されます。通常はhps_isw_handoff/\<hps_entry_name\>/ です。\<hps_entry_name\>は、Qsysのプロジェクト名+HPSのインスタンス名になります。


### 2. Convert Program File でSRAM Object File(.sof)からRAW Binary File(.rbf)を生成する


Quartus のメニューから Convert Program Files... を選び、Convert Program Fileを起動するか、次のように直接起動してください。


```Shell
shell% quartus_cpf -c -o bitstream_compression=on xxxx.sof xxxx.rbf
```


-c はファイル変換(convert)することを示します。

-o bitstream_compression=on で圧縮されたRAW Binary Fileを生成することを示します。

xxxx.sofには生成されたSRAM Object Fileの名前を、xxxx.rbfには生成するRAW Binary Fileの名前を指定します。


### 3. sopc2dtsでDevice Tree を生成する


Altera SoC EDS にはQsysで作られた.sopcinfoファイルからDevice Tree を生成するコマンド(sopc2dts)が用意されています。これを使ってDevice Tree を生成します。
ただし、ここで作られた Device Tree はあくまでも参考程度にしかなりません。このDevice Tree を使ってLinuxを起動しようとすると失敗することが多いです。
ここで作ったDevice Tree から、FPGAに実装したモジュールのレジスタアドレスの記述と割り込み関連の記述だけをメモしておきます。
また、モジュールのレジスタアドレスと割り込み番号が判るのであれば、ここでDevice Treeを作る必要はありません。



sopc2dtsを次のように起動します。


```Shell
shell% sopc2dts --input <qsys_project>.sopcinfo --output xxxx.dts --type dts --bridge-removal-all --clocks
```


--input \<qsys_project\>.sopcinfo で入力する.sopcinfoファイルを指定します。\<qsys_project\>にはQsysのプロジェクト名を指定します。

--output xxxx.dts で生成するDevice Tree のソースファイルを指定します。

--type dts で生成するファイルがDevice Tree Source(.dts)であることを示します。

その他のオプションは、マニュアルを参照してください。


### 4. BSP Generatorを使ってPreloader Support Packageを作る


ここでは BSP Generator を使ってpreloaderを作るための環境を生成します。少し紛らわしいのですが、AlteraではpreloaderのことをSecond Stage Boot Loader(SSBL)と呼ぶこともあり、[Altera SoC Embedded Design Suite User Guide](https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/ug/ug_soc_eds.pdf)にはこの工程をSecond Stage Boot Loader Support Package Generator(SSBL Generator)と記述されています。

具体的には次のように bsp-create-settings コマンドを起動します。


```Shell
shell% bsp-create-settings --type spl --bsp-dir ./build --preloader-settings-dir hps_isw_handoff/<hps_entry_name>/ --settings ./build/settings.bsp --set spl.boot.WATCHDOG_ENABLE false
```


--type でBoard Support Package(BSP)のタイプを指定します。ここではprerloader を作るため、splを指定します。

--bsp-dir でBoard Support Package を作るディレクトリを指定します。ここでは./build を指定しています。

--preloader-settings-dir で1.で生成したpreloaderを作るための各種情報を格納したディレクトリ(Handoff Folder)を指定します。

--settings で生成する BSP settingsファイルを指定します。ここでは、./build/settings.bspを指定しています。

--set spl.boot.WATCHDOG_ENABLE false でpreloaderが動作しているときはウォッチドッグタイマーが動かないようにしています。


### 5. preloaderとU-bootをビルドする


4.で生成したPreloader Support Packageディレクトリに入り、makeコマンドを実行すると、まず Altera SoC EDS にあるu-boot-socfpga.tar.gz がPreloader Support Packageディレクトリのuboot-socfpga/に展開されます。これはU-boot をAltera がカスタマイズしたものです。その後preloaderのビルドが始まります。

```shell
shell% cd ./build
shell% make spl
shell% make mkpimage-spl
shell% make uboot
```

make splで、preloader(./uboot-socfpga/spl/u-boot-spl.bin)がビルドされます。

make mkpimage-spl でpreloaderのイメージファイル(preloader-mkpimage.bin)がPreloader Support Packageディレクトリに生成されます。

make uboot で、U-Bootのイメージファイル(./uboot-socfpga/u-boot.img)がビルドされます。


### 6. prelodaerとu-bootをSD Card の特殊パーティションに書き込む


Altera SoC のステージ0ブートローダー(BootROM)は、SD Cardの特殊パーティション(パーティションタイプ=0xa2)にあるpreloader imageを実行するので、preloaderのイメージファイル(preloader-mkpimage.bin)と、u-bootのイメージファイルをSD Card特殊パーティションに書き込みます。Linuxの場合、次のようにします(SD Card の特殊パーティションが/dev/sdc3の場合)。


```Shell
shell% dd if=preloader-mkpimage.bin of=/dev/sdc3 bs=64k seek=0
shell% dd if=uboot-socfpga/uboot.img of=/dev/sdc3 bs=64k seek=4
```





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

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

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


### 8. Linux Kernel Sourceから作ったDevice Tree と sopc2dts で作った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 socfpga.dts $(LINUX_SRC)/arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dtb
```


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


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



### 9. u-boot.scrを用意する


u-bootがブート時に実行するスクリプト(u-boot.scr)を用意します。

Altera SoC EDS のu-bootはSD Card の第一パーティションにu-boot.scrという名前のファイルがあった場合、ブート時にこのファイルをスクリプトとして実行します。u-boot.scrはテキストファイルではなく、u-bootのスクリプト専用のイメージファイルです。

まず元になるスクリプトをテキストファイルで作ります。


```scr:u-boot.txt
fatload mmc 0:1 $fpgadata xxxx.rbf;
fpga load 0 $fpgadata $filesize;
set fdtimage socfpga.dtb;
run bridge_enable_handoff;
run mmcload;
run mmcboot;
```


この例では、次のようにブートします。

 1. xxxx.rbfという名前のRAW Binary File(.rbf) を変数$fpgadataで示されるメモリアドレスにロード。
 2. 変数$fpgadataで示されるメモリアドレスにロードしたxxxx.rbfのデータをFPGAにロード。
 3. 変数fdtimageにDevice Tree Blobのファイル名(ここではsocfpga.dtb)をセット。
 4. run bridge_enable_handoff というコマンドでFPGAとHPSとのインターフェースをイネーブルにする。
 5. run mmcload でzImageと変数fdtimageに設定されているDevice Tree Blobをメモリにロード。
 6. run mmcboot でメモリにロードしたzImageをブート。

この u-boot.txtをu-bootイメージファイル(u-boot.scr)に変換します。変換には5. でU-bootをビルドしたときに同時にビルドされたツールを使います。


```Shell
shell% ./uboot-socfpga/tools/mkimage  -A arm -O linux -T script -C none -a 0 -e 0 -n "bootscript" -d u-boot.txt u-boot.scr
```



### 10. SD Card の第１パーティション(FAT File System)にzImage、Device Tree Blob(.dtb)、RAW Binary File(.rbf)、u-boot.scrを置く


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

 - 2.で作ったRAW Binary File(.rbf)
 - 7.で作ったLinux Kernelのイメージ(zImage)
 - 8.で作ったDevice Tree Blob(.dtb)
 - 9.で作ったu-boot.scr





## 参考


* [FPGA+SoC+Linuxのブートシーケンス(概要編)](http://qiita.com/ikwzm/items/b4ccae34cf554c80d1b4)
* [FPGA+SoC+Linuxのブートシーケンス(ZYNQ+Vivado編)](http://qiita.com/ikwzm/items/1614c35233e1836c7a26)
* [FPGA+SoC+Linuxのブートシーケンス(ZYNQ+U-Boot-SPL編)](http://qiita.com/ikwzm/items/efacec8d8e0744e17d45)
* [Altera SoC Embedded Design Suite User Guide](https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/ug/ug_soc_eds.pdf)