« 2010年8月1日 - 2010年8月7日 | トップページ | 2010年8月22日 - 2010年8月28日 »

2010年8月15日 - 2010年8月21日の1件の記事

2010年8月15日 (日)

FPGAのSPIインターフェース遂に完成

 ここ1週間ばかり、お盆休みをとっていたのだが、外にもでかけず(まあ猛暑だったこともあるが)FPGAのSPI(スレーブ)インターフェースのデバッグに夢中になっていた。先ほど、やっとのことでバグの原因が判明し、清々しい気分でこのブログを書いている。

 今度も、日頃からお世話になっているkugaさんのヒントで解決したのだが、最後の最後になっても細かいエラーが取れず、今回も苦労した。しかし、苦労が多ければ多いほどその喜びは大きい。人間というのは因果なものである。

なかなか正しいデータが受け取れない(8/8/2010)
 FPGAのLattice XP2基板のSPIインターフェースは、フォトフレームプロジェクトの第5ステップにあたり、送り側のARMプロセッサー(STM32F103)のSDカードデータを送るSPI(マスター)インターフェース(第6ステップ)は、既に完成している。

A8153045

 前にも書いたように、FPGAのSPIスレーブインターフェースは、fpga4fun.comから拝借したソースコードを使わせてもらっている。テスト環境ではARMからデータを送った後、FPGAのUARTのリターンキーを押せば、SRAMに蓄積されていたデータがUART端末に表示される。

 前記事にもあるように、30バイト程度の短いメッセージならほぼ正しく帰ってくるが、少しデータが増えると、カウンターがおかしくなるらしく、表示が止まらなくなる。いわゆる暴走である。UARTで結果が見えるように、テストデータはキャラクターで、SDカードの中のテキストファイルを選んで送信するのだが、派手に字化けする。

 データはともかく、カウンターに目茶目茶な数字がはいっているようだ。切れ切れに見えるメッセージもあちこちでデータが化け、特に日本語の2バイト文字は悲惨な結果になる。まあ、プログラムの最初はこんなものだ。少しづつ不具合を直していこう。

 データの中味をチェックする前に、先にデータカウンターの部分を直さないとテストがやりにくい。いちいちリセットしているのではたまらない。しかし、FPGAはいわゆるprintfデバッグ(変数を途中で出力させて調べること)が出来ない。カウンターの数字を知る方法は何かないかと考えていたら、以前7セグLEDテストのときにバイナリから十進4桁データに変換するモジュールを開発したのを思い出した。

 デバッグ対象のコードにさらに不確定要素を加えるのは、デバッグの常道にはずれるが、この際、背に腹は代えられない。何とか組んでカウンターの数字をUARTに出してみた。ASCIIの数字キャラクターは、バイナリに0x30を加えるだけなので楽である('1'=0x31,'2'=0x32...)。

 テストのためにまずカウンターを固定して、モジュールから数字を取り出す。あれえ、全く違う数字が出る。SPIが読んだ時の数字(でたらめだが)のようだ。やれやれ、こいつのデバッグもしなければいけないのか。ソースを見直す。うはあ、こいつはマスタークロックで何十回かループしないと結果がでないソースコードだった。10進数字を取り出す前に計算終了のフラグをテストするループを加えて、やっと決め打ちした数字がUARTにでた。

 このモジュールが出す読み込んだデータの数は予想通り支離滅裂な数字だった。unsigned変数は0から1を引くと、最大数になり、止まらなくなる。カウンターが0になったときに備えて、0からこれ以上引かないような姑息なロジックも入れるが、これも関係なし。リセットした直後は、まあまあのデータが出るが、続けるとまるで駄目である。

 SPIってこんなに不安定なのか。しかしおかしなことに8ビットフレームを1ビットでも間違えれば、あとはすべて狂うはずだが、奇妙なことに正しいデータが復活する時がある。STM32側のデータはロジアナで追って、バイト数が合っていることを確認した。中味は30バイトほど調べて少なくとも間違っていなかった。まあ、この不具合はすべてFPGA側が原因だろう。

 前にも書いたが、このfpga4funのコードは一風変わっている。各変数のあとにひとつづつクロック単位に動くalways文がついている。例えばSCKの立ち上がりは、3ビットのシフトレジスターを動かし、そのデータが01Xになった時、つまりSCKが立ち上がったときに1になるよう変数を定義し、その変数を使って8ビットデータを拾う。実に巧妙な方法だ。

 このコードは、SPIの入力をマスタークロックでサンプリングして、ビットの立ち上がりを調べている。SPIのSCKクロックで動かす方がより正確なデータがとれるような気がするのだが、人さまの書いたソースなので、原因究明の方法の糸口が見つけられない。

 FPGAのクロック33Mhzと、最初設定したSPIの速度9Mhzの差が近いので、サンプリングが上手く行っていないのかも知れないと、クロックを半分の4.5Mhz(計算ではSCKの1パルスに3回以上サンプリングできる)に落としたが、結果は変わらなかった。

 これ以上何をやって良いのか見当がつかなくなった。サンプリングだとデバッグしにくいので、むしろSCKのクロックに合わせた回路の方が安全のように思えてきた。考えた末、遂にfpga4funのソースをあきらめ、自前で作ることにする。

