どのようにTockをコンパイルするか

Tockには2種類のコンパイル生成物があります。カーネルとユーザレベルのプロセス (アプリ)です。両者は別々にコンパイルします。さらに、プラットフォーム毎に カーネルとプロセスのプログラミング方法が異なります。以下では、カーネルとプロセスの コンパイルを説明し、実際のボードに各プラットフォームをプログラムする方法の例を 示します。

カーネルのコンパイル

カーネルは5つのRustクレート(パッケージ)に分類できます。

  • コアカーネルクレート。これには、割り込みの処理やプロセスのスケジューリングなどの 主要なカーネル操作、TakeCellなどの共有カーネルライブラリ、HIL(Hardware Interface Layer)の定義を含みます。kernel/フォルダにあります。

  • アーキテクチャ(_ARM Cortex M4_など)クレート。コンテキストスイッチングを実装し、 メモリ保護とsystickドライバを提供します。arch/フォルダにあります。

  • チップ固有(_Atmel SAM4L_など)のクレート。割り込みを処理し、チップのペリフェラル 用のハードウェア抽象化レイヤを実装します。chips/フォルダにあります。

  • ハードウェアに依存しないドライバと仮想化レイヤのための1つ(または複数)のクレート。 capsules/フォルダにあります。Tockを使用する外部プロジェクトは各自のドライバ用に 追加のクレートを作成することができます。

  • プラットフォーム固有(_Imix_など)のクレート。チップとそのペリフェラルを設定し、 ドライバにペリフェラルを割り当て、仮想化レイヤを設定し、システムコールインタフェースを 定義します。boards/にあります。

これらのクレートはプラットフォームのクレートを依存関係グラフのベースとして、Rustの パッケージマネージャであるCargoを使ってコンパイルされます。 実際には、Cargoの使用はTockのMakefileシステムにより隠蔽されます。ユーザはboards/ の適切なディレクトリでmakeと入力するだけで、そのプラットフォーム用のカーネルをビルド できます。

内部的には、Makefileは単にビルドを処理するCargoを呼び出しているだけです。たとえば、 imixプラットフォームでのmakeは次のように変換されます。

$ cargo build --release --target=thumbv7em-none-eabi

--release引数は、最適化を有効にしてRustコンパイラを起動するようにCargoに指示します。 --targetは、コンパイラ用のLLVMデータレイアウト定義やアーキテクチャ定義を含むターゲット 仕様をCargoに指定します。

Tockコンパイルのライフ

Cargoがプラットフォームクレートのコンパイルを開始すると、まず、すべての依存関係を 再帰的に解決します。依存関係グラフにわたって要件を満たすパッケージバージョンを選択します。 依存関係は各クレートのCargo.tomlファイルで定義されており、ローカルファイルシステム、 リモートのgitリポジトリ、crates.ioで公開されているパッケージの パスを参照します。

依存関係が満たされると、Cargoは次に各クレイトを順番にコンパイルしていきます。 各クレートはrlib(オブジェクトファイルを含むarアーカイブ)としてコンパイルされ、 プラットフォームクレートのコンパイルによる実行可能なELFファイルに結合されます。

--verbose引数を渡すことによりCargoが実行する各コマンドを見ることができます。 Tockのビルドシステムではmake V=1と実行することで冗長コマンドを見ることができます。

LLVM Binutils

TockはRustツールチェーンに含まれているlldobjcopy sizeの各ツールを使用して、マイクロコントローラ上で実行されるカーネルバイナリを生成します。これには主に3つの意味が あります。

  1. このツールはGNUバージョンとは完全な機能互換性があるわけではありません。両者は 非常に似ていますが、全く同じ動作をしないエッジケースがあります。これは時間が経てば 改善されるでしょうが、予期せぬ問題が発生した場合に備えて注意しておく価値があります。

  2. このツールはRustのバージョンに合わせて自動的に更新されます。このツールはRust ツールチェーンのすべてのバージョンでコンパイルされて出荷されるrustup コンポーネントであるllvm-toolsで提供されます。したがって、RustがRust リポジトリで使用しているバージョンを更新した場合、Tockもその更新を使うことに なります。

  3. Tockはこれらのツールを提供する外部依存関係を使用しなくなりました。これにより、 すべてのTock開発者が同じバージョンのツールを使用することが保証されるはずです。

