ラズベリーパイ(RaspberryPI)の部屋。下から上にかけて新しい記事になっている。
特に指定が無ければPi2について記載することにしている。なお、自己責任で。
先人の方々に感謝しながらリソースを使います。
ええといろいろ読んだ結果、公式資料が若干不親切なのでめちゃんこPDF読みまくるしかないのと、
神々が作った数々のサンプルを動かしてbit弄って描画周りがどのように変化するのか観察して使うしかない。
RaspberryPIでよい描画実装があるのは、pifoxみたいにフレームバッファにソフトウェアでゴリゴリ描く方法と、
VideoCoreIVのPDFにV3Dを使ってHWでポリゴンを描く2つの方法がある。
pifoxはソフトウェアレンダリング。Microsoftと他でQPUを使ったdemoはV3Dを使っている。
RaspberryPIのOpenGLの実装は、VideoCoreIV周りの機能を使って実装されている模様なので、
超高速に描画したい場合は、VideoCoreIVに全部乗っかるのがよろしそう。
V3Dを使って描画まで行うためには以下のステップを踏む必要がある。
・V3Dの機能をmailboxの設定から起動してあげる(V3Dのブロックのバスが見えるようになる)
・V3Dのコントロールリストを使ってbinning, renderingのコマンドを作成する。
・V3Dのbinning実行、renderingを実行する。
・必要に応じてフレームバッファにDMA転送を行う。
VideoCoreIVはPowerVRで有名なタイルアーキテクチャの仕組みをとっている。binningは与えられた描画リソースのリストから、renderingデータに渡すデータを生成するステージで、
renderingはbinningで作成した描画リストのデータを使用して、実際にラスタライズを行うステージになる。
詳細はPDFを参照すること。
BinningDataを作成(1Frame目), RenderingDataを作成(2Frame目)、DMA転送でフレームバッファに転送(3Frame目)などでもOK。
くだんのコンシューマとかはライブラリがそんな実装だった気がする(入力遅延気になるけど)
タイルアーキテクチャについてはGoogle先生に聞くとだいぶ出てくる。
VideoCoreIVではscoreboardという名前でローカルメモリの話題が出てくる。
なお、QPUのアセンブラについては役割だけを把握しておくだけでよさそう。1から作るのは面倒。
アセンブラは以下が有効に使える。
https://github.com/hermanhermitage/videocoreiv-qpu/tree/master/qpu-tutorial
qpuasm.jsがアセンブラ。すごい…。尚、nodejsを使う。
並列処理の最適化については以下の記事が詳しい(日本の方が研究されてる)
http://qiita.com/9_ties/items/15ab7fa198991a61a3a9
上記はPythonアセンブラが実装されている。numpyでの行列乗算もとても参考になる。
ダウンロードは ⇒ rpi2_boot2.zip
フレームバッファにRead/Writeしたい。
そのためにはrpiのGPU側と通信して、フレームバッファを取得する。フレームバッファの取得は本家サイトが親玉のリファレンスとなる。
Mailbox-framebuffer-interface
rpiは、GPU側が持っている機能の通信にmailboxという仕組みを使う。
Accessing-mailboxes
mailboxでやりたいコマンドを選んで、Statusを見ながらRead/Writeにて通信を行う。
以下の手順でフレームバッファを取得する。
・mailboxにフレームバッファを要求するコマンドを投げてレスポンスがあるまで待つ
・取得したフレームバッファのアドレスをARM側のアドレスに変換する
・フレームバッファのアドレスにデータを書き込む(絵が出る)
特にフレームバッファの場合、すでに固定で機能が割り振られているので一発でフレームバッファが取れる。
注意しなければならないのは、受信したデータでアドレスと記載されている箇所は全部GPU側のアドレスなので、
ARM側のアドレスに変換してアクセスしなければならない。詳細はソースみてください。
うまくいけば記事先頭のXORテクスチャが表示される。
レジスタ周りの定義はPDFあたりからごっそりコピペでとってきて整形するのが吉。
ダウンロードは ⇒ rpi2_boot1.zip
※サンプルソースはrpi2用です。
rpiはfirmwareが正常に起動したらkernel.img(image.bin)をSDカードから読み込んでSDRAMの0x8000に書き込み、Jumpする。
開始アドレスはconfig.txtでも変更することができるが、0x8000にしておいても別に十分じゃろう。
必要なブートコードはメモリマップを記載するリンカコードと、起動直後にスタックを初期化して、Cソースにジャンプするソースになる。
よっぽどクリティカルな処理を書かないなら、全部アセンブラで書く必要はない。
また、この部屋はWindows環境で開発しているので、いちいちmakefile書く必要もない。batファイルだけで十分である。
書きたい人は書いて便利にするといいじゃろう。
シリアルボードと、USBがうまくつながってるなら、以下の通りメッセージが出る。
ボーレートは115200に設定しているので、TeraTerm側も設定しておくこと。
シリアルでデバッグができるようになればいかようにでもなるので絶対に最初にやっておくこと。
デバッグの効率が格段に違う。
また、ELFを生成してメモリマップ吐いて確認しておくとうっかりミスを防ぐことができる。
ここまでできたら後は根気の問題。
私の開発では、以下の繰り返しをしている。
・ソースを書く。
・コンパイルしてバイナリを生成。
・SDカードにコピーする
・SDカードをrpiに差し込んでUSBで電源を入れる
・シリアルと画面を確認する
・最初に戻る
私は上記を適当なWindowsのバッチファイルを書いて対応している。
make自体はでかくないので数秒で終わる。あとはSDカードをカードリーダーに接続してコピー(これもドライブ固定でバッチたたくだけでOKにしておく)して、
rpiにさしこんで〜の手順を何度も行えばOKである。
もちろん詳しい人はシリアルからバイナリを書き込む単純なコマンド(ダウンローダ)を自作して、
CPUレジスタを全部リセットして、SDRAM側に展開してジャンプするの繰り返しで、
物理操作なしで開発のイテレーションを高速化することも可能。ここは趣味と開発速度とのトレードオフ。
ダウンローダは設計が重要なので、適当に作るとさっくり動作してくれない。作るのが得意な人はさっさと作ったほうがいい。
趣味レベルの組み込みプログラムの低レベル層の開発はイテレーション速度が重要。
プログラムを組んで確認までの繰り返しがだるかったり遅いと、
あっというまに飽きたり面倒になるので(俺だけかもしれない)
トラブル
@シリアルが動作しない
レジスタの設定が間違ってるので見直すこと。
@焦げ臭い、焼いてしまった
シリアル変換ボードのレベル変換が正しくないとボードを焼いてしまう。RPI側は3.3Vであることに注意。
変換ボードのディップは絶対に注意しながら設定すること。
@ターミナルにごみしかでない
2つ原因候補がありそう。
1) レジスタ設定が合っていない -> レジスタ設定を見直すこと
2) ターミナル側のボーレートが合っていない -> ターミナル側のボーレートを期待している値にしてみる(サンプルは115200)
@起動すらしない -> start.elf, bootcode.bin, config.txtがない可能性と、フォーマットがうまくいっていない可能性もある。
以下の手順でブートさせる。
・SDカードをフォーマットする
・本家サイトからブートローダのバイナリ(start.elf, bootcode.bin)を拾ってきて、SDカードのルートにコピーする。
・config.txtを作成してSDカードのルートにコピーする。
・HDMIケーブルとrpiを接続して適当なHDMIモニタに接続する。
・microUSBケーブルと適当なUSBケーブルをつないでrpiの電源を入れる。
・うまくいけば四角形の虹ポリゴンが表示される(kernel.imgが存在しないけどブートできてる証拠)
私が作成したconfig.txt
config.txtは以下の意味を持っている。
kernel=image.bin #ブートローダが読み込むファイル名をimage.binに変更。単にkernel.imgという名前が嫌だったので。別に無くてもいいです。
hdmi_drive=2 #HDMI mode。サウンドがサポートされているなら出力まで行う(baremetalだとたぶん1でもOK。HDMIに音声出力する口が今のところ無い)
hdmi_group=2 #DMTモード。HDMIのPCディスプレイに接続するのでとりあえず。CEAでも動く。
hdmi_mode=4 #640x480 60Hzの解像度に設定。解像度は作りたいもの、趣味の問題。今回いろいろ計算した結果640x480x32 60Hzでなんか作ろうということに。
disable_pvt=1 #RAMのリフレッシュレートの500ms縛りを無効にする。趣味の問題。
disable_overscan=1 #ディスプレイのオーバースキャンモードを無効にする。
force_turbo=1 #GPU, SDRAM, CPUを全速力のクロックモードにする。
fake_vsync_isr=1 #この後使うはずの、フレームバッファに描いた垂直同期をとるために必要になる。
うまくいけば以下の通りディスプレイに表示される。
config.txtは割と曲者で、ブートの挙動やレジスタの挙動に影響を与えるので注意が必要になる。
ベアメタルの場合、基本は必要な事以外は記載しないことが重要。つまらんことで時間を使うことになる。
あとは他のサイトにあるstart.elf, bootcode.binは絶対に使わないこと。古いだけじゃなく普通に起動しない場合がある。
まだコードを書いていないが、↑のconfig.txtで言うと、image.binを生成してベアメタルしていくことになる(へんてこな言い方)
起動しない場合
LEDランプがチカチカする。チカチカパターンは以下のサイトが詳しい。
http://elinux.org/R-Pi_Troubleshooting
上から引用。
3 flashes: loader.bin not found
4 flashes: loader.bin not launched
5 flashes: start.elf not found
6 flashes: start.elf not launched
7 flashes: kernel.img not found
Firmware since 20th October 2012 no longer requires loader.bin, and the flashes mean:
3 flashes: start.elf not found
4 flashes: start.elf not launched
7 flashes: kernel.img not found
8 flashes: SDRAM not recognised. You need newer bootcode.bin/start.elf firmware.
上記でRaspberryPIの起動失敗要因が大体わかる。
[LEDのチカチカについて若干補足]
ブートローダが起動するとどうやらGPU側でvcos(VideoCoreOS?)というのが立ち上がった状態になって、
rpiのメインのコアからはmailboxという仕組みで通信をすることになる。
mailboxの仕様の中で、HDMIに関する仕様が存在するが、変なパラメータを渡すと、8 flashesの状態になる。
推測の域を出ないけど、vcosのuserlandで以下のコードを見ることができる。
userland/interface/vcos/generic/vcos_abort.c
つまりvcos側がabortになった場合でも8 flashesが発生する可能がある。
とりあえずRaspberryPIをtwitterのクローラに使っていたけどベアメタルで使いたくなったのでとっかえ。
Bare metalってのは何か適当な基板だったりハードウェアを提供されているOSとか何も入れない状態から、
ファームウェア以外を全部1からプログラムを書いていくことを意味するらしい。そういう言葉があることを去年しって割とわくわくしている。
で、RaspberryPIは割と情報もあるし、ちょいと調べるとBootからサッサと使えることで比較的楽だということを調べてわかったので、
個人的にはベアメタルに最適な板だと思っている。
忘備録としてやったことをメモすることにする。
[とりあえずのこの部屋の目標]
絵を自由に出したいので、絵を何か出すことを目標にする。
[基本資料]
https://www.RaspberryPI.org/documentation/hardware/RaspberryPI/bcm2835/BCM2835-ARM-Peripherals.pdf
RaspberryPIのSoCの基本的な周辺機器のレジスタ構成が記載されている仕様書。
レジスタのアドレスはRaspberryPI1, 2で異なること、
ほかに機能が大量にあるのに記載されていない(端的に言って不十分)であることを理解して読む必要がある。
http://www.broadcom.com/docs/support/videocore/VideoCoreIV-AG100-R.pdf
RaspberryPIに使われているGPUの仕様書。先の周辺機器のPDFと合わせて読むと理解が深まる。
テーマは絵を出すことなので、コードを書くときはこの仕様書とgithubのサンプルが中心になる。
[情報を集める]
幸いなことにいろいろな人がBareMetalでrpiを動かしている。
大学のOSの講義などでも使われているからまあ組み込みの入門としてはかなり最適なんだろうと思う。
https://github.com/phire/hackdriver
絵を出したいというテーマで調べたら最初に見つけたrepo。
userland層からコマンドを発行してフレームバッファにポリゴンを書き出すソース。
かなりシリアルに書かれてるので読む分には良い。
https://github.com/PeterLemon/RaspberryPI
ベアメタルで何から何まである。すごい。repo見るとN64のベアメタルまである。クレイジーである。
このrepoの方は本家forumsにいろいろ投下している。
flatassemblerという超高性能マクロアセンブラで作成している模様。
https://github.com/ICTeam28/PiFox
もうこれで十分なんじゃね?というスターフォックスっぽいdemo.
サウンドはDMA転送 + PWMにて実装、描画はソフトウェアレンダリングで実施している。
入力周りはGPIOでとってきている。正直このくらいできればなんでもできそうな気がしてくる。
https://github.com/Microsoft/graphics-driver-samples
天下のMicrosoftのDirectX11相当APIのVideoCoreIVの実装。事実上低レベル層のグラフィックドライバになる。
APIは、DirectX11っぽい感じになってるけど実装できていない箇所は結構ある。
が、VideoCoreIVで扱えるテクスチャの形式のT-FORMATの実装だったり、シェーダアセンブラ(QPU)があったりなど、
割とかゆいところに手が届くソースを見ることができる。素晴らしい。
今はあまりメンテナンスされていない感じ。
[RaspberryPIがBootする仕組み]
こちらのスレや記事が参考になる。
https://www.RaspberryPI.org/forums/viewtopic.php?t=72260
最初はGPU側のFirmwareが全部主導権を握っていることに注意することと、
SDRAMの初期化だったりディスプレイの初期化なども全部やってくれる。なんて便利なんだろう。
気を付けなければならないのは、config.txtでブートの挙動が大きく変わるので以下を見ながら注意。
http://elinux.org/RPiconfig
※本家のドキュメンテーションが正義。ただ見づらいので↑がよろしそう。
[必要な知識]
ARMのアセンブラが若干読み書きできること、基本的な挙動を知っていること、レジスタアクセスの方法がわかること、SDLとかでフレームバッファにアクセスして絵を出せること、英語が読めればOKである。
組み込みプログラムで何か動かしたことがある方なら楽勝である。
ただ、ARM側はGPU側を中心に制御するために存在するようなものなのでARM側でなんでもできるというわけではない。
[予算]
RaspberryPI本体とほかの適当なボード、キットで11,000円程度を見込んでおく。
[開発するためのツール]
@コンパイラ
launchpad.net/gcc-arm-embeddedを使うことにする。インストールしたらbin回りにpathを通しておくこと。
@uartからUSBに変換してシリアル通信できる変換接続ボードと適当なケーブル
FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)
これが無いとデバッグがしんどい。
あたりまえだけどこの板は絶対に5VにDIPを倒さないで使うようにする。rpi側のGPIOが3.3Vだからです。焼けます。1枚焼きました私。
最初Lチカで何とかデバッグしてたけどしんどくなったりつらいバグが大量に出たりしたので、上の板を素直に使います。
接続方法は以下の電子書籍が極めて親切で具合が良いです。
BareMetalで遊ぶ Raspberry Pi - 達人出版会
また接続する際はチクチクさすケーブルがどうしても必要になるので、以下のセットなどを探すか秋月電子で頼む。
私は以下を注文した。とても安いしお得。
https://www.amazon.co.jp/dp/B01AUN1JYW
※これ原価割れしているのではと思うくらい安い。
@TeraTerm
おなじみ定番ターミナルソフト。COMポートとつながるなら何でもいいです。
rpi側からPC側に文字列送信してデバッグするときに必要。いろいろな使い方ができる。
@SDカード(rpi2ならmicroSDカード)
何をやるかに依りますが、1GByteないし4GByteあれば十分。
FAT32でフォーマットして中にfirmwareと自分で作ったkernel.imgをコピーして開発してくことになる。
@カードリーダ
できるだけ安くて、カードの抜き差しで簡単に壊れないタフなものを使うこと(重要)
@HDMIケーブル
グラフィック出したいので、HDMIケーブルが必要。音声回りは特に何も必要ないので、音でなくても大丈夫。
[RPI1,2の違いについて]
GPIO回りとコアとレジスタのベースアドレスが違うので本家参照してください。本ページで開発に使うのは、uart-functionを使うことくらい。
ここのサイトが詳しい。
[道具集めるのがだるい人は]
板買うところから始めないといけないのでだるい!という方でベアメタルやりたい方は、
LEDランプのチカチカから始めるのが良い。
LEDは買わなくても大丈夫です。板に最初から小さいのがついているので、それを制御するようにすれば良い。
GPIOはSchemeみて判断してもいいですが以下のサイトがとても参考になる。
Raspberry Pi と Raspberry Pi2の非互換性 (2015/03/22)
rpi1はGPIO16, rpi2はGPIO47がLED1(黄色いやつ)につながってるので、ソフトウェアからコントロールできる。
ということで環境やらをつらつら記載したので仕様書とコンパイラを中心にソフトを書いていくことになる。