自前のSPIスレーブルーチンもエラー続出(8/10/2010)

 腰を据えて、自前のSPI スレーブコードを書き始めた。思ったより早くコードが完成した。おお、fpga4funのコードより消費スライスも少ない。良いぞ。テストする。最初、200バイトあまりのデータが正しく表示されたので小躍りして喜んだが、少しデータが増えると相変わらず字化けが起きる。ところどころでデータが欠落する。

 不思議に字化けの連続にはならない。8ビットフレームが守られているからか。相変わらず、文字カウントはリセット直後は良いが、あとはデタラメになる。???である。目を皿のようにしてリセットあたりのロジックを確かめるが原因がわからない。

 迷走している。ロジアナでSRAMのアドレスまで出して詳細に調べる。殆どの8ビットフレームはちゃんとSRAMにアクセスし、カウンターも上がっていくが、ところどころ、書き込まれずにスキップしたり、カウンターが変わっていない(または2以上カウントアップ)ところが出ている。

 40センチほどあるSPIの配線が長すぎるかと思ってジャンパー線を減らし短くしてみる。少し改善されたようには見えるが、相変わらず安定しない。カウンターはいまだにでたらめである。SPIは殆どが基板上のディバイス間でしか使われないので、こういうジャンパー線で20センチも延ばしてはいけないのかも知れない。プルアップも効果なし。

Spi811adr

 謎は深まるばかりである。ロジアナを見れば、SPIはほぼ所定のデータを読み、WE(WriteEnable)が8ビット単位に忠実にパルスが上がってSRAMに書き込みをしていることは間違いない。不審なところは、同じalwaysループ内で動いているSRAMアドレスの増え方である。

 アドレスは読み書きで同一のバスを使っているが、このassign文をやめて別個のregにして2重に持たせるようにした。しかしこれでも駄目。一体何だろう。アドレスがところどころで突然0に戻る。しかも、途中で書き込みが終わってしまうときもある。CSにノイズが入っているのか。そんなばかな。これまでにない難関にぶちあたってしまったようだ。少し頭を冷やそう。

やっぱり聞いてみるものだ(8/12/2010)

 この5日間、悩み続けたFPGAの謎は、「すん」さんの掲示板に上げた質問に、いつもお世話になっているkugaさんが助言してくれて、いっぺんに解決した。HDLはプログラムではない回路だと考えろなどと少し偉そうなことを書いていたが、所詮はソフト屋である。

 同じループ内にあれば、全てのステートメントは間違いなく実行されるものと信じていた。ところがループを駆動するタイミングと判断する信号の間が非同期の場合は、その動作は保証されないという、FPGAでは極く初歩(だろうと思う)の常識を知らなかった。

 SPIスレーブは相手マスターからの同期クロックで動くメイン側から見れば典型的な非同期回路である。これを気楽にアドレスカウンターのインクリメントのトリガーに使っていたために、アドレスやカウンターのインクリメントが不定になっていたのである。

 アドレスカウンターは256Kワードなので18ビットである。さらにデータカウンターも18ビット、計36ビットをSPIのデータレディをトリガーにすれば、マスタークロックのタイミングと必ずいずれどこかでぶつかって、そのときの値は不定になってしまう。

 夜中、恐る恐る「こんなことってあるのか」などと大層なタイトルで掲示板に上げたら、ものの1時間もしないうちにkugaさんから回答があった。最初、非同期ということがわからず、反論でもないけれどロジアナのチャートを見せて「どこかでカウンターをこわしているやつがいる」的な主張をしたら、kugaさんから「18段のDFF(D-FlipFlop)を同時に動かすとき」という言葉で目が覚めた。

 これまでのブログをお読みになれば分かるように、当研究所のFPGAの勉強は基礎からやっていない。7セグLEDやUART、カラーバーなどが思ったより順調に動いたものだから、つい甘く見ていた。基礎からの勉強の必要性を痛感した。疑っていたFPGAさんごめんなさい。

 コードの方は、
