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カードにコピーするようになっています。
ただし、このコピー先は環境によって異なりますので、自分の環境に合わせて書き直す必要があります。

Raspberry Pi Zeroにカラーのピンヘッダを装着

rpi-color-gpio.jpg

これまでRaspberry Piを電子工作的には使ってこなかったこともあり、RPi Zero WのGPIOにはピンヘッダをつけていなかったのですが、ベアメタルの実験用にピンヘッダをつけました。

ピンの機能が色分けされた製品があったので、それを使ってみました。
英国で電子工作関連のグッズをいろいろ売っているPimoroniの製品です。

Colour-coded GPIO Header for Pi Zero – Pimoroni

国内では千石電商で買えます。200円でした。

COM1111 Colour-coded GPIO Header for Pi Zero

この色分けの意味ですが、5Vが赤、3.3Vが黄色、GNDが黒です。

rpi-gpio.png

ここまではいいのですが、水色のピンがあって、これは「何も繋ぐな」(Do Not Connect)となっています。
わざわざピンを引き出しておいて、繋ぐなとはどういうことでしょう。
回路図を見ると、この2本のピンの用途が書いてありました。

rpi-gpio-idsc-idsd.png

このピン(ID_SD、ID_SC)は、Raspberry Piに接続するボード(HAT)上のEEPROMを接続するための端子だそうです。
信号的にはI2Cですが、ブート時にここからEEPROMを読み取って、それにあわせたGPIO設定をするためのものなので、それ以外の目的に使ってはいけない、ということです。

この信号の使い方については、Raspberry Piのブログに記載があり、仕様もGitHubで公開されています。

Introducing Raspberry Pi HATs – Raspberry Pi

raspberrypi/hats

OpenClipartにRaspberry Piのピン配列があったので貼っておきます。

Raspberry Pi2でベアメタルプログラミング環境を作成

rpi-baremetal1.jpg

先日ちょっと調べたRaspberry Piのベアメタルプログラミングですが、やはり調べたら試してみたくなるもので、Interface誌2017年2月号の記事の通りにやってみました。

ちょっと試すだけならQIピンでの接続でもいいのですが、UARTとJTAGをつなぐとなると結構な本数になりますので、接続用の基板を作成することにしました。
上の写真のように、Raspberry PiのGPIOヘッダと、JTAG用およびUART用のコネクタを接続した基板です。

ターゲットマシンはRaspberry Pi2ですが、将来的にPi Zeroでの利用も考えて、aitendoのPi Zero用のユニバーサル基板を使いました。

rpi-baremetal3.jpg

GPIOをそのまま接続した40pinボックスヘッダ、JTAG用の20pinボックスヘッダ、UART用の6ピンヘッダがついています。
GPIOのピン配列はこちらが詳しいです。

40pinボックスヘッダは、デバッグしながら他のI/Oも使いたい場合のために付けてあり、ここからフラットケーブルでブレッドボードなどに配線する想定です。
JTAGは手持ちの製品がSEGGER J-Link(のコピー品?)なので、ボックスヘッダもこの製品のピン配列に合わせて配線していますが、基本的にどこの製品でも20pinなら同じピン配列だと思います。
UARTは、以前から使っているスイッチサイエンスの製品です。6ピンになっていますが、配線はGNDとRX、TXだけです。

rpi-baremetal2.jpg

参考までに結線も載せておきます。

表面
rpi_jtag_uart_surface.png
裏面
rpi_jtag_uart.png

これをRaspberry Pi2につないで、Interface誌2017年2月号のDVDに入っている開発環境のVMをインストールして、Eclipse + OpenOCDでJTAG経由でのデバッグを試しました。
今回は記事の通りにすれば良かったので、かなり楽にできましたが、設定項目は複雑な上に「RPi2の場合は1回目はOpenOCDがうまく動かないので2回起動する」といった謎のバッドノウハウもあり、この記事なしに最初から自分でやろうとしたらかなり時間がかかったのではないかと思います。

なお、付録のDVDではOpenOCDで使用するJ-Link用の設定ファイルは

 /usr/local/share/openocd/scripts/interface/jlink.cfg

です。
インタフェースのシリアル番号を記入するようになっていますので、

# Example: Select J-Link with serial 123456789
#
jlink serial 000000123456

のように頭の0を省略せずに指定します。
また、openocdの起動は

$ sudo openocd -f /home/user/arm/cfg/jlink.cfg -f /home/user/arm/cfg/raspi2.cfg

のようにスーパーユーザの権限で行う必要がありました。

なお、初代RPiとRPi Zero Wは同じCPUを使っていますので、初代RPi用のベアメタルのオブジェクトならRPi Zeroでも動作するのではないかと思って試してみましたが、うまくいきませんでした。