2015年9月6日日曜日

世界最小クラスかも知れないFPGAボード 10 (CAN IPを実装してみる 6)

LSEで合成した場合の結果が変な件だが、どうも解せないのでFPGAの設定をMachXO2に変えて合成し生成されたネットリストでゲートシミュレーションを実行してみた。結果は同じでCANが応答しない。

XO2用に合成した場合はモジュール階層がある程度維持されるようで、canモジュールも各サブモジュールの信号名も保持されたネットリストになっており、can_rxpp(受信モジュール)のステートマシンのステートカウンタの信号も残っていたのでその信号を見てみたところ、上図のようになっていた。 stateがステートカウンタ、prv_stateは直前のステート値を保持するレジスタである。
 このステートマシンはステート数16で4bitのステートカウンタの全ての値に意味がある。従って、state[1]が z になるのは変だ。 この z という状態はネットが浮いている、つまりどこにも接続されていないということなので、合成エンジンはstate[1]を不要と判断してその部分のレジスタを削除した可能性がある。

一方のprv_stateは一つ前のstateの値を保持するレジスタで、

prv_stateの値は以下の2箇所(794行目と821行目)で参照されている。

ST_ERROR値は4'd12とST_OVLDの値は4'd14なので、論理式の部分を解くと以下のようになり、確かにprv_state[1]は削除できることが解る。 ( prv_state != ST_ERROR && prv_state != ST_OVLDは、prv_state == ST_ERROR || prv_state == ST_OVLD の反転値なので両者でprv_state[1]は不要である。)

prv_stateの結果は間違っていないがstateの合成結果は明らかに間違っている。合成の最適化オプションの設定が関係しているかも知れない。上記合成は以下の最適化オプション設定で行った。

そこで、最適化オプションのResource SharingをFalseにして合成しなおしてみた。(尚、iCE40用のLSEの設定ではResoure SharingはデフォルトでTrueとなっている。)

生成されたネットリストでシミュレーションしてみると・・・

旨く行った!  state[1]も正常だ。
この結果を受けて、iCEcube2でもResource SharingをFalseにして合成してみた。

生成されたネットリストでシミュレーションしてみると・・・
こちらも旨く行った!   iCE40用の合成の場合は階層情報が維持されないようで、can_rxppモジュールのstateやprv_state等も上記の信号名しか参照できなかった。が、canはACKフレームを出しているので期待通りの動作をしている。