always @(posedge pCLK) spi_rdy <= ( byte_received );

spi_rdy      ...... メインモジュールでのレディ信号
byte_received ..... spi入力(スレーブ)モジュールでのレディ信号
pCLK      ..... メインモジュールのマスタークロック
の一行をspi入力モジュールにつけるだけで、ほぼ正しくデータがSRAMに入るようになった。ばんざい。いやあ、やっとトンネルを越えた。

813spi_ok

 しかし不思議なことがある。最初のfpga4funからもらったSPIはマスタークロックで動いていた。非同期回路ではない。それがどうしてうまく行かなかったのだろう。試しに、fpga4funのコードを入れた見た。ややや、ちゃんと動くぞ。これはどういうことだ。

 しかし、どちらのルーチンもまだ細かい誤データが出る。8ビットの頭のビット(MSB)を間違え、1が半角のア、2がイになる。つまり、0011->1011 となる。ビットマップの画像データは文字データほどシビアではないとはいえ、こんなところでゴミは入れたくない。戦いはまだ終われない。

最後のエラーはUART送信のバグだった(8/14/2010)
 ロジアナを入れっぱなしにし、ソースを少しづつ変えながらUART端末を2つ立ち上げて、ファイルを送っては、チャートを調べ、出力データをチェックする作業を続ける。もう一息なのだけれど、まだ完全にERRORなしのデータは送れない。

 fpga4funの方のソースのSCKの立ち上がりを2ビットでなく、3ビットまで読んで立ち上がりとみなすというロジックに換えて(01Xでなく011で立ち上がり)、かなりエラーは少なくなった。サンプリングするところがSCKの立ち上がりから1クロック分あとになる方がエラーが少なくなるようだ。

 ARMの方にも少し心配が残っている。CS(ChipSelect)を別個のGPIOで出しているのだが、データを送り終える前に、デアサートされてしまう。最後の1バイトはエラーになるし、受信側(FPGA)のレジスターは途中で中断され悪影響が心配である。CSをデアサートする前に、SPIのビジービットがリセットされるのを監視するロジックを加える。

 これでエラー状況が好転するかと淡い期待を持っていたが、残念ながら全く影響はなかった。データカウントがファイルのデータ長と一致したのが唯一の収穫。

Spi_screen

 万策がつきた。この程度のエラー率(1KB送って数バイト)を許容して先に進むかどうかである。QVGAの8ビットカラーなら77KB、16ビットなら154KB、画面が汚れるだろう。そうだ全体のエラーがどれくらいになるか、調べてみよう。

 FPGAのUARTの部分に機能を追加して、スペースキーを押せば、SRAMのデータを一定量づつ際限なく表示するロジックを加えた。ここの部分は、複数データを任意にUART出力できるように改善して大分モニターらしくなっている。簡単に機能が追加できた。

 テストしてみる。すると驚くべき事実が判明した。同じSRAMアドレス上のデータが、表示するたびに変わるのである。あーあーあ、何故これをもっと早くやらなかったのか。これまでSPIのエラーだと思っていたのは、すべてUART送信側のエラーだったのである。SPIはしっかりデータを送っていたのだ。これまで疑っていたSPIのみなさん大変失礼しました。

 これで、プロセッサーからFPGAに画像データを送るルートは確立された。次のステップはいよいよ液晶モニターに画像を表示するステップである。買ってきたデジタル液晶はワイド液晶なので、ピクセルクロックに30Mhz以上を要求されている。これは少しきついので、これまでのアナログ液晶に簡単なラダー抵抗のDACを加えて、とりあえず8ビットカラーにしてみようかとも考えている。フォトフレームプロジェクトは山場を迎える。

| | コメント (2) | トラックバック (0)

« 2010年8月1日 - 2010年8月7日 | トップページ | 2010年8月22日 - 2010年8月28日 »