フラグをクリアするタイミングについて考える [プログラム三昧]
よいテーマを与えていただいたので、少しばかり深く考えてみます。
発端は、某所のサンプルプログラムでした。 ソフトウェアで実現したフラグを割り込みサービスルーチンでセットし、通常のメインタスクでクリアするモデルです。
ソフトウェア・フラグの危険性
このモデルでは、2ステップでフラグをクリアします。
- メインタスクでは、フラグをクリアすべき条件を検出します。
- そして、フラグを実際にクリアする操作をします。
このため、両者のタイミングには時差が生じます。 その僅かな隙を割り込みに付け入られてしまうと、フラグをクリアすべき条件が崩れてしまいます。 つまり、クリアすべきでないフラグをクリアしてしまい、結果として、データを取りこぼしてしまうというわけです。
図の例では、青色のデータの処理中に緑色のデータが到着しています。 緑色のデータが到着したことを示すために、割り込み処理ルーチンでフラグがセットされます。 ところが、メイン・タスクでは、残された青色のデータの処理でフラグをクリアしてしまいます。 その結果、緑色のデータの到着が忘れ去られてしまい、データを取りこぼしてしまいます。
Javaにおける解決策
Javaでマルチスレッドなプログラムを書くと同じ問題が生じます。 Javaでは、セマフォが言語レベルで実装されているので、回避する事ができます。 ただし、デッドロックという別の問題があるので、よく分からずに使用するのは危険です。 参考文献には、マルチスレッドに関わる色々な解決策が記載されています。
HCS08における解決策
HCS08のような小規模なマイコンの場合、割り込みに付け入る隙を与えないようにするため、 メインタスクで割り込みを禁止するのは、簡単です。 マイコンに備わっている「割り込み禁止フラグ」をセットするだけです。 Cで記述する場合には、「DisableInterrupts」というマクロを使用する事ができます。
DisableInterrupts; if (rx_buf_read_pointer==rx_buf_write_pointer) flags.rx_flag = 0; EnableInterrupts;
禁止したままだと、永久に割り込みが発生しなくなりますので、用が済んだら割り込み許可「EnableInterrupts」をお忘れなく。
ハードウェア・フラグだって危険
フラグといえば、SCIのフラグも危険です。 例えば、データあり(RDRF)フラグは、データを準備するハードウェアがセットし、データを取り出すソフトウェアがクリアします。 しかも、ハードウェアがセットする行為は割り込みではありません。 これを禁止するには、SCIの機能を停止する以外に方法が無いので、ソフトウェアから禁止することは、事実上不可能です。
こんな危険性を考慮して、RDRFフラグをクリアするためには、ある面倒なシーケンスを踏む必要があります。
- フラグがセットされている時にSCIS1レジスタのRDRFフラグを読み出す。
- SCIDレジスタから到着したデータを取り出す。
このシーケンスの途中でフラグが再度セットされるような条件が発生したら、フラグをクリアするシーケンスは、キャンセルされ、フラグはセットされたままになります。 ちゃんとした、理由が有るのだから、このシーケンスをケチったりしては、いけません。
ColdFireの場合は、どうだろう
ここからは、私には未知の領域です。 ColdFireのような高級なマイコンには、バスエラーなどという仕組みが有って、命令の実行途中で例外(割り込み)が発生します。 そのため、割り込みを禁止したはずのソフトウェアフラグの処理中でも他のルーチンが動きだします。
これを防ぐためには、TASなどの本格的なセマフォを使用して、クリティカルエリアを指定した方がよいのでしょう。 こういう面倒な処理は、オペレーティングシステムに任せたいな。
また、ColdFireには、ユーザモードという概念があり、このモードでは、そもそも割り込みを禁止する領域を設定する事ができません。 リングバッファを使う場合には、やはり、オペレーティングシステムレベルで提供する必要があるという事ですね。
昔のプログラムの検証
今回の件で、その昔、HC11のために作ったリングバッファのプログラムを思い出しました。 調べたところ、しっかりと割り込みを禁止する領域を設定してありました。
*---------------------------------------------------------------* * Get a character through queue buffer. * *---------------------------------------------------------------* fde4 rxgetq equ * fde4 3c pshx fde5 36 psha fde6 rxget1 equ * fde6 96 76 ldaa rxcnt fde8 27 fc beq rxget1 Wait for data. fdea 0f sei ------------------------No INT in this area.----- fdeb 7a 00 76 dec rxcnt Update counter. fdee dc 6e ldd rxgetp Update get pointer. fdf0 5c incb fdf1 c4 7f andb #$7f Clear Bit-7. fdf3 d7 6f stab rxgetp+1 Update LSB only. fdf5 8f xgdx Move D to X. fdf6 e6 00 ldab 0,x Get a character from queue. fdf8 0e cli ------------------------------------------------- fdf9 32 pula fdfa 38 pulx fdfb 39 rts
*+++++ Check RDRF flag for RX +++++* fe43 scipro2 equ * fe43 1f 74 20 17 brclr scsr1,x $20 scipro3 Check RDRF. fe47 e6 77 ldab scdr,x Receive a character. fe49 96 76 ldaa rxcnt Update counter. fe4b 2b 11 bmi scipro3 Ring buffer full. fe4d 4c inca fe4e 97 76 staa rxcnt fe50 18 8f xgdy Save B register. fe52 dc 70 ldd rxputp fe54 5c incb fe55 c4 7f andb #$7f Clear Bit-7. fe57 d7 71 stab rxputp+1 Update only LSB. fe59 18 8f xgdy fe5b 18 e7 00 stab 0,y Put a character.
まずは、ひと安心。
そういえば、これ以外に危ないプログラムを書いた記憶がないな。
参考文献
HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers
- 作者: Fabio Pereira
- 出版社/メーカー: Booksurge Llc
- 発売日: 2007/11/13
- メディア: ペーパーバック
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編
- 作者: 結城 浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/03/21
- メディア: 大型本