MC9S08JS8のSPIを使おうとしたんだけど、コンパイラではまった [CodeWarrior]
MC9S08JS8のSPIを使おうとしましたが、向かうところ問題だらけでした。
SPRFフラグがクリアできない
MC9S08JS8には、16-bitのSPIが搭載されています。 こいつと74HC595シフトレジスタを使って、XXXしようと思いました。 74HC595は、シリアル転送後にLATCHクロックを発行する必要があるので、シリアル転送の終了を検出する仕組みが必要です。
そのためには、SPRFフラグを使うと便利です。 SPRFフラグを検出したら、フラグをクリアして、LATCHクロックをトグルする。 これで、完璧だったはずなのに、なぜかシフト・データがずれます。 原因を探るうちに、SPRFフラグがクリアされていないことがわかりました。 SPRFフラグがクリアされないと、LATCHクロックをトグルするタイミングが早すぎて、出力データがずれてしまいます。
SPRFフラグのクリアシーケンス
SPRFフラグをクリアするためのシーケンスは、このように書かれています。
SPRF is cleared by reading SPRF while it is set, then reading the SPI data register.
前半部分は、 while 構文を使ってフラグのアサートを待つだけで十分です。 問題は、後半の SPID レジスタの読み出しでした。
最初のコード
うまくシリアル転送が出来ずに、データがずれてしまった最初のコードがこれです。
SPID16 = buf[line][3]; while (!SPIS_SPRF) ; // Dummy read with a void casting. (void)SPID16;
16-bitのSPIDレジスタは、"SPID16"というシンボルで宣言されています。 このレジスタは、"volatile"宣言されているので、(void)修飾をつけると、単なるレジスタの読み出しを行ってくれるはずです。 サンプルコードとして一般に使用されている技法です。 ところが、生成されたコードは、これでした。
0088 L88: 70: while (!SPIS_SPRF) ; 0088 0f00fd [5] BRCLR 7,_SPIS,L88 ;abs = 0088 71: // Dummy read with a void casting. 72: (void)SPID16; 008b b601 [3] LDA _SPID16:1
SPID16 レジスタのLSB側 8-bit だけが読み込まれています。 そのため、データの受け取りが完了したとみなされずに、SPRFフラグがクリアされなかったと考えられます。 なんで、8-bitしか読み出してくれないんだ?
二つ目のコード
読み出した値が使われないのが問題なのだろうと思い、二つ目のコードを書きました。
word wDummy; : SPID16 = buf[line][4]; while (!SPIS_SPRF) ; // Dummy read with a word variable. wDummy = SPID16;
ダミー変数を用意して、そこに値を代入させるようにしました。 すると、こんなコードが生成されました。
006c L6C: 63: while (!SPIS_SPRF) ; 006c 0f00fd [5] BRCLR 7,_SPIS,L6C ;abs = 006c 64: // Dummy read with a word variable. 65: wDummy = SPID16; 006f b601 [3] LDA _SPID16:1
なんだ、全然変わらないジャン。 これもダメみたいです。
最後のコード
二つ目のコードの問題は、"wDummy"が結局は使われないというところにありました。 使わないけど代入して欲しいときには、"volatile"を付けます。
volatile word vwDummy; : SPID16 = buf[line][5]; while (!SPIS_SPRF) ; // Dummy read with a volatile word variable. vwDummy = SPID16;
ダミー変数の宣言を変えただけです。 これで生成されたコードがこれです。
0057 L57: 59: while (!SPIS_SPRF) ; 0057 0f00fd [5] BRCLR 7,_SPIS,L57 ;abs = 0057 60: // Dummy read with a volatile word variable. 61: vwDummy = SPID16; 005a 5500 [4] LDHX _SPID16 005c 9eff08 [5] STHX 8,SP
ようやく、16-bitの読み出しをしてくれるようになりました。 こんな事のために局所変数を割り当てる必要があるなんて、実にもったいない。 現在、 Technical Support に真相を問い合わせています。
こんなコードも試した
"volatile"変数に代入せずに"volatile"扱いにするために、こんなコードも試してみました。
(volatile void)SPID16;
ところが、この場合には、内部エラーが発生してしまいました。
Error : C5701: Internal Error #604 in 'f:\build\Products\Hiware\cw_hc08_Build_Tools\cw_hc08_Build_Tools_070208\hiware\src\ccpp\ccpp.cpp' while compiling file 'C:\Projects\CW\JS08\Sources\main.c', procedure 'main', please report to Freescale main.c line 11 Error : Internal Error Error : Compile failed
これも Technical Support に問い合わせ中です。
留意事項
以上の問題は、私がCodeWarrior for Microcontrollers V6.2 で遭遇したものです。 この記事をごらんになっている時点では修正されている可能性もありますので、ご留意ください。
2009-05-13 追記
freescaleの"Technical Support"から返答がありました。 "Internal Error"が出るのも、16-bitのダミー読み出しが出来ないのも、どちらもバグと認定していただきました。 いつ、修正されるかはわかりませんが、それまでの16-bitのダミー読み出しの対策は、
(void)SPIDH; (void)SPIDL;
だそうです。 つまり、8-bitずつ読み出せと。 これなら、ちょっとサイクル数がかさみますが、局所変数を割り当てることはありません。 しばらく、これで逃げるか。
コンパイラだって、遥か彼方の惑星系から来た文明がもたらした物ではなく、人の創りし物なので、こういったバグを偶に見ると、怒ると言うかむしろ嬉しくなってしまいます。
「ああ良かった、やっぱりどんな人でも間違うんだよね!」
まあそれでも私にとっては依然として雲の上の人ではあるのですが。
前から思うのですけれど、ありきたりなC言語の入門本ではなく、コンパイラを作る側からの視点で書かれたC言語の本が有ったらなぁ。
by hamayan (2009-05-07 21:53)
こういったバグを「たまに」見つけるのなら良いのですが、最近、何かしようとするたびにバグを見つけてしまっています。おかげで、肝心のアプリケーションの進捗が遅いのです。
大学3年生の時にとある講義で出された宿題は、「コンパイラを作れ」でした。 CP/M上のBDS-Cで書いた68000のアセンブラを吐くクロス・コンパイラを書いたのだけど、ターゲットの実物が存在しなかったのでコンパイルの結果が正しいかどうかは確認できなかったのでした。まだ、残っているかなあ。
by noritan (2009-05-08 09:18)
Technical Support から返答がありましたので、「追記」しました。
by noritan (2009-05-13 19:22)