はじめに
ここ最近、Xilinx社のZYNQやAltera社のCyclone V SoC など、Processing System(PS)(CPU+各種Peripheral)に FPGA の Programmable Logic(PL) 部を System on Chip(SoC) として1チップにした製品がリリースされています。
SoC には Linux 等の OS を載せて使うケースも多くなってきました。
SoC の Processing System に Linux を Boot するのは比較的資料も揃っているのですが、Programmable Logic が絡んでくるとこれらの設定なども並行して行う場合があるので、ここで少しまとめてみました。
Processing System の Linux 起動までのブートシーケンス
まず、Processing System だけで Linux を起動する場合のブートシーケンスをおさらいしておきます。
Processing System で Linux を動かすのに必要なコンポーネント
Processing System で Linux を動かす場合、以下のコンポーネントの設定が必要です。
####I/O Pin
SoC は大抵 複数のペリフェラルがI/O pinを共有しています。I/O pinをどのペリフェラルが使うか、またI/O pin の電気的特性を設定しておく必要があります。
####Processing System(PS) Internal
Processing SystemはCPUの他に複数のペリフェラルを持っており、これらを使うためにはまず内部クロックや内部インターコネクトの設定と起動が必要です。
####SDRAM
SoC は DDR-SDRAM Interface を持っています。DDR-SDRAM を使う場合はこれらの設定と起動が必要です。
####Device Tree
ARM で Linux を起動する場合、Internal System の構成や各種ペリフェラルのレジスタアドレスや割り込みの構成などを Device Tree で指定します。この Device Tree は Linux の起動前(正確にはデバイスドライバの起動前)になんらかの方法でメモリにロードしておく必要があります。
####Peripherals
SoC には CPU の他に UART、I2C、USB、SDIO、SPI、Ethernetなど複数のペリフェラルがあります。このうち、特にBoot時に必要なペリフェラル(UART、Timer、SDCardからBootする場合はSDIO、I2CメモリからBootする際はI2Cなど)は、Bootする前に設定と起動が完了していなければなりません。
Processing Systemのブートシーケンス
最近は次のように4段階でLinuxをbootします。
図1 Processor System の Linux の起動までのブートシーケンス
####ステージ 0 (BootROM)
Processing Systemは内部にハードコードされた BootROM を持っており、パワーオンリ セ ット または システムリ セ ット 後に CPU で実行されます。このステージではProcessing Systemが動作する最低限の設定を行います。BootROMのプログラムは、SDCard やI2CなどのBootデバイスにプログラムされた ステージ1ブートローダーを内部SRAMに転送し制御を移します。内部SRAMは初期化などの設定が必要無いためすぐに使えますが容量が限られています。
####ステージ1 (FSBL/Preloader/U-boot-SPL)
ステージ1ブートローダーは、XilinxはFirst Stage Boot Loader(FSBL)、AlteraはPreloader、U-BootはSecondary Program Loader(SPL)と呼んでいます。
ステージ1ブートローダーは小容量の内部SRAMで実行されるため、あまり複雑な処理はできません。一般的にはブートに必要な設定(I/O Pin、内部クロック、内部インターコネクト、UART、Bootデバイス、SDRAM I/F)だけを行います。SDRAM I/F の設定後、Bootデバイスにプログラムされたステージ2ブートローダーをSDRAMに転送し制御を移します。
####ステージ2 (U-Boot)
ステージ2ブートローダには U-Boot が使われることが多いです。U-Boot はブートデバイスから Linuxのカーネルとデバイスツリー、場合によってはルートファイルシステムをメモリにロードしLinuxカーネルに制御を移します。
ステージ2ブートローダーはSDRAM等の比較的大容量なメモリで実行されるため、ステージ1ブートローダーよりも複雑な処理が出来ます。
####ステージ3 (Linux Boot)
最後にLinuxカーネルの初期化シーケンスが起動します。カーネルに含まれているデバイスドライバが、メモリにロードされているデバイスツリーを参照しながら、各種ペリフェラルの設定と起動を行います。
Programmable LogicをLinux上で動作させるまでのシーケンス
Programmable Logicを動作させるまでのシーケンス
####Programmable Logic のクロック設定
Processing SystemがProgrammable Logicにクロックを供給する場合は、Programmable Logic を起動する前に、このクロックの周波数などを設定しておく必要があります。
####Programmable Logic にプログラムをロード
PLをプログラムするためのペリフェラルがProcessing Systemにあるので、それを使ってプログラムをロードします。ロードしている間は、誤動作しないようにPL部をリセットしておくなり、PL-PS I/F をディセーブルしておくなりしておく必要があります。
PLのプログラムにはPL側のI/O pin の設定も含まれており、プログラムのロードが完了した時点でI/O pinもイネーブルされます。
####Programmable Logic-Processing System I/F の設定
PLとPSのインターフェースを設定します。ただし誤動作を防ぐため、PLへのプログラムのロードが終了するまでインターフェースは停止させておく必要があります。
####Programmable Logic の起動
通常はPLへのプログラムのロードが完了した時点で自動的にPLが起動しますが、場合によってはなんらかの処理(例えばPL側へのリセット信号解除など)が必要な場合があります。
Linuxデバイスドライバを動作するのに必要なコンポーネント
####Device Tree
Programmable Logicの制御をLinuxのデバイスドライバで行う場合、そのレジスタアドレスや割り込み番号をDevice Tree で指定します。Device Tree はLinuxの起動前(正確にはデバイスドライバの起動前)になんらかの方法でメモリにロードしておく必要があります。
####デバイスドライバ
LinuxでProgrammable Logicの制御を行いたい場合、デバイスドライバが必要です。PLのために新たに用意するか、あるいはLinuxに標準でついてくるUIO(User space I/O)ドライバを使うことが多いです。
このデバイスドライバを起動する前に、PLは起動済みでなければなりません。何故なら、デバイスドライバはPLのレジスタにアクセスする必要があるからです。同様に、デバイスドライバを起動する前にLinux がこのPLのDevice Tree を読んでおく必要があります。何故なら Device Tree にレジスタのアドレスや割り込み番号が記述されているからです。