Booting an embedded program in SD card via HSS (Hart Software Services), on PolarFire SoC.
前回までに、SoftConsole 6.4 に含まれるサンプルプロジェクト mpfs-blinky を JTAG エミュレータ(FlashPro)からロードして実行することができるようになりました。プログラムの構造や、ベアメタルライブラリの使い方を勉強したい気持ちもありますが、まずは、この mpfs-blinky を SD カードなどの不揮発 ROM に書き込んでブートする実験をしてみたいと思います。というのは、今後、取引先などでデモする場合は、やはり電源オンでデモが動作したほうがスマートに見えるだろうからです。
E51 コアを使わずに mpfs-blinky を動かすには
不揮発 ROM からのブートでは、eNVM に直接ターゲットプログラムを書き込むのではなく、Microchip が提供する一次ブートローダ HSS(Hart Software Services)を使用することにします。
最初に必要なのは、プログラムから E51 コア用のコードを取り除くことです。なぜなら、HSS は E51 に常駐するように作られており、将来、AMP(Asymmetric Multiprocessing)の技術を使って Linux とリアルタイム処理を共存させるには、HSS を活用することになるだろうと想像されるからです。また、オリジナルの mpfs-blinky の E51 で実行している内容は、本質的に HSS と処理が重複する部分が多く、HSS を前提とした場合には mpfs-blinky の構造は大きく違ってくるものになるかと思います。
以下に、コードの変更点を説明します。今回の目的は動作確認なので、変更はできるだけ少なくし、ベアメタルライブラリの使い方など、あまり深入りしないことにします。
- src/application/hart0/e51.c のコード全体をコメントアウトするか、プロジェクトから削除します。
- その代わりに、e51.c に含まれていたコードのうち、関数 e51() を除いてすべて hart1/u54_1.c の中にコピーします。
- 関数 u54_1() の先頭に、e51_setup() の呼び出しを追加します。つまり、E51 コアがしていた処理を U54_1 にやってもらうようにする訳です。
- ただし、e51_setup() の中から、以下の処理をコメントアウトします。
- MSS_GPIO_… 関連の関数呼び出しを、一時的にコメントアウトします。割込関連の動作確認は、今後の宿題とします。
- raise_soft_interrupt(1u) の呼び出しをコメントアウトします。今回は U54_1 が初めから動作しているので、U54_1 を起こす必要がないからです。
- u54_1_init_hal() の中から、__asm("wfi") を含む do … while() ループ部分を削除します。
- mss_sw_config.h の中で、MPFS_HAL_FIRST_HART と MPFS_HAL_LART_HART を共に 1 に変更します。(first hart に関しては自明だと思いますが、last hart の変更を忘れないようにしましょう。これを忘れると、関数 main_first_hart() の while ループ中で他コアの「WFI 待ち」(HLS_DATA_IN_WFI)を探し続けて、 ループから抜けられなくなります。)
- リンカファイル mpfs-lim.ld の中で、MEMORY の ram (rwx) を ORIGIN = 0x08020000, LENGTH = 1792k に変更します。
- 同様に、SECTIONS 中の . = 0x08000000 という記述を . = 0x08020000 に変更します。リンカスクリプトの変更については、後述します。
リンカスクリプトの修正について
実験した感じでは、HSS はプログラムを DDR でなく L2-LIM(コア間共有のメモリ)にもロードすることができますが、HSS 自身が L2-LIM の先頭付近をスクラッチパッドとして利用しているので、そこにプログラムをロードすることはできません。HSS の現状のプログラムでは先頭から 90K バイトほどを使っているので、U54_1 用のメモリを後ろにずらす必要があります。今回はきりの良い 128K バイトとし、0x0802_0000 から配置することにしました。
余談: PolarFire SoC の MSS(Microprocessor Sub-System)に搭載される L2-LIM は、以前にも説明したように、キャッシュとしてもマップトメモリとしても使える、コア間共有の SRAM です。この L2-LIM は柔軟にできていて、1 〜 16-way set associative まで、1 way 単位で選択して設定できるようになっています。起動時には 1 way だけが有効になっており、0x0800_0000 から 1920KiB(1920 × 1024バイト)をマップトメモリして利用できるようになっていますが、ソフトウェアの設定で way 数を増やすことができます。
ベアメタルライブラリでは、mss_sw_config.h 内でマクロ MPFS_HAL_HW_CONFIG が #define されているとき、platform/mpfs_hal/system_startup.c 内で config_l2_cache() という関数を呼ぶことで way 数を設定しています。way 数の値は soc_config/memory_map/hw_cache.h の中で、マクロ LIBERO_SETTING_WAY_ENABLE として定義されています。注意点は、この値に 1を加えた数が実際の way 数になる、という点です。(通常は 7 と定義されているので、L2-LIM の半分(8 way)が L2 キャッシュ、残りがマップとメモリになっています。)
ここまでできたら、まずはプログラムをビルドして、JTAG エミュレータからロードして動作することを確認しておいてください。
HSS ブートイメージの作成方法
HSS と HSS ブートイメージの作成方法については、以前こちらで説明しました。そこで説明したツールを使い、HSS ブートイメージを作ってみましょう。
まず最初に必要なのは、上記でビルドした Debug/mpfs-blinky.elf から .bin ファイルを作成することです。
bash$ riscv64-unknown-elf-objcopy -O binary Debug/mpfs-blinky.elf Debug/mpfs-blinky.bin
続いて、bin2chunks コマンドを実行します。
bash$ cd (どこかの)hart-software-services/tools/bin2chunks
bash$ make
bash$ ./bin2chunks 0x08020000 0 0 0 \
32768 mpfs-blinky_payload.bin \
1 3 (上で作った)mpfs-blinky.bin 0x08020000
すると、mpfs-blinky_payload.bin というファイルができますので、これを eMMC や SD カードの BIOS boot partition にコピーします。Linux で実行するものとして、パーティションが /dev/sdX2 であれば、
bash$ sudo dd if=mpfs-blinky_payload.bin of=/dev/sdX2 bs=512 \
iflag=fullblock oflag=direct conv=fsync status=progress
とします。(ls コマンドで /dev/sdX2 が見えることと、これが目的のパーティションであることを再確認します。間違うと、ハードディスクや USB メモリの内容を壊してしまうことがあります。)
SD カードであれば PolarFire SoC Icicle Kit のスロットにセットし、電源を投入し、HSS からブートしてみてください。5秒間「…..」が表示された後、
Hart 1, mcycle_delta=0 SW_IRQs=0 mcycle=5955601578 Hart 1, mcycle_delta=10942740 SW_IRQs=0 mcycle=5966543176 Hart 1, mcycle_delta=10943334 SW_IRQs=0 mcycle=5977499738 Hart 1, mcycle_delta=10957572 SW_IRQs=0 mcycle=5988458026
のような連続出力があれば、正常に動作しています。
今回の内容はサラっとしていますが、確認にはデバッガ GDB を片手にかなりの時間を要しました。御不明な点があれば、お問い合わせください。今日はここまで。