移植性
組込み環境においては、移植性は非常に重要なトピックです。1つのメーカから、それぞれのベンダや各プロダクトファミリが、異なるペリフェラルや機能を提供します、 異なるペリフェラルでは、ペリフェラルとやり取りする方法も異なります。
このような違いを吸収する一般的な方法は、ハードウェア抽象化レイヤまたはHALと呼ばれるレイヤを導入することです。
ハードウェア抽象化は、プラットフォーム固有の細部をエミュレーションして、プログラムにハードウェアリソースへ直接アクセスする方法を提供する、ソフトウェアの一連のルーチンです。
それらのルーチンがハードウェアへのオペレーティングシステム(OS)コールを提供することで、プログラマは、デイバスに依存せず、高性能なアプリケーションを書くことができます。
Wikipedia: ハードウェア抽象化レイヤ
組込みシステムは、通常オペレーティングシステムを使わず、ユーザがインストールできるソフトウェアもありません。 全体として1つにコンパイルされたファームウェアイメージであり、様々な制約を持つ、という点が特殊です。 そのため、Wikipediaで定義されている従来のアプローチでもうまく機能する可能性はありますが、移植性を確保するための最も有効なアプローチではない可能性があります。
Rustではどうするのでしょうか?embedded-halを見ていきましょう。
embedded-halとは?
一言で言えば、HAL実装、ドライバ、アプリケーションまたはファームウェア間の実装規約を定義するトレイトの集まりのことです。 それらの規約は、機能(ある型にトレイトが実装されていれば、HAL実装はそのトレイトの機能を提供します)とメソッド(あるトレイトを実装している型のオブジェクトを作成できれば、そのトレイトに含まれるメソッドが利用可能であることが保証されます)を含んでいます。
典型的なレイヤ構造は、次のようになります。
embedded-halで定義されているいくつかのトレイトを示します。
- GPIO(入出力ピン)
- シリアル通信
- I2C
- SPI
- タイマ/カウントダウン
- アナログデジタル変換
embedded-halトレイトと、それらを実装して使用するクレートを持つ主な理由は、複雑さを抑えることです。 アプリケーションがハードウェアのペリフェラルを使うコードだけでなく、追加するハードウェアコンポーネントのドライバを実装しなければならない場合を考えると、再利用性は非常に制限されます。 数学的に表現すると、MがペリフェラルHAL実装の数で、Nがドライバの数とするならば、全てのアプリケーションで車輪の再発明を行うことになります。 その結果、M*Nの実装が必要になります。 一方、embedded-halトレイトで提供されるAPIを使うことで、実装の複雑さはM+Nになるでしょう。 もちろん、明確に定義されたすぐに利用可能なAPIによって試行錯誤を減らすなど、他にも利点があります。
embedded-halのユーザ
上記のように、HALには主に3つのユーザがいます。
HAL実装
HAL実装は、ハードウェアとHALトレイトのユーザとの間のインタフェースを実装します。典型的な実装では、3つの部分から構成されます。
- 1つ以上のハードウェア固有の型
- そのような型のオブジェクトを作成し、初期化する関数。多くの場合、様々な設定オプションを提供しています(速度、動作モード、使用ピンなど)
- 1つ以上のその型に対するembedded-halの
trait
impl
このようなHAL実装は、様々な種類があります。
- 低レベルのハードウェアアクセスによるもの、例えばレジスタ
- オペレーティングシステムによるもの、例えばLinuxの
sysfs
の使用 - アダプタによるもの、例えばユニットテストのための型のモック
- ハードウェアアダプタ用のドライバによるもの、例えばI2CマルチプレクサやGPIOエキスパンダ
ドライバ
ドライバは、embedded-halトレイトを実装しているペリフェラルに接続されている、内部または外部コンポーネントに対するカスタム機能を実装します。 そのようなドライバの典型的な例は、様々なセンサ(温度、磁気、加速度、輝度)、ディスプレイデバイス(LEDアレイ、LCDディスプレイ)や、アクチュエータ(モータ、トランスミッタ)を含みます。
ドライバは、embedded-halの特定のトレイトを実装する型のインスタンスと共に初期化する必要があります。 そのトレイトは、トレイト境界により保証され、駆動するデバイスとやり取りできるインスタンスをカスタムメソッドと共に提供します。
アプリケーション
アプリケーションは、様々な部品を結合し、必要な機能が確実に実現できるようにします。 別システムに移植する際、アプリケーションは最も適合作業が求められる部分です。アプリケーションは、HAL実装を通じて、実際のハードウェアを初期化する必要があるからです。 異なるハードウェアの初期化は、極端に異なる場合があります。 ユーザの選択は、多くの場合、大きな影響があります。 コンポーネントが別の端子に物理的に接続されたり、ハードウェアバスが機器構成を満たすために外部ハードウェアを必要とする場合があったり、内部ペリフェラルを使えるようにするための異なるトレードオフがあったりします。例えば、異なる機能を持つ複数のタイマが利用可能であること、や、ペリフェラルが他のペリフェラルと競合すること、です。