Boot procedure and memory structure of PolarFire SoC
先日は、PolarFire SoC Icicle Kit の電源を入れ、Linux のブートを確認しました。多くの皆様は既にそこまでお試しのことと思いますが、ここから先に進むにあたり、どこで何を調べたらいいのか呆然となさっている方もあるかと思います。
SoC デバイス自体に組み込まれているブートの仕組(MSS boot)については先日簡単に説明しましたが、今回は、そこから Linux のブートまでに至るプロセスを説明したいと思います。
その前に、PolarFire SoC のメモリ構成について簡単に説明しておきましょう。
PolarFire SoC のメモリ構成
PolarFire SoC で利用できるメモリは、大きく分けて 3つの種類に分類できます。
- FPGA ファブリックに含まれるメモリ
- RISC-V MSS(Microprocessor Sub-System)に含まれるメモリ
- 外部メモリ
さらに、それらのメモリがどのように MSS のメモリマップに配置されるか、という視点もあるでしょう。
まず最初に、FPGA ファブリックに含まれるメモリから説明しましょう。(私も勉強中なので、間違って理解していたらごめんなさい。)
FPGA ファブリックに含まれるメモリ
FPGA 内部には、MSS に関係なく、従来の(MSS 非内蔵)FPGA にも存在する embedded memory block というものがあります。PolarFire SoC には以下のような memory block があります。(詳細は、Microsemi: UG0912 User Guide PolarFire SoC FPGA Fabric(PDF)の 4節 Embedded Memory Blocks を御参照ください。)
- LSRAM(メモリブロックとして、FPGA で合成される論理回路から使用できるもの)
- μSRAM(略)
- μPROM(LSRAM や μSRAM を初期化するための不揮発メモリ)
- sNVM(同上)
MSS に含まれるメモリ
- eNVM(128Kバイト。主に、E51 コアのブートローダを置くための不揮発メモリ)
- ITIM(16あるいは 32Kバイト。各コアに含まれる命令キャッシュメモリ。マップトメモリとしても使える)
- dCache(32キロバイト。U54 コアに含まれるデータキャッシュメモリ)
- DTIM(8キロバイト。E51 コアに含まれるデータキャッシュメモリ。マップトメモリとしても使える)
- L2-LIM(2Mバイト。コア間で共有。L2 キャッシュとしてもマップトメモリとしても使える。ECC 付き)
外部メモリ(Icicle Kit の場合)
- SPI フラッシュ(128Mバイト
。FPGA ファブリックのコンフィグレーション用) - eMMC(8Gバイト。Linux のファイルシステムを置くなど)
- DDR-SDRAM(2Gバイト。プログラムやデータを置くための大きな RAM)
- その他、SD カードを利用するための QSPI インターフェイスあり
追記(11月2日): Microsemi PolarFire SoC の特徴は、FPGA ファブリックの構成用に SRAM や、外部のコンフィグレーション用 ROM を持たないことだそうです。FPGA の構成は、特殊な半導体プロセス(SONOS と呼ぶそうです)の技術により、各ロジックエレメントの構成やルーティングは、オンチップのこの不揮発プロセス技術によりプログラムされるようです。詳細は、また勉強したいと思います。
ブートに関連のあるメモリ
上記の中で、MSS のブート時に(プログラム読み出しの)対象となるのは、eNVM、eMMC あるいは SD(MMC)カードです。
MSS へのメモリマップ
MSS におけるメモリマップは、UG0880 User Guide PolarFire SoC FPGA Microprocessor Sub-System(PDF)の 11節 MSS Memory Map が詳しいですが、抜粋しますと、
- 0x0100_0000: E51 用の DTIM(高速だけど、サイズは小さい)
- 0x0800_0000: L2-LIM(ITIM、DTIM についで速い。RTOS だけのシステムであれば、ここにプログラムを置くのが簡単)
- 0x2022_0000: eNVM(E51 のブートローダを置くための不揮発メモリ。前述)
- 0x8000_0000: DDR(キャッシュ対象。大きなシステムであればここにプログラムを置く)
- 0xC000_0000: DDR(キャッシュ非対象。キャッシュを通したくないときに使う)
ブートの仕組
お待たせしました。ようやく、ブートの仕組を説明できます。
eNVM と HSS
MSS User Boot については前回説明しましたので、そこで Non-secure ブートが選択されたとしましょう。すると、E51 は eNVM 上のプログラムにジャンプし実行開始します。(エントリポイントをどのように設定するか、などはまだ調べていません。)
余談: eNVM には、Libero SoC や FlashPro Express というソフトでも書き込むことができます。しかし私が試したところでは、どうもブートモードまでは正しく設定されないようで、ブートモードを設定するには、SoftConsole に含まれる(後述の)mpfs-program-bootmode.sh というスクリプトを使わないとうまくいかないようです。(Libero SoC 12.5 にて確認)
このようにして、シングルコアの簡単なプログラムを書き込むだけであれば、eNVM に直接プログラムを置くこともできますが、サイズが 128Kバイトと小さいこともあり、通常はここに Hart Software Services(HSS) という一種のブートローダを置くことになります。
HSS を上記 GitHub リンクからダウンロードしてビルドすると、Default というディレクトリに hss.elf というファイルが生成されます。そうしたら、次のコマンドを実行することで eNVM 上に HSS を書き込むことができ…。
あ、その前にプログラミングツールの話をしないと。
プログラミングツール
PolarFire SoC にビットストリームを書いたり、SoftConsole からデバッグしたりするためには、FlashPro という JTAG プログラマが必要になります。先日説明したように、現状では Icicle Kit 上のオンボード FlashPro は利用できず、外部のプログラマが必要になります。そのためのソフトウェアは Libero SoC にも SoftConsole にも含まれず、ここからダウンロードし、別途インストールする必要があります。以前は FlashPro プログラミングソフトウェアというものがあったようですが、現在は Programming and Debug Tools という名前のツールに取って代わられ、この中にある FlashPro Express というソフトウェアを使うことになっています。
以下、Libero SoC、SoftConsole、また Programming and Debug Tools をインストール済みという前提で説明します。
HSS を書き込んでみる
ここでは SoftConsole 6.4、Libero SoC 12.5 を使います。必要に応じて、パス名は変更してください。
以下のコマンドを実行することで、ブートモードが 1(non-secure boot)に設定され、hss.elf が書き込まれます。ポイントは、hss.elf が置かれているディレクトリ上で実行しなくてはいけない、という点です。
bash$ cd hart-software-services/Default bash$ export SC_INSTALL_DIR=~/Microchip/SoftConsole-v6.4 bash$ export FPGENPROG=/usr/local/microsemi/Libero_SoC_v12.5/Libero/bin64/fpgenprog bash$ ~/Microchip/SoftConsole-v6.4/extras/mpfs/mpfs-program-bootmode.sh --bootmode 1
USB-UART ブリッジを経由で UART(2番目の仮想ポート)を覗くと、書き込んだ(最新版の)HSS が起動してくることが分かると思います。
注意点
いくつか注意点があります。
まず最初に、上記では HSS を書き込むことができますが、他に FPGA のコンフィグレーションも完了していることが前提です。FPGA が構成されていないと、MSS は動作しません。Icicle キット用の最新版のコンフィグレーション(合成済みのビットストリーム)は、こちら(リンク先が無くなりました)からダウンロードできます。なお、GitHub サイトでは詳しく書かれていませんが、Icicle-Kit-emmc-2020.10.zip の中にある .job ファイルを扱うには、FlashPro Express(/usr/local/microsemi/Program_Debug_v12.5/Program_Debug_Tool/bin/FPExpress)を起動して、そこから利用することになります。
2つめの注意点は、もし PolarFire SoC の MSS がブートモード 0 になっている場合、FlashPro Express で .job ファイルを処理してプログラムしても、HSS はブートしない、ということです。前述のように、mpfs-program-bootmode.sh を使わないと、ブートモードが正しく設定されないのではないか、と想像しています。
追記(10月19日): 3つめの注意点として、eMMC 用の Linux ブートイメージと SD カード用のイメージは同じでない、というものです。上記 GitHub サイトから(.wic.gz あるいは .wic.zip ファイル)をダウンロードする時には注意してください。例えば、eMMC 用のブートイメージで SD カードからブートすると、
[ 0.805143] Waiting for root device /dev/mmcblk0p3...
のようなカーネルメッセージを出して止まってしまいます。具体的にブートイメージの何が違うかというと、(.dts から生成される).dtb ファイルが異なります。.dts の差分を見てみましょう。
$ diff -cw icicle-kit-es/icicle-kit-es.dts icicle-kit-es-sd/icicle-kit-es-sd.dts *** icicle-kit-es/icicle-kit-es.dts Sun Oct 18 12:14:25 2020 --- icicle-kit-es-sd/icicle-kit-es-sd.dts Sun Oct 18 12:14:25 2020 *************** *** 246,252 **** clocks = <&clkcfg 11>; status = "okay"; }; ! emmc: sdhc@20008000 { compatible = "cdns,sd4hc"; reg = <0x0 0x20008000 0x0 0x1000>; interrupt-parent = <&L1>; --- 246,252 ---- clocks = <&clkcfg 11>; status = "okay"; }; ! /*emmc: sdhc@20008000 { compatible = "cdns,sd4hc"; reg = <0x0 0x20008000 0x0 0x1000>; interrupt-parent = <&L1>; (略)
分かりましたね? 回路図を見ても分かるのですが、eMMC と SD カードでは、インターフェイスの仕様が違うのですね。
追記(10月22日): HSS(Hart Software Services)のバイナリも、eMMC 版と SD カード版では異なります。
一点目ですが、hart-software-services の boards/icicle-kit-es/ にある def_config と def_config.sdcard を比べてみてください。SD カードを使うときは、後者を(.config にコピーするなどして)使います。
二点目ですが、boards/icicle-kit-es/soc_config は eMMC 用の XML ファイルから生成されています。SDカードで使うときは、SD カード用の XML ファイルで boards/icicle-kit-es/soc_fpga_design/xml/ICICLE_MSS_0.xml を置き換えます。soc_config ファイルは make の途中で上書きされてしまうので、soc_config を差し替えてもダメです。
HSS と HSS ブートイメージ
さて。上記までで HSS が無事に起動するようになりましたが、それだけでは Linux や RTOS アプリケーションを起動することはできません。もちろん、出荷時には eMMC に HSS ブートイメージとして U-Boot が書き込まれていますから、そのまま Linux のブートを確認することはできますが、eMMC の内容はどうやって用意したら良いのでしょうか。
HSS は、自分が次にブートする(2次ブート)のイメージを「HSS boot image」と呼んでいます。これは eNVM に書き込んだ hss.elf とは別物ですので注意しましょう。この HSS boot イメージは eMMC(あるいは SD カード。以下略)に置かれます。単純に、eMMC 上の先頭セクタから置くかというと、HSS は凝った設計になっており、まず eMMC が GUID Partition Table(最近のハードディスクや SSD の標準的なパーティション構造。略して GPT)で管理されていることを前提としています。eMMC が GPT でフォーマットされていることを確認すると、次にその中から BIOS boot partition というものを探します。それが見つかると、その先頭セクタから HSS boot image として取り込みます。HSS boot image の先頭には特殊なマジック値(0xde, 0xc0, 0x07, 0xb0)があるので、確かにそれが HSS boot image であることが分かる訳です。
さて、ようやく HSS boot image を取得できましたが、これも単純な ELF ファイルではなく、マルチコアを考慮した構造データとなっています。詳しくは、以前のウェビナーシリーズの第 13回で取り上げられているので、参考にしてみください。簡単に説明すると、次のような構造になっています。
- ヘッダ
- U54 コア 1 用のブートブロック
- ロードアドレス
- エントリポイント
- サイズ
- CRC32
- U54 コア 2 用のブートブロック(同上)
- U54 コア 3 用のブートブロック(同上)
- U54 コア 4 用のブートブロック(同上)
- U54 コア 1 用の BSS(ゼロ初期化)用情報
- 開始アドレス
- サイズ
- U54 コア 2 用の BSS(ゼロ初期化)用情報(同上)
- U54 コア 3 用の BSS(ゼロ初期化)用情報(同上)
- U54 コア 4 用の BSS(ゼロ初期化)用情報(同上)
もちろん、この HSS boot image を生成してくれるツールがあり、それが HSS のディストリビューションの中の、tools/bin2chunks です。例えば、
bash$ (どこかの)/tools/bin2chunks/bin2chunks \ 0x80200000 0x80200000 0x80200000 0x80200000 \ 32768 payload.bin 1 1 (どこかの)/u-boot.bin 0x80200000
のように使います。(これは、Buildroot 用に定義されていた例で、現在デフォルトで使われている Yocto のものとは少し違うかも知れません。)
上記の例では、各 U54 コアの実行開始アドレスは 0x80200000 であり、チャンクサイズ(ブロックの分割サイズ。特に変更せず、ここままの値で良いぽい)が 32768、生成するブートイメージが payload.bin、コア 1 用のイメージが u-boot.bin で、ロードアドレスが 0x80200000 であることを示しています。「1 1」とあるのは、初めの 1 がコア ID で、次の 1 は、実行モードがスーパバイザモード(RISC-V のマニュアル参照)であることを示しています。
Yocto/OpenEmbedded のパーティションテーブル
上記までを読んで、U-Boot が起動するまでのおおまかな流れはお分かりになったかと思います。最後に、eMMC 上の他のパーティションを簡単に説明しておきます。
上記の BIOS boot partition 以外に、FAT のパーティションと ext4 のパーティションが用意されており、前者には
- boot.scr.uimg: U-Boot 用のスクリプトファイル?
- fitImage: Linux カーネルや initramfs、DTB ファイルで構成された FIT イメージ
- payload.bin: なぜかここにも HSS boot image(用途は不明)
後者には Linux の root パーティションが含まれます。
OpenEmbedded や Buildroot については皆様のほうがお詳しいと思いますので、私は次回から、bare-matel ライブラリを使った組込プログラミング評価のほうに移っていきたいと考えています。
今回もおまけ(VMware との相性は?)
私は普段、Linux 環境は VMware のゲスト OS として用意しているのですが、そこで Programming and Debug Tools や OpenOCD を動かすと、異様に動作が遅いことに気づきました(VMware Fusion 12.0.0 で確認)。特に後者は、あまりに遅すぎて OpenOCD や GDB がタイムアウトしてしまうほどです。実環境の Linux で動かしたら問題は解決しました。
私は実環境の PC をたくさん用意するのは嫌いなので困ったことですが、やむを得ず、以前使っていた MacMini を持ち出し、そこに Ubuntu を入れてみました。そこでも Fusion Drive 絡みで一苦労あったのですが、それはさておき。
実環境の Linux に Libero SoC や SoftConsole を入れて困るのは、机の上にディスプレイやキーボードを新たに用意しないといけないことです。そこで X11 サーバー(XQuartz)や VNC を駆使しようとしたのですが、あちらを立てればこちらが立たず状態で、これまた一苦労。結論としては、Libero SoC や Programming and Debug Tools は X11 に飛ばすのが良さそうです。よくあるフォント絡みの問題も、今のところなさそう。ただし SoftConsole は X11 だとうまく動作せず(無理やり起動させることはできましたが、ソース編集画面がちらついたりして実用にならず)、かといって VNC ではレスポンスが悪すぎて、これまた実用になりません。
今のところの解決策としては、
- FPGA のプログラミング時は、Libero SoC や Programming and Debug Tools を X11 経由で使う。
- Libero SoC 上での設計は VMware 上で行う。
- ソフトのデバッグ時は、実環境 Linux 上のコマンドラインで OpenOCD だけ立ち上げ、VMware 上の SoftConsole からリモートで OpenOCD に接続して使う。
というのが現実解のようです。つまり、実環境とVMware 上と両方にツールのインストールが必要な訳ですね。
それにしても JTAG エミュレータと VMware は、いつも相性の問題で悩まされます。(TI のかつての XDS510 は全く動かず。XDS100 も遅い。XDS560 は大丈夫。) これは、JTAG エミュレータによる通信は小さなデータを双方向に高頻度でやりとりすることが多く、VMware による USB エミュレーションのわずかの性能劣化が、大きな遅延(レイテンシ)に繋がるためだと思います。デバイスメーカーさんには、ぜひとも VMware のような仮想環境での JTAG エミュレータの使い勝手に、もう少し配慮をお願いしたいところです。(近年、個人的には VMware 無しの開発は考えられない状況になっています。)
今日はここまで。