リアルタイム制御
リアルタイム制御について解説してみます。
リアルタイム制御とは、「規定時間内で処理できるような制御方法」と定義されています。端的には、発生した事象に対して即座(リアルタイム)に対処できる制御です。
例えば、パソコンの作業でKB入力するのもリアルタイムの制御のひとつで、KBで打たれたキーがハードウェア信号によってパソコン操作上のプログラムで認識され、そのキーの意味をアプリへ反映するということになります。具体的には、パソコンのOS下で動作するドライバというプログラムでKB配列などの入力情報を管理し、アプリへはOSを経由してドライバとやり取りされ、入力されたKB情報をどう扱うかはアプリ次第となります。
パソコン上では(あたかも)リアルタイムで制御されているようにみえますが、いくつかの処理を経由していることになります。解説するリアルタイム制御はその制御に特化した内容と考えていただきたい。
具体例をもってそれらを示してみます。
以下は、磁気カードリーダーでの開発例です。
装置仕様
磁気カードリーダーは数種類の磁気ストライプを持つ磁気カードを読むことができ、かつ、両面まで対応しているものです。シリアル・インターフェースを持っていて、上位からのコマンドによって制御も可能です。また、コード入力のためのテンキーが付属されていて、状態表示用としてLEDもあります。電源はケーブルを通して供給されています。
コマンドはインターフェース仕様で、装置の機能は装置仕様で決められていて、ひとつひとつの状態については状態遷移図によって開発仕様として決めています。電源が投入され初期化後アイドル状態となり、コマンドあるいはディップSWで装置機能レベルを変更することができます。
MAXパフォーマンスとしては、両面磁気ストライプを読み取りながらインターフェース信号を受信し、KB入力を認識して、LEDを点灯/消灯させるというところです。
アーキテクチャ
アーキテクチャとは、H/W上の構成を示すものです。
制御部として、CPU、ROM(Read Only Memory)、RAM(Random Access Memory)、I/Oポート、Interfaceコネクタ を構成して、内部Interfaceでカードリーダー・ユニット、テンキー・ユニットを接続し、各入力はI/O用ICを経由して、割込(Interrupt)でプログラムによって認識されるようになっています。
≪参考資料≫に詳細を記載します。
アルゴリズム
アルゴリズムとは、一連の処理を実現させる方法あるいは考え方です。
リアルタイム制御を実現するプログラムは一概ではないですが、いくつかのベースとなるアルゴリズムがあります。もちろん開発環境や実行環境で利用可否は分かれますが、実際に利用したアルゴリズムを紹介してみます。
タイムシュアリング(時分割)処理
リアルタイム制御では必要である処理と思います。
CPUはクロックに応じて順次演算を実行していきますが、それをプログラミングによって(あたかも)パラレル(並列)処理しているようにするのがタイムシュアリング(時分割)処理です。
発生した事象に対してリアルタイムで処理するための仕組みといってもいいでしょう。時分割するといっても同等に処理時間を割り当てるわけではなく、状態に応じて必要な処理時間を割り当てることで、例えば、以下に示すスケジューラーなどを利用して実現させます。
スケジューラー
タイムシュアリングを実現させるための簡易なオペレーティング・システムのようなものです。簡単にいうと、優先順位と処理の順番を決めてプログラムを実施させる仕組みです。スケジューリングされた順番に状態の確認やタスクの実施をおこなわせるものです。
割込処理
KB入力やインターフェース信号などの外部入力をプログラムが常に監視しているとパフォーマンスが低減してしまいます。そこで、ハードウェアで外部入力が感知された場合、決められたプログラムが実行されるようにするのが割込処理です。割込処理では必要最小限の処理のみをおこない、発生時の処理に戻します。その後、スケジューリングされた処理で割込みされた内容を確認し、タスクとして処理することになります。
排他制御
割込の優先度などの他の処理を制限させる必要がある場合、状態のenable(有効)、disable(無効)を設定する制御です。例えば、磁気カードのスキャン時はKB入力を無効にすることです。
スタック制御
スケジューリングしたタスク処理を実行する場合、サブルーチンのスタック・ポインタを操作して順次制御する方法があります。サブルーチンは呼び出された場所へ戻るため、呼び出されて時点でそのポインタを保持します。スケジューリングで呼び出されたサブルーチンの開始・終了ポインタを操作することで処理する場所をシフトさせるということです。プログラム制御自体を操作するのでリスクの高い処理ですが、容量に制限がある場合に利用します。プログラムの構造を見やすくするメリットはありますが、十分な設計や確認が必要となります。現在はプログラム機能や構造・環境も変わっていますので基本的に利用しないと思います。
QUE(キュー)
処理を順番通り実施するために利用します。STACK(スタック)は後入れ・先出しですが、QUE(キュー)は、先入れ・先出しの入れ物で、先に入ったものが先に出せる構造です。処理を順番通りに実行するためのデータ構造ということになります。
リソース管理・メモリ制御他
例題の装置ではおおよそ上記の制御を利用しています。その他にも必要に応じて利用するアルゴリズムがあります。多くのリソースを使用するシステムやメモリを有効活用するシステムではページングなどの制御があります。必要な処理を必要なシステムで利用するということになり、その知識はわかっていた方がいいということです。
開発環境や利用言語で利用できる範囲が異なったりしますが、情報処理の知識として身に着けておいた方がいいということです。
処理の流れ
アルゴリズムの内容だけではわかりづらい部分も多いと思います。例題装置での実際の流れで説明してみます。
基本制御
電源を投入するとハードウェア診断をおこない、各ポート・メモリなどを初期化します。装置制御であれば、まず実施される処理です。その後は仕様に則った制御となりますが、例題装置ではイベント(割込など)が発生するまではメーンルーチンでスケジューリングしながら待つことになります。
インターフェース受信処理
イベントとして、受信割込を検知するとインターフェース信号を読み取りにいきます。その割込処理で読み取ったデータをもとにタスク処理を決めます。例えば、あるデータ開始コードであればデータフォーマットがわかりますので、それに準じて受信タスク処理を実行していくことになります。
IDカード読み取り処理
IDカードをスキャンすると割込が発生し、そのデータを取りにいきます。読み取り可能状態でなければ、割込自体が無効になっていますし、有効なデータでなければ受け捨てる等の処理をおこないます。データの読み取り手順についてはインターフェース受信処理とあまり変わりません。
※IDカードで両面読み取りとなると処理時間がタイトになります。リアルタイム制御では集中処理はできませんので、クロック数単位で計算してミニマムの処理可能時間を割り出します。
インターフェース送信処理
上位へ送信する処理です。送信するデータをセットして送信用のポートをたたくと上位へ送信できる仕組みとなっています。
その他の処理
その他、KB入力やLEDも状態や状況に応じて制御します。また、アラート発生時やメンテナンス起動時などは専用処理へ移行します。アラート発生時は必要なログをセーブしてホールドさせますし、メンテナンスでは初期化から起動しなおします。
例題装置は付随装置ですので、アラート時は可能であれば上位へその情報を送信します。
まとめ
開発環境などで制御は変わると思いますが、リアルタイム制御が必要な装置では、概ね、上記のような処理がおこなわれているはずです。ひとつの例として、参考にしていただければ幸いです。
≪参考資料≫にコーディング例や具体的なアーキテクチャ、フローチャート、概念図などを載せます。
現在はシミュレーターなどが充実して、開発はパソコン上でおこなっていることが多いと思います。投稿者の時代もパソコン上でコーディングはしていましたが、デバッグはデバッガーという専用マシンでおこなっていました。開発環境や開発言語も変わってきていますし、開発者にとって負担のない環境になってきていると思います。個人的には、その分、機械から遠ざかっているようで寂しい気もします。
それでも、プログラミングの基本はあまり変わっていないように思えます。そうでなければ、再帰処理やループがずっと試験で扱われないでしょう。プログラミングについては、機会があれば投稿したいと思っています。
組込ソフト(注釈)
投稿者が開発業務していた時は、装置制御プログラムは単に「ファームウェア」とか「マイクロプログラム」と称していました。なので、制御担当者は「ファーム屋」と呼ばれていました。他、メカ担当は「メカ屋」、エレキ担当は「エレキ屋」など。たぶんですが、高級言語で開発するようになってから、「組込ソフト」と称されるようになったのではないでしょうか。
FPGA (フィールド プログラマブル ゲート アレイ)
昔、「カスタムIC」といわれていたものに該当すると思われます。専用に作られた論理回路をICにしたもので、それを外部からプログラミングでも設計できるようにしたもののようです。単純な決められた処理を組み込んだ論理回路をチップにしたものと解釈しています。ハードウェア上のモジュール化といってもいいでしょうか。昔の「カスタムIC」は開発コストが掛かるので、ある程度数量が出せる装置でしか適用できなかったと思います。コスト比較はしていませんが、それよりも安く簡単に作れるのでしょう。
ついでに、ワンチップCPUというのがあって、制御部をひとつのICとして作られたものです。非同期で動作させるメカ制御などで利用します。メインCPUとは内部インターフェースや共有メモリを介して、情報をやり取りします。
装置開発でどのようなアーキテクチャにするのかは、機能面はもちろんですがやはりコスト面が大きいかと思います。収益の出せない装置は開発できないということです。近年、日本の企業がものづくりを撤退した要因のひとつですね。いいものは作れるのですが、時代の流れとはいえ、どうにかできなかったのかと思います。
≪参考資料≫
装置仕様(アーキテクチャ)
例題装置仕様です。
CPU:8085、ROM:32k、RAM:8k、I/O:8251、8155
プログラムの担当者にとっては、32k(0~7FFF)は要求仕様に対して非常に厳しいものでありました。それゆえに、テクニカルなプログラムが必要だったのです。
以下、主だった処理やコーディングを示します。
メーン・ルーチン
フローチャート
プログラムとは、(基本的に)条件判断とその条件に即した処理を実行させる手続きです。フローチャートとは、そのプログラムの流れを図示したものです。大体、こんなイメージというもので、これを参考にコーディングするのですが、コーディングは担当者の意思が入るのでイコールにはならないでしょう。コーディングを変えたからとフローチャートの書き直す奇特な人は少なかったと思います。
プログラムは、特に決めがなければ"0"アドレスからスタートします。まず、必要な初期化として、I/Oの初期設定、ROMのチェック、RAMの初期値設定をおこいます。
異常がなければ、メーン・ルーチンへ移動しアイドル状態としてイベント待ちします。状態監視や時間監視をしながら、必要な処理(タスク)へ移行して実行しています。前述していますが、タスク処理をしても必ずメーン・ルーチンへ戻してスケジューラーを回していきます。
割込処理
ハードウェアで決められたポートに入力信号を検知すると、設定されたプログラムアドレスへ処理が飛びます。これを割込(Interrupt)といいます。規定時間内に処理を終えて、割込発生時のセーブしたアドレスへ戻します。
※リストの見方
掲載したのはオブジェクト・リストです。アセンブルしたあとのオブジェクト生成後のリストで、左からプログラムアドレス、機械(マシン)語、ラベル、命令(ニーモニック)、コメントとなっています。
タスク
「タスク」の一部リストを示します。磁気ストライプの読み取り開始から終了までの処理です。ポイントごとにスタック制御(操作)を使って処理開始位置をシフトさせています。
リストだけで「タスク」の流れを追うのは難しいと思います。そのため、設計仕様書が必要になるわけです。理解しやすい設計仕様書を作成することもいいプログラマーの条件となるといわれています。
ただ、知り得る限りプログラマーという人種は仕様書を作成するのを好まないようです。
補足
プログラム構造
CPUはクロックに応じて順番に処理します。なにもしなければシーケンシャルな処理を実行するということです。それをプログラムによって時分割(タイムシュアリング)で処理するようにし、あたかも並列処理しているようにします。
プログラムの構造上は、メーンルーチンでループさせ、サブルーチンでそれぞれの状態・状況を確認します。事象が発生するとサブルーチンをタスクとして実行させます。各処理で共通する処理もありますので、そこからさらにサブルーチンを呼び出す場合もあります。2あるいは3階層までの深さ以上にするとわかりづらくなるので注意が必要でしょう。
ポインター
「ポインター」は、スタックやキューでも用いられる概念です。入れ物に対して、どの位置を示しているかを保持するものです。データがランダムで保存されていてもその位置や情報が保持されていれば扱いやすいということです。マトリックスを扱う場合でも利用されます。