モデルから生成されたコードの実行
コード ジェネレーターによってモデルで定義したとおりにアルゴリズム コードが生成されます。外部コード統合ワークフローの選択で説明されている手法を使用してモデルに外部コード (カスタム コードやレガシ コードなど) を含めることができます。
コード ジェネレーターは、生成されたモデル コードを実行するインターフェイスも提供します。このインターフェイスとモデル コードは一緒にコンパイルされ、実行可能プログラムが作成されます。次の図は、実行ファイルの高水準オブジェクト指向図を示します。
リアルタイム プログラムのオブジェクト指向図
一般に、生成コードの組み込みスタイルとラピッド プロトタイプとでは、モデル実行ドライバーの概念設計に相違はありません。以下の節では、シミュレーション (非リアルタイム) とリアルタイムの両方のシングルタスク環境とマルチタスク環境のモデルの実行について説明します。ほとんどのモデル コードについて、マルチタスク環境ではモデルの実行が最も効率的になります (最速のサンプル レート)。
以下の概念は、モデル コードの実行の仕組みを説明する際に役立ちます。
Initialization:
は、インターフェイス コードとモデル コードを初期化します。model
_initializeModelOutputs:現時点でサンプル ヒットをもつモデル内のすべてのブロックを呼び出し、それらに出力を生成させます。
は、メジャー タイム ステップまたはマイナー タイム ステップで実行できます。メジャー タイム ステップでは、出力は指定されているシミュレーションのタイム ステップです。マイナー タイム ステップでは、連続状態を更新するため、インターフェイスによって微係数が積分されます。model
_outputModelUpdate:
により、現時点でサンプル ヒットをもつブロックが呼び出され、それらに離散状態や同様のタイプのオブジェクトを更新させます。model
_updateModelDerivatives:連続状態をもつモデル内のブロックを呼び出し、微係数を更新させます。
は、マイナー タイム ステップにおいてのみ呼び出されます。model
_derivativesModelTerminate:
は、有限時間で実行するよう設計されている場合にプログラムを終了します。それにより、リアルタイムのモデル データ構造体は破棄され、メモリの割り当ては解除され、データのファイルへの書き込みが可能となります。model
_terminate
プログラムの実行
リアルタイムのプログラムでは、CPU 時間が 100% 必要というわけではありません。この要件により、自由時間にバックグラウンド タスクを実行する機会が与えられます。
バックグラウンド タスクには、データをバッファーまたはファイルに書き込んだりする操作も含まれ、これによってサードパーティ製のデータ監視ツールによるプログラム データへのアクセスや、プログラム パラメーターの更新が可能となります。
しかし、モデル コードのリアルタイム実行を行うには、プログラムがバックグラウンド タスクをプリエンプトできることが重要となります。
プログラムがタスクを管理する方法は、操作環境の機能によって異なります。
プログラムのタイミング
リアルタイムのプログラムでは、他のタスク呼び出しが (割り込みまたはリアルタイム オペレーティング システムの作業プリミティブのどちらかによって) 実行される前にモデルのコードの実行を完了させるようにするには、タスクを呼び出すタイミングに注意が必要です。このタイミングには、外部ハードウェアとのデータの読み取りや書き込みのための時間も含まれています。
時間間隔構成の重要性を示す次のシナリオを検討します。
一方のシナリオでは、連続した呼び出し間の時間間隔が、コードの実行の完了に十分です。
もう一方のシナリオでは、連続した呼び出し間の時間間隔が、コードの実行の完了に不十分です。
次の図は、割り込みタイミングのシナリオを示しています。
タスクのタイミング
サンプルの間隔は、タスクの呼び出し間でモデルコードを実行するのに十分な長さでなければなりません。
上の図では、2 つの隣接した縦矢印の間の時間がサンプルの間隔です。上の図の空のボックスは、1 つのステップを間隔内で完了できるうえ、バックグラウンド タスクのための時間もさらに確保できるプログラムの例を示しています。図の下方にあるグレーのボックスは、サンプルの間隔が短すぎた場合にどうなるかを示しています。タスクが完了する前に他のタスク呼び出しが実行されます。そのようなタイミングは、実行エラーとなります。
リアルタイム プログラムが恒久的に実行されるよう設計されている場合 (つまり、while
ループが終了しないよう最終時間が 0 または無限の場合)、シャットダウン コードは実行されません。
タイミング エンジンの仕組みの詳細については、Timer Representation and Computationを参照してください。
エクスターナル モード通信
エクスターナル モードにより、Simulink® ブロック線図と生成コードから作成されるスタンドアロン プログラムとの間の通信が可能となります。このモードでは、リアルタイムのプログラムは、プロセス間通信サーバーとして機能し、Simulink エンジンからの要求に対して応答します。
シングルタスクおよびマルチタスクモデル実行でのデータログ記録
デバッグ用のモデルの構成では、モデル実行の完了時にシステムの状態、出力、時間を MAT ファイルに保存する方法について説明しています。データ ログ記録を行う関数 LogTXY
は、シングルタスク環境とマルチタスク環境とで異なった動作をします。
シングルタスク環境やマルチタスク環境で LogTXY
がどのように呼び出されるかについて見ると、シングルタスクでは、LogTXY
が ModelOutputs
の後で呼び出されることに注意してください。この ModelOutputs
の呼び出しの間、時間 t でヒットするブロックが実行されます。一方で、マルチタスクでは、LogTXY
は、時間 t でヒットするブロックとタスク識別子が 0 のブロックのみを実行する ModelOutputs(tid=0)
の後で呼び出されます。これにより、シングルタスクとマルチタスクのログ記録では、ログが記録された値における違いが存在します。特に、周期が 1.0 秒の高速サンプル時間と 10.0 秒の低速サンプル時間の 2 種類のサンプル時間をもつモデルを検討します。時間 t = k*10, k=0,1,2... では、高速 (tid=0
) ブロックと低速 (tid=1
) ブロックの両方が実行されます。マルチタスク モードで実行する場合、LogTXY
が呼び出されると、低速ブロックは実行しますが、前回の値のログが記録されます。一方、シングルタスクでは、現在の値のログが記録されます。
もう一つの違いは、Enabled Subsystem にデータをログ記録する際に生じます。Enabled Subsystem 内でイネーブル端子と高速ブロックを駆動する低速信号をもつような Enabled Subsystem を検討します。この場合、有効な信号の評価は低速タスクで生じ、高速ブロックは 1 つのサンプル期間の遅延を確認します。このように、ログが記録された値は、これらの相違を示します。
シングルタスクとマルチタスクでログが記録されたデータの相違について要約すると、相違がみられるのは以下のような場合です。
ルート Outport ブロックのサンプル時間が、最高速サンプル時間より遅い
状態をもつブロックのサンプル時間が、最高速サンプル時間より遅い
ブロックが Enabled Subsystem にあり、イネーブル端子を駆動する信号が、この Enabled Subsystem 内のブロックのレートより遅い
最初の 2 つのケースでは、シングルタスクとマルチタスクとでログが記録された値が異なる場合でも、モデルの結果は相違ありません。実際の相違は、ログ記録がどこで (どの時点で) 行われたかという点のみとなります。3 番目 (Enabled Subsystem) のケースでは、リアルタイム環境で見られる遅延となります。
非リアルタイムのシングルタスク システム
この疑似コードは、非リアルタイムのシングルタスク システムのモデルの実行を示しています。
main() { Initialization While (time < final time) ModelOutputs -- Major time step. LogTXY -- Log time, states and root outports. ModelUpdate -- Major time step. Integrate -- Integration in minor time step for -- models with continuous states. ModelDerivatives Do 0 or more ModelOutputs ModelDerivatives EndDo -- Number of iterations depends upon the solver Integrate derivatives to update continuous states. EndIntegrate EndWhile Termination }
最初に初期化フェーズが開始します。これは、モデル状態の初期化と実行エンジンの設定で構成されています。その後でモデルはステップごとに実行されます。最初に ModelOutputs
が時間 t に実行され、次にワークスペース I/O データがログに記録され、ModelUpdate
によって離散状態が更新されます。次に、モデルに連続状態がある場合、ModelDerivatives
は、時間 (ここで、"h" はステップのサイズ) の状態を生成するため、連続状態の微係数を積分します。そして、時間は に向かって進み、処理が繰り返されます。
モデル実行の ModelOutputs
フェーズと ModelUpdate
フェーズの間は、時間内に現在の位置に到達したブロックのみが実行されます。
非リアルタイムのマルチタスク システム
この疑似コードは、非リアルタイムのマルチタスク システムのモデルの実行を示しています。
main() { Initialization While (time < final time) ModelOutputs(tid=0) -- Major time step. LogTXY -- Log time, states, and root -- outports. ModelUpdate(tid=0) -- Major time step. Integrate -- Integration in minor time step for -- models with continuous states. ModelDerivatives Do 0 or more ModelOutputs(tid=0) ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives to update continuous states. EndIntegrate For i=1:NumTids ModelOutputs(tid=i) -- Major time step. ModelUpdate(tid=i) -- Major time step. EndFor EndWhile Termination }
マルチタスク操作は、出力関数と更新関数がこれらの関数に渡される "タスク識別子" (tid
) によって分割されるため、シングルタスク実行より複雑です。これにより、重複割り込みを使用し、異なるタスク識別子をもつこれらの関数を複数回呼び出すことが可能となります。また、リアルタイム オペレーティング システムを使用すれば、複数のタスクが可能となります。シミュレーションでは、リアルタイム システムにプリエンプションが存在しない場合に発生順にコードを実行することで、複数のタスクがエミュレートされます。
マルチタスク実行では、タスクのレートは基本レートの倍数だと仮定されます。Simulink 製品では、固定ステップ マルチタスク モデルを作成した場合、これが強制されます。マルチタスクの実行ループは、ModelOutputs
および ModelUpdate
に対してタスク識別子 (tid
) の引数を使用する点を除き、シングルタスクの実行ループと非常に似ています。
Simulink Coder™ ではなく、ターゲット ファイルで生成されたコードからの tid
値は使用できません。特定のサブシステムまたは関数タイプのコードを生成する際、Simulink Coder は tid
の使用を追跡します。ターゲット ファイル内でコードを生成する際は、スコープがサブシステムまたは関数のタイプをもたないため、この引数を追跡できません。そのため、tid
が未定義の変数となってターゲット ファイルのコンパイルが失敗します。
リアルタイム シングルタスク システム
この疑似コードは、モデルが割り込みレベルで実行されるリアルタイム シングルタスク システムにおけるモデルの実行を示しています。
rtOneStep() { Check for interrupt overflow Enable "rtOneStep" interrupt ModelOutputs -- Major time step. LogTXY -- Log time, states and root outports. ModelUpdate -- Major time step. Integrate -- Integration in minor time step for models -- with continuous states. ModelDerivatives Do 0 or more ModelOutputs ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives to update continuous states. EndIntegrate } main() { Initialization (including installation of rtOneStep as an interrupt service routine, ISR, for a real-time clock). While(time < final time) Background task. EndWhile Mask interrupts (Disable rtOneStep from executing.) Complete any background tasks. Shutdown }
リアルタイムのシングルタスク実行は、コードが自由に実行されるのではなく周期的なタイマーの割り込みによって関数 rt_OneStep
が駆動されるという点を除き、非リアルタイムなシングルタスク実行と非常に似ています。
プログラムの基本サンプル レートで指定した区間では、モデル コードを実行するために割り込みサービスのルーチン (ISR) がバックグラウンド タスクをプリエンプトします。基本サンプル レートは、モデルで最高速となります。モデルが連続ブロックをもつ場合、積分ステップ サイズにより基本サンプル レートが決定されます。
たとえば、モデルコードが 100 Hz で動作するコントローラーの場合、0.01 秒ごとにバックグラウンド タスクが割り込まれます。この割り込みの間、コントローラーはアナログ デジタル コンバーター (ADC) からの入力を読み取り、出力を計算し、これらの出力をデジタル アナログ コンバーター (DAC) へ書き込み、その状態を更新します。そして、プログラム コントロールはバックグラウンド タスクに戻ります。これらのステップは、次の割り込みの前に実行しなければなりません。
リアルタイム マルチタスク システム
この疑似コードは、モデルが割り込みレベルで実行されるリアルタイム マルチタスク システムでモデルがどのように実行されるかを示しています。
rtOneStep() { Check for interrupt overflow Enable "rtOneStep" interrupt ModelOutputs(tid=0) -- Major time step. LogTXY -- Log time, states and root outports. ModelUpdate(tid=0) -- Major time step. Integrate -- Integration in minor time step for -- models with continuous states. ModelDerivatives Do 0 or more ModelOutputs(tid=0) ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives and update continuous states. EndIntegrate For i=1:NumTasks If (hit in task i) ModelOutputs(tid=i) ModelUpdate(tid=i) EndIf EndFor } main() { Initialization (including installation of rtOneStep as an interrupt service routine, ISR, for a real-time clock). While(time < final time) Background task. EndWhile Mask interrupts (Disable rtOneStep from executing.) Complete any background tasks. Shutdown }
リアルタイムなマルチタスク環境においてモデルを割り込みレベルで実行するのは、重複割り込みがタスクの同時実行に使用される点を除き、前記のシングルタスク環境に非常に似ています。
リアルタイム オペレーティング システムの作業プリミティブ使用時のシングルタスク環境またはマルチタスク環境でのモデルの実行は、上記で説明した割り込みレベルの例に非常に似ています。以下の疑似コードは、リアルタイム作業プリミティブを使用したシングルタスク モデル用です。
tSingleRate() { MainLoop: If clockSem already "given", then error out due to overflow. Wait on clockSem ModelOutputs -- Major time step. LogTXY -- Log time, states and root -- outports ModelUpdate -- Major time step Integrate -- Integration in minor time step -- for models with continuous -- states. ModelDeriviatives Do 0 or more ModelOutputs ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives to update continuous states. EndIntegrate EndMainLoop } main() { Initialization Start/spawn task "tSingleRate". Start clock that does a "semGive" on a clockSem semaphore. Wait on "model-running" semaphore. Shutdown }
このシングルタスク環境では、モデルはリアルタイム オペレーティング システムの作業プリミティブとして実行されます。この環境では、シングルタスク (tSingleRate
) を作成し、モデル コードを実行します。このタスクは、クロック ティックが実行されると呼び出されます。クロック ティックにより、clockSem
(クロックのセマフォ) がモデル タスク (tSingleRate
) に与えられます。モデルのタスクは、実行前にセマフォを待機します。クロック ティックは、モデルの基本的なステップ サイズ (基本レート) で実行されます。
リアルタイム作業プリミティブを使用したマルチタスク システム
この疑似コードは、リアルタイム作業プリミティブを使用したマルチタスク モデル用です。
tSubRate(subTaskSem,i) { Loop: Wait on semaphore subTaskSem. ModelOutputs(tid=i) ModelUpdate(tid=i) EndLoop } tBaseRate() { MainLoop: If clockSem already "given", then error out due to overflow. Wait on clockSem For i=1:NumTasks If (hit in task i) If task i is currently executing, then error out due to overflow. Do a "semGive" on subTaskSem for task i. EndIf EndFor ModelOutputs(tid=0) -- major time step. LogTXY -- Log time, states and root outports. ModelUpdate(tid=0) -- major time step. Loop: -- Integration in minor time step for -- models with continuous states. ModelDeriviatives Do 0 or more ModelOutputs(tid=0) ModelDerivatives EndDo (number of iterations depends upon the solver). Integrate derivatives to update continuous states. EndLoop EndMainLoop } main() { Initialization Start/spawn task "tSubRate". Start/spawn task "tBaseRate". Start clock that does a "semGive" on a clockSem semaphore. Wait on "model-running" semaphore. Shutdown }
このマルチタスク環境では、モデルはリアルタイム オペレーティング システムの作業プリミティブを使用して実行されます。そのような環境においてモデルのコードを実行するには、いくつかのモデル タスク (tBaseRate
およびいくつかの tSubRate
タスク) が必要となります。基本レート タスク (tBaseRate
) は、サブレート タスクより優先されます。 tid=1
のサブレート タスクは、tid=2
のサブレート タスクより優先されるという具合です。この基本レート タスクは、クロック ティックが実行されると呼び出されます。クロック ティックにより、clockSem
が tBaseRate
に与えられます。tBaseRate
が最初にすることは、時間内に現在の位置でヒットをもつサブタスクにセマフォを与えることです。基本レート タスクのほうが優先順位が高いため、実行が継続されます。次に、サンプル時間が最も高速なモデルのブロックで構成される最も高速なタスク (tid=0
) を実行します。この実行の後で、クロックのセマフォの待機を再開します。クロック ティックは、モデルの基本的なステップ サイズで実行するために構成されます。
ラピッド プロトタイピングと組み込みモデルの実行の相違
ラピッド プロトタイピング プログラムのフレームワークでは、モデル定義間で変更のない一般的なアプリケーション プログラミング インターフェイス (API) が装備されています。
Embedded Coder® 製品は、組み込みプログラム フレームワークと呼ばれる別のフレームワークを提供しています。組み込みプログラム フレームワークでは、モデル向けに調整した API を最適化して提供しています。生成コードの組み込みスタイルを使用する場合、組み込みシステムでどのようにコードを実行するかをモデル作成します。したがって、モデル内の定義は、組み込みターゲットに対して特定のものである必要があります。モデル名、パラメーター、信号ストレージ クラスなどの項目は、コードの組み込みスタイルの API の一部に含まれます。
生成コードの組み込みスタイルとラピッド プロトタイピングの主な相違点は、後者には含まれているエントリポイント関数が少ないという点です。組み込みスタイルのコードは、1 つの関数
のみをもつように構成できます。model
_step
このためモデルの実行コードは、Loop...EndLoop
ステートメントを排除し、ModelOutputs
、LogTXY
および ModelUpdate
を 1 つのステートメント
にまとめます。model
_step
生成された組み込みコードの実行方法の詳細については、生成された C 関数インターフェイスをモデルのエントリポイント関数用に構成を参照してください。