Raspberry Pi Zero WでベアメタルLチカ

先日環境を整備したRaspberry Piでのベアメタルプログラミングですが、Raspberry Pi 2では動作したものの、Pi Zero Wは置き去りになっていました。
どちらかというとPi Zero Wのほうが小さくて実験に使いやすいので、こちらでもベアメタルプログラミングを試してみました。

JTAGはちゃんと動くようになるまで大変すぎたので、初心に戻ってシンプルなLチカから開始です。
教材には「BareMetalで遊ぶ Raspberry Pi – 達人出版会」を使いました。

環境構築で利用したインタフェース2017年2月号は、Raspberry Piに限らずARM全般を扱おうとしていますが、Raspberry Pi固有の事情には上記の本のほうが詳しいですし、紙数がある分、わかりやすくなっていると思います。

rpi-led.png

というわけで、まずはこの本のLチカを試してみました。オンボードのLEDを点滅させるもので、配線しなくていいのでお手軽です。
ただ、この本は初代Raspberry Piを前提としていますが、Raspberry Pi Zero Wでは使用するGPIOポートが違います。

ACT LED on RPi Zero W – Raspberry Pi Forums

このへんの情報は上記のRaspberry Piフォーラムに書かれていました。
整理すると、

 GPIO16を使用: model A/B
 GPIO47を使用: model A+/B+/2B/Zero/Zero W

 GPIO=Lで点灯: model A/B/Zero/Zero W
 GPIO=Hで点灯: model A+/B+/2B

となっているそうです。
この情報は回路図からは読み取れないので、device treeを見るとよい、とコメントされています。

ちなみに、上記のスレッドには、Raspberry Pi/Pi Zero用のベアメタルのコードを沢山リリースされているdwelch67さんが「ベアメタルってこうなんだよね。コードを書くよりも情報を探すほうが難しい。Linuxのソースを読んだり、フォーラムを読んだり。ドキュメントは間違っていると仮定しなけりゃならないし、チップやペリフェラルにはたいていバグがある。」なんていう感じのコメントをされています。

もう一つRaspberry PiのGPIOで押さえておかないといけないのは

・モード切替(GPFSEL0 ~ GPFSEL5)、ピンをHにする(GPSET0、GPSET1)、ピンをLにする(GPCLR0、GPCLR1)が別レジスタ
・モード切替は1つのGPIOにつき3ビット使用する

という点です。
特にモード切替レジスタは、1つのレジスタが32bitなので、10本のGPIOしか表現できません。
一方でGPIOは0~53まで、54本ありますので、モード切替レジスタが6個もあります。
今回はGPIO47をいじるので、GPFSEL4のbit21~23を設定しなければなりません。

なお、上記の本のサンプルでは、点滅にRaspberry Pi内蔵のタイマーを使っていますが、このコードはZero Wでもそのまま動作します。

ベアメタルなので、初期化コードはアセンブラ、メモリマップに従ってリンカスクリプトも書かなければならない、ということでどうやら動作するものができました。
ファイル一式は以下のリポジトリに置いてあります。
Interface2017年2月号の付録の開発環境で、ビルドできることを確認しました。

bare_matal_rpi_zero/led_blynk at master · boochow/bare_matal_rpi_zero

通常、初期化コードだけはアセンブラで書きますが、今回はCのインラインアセンブラを使いました。
このコード(void Init_Machine(void))はmain.cの中で他のコードよりも前に置かなければなりません。
Raspberry Piはブート時にコードを$8000から開始するようになっているので、$8000にこの実行コードを置かなければならないからです。
リンカスクリプトをうまく書けば特定の関数を先頭に持ってこれないか、といろいろ試してみたのですが、うまくいきませんでした。
オブジェクトファイルが分かれていれば順序を指定できるようですが、同じオブジェクトファイルに含まれる関数のメモリマップ上での配置は指定する方法が無いみたいです。
リンカはめったにいじらないので、私が不慣れなだけかもしれません。やり方をご存知の方はコメント下さい。

また、Init_Machine(void)はどこからも呼ばれていないが削除してはいけないので、リンカスクリプト内でKEEP指定をしています。
そして、ソースコード内では関数の頭にコンパイラが勝手にコードを付加しないようにnaked指定をしています。

makeするとkernel.imgが作成されますので、これをbootcode.binおよびstart.elfと共にSDカードへコピーするとLチカが動作します。
最新のbootcode.binとstart.elfはRaspberry PiのGitHubからダウンロードして下さい。

なお、Makefileでは、make deployでkernel.imgをSDカードにコピーするようになっています。
ただし、このコピー先は環境によって異なりますので、自分の環境に合わせて書き直す必要があります。

コメント