ベアメタルARM再び

多少苦戦しつつもベアメタルARMを自力で動かせたので、試行錯誤の備忘録を書こうと思います。情報の整理はしていません。

方針

ひとまずARMv7-MやARMv8-M向けに書いたプログラムを気軽に動かしたりデバッグできる環境が欲しい。実機上での動作は機材準備の心理的ハードルが高いのでNG。CortexMの開発に慣れるまで実機とペリフェラルに触ってはいけない。

プログラムはqemuで動かす。qemuはWindows向けのビルドを使うが、うまく動かなかった場合は別の方法を考える。そろそろWSL2で動かしてもいいかも?動けばなんでもOK。

linuxで便利なツールを使うためにMSYS2をインストールする。これはWindows環境で開発するためのハンディキャップ。WSL2使うなら不要そう。

ツールチェインはCMakeを使う。C/C++開発においては強力な味方になるはず。

コンパイラはgcc-armを使う。armccやclangにも興味があるが、今は手を出してはいけない。

メモ

手始めにhello world!したい所だけど無理。hello world!するにはprintfが必要で、組込環境からの文字出力はARMのsemihostingやITMなどが必要。Linux上ならばユーザーモードQEMUであるqemu-armを使えば、組込側でシステムコールを発行することができる[1]。それか、無難にUARTを使う。

最低限動くという意味のHello World!なら、アセンブリファイルだけでもいい。

CortexMは0x0000_0000番地から処理が始まる。ここにベクタテーブルを配置する。ベクタテーブルの先頭0x0000_0000はスタックポインタ、0x0000_0004はプログラムカウンタである。ベクタテーブルを0x0000_0000に配置したり、RAMにスタックポインタを向けるリンカ設定が必要。

FlashとRAMのアドレスはマイコンによって異なる。調べる限り、Flashは0x0000_0000でRAMは0x2000_0000のマイコンが多い気がする。STM32のFlashは0x0800_0000で始まり、0x0000_0000からマッピングされている。そういえば、0x1A00_0000始まりのマイコンもあったな。マイコンを調べないとメモリマップは書けない。

Cortex-M7の場合、ITCMと呼ばれる高速なFlash/RAMがある。メモリマップが複雑なので注意。

cmakeで適当にプロジェクトを書いてビルドすればOK。とにかくリンカ設定に不慣れ。--gc-sectionsしたらvector-tableが消えたり、-nostartfilesを指定しないと.bssがないと怒られたり。動くようになるまでがしんどい。

まずelfをビルドして、objcopyでbinファイルを生成する。mapファイルやlist(readelf)もついでに出力しておくといいかも。

あとは動かすだけ。

qemu-system-arm \
    -M lm3s6965evb \
    -cpu cortex-m3 \
    -gdb tcp::3333 \
    -S \
    -nographic \
    -monitor null \
    -serial null \
    -kernel build/app.bin

FPGA内蔵型のリファレンスCPUとしてmps2-an385(CM3), mps2-an386(CM4), mps2-an500(CM7)などが使える。

ホスト側からarm-none-eabi-gdb -q build/app.elfした後、target extended-remote localhost:3333してqemuに繋ぐ。target remote ~よりも便利。

sでステップイン、nでステップオーバー、info registersでレジスタの値の確認、info frameでスタックフレームの中身を覗く。使いこなせるかな・・・?。


  1. https://stackoverflow.com/questions/46128604/compiling-and-running-arm-assembly-binary-on-cortex-m4-simulated-in-qemu/46137063#46137063 ↩︎