特別な.appsセクション

Tockカーネルは、アプリケーションがロードされるものと同じ物理アドレスにある.apps セクションをカーネルの.elfファイルの中に含んでいます。カーネルをコンパイルする際、 これは単なるプレースホルダであり、意味のあるデータは置かれません。これは、カーネルと アプリを一緒にフラッシュできるように、カーネル.elfファイルとアプリケーション バイナリをモノリシックな.elfファイルとして簡単に更新できるようにするために 存在しています。

Tockビルドシステムがカーネルバイナリを作成する際、このセクションを明示的に削除し、 プレースホルダがカーネルバイナリに含まれないようにします。

特別な.appsセクションを使用するためにobjcopyでプレースホルダを実際のアプリ バイナリに置き換えることができます。一般的なコマンドは次のようになります。

$ arm-none-eabi-objcopy --update-section .apps=libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf target/thumbv7em-none-eabi/release/stm32f412gdiscovery.elf target/thumbv7em-none-eabi/release/stm32f4discovery-app.elf

これは、カーネルELFであるstm32f412gdiscovery.elf内のプレースホルダセクション .appsを"c_hello"アプリケーションTBFに置き換え、stm32f4discovery-app.elfと いう名前の新しい.elfを作成します。

プロセスのコンパイル

他の多くの組み込みシステムとは異なり、Tockではアプリケーションコードのコンパイルは カーネルのコンパイルとは完全に分離されています。アプリケーションは少なくとも2つの ライブラリlibtocknewlibとともにコンパイルされ、独立したバイナリが構築されます。 このバイナリはTockプラットフォーム上にアップロードされ、ロードされてすでに存在する カーネルとともに実行することができます。

Tockは次の要件を満たす任意のプログラミング言語とコンパイラをサポートしています。

  1. アプリケーションは、位置独立なコード(PIC)としてビルドされていること。

  2. アプリケーションは、Flashコンテンツを0x80000000より上のアドレスに、RAM コンテンツをそれより下に配置するローダスクリプトでリンクされていること。

  3. アプリケーションのバイナリは、バイナリ内のセクション位置を詳細に記述したヘッダ から開始していること。

第一要件はこの直後に説明しますが、他の2つの要件はTockバイナリフォーマットで 詳しく説明します。

位置独立なコード

Tockはカーネルとは別にアプリケーションをロードし、複数のアプリケーションを同時に実行する ことができるので、アプリケーションは事前にどのアドレスにロードされるかを知ることが できません。この問題は多くのコンピュータシステムに共通しており、通常は、実行時に動的に コードをリンク・ロードすることで対処しています。

しかし、Tockはこれとは異なる選択をしており、位置独立なコードとしてコンパイルされることを アプリケーションに要求しています。PICでコンパイルすると、指定された絶対アドレスへの ジャンプは使用せず、すべての制御フローが現在のPC相対になります。すべてのデータアクセスは そのアプリのデータセグメントの開始アドレス相対になり、データセグメントのアドレスは base registerと呼ばれるレジスタに格納されます。これにより、FlashとRAM内の セグメントを任意の場所に配置することができ、OSはベースレジスタを正しく初期化するだけで 済みます。

PICコードはx86のようなアーキテクチャでは効率が悪い場合がありますが、ARM命令セットは PIC操作に最適化されており、ほとんどのコードをほぼオーバーヘッドなしで実行することが できます。PICの使用には実行時に多少の修正が必要ですが、再配置は容易であり、 アプリケーションがロードされる際に一度コストがかかるだけです。アプリケーションの動的 ローディングに関するより詳細な議論はTockのウェブサイト: Dynamic Code Loading on a MCUで見ることができます。