上述の通り、can_rxppモジュールのステートマシンはステート数が16あるステートマシンだ。
ステート間の遷移パターンは49パターンある。
上図のcovered_fsm、... はCoveredというコードカバレッジツール用のもので、このステートマシンの(記述されている)ステート遷移がシミュレーションで全てカバーされているか(検証されているか)を見るための記述である。 このcan_ip開発時に検証した。(CAN IPのDEBUG 8

上記のような複雑なステートマシンの場合は、Resource Sharingは旨く作用しない場合があるのかも知れない。  ということで、Synplifyで旨く行って、LSEで旨く行かないことの原因らしきものは判った。

次は、実機でFPGAが反応しない(ACK応答しない)件だ。
Resource SharingをFalseにして合成したビットマップファイルをFPGAにダウンロードして実機で動作させてみたところ、Synplifyの場合と同じでACKフレームが出ない。反応していないように見えた。  そこで、can_ipの内部信号をFPGAの未使用ピンに出して、その状態をロジアナで観測してみた。

引き出したのはステートマシンがIDLEステートにいることを示す信号と、ERRORステートにいることを示す信号、そして、can_phyモジュールで生成されるcanのRXD信号をサンプリングするためのイネーブル信号(b2r_spen)だ。

その結果、ステートマシンは動いてはいるようだが途中でエラーステートに遷移していることが判った。 さらに細かく見ていくと、b2r_spenの間隔が異常に短い箇所があることが判った。

b2r_spenはcan_phyモジュールで生成されていて、信号受信が無い(RXD信号が変化しない)場合は、自走してビットレートに応じた周期のパルスになる。今の場合は1Mbpsなので1us周期のパルスとなる。また、RXD信号に0➔1または1➔0の遷移がある場合は、そのエッジで周期が微調整される。その様子は以前CAN IPのDEBUG 7に書いた。
俯瞰して見てみると、この現象には周期性があるようだ。

なので、ノイズでの誤動作とかメタステーブルとかが原因では無さそうだ。
念の為オシロスコープでも見てみた。

そこで、ロジアナで採取したCANのRXD信号をテストベンチに取り込んで、シミュレーションで再現するかを見てみることにした。その為には・・・
まず、GTKWaveで観測している波形をTIMファイルにエクスポートする。

TIMファイルは以下のような形式のテキストファイルで全ての信号の情報を含んでいるので、そこからRXDの部分を抽出する。


そして、時刻情報と信号の値をテキストエディタなどを使ってテストベクタに変換する。

このベクタでRTLシミュレーションを実施したところ、現象が再現された。

色々原因を探った結果、Zynq側のビットレートが1Mbpsに対して9%程度低い可能性が見えてきた。
以下の波形はRXD信号に変化が無くてcan_phyが自走してb2r_spenを生成している部分だが、パルス間隔は1us (=1Mbps)となっていて正しい。


これに対して、RXDを見てみると、以下の波形は9シンボル分の間隔を見ているが、その値は9.83usとなっている。 9.83÷9 ≒ 1.09なので9%程度ビットレートがズレている。
CANの規格では送受信間のクロックのズレは最大1.58%となっているので9%は大きすぎる。


Zynq側に何か問題がありそうだ。
ZynqのCANモジュールのリファレンスクロックは24MHzだが、IO_PLLのから供給していて実際は23.809525MHzが供給されている。

ただ、誤差は0.8%程度なのでこれが原因とは考えにくい。とすると、CANモジュール内部のクロック関係の設定が怪しい。クロック関連の設定要素としてはBRPR(プリスケーラ)レジスタとBTRレジスタがある。それぞれの設定とビットレートとの関係はZynq-7000 AP SoC Technical Reference Manualに書いてある。現在のプログラムの設定値もここを参考にして決定した。

が、よく見ると、最下段のfreqBIT_RATEの式は間違っているようだ。Sync Segmentの分が含まれていない。正しくは、
freqBIT_RATE = freqCAN_REF_CLK / ((can.BRPR[BRP] + 1) * (3 + can.BTR[TS1] + can.BTR[TS2]))
でなければならない筈だ。現在の設定値はBRPR[BRP]が1,BTR[TS1]が8、BTR[TS2]が2なので、間違っている方の式でビットレートを求めると、24/((1+1)×(2+8+2)) = 1と1Mbpsになるが、正しくは、24/((1+1)×(3+8+2)) ≒ 0.923の筈で、これは1/0.923 = 1.083なので8%程度の誤差となる。
原因はここのようだ。
そこで、プログラムのTS1の値を7に変更することにした。


修正したプログラムを走らせてみたところ、旨くいった!!  やたっ!

以下のプログラムではPWMのDutyを0%から100%まで5%刻みでCANバス経由で設定しているが、そのとおりにPWMの出力は変化している。




ということで、遂に世界最小クラスかもしれないFPGAボードでCANを動かすことが出来た。!!
やったー \(^_^)/

iCE40LMのパッケージへのハンダ付けの挑戦をするためにデジタル温調ハンダゴテを購入したのが2月だから、ここまで来るのに8ヶ月位かかったが、なんとかCANの動作まで漕ぎ着けた。  また、今回は2Gsのオシロよりも100Msの自作ロジアナの方が役に立った。このロジアナは作ってよかったと実感している。自作したものが十分使えて役に立ったという点も非常にうれしい。

次はCANバス経由でのリードバックを確認しよう。

2015年8月30日日曜日

世界最小クラスかも知れないFPGAボード 9 (CAN IPを実装してみる 5)

I2Cのトライステートの配線が出来ない件は、原因が判明した。 こんなのRTLのバグじゃ有り得ないと思っていたのだが、悪いのは自分で、RTLの記述ミスだった。。。 orz
rx_pipe.vモジュールにCANの受信フレームをパターンマッチングで、どのレジスタ宛かを検出する部分があるのだが、そこの拡張ID部のビット幅を1bit短く記述していた。拡張ID部は18bit長なので、この部分は[17:0]でなければならない。パターンマッチングは基本ID部と拡張ID部その他IDのモードやマッチングをする/しないの情報を連結して行っていたので、それらの情報が1bit右にズレてしまっていた。そのため、今回の実装ではパターンマッチングを行わないという風に合成エンジンが解釈し、その信号が伝搬する下流の回路の生成がされないかあるいは変な状態になってしまっていたようだ。。。

修正前

修正後

修正後はLSEでもSynplifyでもトライステート部の回路は生成されるようになった。

RTL記述

LSEでの合成結果

Synplifyでの合成結果


これで両方の回路とも正常動作を期待したのだが、どうもまだ挙動が怪しい。

Zynq-CANの代わりにOpenCoresのCAN-IPを使い、ターゲットとしてうちの子(fpga_top)のRTL、合成後のゲートネットリスト、及び can-ip 単体を接続したテストベンチを作成して、OpenCoresのIPからPWMのdutyを設定するフレームを送信してちゃんと受信されるかを見てみた。

ベクタ ... pwmのdutyを0x40 (25%)に設定するフレームを5回連続して送信してみた。


結果
ref_modelはOpenCoresのCAN-IPの送受信信号で、rtl部はfpga_topのRTL、core部はcan_ip単体、gateは合成して生成されたネットリストによるものである。

Synpliftyで合成したネットの場合

RTL, gate, core 全てが同じ動作をしており、ちゃんとACK応答をしている。また、dutyも設定されている。

LSEで合成したネットの場合
gateは全く反応していない。。。  なんか変だなぁ−、まだ何かあるんだろうか?

試しに、Synplify版のビットデータを実デバイスに書いて、Zynqとの間で同様の通信をさせてみた。

Zynq側のプログラム

観測点はCANトランシーバとFPGA間の信号で、黄色は送信信号(FPGA➔Zynq)、紫は受信信号(Zynq➔FPGA)だ。
この分解能だと見辛いのでロジアナても見てみた。
採取した波形 ... 応答しない場合と応答する場合があるようだ。

上述のSynplify版ネットリストでのシミュレーション波形と比較してみた。
上がシミュレーションの波形、下がロジアナで採取した波形で、1発目の通信の部分だ。
FPGA側がACKやCRCエラー等で一切応答しないために、タイムアウトエラーとなっているようだ。Zynq((及びref_model)から送信されるフレームはどちらも同じなのでそこは問題なさそうだ。 

何かしらの応答をしている箇所を見てみた。 FPGAから応答がないのでZynqからのエラーフレームに反応する形でこちらのFPGAもエラーフレームを出している。。。ということはエラーの検出はしているようなのだが???

と、言うわけでまだまだ先は長そうだ。 うーむ・・・でも、楽しい。

2015年8月16日日曜日

世界最小クラスかも知れないFPGAボード 8 (CAN IPを実装してみる 4)

ZYBO側の基板も出来た。


ZYNQでCANを使うためのデザインを作った。今回はPL側は使用せずPS側だけを使う。
久しぶりにvivadoを使うので、過去のデザインを流用するのではなく、使い方の復習を兼ねて最初から作成してみた。




最初から、とは言いつつ、私のVivado環境はZYBOのボードが選択出来るように細工してある(http://bravo-fpga.blogspot.jp/2014/10/zybo-6.html)ので、ここではZYBOボードを選択する。

IPインテグレータでブロックデザインをする。

PL部は使わないので配置するのはZYNQ7 Processing Systemのみだ。


CAN0を使えるようにする。

IO PeripheralsのCAN 0をクリックするとMIO Configuration画面が開いた。

CAN 0にチェックを入れた。使用するMIOは10,11にした。クロックは内部のリファレンスクロックを使うのでMIO CLKにはチェックを入れていない。

次にClock Configuration画面でCAN CLKの周波数を24MHzに設定した。


PLを使わないのでPL Fabric Clockは全てチェックを外した。

DMAやHP Slave AXI Interface、GP Master AXI Interfaceもチェックを外した。



すると、こんなにスッキリしたデザインになった。



ブロックデザインの生成ができたらラッパーモジュールを生成する。


生成できたら、合成した。
PL部は使っていないので実際は回路はなにも生成されない。

HardwareをExportしてSDKを立ち上げる。




プロジェクトを一つ作成した。



まず、このデザインがちゃんと動作するか確認したかったのでPS側のGPIOに接続されているLEDを点滅するプログラムを書いて動かしてみた。


大丈夫そうだ。
いよいよCANのプログラムに入る訳だが、system.mssをみるとCANの例題プログラムがあるようなので、見てみることにした。


このプログラムは診断用プログラムで、CANをループバックモードで1回だけ動作させていた。ビットレートは40Kbpsだ。
このプログラムをノーマルモードの1Mbpsで動作するように改造して動かしてみた。
CAN busの信号波形を確認したところ信号が出ていた。

ZYNQ側は、CANを動かすのはとても簡単にできそうだ。

と言うことで、自作IPの基板側との通信の確認に着手した。
ここからはXilinxとLatticeの開発ツール両方を使いながらの作業になる。

まず、Zynq側から自作IP側へPWMを設定するフレームを送信してみているのだが反応しない。
Zynqからの送信信号は受信モジュールまでは到達しているのだが、ACKやエラー等を一切返さない。どうもステートマシンがどこかで止まってしまっているようだ。
シミュレーションでは動作しているのだが・・・


そこで、今度は自作IP側から一定周期でフレームを送信するようにして動作するかを見てみたが、こちらもRTLシミュレーションでは動作するが実機では全く動作しない。RTLのバグにしては有り得ない現象なので、iCEcube2が生成したネットリストでSDFはアノテートせずにゲートシミュレーションをしてみたところCAN部が全く動作していないことが判った。どうも論理合成が正しく行われていない気がする。 ICEcube2では論理合成エンジンとしてSynplifyとLSEのどちらかを選択できるようになっていて、今まではLSEの方で合成をしていた。 これを、Synplifyに変えて合成し、生成されたネットリストでゲートシミュレーションを実行してみた。

結果は、CAN部は動作した。
こちらは、同じRTLをLSEで合成したネットリストでのシミュレーション結果だ。

LSEの場合には何か設定が必要なんだろうか????
また、SynplifyではCAN部が動作する回路が生成されてはいるが他に問題がある。上記波形でI2C_SDAが不定となっている。ネットリストを確認したところ、トライステートバッファの接続が不完全になっていた。Synplifyだとトライステートバッファの回路が旨く生成されず、LSEだとCANの回路がまともに生成されない・・・ うーむ。記述の仕方が悪いのかな???

取り敢えず、I2C_SDAの部分は置いといてSynplifyで合成して出来上がったビットマップファイルを実デバイスに流し込んで動作を見てみたところ、送信信号が出力された。


ということで、合成エンジンはSynplifyを使うことにして、トライステートバッファの件の解決策があるか試行錯誤してみようと思う。

ERROR: Failed to spawn fakeroot worker to run ...

なにかと忙しくてなかなか趣味の時間を確保できない。 ...orz  家の開発機のOSはLinux Mintなのだが、最近バージョンを22に更新したところ、myCNC用のpetalinuxをビルドできなくなってしまった。ビルドの途中で ERROR: Failed to spawn ...