2011年5月6日金曜日

Stellarisのバグ?(その2)

UART+uDMAを使った受信において気づいたこと。

例として、10バイトのデータをuDMAを使って受信することを考える。データシートやサンプルに従って設定すれば期待通りに動作した。しかし、不測のデータ受信やエラーリカバリ処理に難儀した。

通信相手が10バイトではなく13バイト送ってきたと仮定する。
uDMAをBASICモードで設定していれば、まず最初の10バイトをDMA受信した時点で割り込みが発生した。これは期待通り。uDMAをBASICモードで設定しているので、この時点でuDMAの動作はSTOPになっている。
問題は、このあと残りの3バイトを受信したときにStellarisがどのように振る舞うかだ。
このときStellarisは残り3バイトについて、通常の(DMAを使わない)ソフトウェア+割り込みを使う方法と同じように、受信割り込みを発生させてしまう。ここで誤ってその3バイトの受信データをソフトウェアで読み出してしまうと話がややこしくなる。

UART自体は、RXDMAEフラグがセットされている以上は受信データが1バイトでも検出されると、そのUART回路内部でDMAリクエストを保持してuDMAコントローラの方に送っているようで、たとえUARTの受信FIFOの内容をすべてソフトウェアで読み取った後でもそのDMAリクエストはUART内部で残り続けている。つまり、余分な3バイトをソフトウェアで読み取ってもうFIFOには受信データが残っていなくても、次回uDMAの受信設定を行うとまだ保持され続けているUARTからのDMAリクエストによって1バイト分の受信データ転送トランザクションが発生してしまう、という誤動作につながるようだ。

これを回避する方法としては、

(A)DMAを使うときはソフトウェアで直接FIFOを読み出すようなことはしないこと。

(B)上記のような3バイトを「余分なデータ」として読み捨てる場合は、ソフトウェアでFIFOを読み出しても良いが、次回DMAを設定する前にUARTからのDMAリクエストをクリアする方法を行うこと。

の2案が考えられる。
上記の例で余分に受信された3バイト分が、本来次に受信されるべき10バイトのうちの先頭3バイトであれば(=すなわち、正常な次のパケットの一部などであれば)、この(A)を採用してそのまま次のDMA受信を設定&開始すれば問題なく(DMAで)受信される。

しかし、受信された3バイトが本来必要ではないゴミデータであればそれを読み捨てる(B)案を採用せざるを得ない。その(B)案を行う具体的な方法としては、

(B-1)Software Reset Control 1レジスタのUARTnビットを使ってUARTそのものをリセットしてしまう方法

(B-2)UART ControlレジスタのUARTENフラグにいったん0を書いて、1に戻すという方法

の2つが有効であるようだ。
(B-1)の方法は、UARTの設定そのものがリセットされてしまうので、再設定を余儀なくされる。
(B-2)の方法は、UARTの設定は保持されるがUARTENに0を設定している瞬間は受信処理が止まるのでその瞬間に受信データが来ると受信し損ねてしまう、という問題が残る。
どちらにしてもUARTの動作は一瞬でも止まるので、データを受信し損ねる可能性は排除できない。

もうちょっとうまいエラーリカバリ方法を用意してくれないものかなぁ。

0 件のコメント:

コメントを投稿