ぐちゃーっとした配線で何が何だかわからないと思います。
そこで、TOP層、内層1、内層2だけを表示させてみます。
今までの配線は、こうなっていました。
データ線やDM、DQSなどは1対1でFPGAとDDR3が接続されているのですが、アドレス線やCAS、RASなどの制御線がT型に分岐しているのです。
DDR3の配線は等長配線のT型にするか、数珠つなぎ型にするかという選択肢はあるのですが、この基板ではT型に分岐した下側のDDR3メモリにだけ終端抵抗が付いているという間違った設計になっていました。
そのため、DDR3メモリを800MHzで動かしているうちはよかったのですが、1600MHzで動かそうとすると、2個あるDDR3メモリのうち、下側のほうだけしか動かないという現象に見舞われました。(実際には1000MHzくらいで限界になる)
高速オシロで見ると、確かにSSTL-15の信号レベルが暴れていました。
そういうわけで、この配線を全面的に見直して、数珠つなぎ型の一直線の配線になるようにして、最後の部分で終端をするように変えました。
FPGAから出た配線が全体的に上から下へ流れていくのがわかると思います。アドレス・制御線の信号長の差も1cm以内に収まりました。
これで1600MHzアクセスができるといいのですが、どうなるでしょう。
JESD204Bのはじめてのデータ受信
2017.01.12
昨年11月に作ったJESD204B出力のADCボードと、データ取り込み用ボードで、ようやくデータが受信できるようになりました。
使用しているADCは、TI社のADS54J60です。
2つのボードをこのように40Gbの光ファイバでつなぎます。
2つのボードの間は10Gbpsのリンク(現在は半分の5Gbpsで運用)が4本でつながっています。ADCのSYNCという端子をLにすると、K28.5のCOMが送られてきます。
K28.5は16進数で1BCなので、最初は4つのチャネルにBCBCBCBCというデータが来ます。
SYNCをHにすると、COM以外の何かが送られてきます。
K28.5 キタ━━━(゚∀゚).━━━!!!
K28.5以外のものが来るパターンが上の波形には2回見えていて、最初のは
GTX0 1BC 11C 00 F0 E5 FF FE 00・・ GTX1 1BC 11C 05 FF FF 00 04 00・・ GTX2 1BC 11C 10 FF FA 00 04 FF・・ GTX3 1BC 11C F5 00 0A FF F5 FF・・
と読めます。1BCはK28.5で/K/という記号で表します。11CはK28.0で/R/という記号で表します。/R/は「マルチフレーム先頭文字」の意味だそうです。
2回目のK符号は
GTX0 17C 11C 19C 00 00 01 1C 1C・・ GTX1 17C 11C 19C 00 00 00 03 03・・ GTX2 17C 11C 19C 00 00 03 03 03・・ GTX3 17C 11C 19C 00 00 02 00 00・・
と読めます。17CはK28.3で/A/で、「レーンアラインメント文字」だそうです。19Cは/Q/の「リンクコンフィグ先頭文字」だそうです。なので、/A/ /R/ /Q/ ・・・
その続きの波形を見てみると、
1個目が
GTX0 17C 11C E3 FF FE FF FF 00 GTX1 17C 11C FD 00 02 FF FC 00 GTX2 17C 11C 00 FF FF 00 FF 00 GTX3 17C 11C ED FF F8 FF F5 FF
と読めます。/A/ /R/ ・・です。
2個目は
GTX0 17C 11C 00 00 01 FF FB 00 GTX1 17C 11C FD 00 00 00 FA 00 GTX2 17C 11C 10 FF FA 00 00 FF GTX3 17C 11C F1 FF FE FF F9 00
これもまた/A/ /R/ ・・です。
3個目は
GTX0 17C 00 02 00 15 FF FE FF GTX1 17C 00 01 00 08 FF FD FF GTX2 17C 00 0F FF F7 00 06 FF GTX3 17C FF F5 00 09 00 01 FF
で、/A/です。
このような感じで、SYNCをONにしたときに5回のK符号のフレームが来るのですが、トラ技2016年9月号を読んで、意味がわかりました。
最初の/R/から/A/までが1セット目のフレームでILAS(Initial Lane Alignment Sequence)というそうです。
/R//Q/・・・/A/が2つ目のセットでマルチフレーム。この中にはコンフィギュレーションコードというのが入っています。
3個目、4個目は/R/・・/A/で、マルチフレーム1と同じ内容でデータに意味はないということでした。
大事なのは/R/ /Q/の続きの14オクテットで、ここにコンフィギュレーションレジスタという情報が書かれているそうです。本当かいな。
GTX0 00 00 01 1C 1C 16 01 12 CF DC 00 00 00 CD GTX1 00 00 00 03 03 09 01 0D 2F 23 00 00 00 31 GTX2 00 00 03 03 03 09 01 0D 2F 23 00 00 00 34 GTX3 00 00 02 03 03 09 01 0D 2F 23 00 00 00 03
解読した値を書いていてい気が付いたのですが、おそらくGTX0のチャネルはRXPとRXNが反転していますね。GTX1~GTX3は同じような感じのデータなので、そちらはたぶん正解なのでしょう。
解読すると、
L=3、F=3、K=9、M=1、N=13、SUBCLASSV=1、N'=15、JESDV=1、S=3、HD=0、CF=0、RES1=0、RES2=0、FCHK=xx
となりました。
+1する補正を考慮すると、
L=4、F=4、K=10、M=2、N=14、SUBCLASSV=1、N'=16、JESDV=1、S=3、HD=0、CF=0、RES1=0、RES2=0、FCHK=xx
読める、読めるぞ・・
解読したところ
- L:4 リンク数は4
- M:2 デバイスには2つのコンバータ
- F:4 1フレームあたり4オクテット
- S:4 コンバータあたりフレームあたりのサンプル数
- K:10 1マルチフレームあたり10フレーム
- N:14bitでサンプリング
- N':1サンプルあたり16bit
- JESD204B
となりました。ADCの分解能が14bitというのが納得いきませんが、概ね良い感じだと思います。
JESD204B 1G ADCの動作確認
2017.01.11
昨年から開発を続けているJESD204B対応の1Gs/s ADC「HyperFADC」ですが、高速シリアルのデータをデコードしようと頑張っています。使用しているADCのADS54J60にはSYNCという端子があるのですが、これをLにすると、1BC 1BC 1BC 1BC、つまりK28.5のCOMが送られてきます。
SYNCをHにすると、K28.5ではなく、D0.0に変わったかと思ったのですが・・
rxnotintableという信号が立ちあがっているので、D0.0が送られてきているわけでもなさそうです。
いまいち使い方がよくわかりません。
Kintex-7のPCI Expressのバーストライト
2017.01.08
Kintex-7のPCI Expressでバーストライトをさせて、その速度を見てみました。
使ったのは、Cosmo-K+というボード。
PCI Express Gen2でx4という構成です。理論的には2GByte/sec出るはずです。
しかし、実際にやってみると514MBくらいしか出ません。
原因の1つは、デバイスドライバの問題で、ユーザ空間とカーネル空間を行き来するときにバッファの内容がコピーされるのですが、そのコピーの時間が無駄にかかっているためです。
もう一つの理由は、WriteCombinedで書き込んでいるのですが、AXIから読み出すときに、m_axis_tx_ready_intという信号がパタパタと遷移していて、1つのデータの読み出しに2クロックかかっているためです。
Gen1 x1のときには250MB/secしか出ないので、XILINXのCore Gen IPが作るユーザ側インタフェースは64bit 62.5MHz(最低速度)でも十分に間に合いました。
最低速度の半分のレートしか出ないので、AXバスをパタパタさせてもオーバーヘッドはなかったのですが、Gen2 x8だと64bit 250MHzになるので、もはやReadyをパタパタさせてはなりません。
Gen1のx1では、WriteCombinedで200MBくらい出るので、DMAよりも手軽で速く、速度的にも満足だったのですが、Gen2x4ではそうはいかなくなってきました。
毎クロックできびきび動作するよう、ステートマシンの作り直しが必要なのと、バッファのコピーを避けるためドライバがユーザ空間のメモリ領域のアドレスを見つけて、直接DMAする必要なのでしょう。
LinuxでのCombined Write
2017.01.07
PCI Expressにおけるメモリ書き込みは、Combined Writeを使うと高速に行えます。
Write Combinedを使わないと、PCI Expressは長さ1(4ワード)のトランザクションを発行してしまうので、極めて遅くなります。
Combined Writeというのは、CPUの書き込み機構をうまく利用した方法で、複数のメモリ書き込みを1つに結合して長いパケットにして発行するやり方です。
LinuxでCombined Writeを使う方法はよくわからないのですが、デバイスドライバの中でBARの物理アドレスを仮想アドレスにマップするときに、
ioremap_wc(BARx_BaseAddr,BARx_Length);
という関数を使うと良いようです。
ioremap(BARx_BaseAddr,BARx_Length);
や
ioremap_nocache(BARx_BaseAddr,BARx_Length);
という関数を使うと通常のWriteとなってしまうようなのですが、正直なところよくわかりません。うまく切り替わってくれないことが多々あります。
では、実際に波形をみてみましょう。MITOUJTAGのロジアナ機能を使って、PCI Expressの書き込みトランザクションを見てみます。
書き込みは、
memcpy_toio(Mapped_BAR1_BaseAddr , WriteBuffer , count);
を使いました。
まずは、普通のWriteの場合です。128バイトのデータ転送を行ってみたところ、32回のトランザクションが発生して、5.45μ秒かかっています。したがって、転送速度は23MB/secとなります。
拡大してみると、先頭のワードが40000001なので、1ワードの書き込みトランザクションになっていることがわかります。これでは遅いですね。
次は、Combined Writeにした場合の波形です。先頭のワードが40000010なので64バイトの書き込みトランザクションが2回になっています。128バイトを752nsで書き込んでいるので、速度は170MB/sとなります。
圧倒的に速くなりました。
切り替えは、ioremapを使うか、ioremap_wcを使うかといった違いだけなのですが、どうやらLinuxの起動時にはWriteCombiedを使うようになっていて、関数を変えるだけでは切り替わらないようです。
rescanを行う際に、
sudo chmod 666 /sys/bus/pci/devices/0000\:04\:00.0/rescan
sudo echo 1 > /sys/bus/pci/devices/0000\:04\:00.0/rescan
のように、バスの中までrescanをすると、この機能が切り替わるようなのですが、切り替わらないときもあります。
何がトリガーとなって、ioremapとioremap_wcが切り替わるのかは、正直、よくわかりません。OSごと再起動すると切り替わることが多いように見えます。
PCI ExpressのFPGAを効率的に開発する方法(Linux版)
2017.01.06
PCI ExpressをFPGAで実装すると、新しいデザインが出来たら当然、書き換えることになります。
FPGAを書き換えると内部のレジスタがすべて0にクリアされてしまいます。BARなどの設定値が全部消えてしまうので、アクセスできなくなってしまいます。
普通はFPGAを書き換えるたびにOSを再起動しなければなりません。再起動を待つ時間がもったいないだけでなく、アプリの起動やディレクトリの移動など面倒な手間が増えます。
PCI ExpressのFPGAを書き換えても、OSの再起動なしに続行したいわけです。
Windowsの場合ならばデバイスマネージャから無効/有効を行えばBARの再設定が行われるので再起動する手間を省けますが、Linuxでやる方法を調べました。
ここではScientific Linux CERN 6 (SLC6)というディストリビューションを使用しています。
基本的なやり方は、rescanというドライバを使って、
sudo echo "1" > /sys/bus/pci/rescan
のようにします。一瞬、??となる書き方なのですが、rescanというのは、
--w--w---- 1 root root 4096 1月 6 16:09 2017 /sys/bus/pci/rescan
というスペシャルファイルで、このファイルに1を書き込むと再スキャンが行われるという代物のようです。
ただ、私が使っているSLC6というディストリビューションではrescanに書くだけではうまく行かず、sudoを付けていても許可がありませんと出てしまいます。
そこでchmod 666でドライバrescanに読み書き属性を付与してから行っています。また、なぜかrescanだけではうまくいかないので、removeというドライバにも同様に1を書き込んでから行います。
結局のところ、以下のようなスクリプトを書いて実行しています。
#!/bin/sh sudo chmod 666 /sys/bus/pci/devices/0000\:0\:00.0/remove sudo echo 1 > /sys/bus/pci/devices/0000\:0\:00.0/remove sudo chmod 666 /sys/bus/pci/rescan sudo echo "1" > /sys/bus/pci/rescan
使い方は、./rescan 4 のようにrescanの後にバス番号を指定します。
バス番号というのは、lspciで表示される一覧の一番左の数字です。
上のDPIO moduleというのがFPGAで作ったPCI Expressのファンクションです。この数字が4なので、rescan 4とすると、上のスクリプトが起動して、PCI Expressデバイスが削除されてから再スキャンされて、BARが再設定されるというわけです。
これで、OSを再起動することなく、FPGAのみ書き換えて動作を続行することができるようになります。
実際にやってみてわかった注意点がいくつかあります。
- BARのサイズを増やすと、再設定が行われない場合がある。
つまり、最初は16kB程度の領域を確保していて、再スキャン後に64MBに増えているような場合は、うまくいかない。FPGAを書き換えてもリクエストするメモリ空間のサイズは変えない方が良い。 - BARサイズを減らすのは大丈夫なようだ。
- VIDやDIDが変わるのはOK。
以下に、実際の実行結果を示します。
まずは、起動したLinuxを直後にlspciでデバイスのコンフィギュレーション情報を見たところです。1bc8:1080のデバイスが見えています。Region 0..2の表示にも注目してください。
[user@px ~]$ lspci -d 1bc8:* -vvvx 04:00.0 DPIO module: Device 1bc8:1080 Subsystem: Device 1bc8:1080 Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 256 bytes Interrupt: pin A routed to IRQ 11 Region 0: Memory at dbfec000 (32-bit, non-prefetchable) [size=16K] Region 1: Memory at dbff0000 (32-bit, non-prefetchable) [size=64K] Region 2: Memory at dc000000 (32-bit, non-prefetchable) [size=64M] Capabilities: <access denied> 00: c8 1b 80 10 07 00 10 00 00 00 00 11 40 00 00 00 10: 00 c0 fe db 00 00 ff db 00 00 00 dc 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 c8 1b 80 10 30: 00 00 00 00 40 00 00 00 00 00 00 00 0b 01 00 00
FPGAを書き換え中にlsmodしてみました。すべてのレジスタがffになって見えないことがわかります。
[user@px ~]$ lspci -d 1bc8:* -vvvx 04:00.0 DPIO module: Device 1bc8:1080 (rev ff) (prog-if ff) !!! Unknown header type 7f 00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
下の表示はFPGAの書き換えが完了した直後の状態です。Region 0,1,2がvirtualと出ています。
[user@px ~]$ lspci -d 1bc8:* -vvvx 04:00.0 DPIO module: Device 1bc8:1080 Subsystem: Device 1bc8:1080 Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Interrupt: pin A routed to IRQ 255 Region 0: [virtual] Memory at fb310000 (32-bit, non-prefetchable) [size=16K] Region 1: [virtual] Memory at fb300000 (32-bit, non-prefetchable) [size=64K] Region 2: [virtual] Memory at fb400000 (32-bit, non-prefetchable) [size=4M] Capabilities: <access denied> 00: c8 1b 80 10 00 00 10 00 00 00 00 11 00 00 00 00 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 c8 1b 80 10 30: 00 00 00 00 40 00 00 00 00 00 00 00 ff 01 00 00
ここでrescanすると、
[user@px ~]$ lspci -d 1bc8:* -vvvx 04:00.0 DPIO module: Device 1bc8:1080 Subsystem: Device 1bc8:1080 Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Interrupt: pin A routed to IRQ 255 Region 0: Memory at fb310000 (32-bit, non-prefetchable) [disabled] [size=16K] Region 1: Memory at fb300000 (32-bit, non-prefetchable) [disabled] [size=64K] Region 2: Memory at fb400000 (32-bit, non-prefetchable) [disabled] [size=4M] Capabilities: <access denied> 00: c8 1b 80 10 00 00 10 00 00 00 00 11 00 00 00 00 10: 00 00 31 fb 00 00 30 fb 00 00 40 fb 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 c8 1b 80 10 30: 00 00 00 00 40 00 00 00 00 00 00 00 ff 01 00 00
virtualと表示されていたregionが設定され、使用できるようになります。
この情報が、Linux上で動くPCI Expressのアドインカードを開発する人の助けになれば幸いです。