はじめに
ARM CPUと一体化されたFPGAでLinuxを動作させているときの話です。
デバイスツリー オーバーレイを使い、LinuxからFPGAをコンフィグレーションする方法について、Terasic DE10nanoというボードを使ってやってみた手順について書かせていただきます(Intel系FPGAを使った場合の記事もあまり見当たらなかったので)。
なお、この仕組みは、「デバイスツリー」、「デバイスツリーオーバーレイ」、「FPGA Subsystem」を使ったものになります。この記事を書くにあたり、それぞれについて調べた内容も書かせていただきました。よろしければこちらもご参照下さい(下記3つの内容はFPGAメーカ非依存の内容になるかと思います)。
資料1:デバイスツリーについて調べてみた 、
資料2:デバイスツリー Overlay について調べてみた
資料3:FPGA Subsystem(FPGA-region)でFPGAをコンフィグする
#ベースとなるSD card の準備
ボードは、Terasic DE10nanoというIntelのCyclone V SoC FPGAが載ったものを使っています。(Intel系のFPGAであれば、異なるボードやFPGA FamilyでもLinuxがbootできるSD cardさえあれば同様の手順でイケると思います。)まずは、ベースとなるLinuxが起動するSD cardを準備します。ボードベンダーが提供してくれていると思います。ここでは、Terasicサイトの DE10-Nano Kit Resources ページのリストから、"Linux LXDE Desktop (kernel 4.5)" 使います。このimageは、RootFile SystemがUbuntu16.04ベースなので、必要なものはaptで取ってこれたりして何かと便利です(Raspiでできることは大抵できる)。以降でKernelを入れ替えていますが、RootFile System(Ubuntuのコマンド系)の方はそのまま使えます。
SD cardへの書き込みができたら、このSD cardでのDE10nanoの起動を確認して下さい。HDMIに接続したモニタにLXDEデスクトップ画面が表示されればOKです。
#Linux Kernel とBaseデバイスツリーのコンパイル
上で用意したSD card imageのKernelを自分でビルドしたものに変更します。一般的に、提供されているイメージ中のKernelは古いバージョンの場合が多いのと、driver やデバイスツリーの中身を見たり修正したりするには、Kernel Sourceが必要なので、自分でKernelをビルドして、動かすというのが良いと思います。
基本は、Intel SoC FPGA 用Linuxのポータルサイト RocketBoards.org のドキュメントに従い、最近のLinux Kernelをビルドし、zImage と DE10nano用のデバイスツリー(.dtb)を作成します。
ただし、ここではオーバーレイによりframe bufferが動作することを確認したいので、zImageにframe buffer driverを追加しておきます。手順はこちらの記事DE10nano 向けLinux Kernel Buildの「おまけ FrameBuffer driver 追加」になります。Kernelパラメータに、Device Drivers-> Graphics support -> Frame buffer Devices -> Altera VIP Frame Reader framebuffer support
を追加してzImageを作成してください。なお、.dtb に関しては、修正の必要はなく、デフォルトのsocfpga_cyclone5_de0_sockit.dtbを使って下さい(デバイスツリーへの frame buffer 追加はオーバーレイで行います)。
出来上がったsocfpga_cyclone5_de0_sockit.dtbは、soc_system.dtbとリネームし、zImageとsoc_system.dtbをSD cardのFATパーティションにコピーします(同名ファイルを上書き)。
Base デバイスツリー内容の確認
オーバレイでコンフィグレーションを行うには、最初に読み込むデバイスツリー(Base tree)にFPGA region nodeがそれなりの内容で定義されている必要があります(資料3 )。これについて確認しておきましょう。
今回使う.dtbファイルのソースは、socfpga_cyclone5_de0_sockit.dtsですが、このファイルが最終的に#includeしているsocfpga.dtsiに下記記述があります。
/ {
...
soc {
...
base_fpga_region: base-fpga-region {
compatible = "fpga-region";
fpga-mgr = <&fpgamgr0>;
#address-cells = <0x1>;
#size-cells = <0x1>;
};
...
};
}
/soc/base-fpga-regionというpath(ラベル名 base_fpga_region)で FPGA region nodeがあり、その中にfpga-mgr propertyも設定されています。この.dtbを使ってデバイスツリーオバーレイによるコンフィグレーションが行えることがわかります。
ちなみに、socfpga.dtsi
は、DE10nanoに限らず、CycloneV, ArriraV, Arria10 用のボードの全.dts に共通して #include されているファイルです。Kernelソースに入っているデフォルトのデバイスツリーでオーバーレイによるコンフィグレーションready になっているということになります。
オーバレイファイルの作成とコンパイル
実際のオバーレイファイルの例です。soc_system.rbf
というビットストリームファイルでコンフィグレーションした後、Frame Bufferを有効にする、というオーバレイファイルです。
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&base_fpga_region>;
#address-cells = <0x1>;
#size-cells = <0x1>;
__overlay__ {
firmware-name = "soc_system.rbf";
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges = <0x00000000 0xff200000 0x00200000>;
alt_vip_vfr_hdmi: vip@31000 {
compatible = "ALTR,vip-frame-reader-14.0", "ALTR,vip-frame-reader-9.1";
reg = <0x00031000 0x00000080>;
max-width = <1024>; max-height = <768>;
bits-per-color = <8>; colors-per-beat = <4>;
beats-per-pixel = <1>; mem-word-width = <128>;
};
};
};
};
-
target = <&base_fpga_region>;
読み込まれているデバイスツリー中で、base_fpga_region
のラベル1を持つnodeに対してオーバーレイを行います。
target-path = "/soc/base-fpga-region"
と書いても等価ですね。 -
firmware-name = "soc_system.rbf";
/lib/firmware/soc_system.rbf
ファイルでコンフィグレーションします。このrbfには、Frame Buffer IPが含まれているものとします。 -
alt_vip_vfr_hdmi: vip@31000 { ... };
Frame Buffer用のnode記述です。{ ... }
内に書かれている内容は、Frame Bufferのドキュメントに従っています(devicetree の書き方のお作法)。
この様に、新たにコンフィグレーションしたことにより追加されたハードウェアを用のnodeを__overlay__
ノードの中で定義します。他にも追加したいドライバがあれば、続けて追記します。
#address-cells
,#size-cells
,ranges
は、Frame Buffer nodeのメモリマップを指定するのに必要なpropertyです(devicetree の書き方のお作法)。
DeviceTree Overlay fileのコンパイル
Kernelをビルドした環境に含まれるdtcコマンドを使います。(FPGA の開発ツールに含まれるdtcコマンドでも構わないとは思います。)
先程の例のmy_overlay1.dtsoをコンパイルしてmy_overlay1.dtboにする場合:
<Kernel Source path>/script/dtc/dtc -o my_overlay1.dtbo my_overlay1.dtso
バイナリのオーバレイファイル my_overlay1.dtso が生成されます。
テスト用SD card作成
オーバレイによるコンフィグレーションの動作テスト用のSD cardを以下の様に作ります。動作テストの全容は以下となります。
電源投入後は、
FPGA: Frame Bufferを含まないデザイン
zImage: Frame Buffer ドライバを含む
DeviceTree: FPGA regionを含む、Frame Bufferは含まない
という状態で起動します。
その後、オーバーレイにより、
FPGA: Frame Bufferを含むデザインに再コンフィグ
DeviceTree: Frame Buffer node 追加
とします。
前節で作成したSD cardのファイルを以下のファイルで置き換えorコピーします。
-
「Linux Kernel とBaseデバイスツリーのコンパイル」で作った zImage ->
FAT partitionの zImage
-
「Linux Kernel とBaseデバイスツリーのコンパイル」で作った socfpga_cyclone5_de0_sockit.dtb ->
FAT partitionの soc_system.dtb
-
DE10nano付属のSystemCDの Demonstrations/SoC_FPGA/ControlPanel/Quartus フォルダ
Demonstrations/SoC_FPGA/DE10_NANO_SoC_GHRD/output_files/soc_system.rbf ->FAT partitionの soc_system.rbf
-
DE10nano付属のSystemCDの Demonstrations/SoC_FPGA/ControlPanel/Quartus/output_files/soc_system.rbf ->
ext3 partitionの /lib/firmware/soc_system.rbf
(もともとのSD imageのFAT partitionのrbfは、このControlPanelのrbfを使っています。ですので、最初にFAT partitionのrbfを /lib/firmware にコピーするということでもOKです。)
オーバーレイ(=コンフィグレーション) 実行
テスト用SD cardでDE10nanoを起動し、root でログインします(以下すべてuart terminal からの作業)。この状態では、HDMIに接続したモニタには画像出力がされていません。また、デバイスファイルにFrame Bufferが存在しないことを確認してみて下さい。
root@DE10_NANO:~# ls /dev/fb?
ls: cannot access '/dev/fb?': No such file or directory
root@DE10_NANO:~#
作成した my_overlay1.dtbo をscpなどでコピーしてきます。
ConfigFsを使った方法(資料2 )でオーバーレイを実行します。オーバーレイのディレクトリ名は test_fb とした例です。
mkdir /sys/kernel/config/device-tree/overlays/test_fb
cat my_overlay1.dtbo > /sys/kernel/config/device-tree/overlays/test_fb/dtbo
正常に実行されれば、cat ... > .../test_fb/dtbo
を実行した瞬間にLEDが一瞬消灯し、再度点灯するはずです。また、デバイスファイルを確認すると以下の様にFrame Bufferデバイス /dev/fb0 が生成されています。
root@DE10_NANO:~# ls /dev/fb? -l
crw-rw---- 1 root video 29, 0 May 21 00:27 /dev/fb0
root@DE10_NANO:~#
Frame Bufferデバイスが有効になったことにより、HDMIに接続されたモニタには、カーソルの点滅が見えてきているはずです。さらにもう一つ、このFrame Bufferを使ってLXDEデスクトップを起動してみましょう。
systemctl start lightdm
これで、最初にTerasicサイトからダウンロードしてきたSD card image("Linux LXDE Desktop" )と同様の状態になりました。
また、Kernelのログにも fpga manager や、frame buffer のログが記録されていますので、dmesg
で確認してみて下さい。
さらに別のコンフィグレーションを行う場合
このオーバーレイを使って何度もFPGAのコンフィグレーションが可能となります。が、FPGAの再コンフィグレーションする際は、FPGA内のハードウェアをアクセスしているARMソフトウェアプロセスを停止してやることが必要ですので、この点は注意してください。この例ですと、FPGAのハードウェアは、Frame Bufferだけですが、これをアクセスしているプロセスとしては、LXDEデスクトップとfbcon(カーソル点滅)2がありますので、それらを停止してから、オーバーレイの削除、新たなオーバーレイの実施、をするべきでしょう。具体的には、
systemctl stop lightdm
echo 0 > /sys/class/graphics/fbcon/cursor_blink
の後、
rmdir /sys/kernel/config/device-tree/overlays/test_fb
を行った後、次のFPGAコンフィグを行うオーバレイを実施します。
mkdir /sys/kernel/config/device-tree/overlays/test_fb2
cat my_overlay2.dtbo > /sys/kernel/config/device-tree/overlays/test_fb2/dtbo
注意点
いくらFPGAが丸ごと書き換えられると言っても、動作の仕組みから考えると、書き換えられるFPGAデザインには制約があると思われますので、その点についてちょっと。SoC FPGAの最初の最初のbootには、preloaderというコードが走りますが、このpreloaderは、FPGA のコンパイル時に生成されるhandoff_file情報も加味してbuildするので、今の起動に使われたpreloaderでの設定を保った範囲のFPGAデザインに書き換える必要があるはずです。具体的には、handoff_file情報は、FPGAのデザインで使うPlatform Designer(Qsys)での HPS IP(ARM CPUが入ったモジュール)の設定情報なので、HPS IPの設定が同一のFPGAデザインなら、Linuxを起動したままでも書き換えて動作させることができるということになるかと思います。
HPSとFPGA間のbridgeの設定(ビット幅やバス本数)はHPS IPでの設定する内容となりますが、これらバスブリッジの設定に関しては、動作中にコンフィグレーションし直して変更することはできない可能性大です。実際にFPGA2SDRAMの設定に関しては、後から追加したbusは、Linux からのコンフィグでは開通しませんでした(preloaderの変更&再起動が必要)。
参考資料・リンク
FPGA+SoC+LinuxでDevice Tree Overlayを試してみた -Qiita
Linux から FPGA コンフィグレーションする方法 by Macnica
-
LinuxのMakefileで作られたbase treeにはシンボル情報も含まれている(-@付きでコンパイルされている)のでオーバーレイ側でbase treeのラベルがオーバーレイファイル中でも使えます。 ↩
-
なぜカーソル点滅が勝手に実行されているかですが、Kernelのコンフィグレーションにて、
Device Drivers-> Graphics support -> Console display driver support -> Framebuffer Console support
がenableになっているためです。Kernelを作る際にこれをdisableにしておけばfbconのケアは必要なくなります。 ↩