SOLID-OSとは
組み込みアプリ開発において、リアルタイムOS(RTOS)を使うメリットをこれまでに十分説明してきました。今回は、いよいよタスクの実装です。組み込みアプリを構築する上で、最も重要な部分です。使用する「SOLID-OS」は、Toppers/ASP3をベースにしたμITRON準拠のRTOSです。
SOLID-OSは、コンパクトで省電力性に優れたティックレスカーネルを採用。μsecオーダの時間管理が可能です。静的生成と動的生成をサポート。SOLID-IDEと密接に連携しているため、スタック解析はもとより、システムコールのトレースやRTOSの資源表示、タスクの状態や変数の状態もIDE上に表示します。
これまで紹介したテクニックを使って、効率よく実装しましょう。
タスクの実装
RTOSを使用したシステムにおいて、タスクは少なくとも一つは存在しなければいけないOSのリソースになります。SOLIDのプロジェクトを新規で作成する場合も、下記のように必ずroot_task()
が定義されています。
#include <syslog.h> #include “kernel_cfg.h” #include “Main.h” /* * メインタスク */ void root_task(intptr_t exinf) { /* ここに処理を記述します */ }
このroot_task
内で、タスクやOSのリソースの生成を行います。
タスクの定義
タスクを実装するには、Toppers/ASP3の仕様に従う必要があります。使用するサービスコールも仕様書(TOPPERS第3世代カーネル(ITRON系)統合仕様書 Releas 3.0.0)に従ったものです。Toppers/ASP3では、タスクの定義とタスクの実体、タスクのIDが必要です。OSのリソースはIDでナンバリングして管理します。タスクやイベントフラグなど、OSの管理下でリソースが分かれているものについては、番号が重なっても問題ありませんが、同一リソース内では、番号が重なってはいけません。
実際の記述例を見てみましょう。
T_CTSK ctsk; ctsk.tskatr = TA_NULL; /* ・・・・・・・① */ ctsk.task = adc_tsk; /* ・・・・・・・② */ ctsk.itskpri = 3; /* ・・・・・・・③ */ ctsk.stksz = 0x400; /* ・・・・・・・④ */ ctsk.stk = NULL; /* ・・・・・・・⑤ */
重要なのは、②と③と④です。②の「adc_tsk
」は、タスク名そのものです。通常の関数形式です。ただし、戻り値なしで、引数は(intptr_t exinf
)を指定します。
void adc_tsk(intptr_t exif) { /* ToDo */ for(;;) { } }
タスクの優先度
③は、タスクの優先度を示します。数字が低ければ低いほど、優先度は高くなります。優先度は、1-255までの範囲で指定可能です。
アプリケーションの実行中に優先度を変更することも可能ですが、ここでの説明は割愛します。Toppers/ASP3のドキュメントを参照して下さい。
スタックサイズ
④は、スタックサイズです。タスクを定義する時には、必ず必要になります。このスタックサイズは、タスク内で使われるローカル変数、呼び出しAPIのサイズなどに依存します。
例えば、スタックサイズを「1024バイト(0x400)」を確保したとしても、使用しているローカル変数で、符号なし8ビット長の配列を1024バイト定義してしまうと、それだけでも実行時にエラーとなります。コンパイルしただけでは分からないので、タスク定義の時は、予め大きくとるといいでしょう。後述しますが、RTOSを使用した時の不具合のほとんどの原因は、スタックサイズに関わるもので、「スタックオーバーフロー」として扱われます。
タスクの生成とタスクの起動
RTOSを使用したアプリケーションでは、個々のタスクがアプリケーションの機能を担うようになっています。そのタスクの動かし方を簡単に説明します。
- タスクの生成は、
acre_tsk()
を実行
tskid = acre_tsk(&ctsk);
- タスクの起動は、
act_tsk()
を実行
act_tsk(tskid);
タスク実装のポイントは、「待つ」こと
タスクを記述する上で、最も重要なのは、「待ち」を入れるということです。「待ち」を入れると、他のタスクに処理が移るのがRTOSの基本機能です。この待ちを上手に設計することが組み込みシステムのアプリケーション構築の醍醐味になります。タスク実装における「待ち」とは、wai_flg
でイベントフラグを待つことや、dly_tsk
で指定された時間だけ待つことや、rcv_dtq
でデータキューからの受信を待つことなどです。
void adc_task(intptr_t exif) { /* ToDo */ for(;;) { dly_tsk(1000000); } }
この例では、1秒待つタスクにしています。Toppers/ASP3のdly_tsk
は、1usを基準としています。なので、1秒待つには、「1000000」を指定する必要があります。もし、過去のμITRON系の資産から流用する場合には、時間指定の箇所を必ず書き換えてください。
他にも、slp_tsk()
を実行すると、他のタスクからwup_tsk()
が実行されるまで、待ちに入ります。
アドレスサニタイザとRTOSビューアーで、スタックオーバーフローを回避した効率的デバッグ
RTOSを使用する上で、スタックサイズの調整は、実行時でなければ分からないため、これまで難しいものとされてきました。しかし、SOLIDの「アドレスサニタイザ」と「RTOSビューアー」を組み合わせれば、アプリケーションが暴走(スタックオーバーフローなど)する前に停止してくれるため、どこに原因があるかがすぐにわかります。簡単な例ではありますが、冒頭の動画の中でもスタックサイズを超える設定をしてみて、すぐにデバッガが「例外」をキャッチして停止するのが確認できます。
スタックオーバーフローが発生しないようにアプリケーションを構築することこそが、RTOSを使用した組込みシステム開発の「コツ」になります。SOLIDを使用すれば、「RTOSビュー」と、アドレスサニタイザで快適な開発が期待できます。
RTOSビューアー
RTOSビューアーは、デバッガでプログラムを停止した時に、タスクの状態やスタックの使用量がグラフィカルに表示できる機能です。
また、OSのリソース(イベントフラグや周期ハンドラなど)も表示することができ、イベントフラグには、どのようなパターンがセットされたのか、が一目瞭然です(図1)。
アドレスサニタイザ
アドレスサニタイザは、いわゆる「動的解析ツール」です。コンパイルだけではわからないプログラム上の問題点を洗い出してくれる便利な機能です。
冒頭の動画の中でも紹介していますが、アドレスサニタイザが持っている機能をまず確かめてみることをお勧めします。使い方は、デバッグ構成を「Debug – tasan」に変更するだけです(図2)。
発生した例外の種類から、どんな不具合が検出されたのか予測する
SOLIDではスタックの使いすぎについて、二つの方法で自動的に問題発生を検出できます。
一つはローカルに宣言した変数をつかいすぎた場合で、宣言した配列をオーバーランするような場合です(図3)。この場合は、アドレスサニタイザが、オーバーランアクセス発生時にプログラムを停止させ、IDEに状況表示します。もう一つは関数の呼び出しなど深くなりすぎて、ローカル変数などでスタックが使い切られる場合です。この場合はSOLID-OSのスタックフェンス機能が働き、タスクに割り当てたスタックを突き抜けた時点で、プログラムを停止させ、IDEに状況表示します(スタックフェンスはサニタイザ無効時でも利用可能です)。
また、例外が発生したときは、どの呼び出しからなのかを追跡することができます(図4)。
まとめ
今回は、実際にSOLID-OSのリソースであるタスクの実装例を中心に、Toppers/ASP3の使い方と、実際に遭遇する例外に対して、アドレスサニタイザがいかに有効であるかを解説してみました。
冒頭の動画では、図5のように割り込みサービスルーチンや、イベントフラグを使用した時のデバッグ方法や、RTOSビューアーを使用した効率的なデバッグ方法を解説しました。
タスク以外のOSリソースは、RTOS編の番外編に記載してあります。
SOLID Starter Kit for RZ/A1H
今回使用するSOLID Starter Kit for RZ/A1Hは、評価ボード、デバッガ、IDEなどが、すぐに使えるAll in One Packageになっています。
“SOLID”は、コンパイラ、RTOS、デバッガ、IDE全てが含まれ、またそれぞれが専用設計された、京都マイクロコンピュータの組み込み向けソフトウェア開発プラットフォーム。ITRONベースでありながら、本格的なプログラムローダーや、バグの実行時自動検出機能などを搭載します。
最新情報をメーカーサイトで見る
Amazon.co.jpで買う
こちらも是非
“もっと見る” Cortex-A+RTOS編
ローダーを使いこなして、分割開発の悩みを解決
組み込みシステムにおけるローダーとは、電源が入り、RESET信号が解除されて動き出す最初のプログラムです。ローダーの役割は、プログラムをメモリ上に読み込むためにメモリコントローラの初期化を行い、プログラムが保存されているフラッシュメモリからSRAMやDRAMなどのメモリに展開して実行するまでの動作を行います。
デバイスドライバの役割
組み込みシステムのアプリケーションを作っている場合、CPUの機能だけを使うことはほぼないと言っていいでしょう。通常、ハードウェアの操作を行うために用意するソフトウェアを「デバイスドライバ」と呼んでおり、パソコンなどでも頻繁に利用されています。
キャッシュを有効活用するためのMMU設定
「システムが暴走してRESETしなければいけなくなった」という経験は誰にでもあるかと思いますが、組み込みシステムでは、そのリスクを限りなく0にする必要があります。WDTなど周辺機能がサポートしてくれることもありますが、その時点で中核となるソフトウェアが停止してしまっている状況は、誰にとってもメリットは少ないものです。