Raspberry PiのMailbox Property Interfaceの実験


Raspberry PiにはARMのCPUとBroadcomのGPUが同居しており、両者はmailboxという共有メモリを使ったインタフェースで情報交換ができます。
Mailboxには複数の「チャネル」があり、それぞれ交換できる情報の種類が異なるのですが、今回は「Property」チャネルを使ってGPUと様々な属性値をやりとりしてみました。

GPU側では、Broadcomのソフトウェア「VideoCore」が動作しており、グラフィックス以外にARMプロセッサや周辺回路のクロックなども制御しています。
そのため、クロック関連やハードウェアの各種IDなどの属性情報をPropertyチャネルを使ってVideoCoreとやりとりできます。
上のスクリーンショットは、その属性の一つ「Command lineパラメータ」を取得してみたものです。

このパラメータは、以前紹介したARM boot tagとは異なっています。ARM boot tagはARMプロセッサに共通の仕組みでしたが、このコマンドラインパラメータはRaspberry Pi専用の仕組みで、SDカードに置くconifg.txtで指定がない場合のデフォルト値と思われます。
実際に渡される値は以下のようになっていました。

b’bcm2708_fb.fbwidth=1184 bcm2708_fb.fbheight=624 bcm2708_fb.fbswap=1 dma.dmachans=0x7f35 bcm2708.boardrev=0x9000c1 bcm2708.serial=0xd941df3d bcm2708.uart_clock=48000000 bcm2708.disk_led_gpio=47 smsc95xx.macaddr=B8:27:EB:41:DF:3D vc_mem.mem_base=0xec00000 vc_mem.mem_size=0x10000000 console=ttyS0,115200 kgdboc=ttyS0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstypblockquotext4 rootwait’

Mailboxインタフェースは以前にFrame bufferパラメータをやり取りする際に使用しました。
このときはFrame buffer用の1番のチャネルを使っていましたが、今回使うのはProperty Interface用の8番チャネルです。

Property Interfaceで使用するデータ構造の解説は、以下が分かりやすいです。

The Property Mailbox Channel

データ構造は「メッセージ」一種類だけです。
メッセージは32bitのIntegerの配列です。最初の要素はメッセージ全体のバイト長、その次の要素は「要求」と「応答」を識別するフラグです。
このフラグがあるのは、VideoCoreへのリクエストに対するVideoCoreからのレスポンスが、同じメッセージ領域を上書きして行われるためです。

3番目以降の要素は、属性値の読み書きリクエストが入っています。
このリクエストは、よくあるTLV(Tag-Length-Value)形式、つまりデータ種別を表すTag、データ長を表すLength、データを表すValueをこの順でつなげた構造になっています。
この構造では複数のリクエストを続けて書くことができますので、最後に必ずEnd Tagを付けて、終了を表します。

様々なリクエストと、そのTLV書式の一覧は以下にあります。

Mailbox property interface · raspberrypi/firmware Wiki

ただ、リクエストの領域に、そのリクエストに対するレスポンスが書き込まれますので、Lengthはリクエストとレスポンスのどちらかより長いデータ長のほうを採用する必要があります。
また、メッセージのデータ構造と同様に、すべてのリクエストにおいて、「要求」と応答」を識別するフラグが実際のValueの前(Lengthの次)に入ります。

例えば、「Get firmware revision」のリクエストは

    • Tag: 0x00000001
    • Request:
      • Length: 0
    • Response:
      • Length: 4
      • Value:
        • u32: firmware revision

となっています。

リクエストは長さ0ですが、レスポンスのために4バイトの空間があらかじめ必要です。
従って、このタグに必要な配列は

{ 0x00000001, 0x4, 0, 0}

となります。
先頭がタグ、その次の4はValueのための空間が4バイトであること、その次の0はリクエストであることを表し、最後の0がValue(今回はリクエストでは使わないがレスポンスで使用)です。

このタグだけを入れたメッセージは、

{0x20, 0, { 0x00000001, 0x4, 0, 0}, 0}

となります。
全体で28バイトですが、データ長が0x20になっているのは、メッセージのメモリは16バイト境界に置き、使わない部分は0で埋めることになっているためです。
次の0はリクエストを表します。最後の0はEnd Tagです。

これをMailboxのPropertyチャネル(8番チャネル)でVideoCoreに送ると、次のような応答が返ってきます。

{0x20, 0x80000000, { 0x00000001, 0x4, 0x80000004, 0x5ab27036}, 0}

最初の0x80000000はこのメッセージがレスポンスであり、かつリクエストが正常に処理されたことを表します。タグが処理できなかった場合は0x80000001になります。
タグデータの0x80000004は、MSBが1になっていることでこのタグを処理できたことを示し、残り31bitはレスポンスのデータ長(今回は4バイト)を表します。
その次の0x5ab27036が、今回得られた実際のデータ(firmware revision)です。

このProperty Interfaceですが、もちろんMicroPythonに実装してみました。
gpuモジュールにvc_property(tag, param1, param2, param3, ...)という関数を追加しています。パラメータは最大7個までですが、第2パラメータとしてリストやタプルを使ってvc_property(tag, [param1, param2, param3, ...])のようにパラメータを与えることもできます。

冒頭の例の他、こんな感じで使えます。
1つ目はCPUクロックレート、2つ目はペリフェラル用のcoreクロックレート、3つ目はフレームバッファの物理信号の解像度です。

>>> import gpu
>>> gpu.vc_property(0x00030002, 3)
(3, 700000000)
>>> gpu.vc_property(0x00030002, 4)
(4, 250000000)
>>> gpu.vc_property(0x00040003)
(1920, 1080)
>>>

戻り値はtagの種類によって異なります。integerの配列の場合は上のようにタプルを返していますが、最初のコマンドラインのようにbytes型やbytesarrayを返す必要があるものもあります。
これは仕方ないので、実装のほうでif分岐でゴリゴリ書いています。

なお、Property Interfaceでは、上の例にもあるようにFrame Buffer関連のパラメータもやり取りできます。
ただ、設定値の書き込みについては
Mailbox property interface

If an allocate buffer tag is omitted when setting parameters, then no change occurs unless it can be accommodated without changing the buffer base or size.

と記載されており、単発のタグではなく解像度の設定やバッファの確保およびアドレスの取得などの一連のタグを一つのメッセージで送信する必要があります。

上記のvc_property()はタグを1つしか使えないので、フレームバッファの設定値の書き込みには使えません。
gpuモジュールではフレームバッファの設定は専用メソッドを用意していますので、特に問題はありませんが、確認の意味でProperty Interfaceでフレームバッファの設定を行うプログラムを作ってみました。
コードは以下に置いてあります。

bare_matal_rpi_zero/video2 at master · boochow/bare_matal_rpi_zero

このコードは画面に出てくるものは以前のものと全く同じです。
違いは、フレームバッファの設定をProperty Interfaceで行っていることと、シリアルポートにmailboxの中身を表示していることです。

コメント