アプリケーションをarm-none-eabi-gccでコンパイルする際、Tockが必要とするPICコードを ビルドするには次の4つのフラグが必要です。

  • -fPIC: 相対アドレスを使用するコードのみを出力します。
  • -msingle-pic-base: データセクションに一貫して_ベースレジスタ_を使用するよう強制します。
  • -mpic-register=r9: ベースレジスタとしてr9レジスタを使用します。
  • -mno-pic-data-is-text-relative: データセグメントがテキストセグメントから一定のオフセットに配置されていると仮定しません。

Tockアプリケーションは、Flashをアドレス0x80000000に、SRAMをアドレス 0x00000000に配置するリンカスクリプトを使用します。これにより、Flashを指し示す 再配置とRAMを指し示す再配置を簡単に区別することができます。

Tockバイナリフォーマット

アプリケーションを正しく読み込むために、アプリケーションはTockバイナリフォーマットに 従う必要があります。これはTockがアプリケーションを正しくロードできるようにTockアプリの 先頭バイトがこのフォーマットに従わなければならないことを意味します。

実際にはこれはアプリケーションに対して自動的に処理されます。コンパイルプロセスの一貫として Elf to TABと呼ばれるツールが、ELFからTockが 期待するバイナリフォーマットへの変換を行い、セクションが期待する順序で配置されることを 保証し、ロード時に必要な再配置を列挙するセクションを追加し、TBFヘッダを作成します。

Tockアプリケーションバンドル

使い易く配布可能なアプリケーションをサポートするために、Tockアプリケーションは複数の アーキテクチャ用にコンパイルされ、"Tock Application Bundle"として.tab ファイルにまとめられます。これにより、Tockをサポートしている任意のボードにフラッシュ できるアプリケーション用のスタンドアロンのファイルが作成され、アプリケーションの コンパイル時にボードを指定する必要がなくなります。TABにはほとんどすべてのTock 互換ボードにフラッシュするために必要な情報が含まれており、コンパイル時ではなく、 アプリケーションがフラッシュされる際に正しいバイナリが選択されます。

TABフォーマット

.tabファイルは、TBF互換のバイナリとアプリケーションに関する追加情報を含む metadata.tomlファイルからなるtarアーカイブです。.tabファイルを作成する 簡単なコマンド例は次のとおりです。

tar cf app.tab cortex-m0.bin cortex-m4.bin metadata.toml

メタデータ

.tabファイル内のmetadata.tomlファイルは、1行に1つのキーと値の組を含む 一連のTOMLファイルであり、より詳細な情報を提供し、アプリケーションをフラッシュする際に 役立ちます。既存のフィールドは次のとおりです。

tab-version = 1                         // TABファイルフォーマットのバージョン
name = "<package name>"                 // アプリケーションのパッケージ名
only-for-boards = <list of boards>      // このアプリケーションがサポートするボードカーネルリスト(オプション)
build-date = 2017-03-20T19:37:11Z       // アプリケーションのコンパイル日時

カーネルとプロセスのボードへのロード

ボードにコードをロードする方法には特に制限はありません。JTAGと様々なブートローダが 全て等しく可能です。たとえば、Hailimixプラットフォームは主にシリアルの "tock-bootloader"を使用し、他のプラットフォームはjlinkopenocdを使って JTAG接続でコードをフラッシュします。一般に、これらの方法はそのプラットフォームの ユーザにとって何が最も簡単であるかに基づいて変更される可能性があります。

複数のアプリケーションを同時に使用するための最も簡単な選択肢はtockloadergit repo)を使用して プラットフォーム上で複数のアプリケーションを管理することです。重要なことは、 現在、アプリケーションはカーネルと同じアップロードプロセスを共有していますが、 将来的には別の方法がサポートされる予定があることです。特に、ワイヤレスによる アプリケーションのロードがTockの将来のエディションの目標になっています。