SSブログ

フラグをクリアするタイミングについて考える [プログラム三昧]このエントリーを含むはてなブックマーク#

よいテーマを与えていただいたので、少しばかり深く考えてみます。

発端は、某所のサンプルプログラムでした。 ソフトウェアで実現したフラグを割り込みサービスルーチンでセットし、通常のメインタスクでクリアするモデルです。

ソフトウェア・フラグの危険性

RxRingBufferSequence1.png

このモデルでは、2ステップでフラグをクリアします。

  1. メインタスクでは、フラグをクリアすべき条件を検出します。
  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フラグをクリアするためには、ある面倒なシーケンスを踏む必要があります。

  1. フラグがセットされている時にSCIS1レジスタのRDRFフラグを読み出す。
  2. 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

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編

  • 作者: 結城 浩
  • 出版社/メーカー: ソフトバンククリエイティブ
  • 発売日: 2006/03/21
  • メディア: 大型本

nice!(0)  コメント(2)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 2

Tsuneo

えーとですね。
まあ、DisableInterrupts / EnableInterrupts でも目的を果たしているんですが、
HCS08場合の最適解は、選択的に SCI の割込み許可を落とします。
いやあ、バグレポートにはわざと解決法のコードを書かなかったのは、これをどう解釈するか見たかったんです。(まったくいぢわるなんだから)。
え、でも、ちゃんと書いてますよ。ほら。
To solve this problem, guard the (a) line from the SCI interrupt; disable the SCI interrupt around the line.

Tsuneo
by Tsuneo (2008-07-01 17:36) 

noritan

> HCS08場合の最適解は、選択的に SCI の割込み許可を落とします。

これが、本当にお奨めできるかどうかは、私は疑問だと思います。

インストラクションSEIは、実行直後に発生する割り込みを確実に禁止することが出来るのですが、ローカル・マスクである、RIEが何サイクル後に割り込みをマスクしてくれるかについては、保証されていないと思います。
たとえば、割り込みソースのプライオリティエンコーダにパイプライン状の遅延が入っていたら、割り込みが発生する可能性があります。

また、昔のマイコンの場合、ローカル・マスクが非同期組み合わせ回路で構成されていたので、RIEを変更するとグリッチで割り込みが発生したりすることもありました。さすがに、最近のマイコンでは無いよな。(無いと信じたい。)

似たような問題は、"CLI;WAIT"の組み合わせにもあります。割り込みを確実に"WAIT"で受けるためには、"CLI"の次のサイクルで割り込みを禁止する必要があるので、デコード直後に割り込みを禁止する専用ロジックが必要でした。

by noritan (2008-07-01 18:06) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。