このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
Xilinx社製のデータセンター向けFPGAボード Alveo U50 がIBM Power8マシン(以降Power8と表記)で動作するのか試してみました。
また、Power8固有ではないですが、Alveo U50を動かすにあたっていくつか気を付けておいたほうがいい点についても記載します。
Xilinx製のデータセンター向けの小型で安価なFPGAボード Alveo U50 が、2019年8月6日に発表されました。
既存のAlveoシリーズのU200, U250, U280と比較して、U50はハーフハイト・ハーフレングスという特徴がありますので、従来U200系統のXilinx製FPGAボードを導入したくてもホスト側の筐体の制約上導入できなかったところに導入できるということで、ご興味をお持ちの方も多いかと思います。
このときに気になるのが、FPGAボードを導入したいホストでAlveo U50が動作するのかという点です。PCI Expressのカードとして認識する可能性は高いですが、それ以外にもXilinxのリファレンス・ドライバが正しく動作するのかなど、懸念事項があります。
Xilinx公式の情報としては、Alveo U50の動作確認対象のホストの一覧がデータシート(DS965)の `Validated Servers` の項に記載されています。このリストに記載されているホストとして現時点(DS965 v1.1)では4つの型式が挙がっていますが、すべてIntel製CPUを使用したマシンとなっています。
弊社ではIntel製CPUを搭載したホスト以外にも、Power系のCPUを搭載したホストを使用することもあるため、Power8を搭載したマシンでAlveo U50を動かせるのかは気になるところですので、社内にあるPower8マシンで動作確認を行いました。
今回は Power8サーバーである S822LC にて動作確認していますが、すべてのPower8サーバーにてAlveo U50の動作を保証するものではありません。
Power8サーバーでの動作確認ということで、以下の3つの項目について問題がないかどうか確認することを目標としました。
以下の環境で動作確認を行いました。
XilinxのサイトのAlveo U50のページに記載されている通り、Alveoの開発フローとしては大きく分けて
の2つが用意されています。
実験のためになるべくFPGA側の構成を自分で把握しやすいほうが便利なので、今回はVivadoを使った開発フローを対象としました。
S822LCのマニュアルによると、プロセッサ・モジュールに直結しているPCIeインターフェースに対応するスロットは、 Slot1、Slot2、Slot4、Slot5の4つあります。
このうち Slot2はハーフハイト・ハーフレングスのカードのみ挿せるスロットとなっており、元々はネットワークカードが挿してありました。今回はネットワークカードを使用しないので、ネットワークカードをSlot2から取り外し、U50をSlot2に取り付けました。
U50より前のAlveoの場合、FPGAカード上にJTAGコンフィグレーション用の回路が搭載されており、カードのブラケット側にある USB micro-Bのコネクタ を開発用のマシンに接続してJTAGコンフィグレーションを行えるようになっていました。
一方、Alveo U50はFPGAカード上にJTAGコンフィグレーション用の回路が搭載されていません。代わりに、Alveo Programming Cable というUSB-JTAGプログラマ相当のものが付属しています。
U50をブラケットを左側、PCIeのカードエッジ・コネクタを下側に向けて置いたときに、右上の端に上向きに2×15ピンのコネクタが実装されています。このコネクタと Alveo Programming Cable の間は付属のフラット・ケーブルで接続する必要があります。このため、このフラット・ケーブルをホストマシンの筐体の外側に引き出す必要があります。
Alveo Programming Cableのユーザー・ガイドには、 “Two adjacent PCI slots are necessary: one for the Alveo card and the other for ribbon cableaccess.” と記載されており、U50を挿したPCIeスロットの隣のスロットのカバーを外してフラット・ケーブルを引き出しています。 今回はU50を接続した Slot2 の隣のスロットが空いていたため、ユーザー・ガイドに記載のとおり、その場所から筐体外部へフラット・ケーブルを引き出しています。
このあたりに関しては、従来のAlveoカードと同様に、カード上にJTAGコンフィグレーション用の回路を搭載して欲しかったところですが、基板の面積的に USB micro-Bのコネクタを実装するのが難しく、仕方がなかったのかなとも思います。SDAccelを使った開発フローの場合は、PCI Express経由でコンフィグレーションを行うため、JTAGが不要というのも関係しているかもしれません。
Vivadoは現在のところx86系CPUでしか動作しませんので、開発用に別のPCを用意してVivadoをインストールします。
https://japan.xilinx.com/support/download.html
Alveo U50の開発には Vivado 2019.1以降が必要ですので、Vivado 2019.1をインストールしました。このとき、 Vivado 2019.1.3のアップデートは適用しない ようにします。理由は コンフィグROMへの書き込みの項で説明します。
動作確認のためのデザインとして、以下のPCIe経由でURAMを読み書きするデザインを作成しました。
Hierarchy ram_hier_n
の中身はすべて ram_hier_0
の中身と同じ構造になっており、単にAXI BRAM ControllerとBlock Memory Generatorがつながっているだけです。ただし、以下の通りBlock Memory GeneratorはBRAMではなくURAMを使う設定に変更してあります。
Address Editorで各AXI BRAM Controllerの領域を2[MiB]に設定して、合計8[MiB]分のURAMがホストから見えるようにします。
DMA/Bridge Subsystem for PCI ExpressのインスタンスやPCIe用のクロックは、Alveo U50用のプリセット構成が用意されていますので、Block Designの画面の左側にあるBoardタブからドラッグ&ドロップしてインスタンスを作成しておきます。
プリセットからDMA/Bridge Subsystem for PCI Expressのインスタンスを作成するときに、PCIeの規格とレーン数を聞かれますので、必ずGen3 x16を選ぶようにします。
後ほどコンフィグROMへの書き込みを行うために、UG1371に記載されているMCS用のビットストリームの設定を制約ファイルに追加しておきます。
# Bitstream Configuration
# ------------------------------------------------------------------------
set_property CONFIG_VOLTAGE 1.8 [current_design]
set_property BITSTREAM.CONFIG.CONFIGFALLBACK Enable [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 85.0 [current_design]
set_property BITSTREAM.CONFIG.EXTMASTERCCLK_EN disable [current_design]
set_property BITSTREAM.CONFIG.SPI_FALL_EDGE YES [current_design]
set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design]
set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR Yes [current_design]
# ------------------------------------------------------------------------
デスクトップPCの場合、リブート時はPCI Expressへの電源は落ちずにファームウェアの実行から処理を再開します。一方、今回使用した S822LC はリブート時もPCI Expressへの電源を切断したのち、再度電源を投入するという動作をします。 このため、PCI Express用のデザインの動作確認に、JTAGでコンフィグレーション後にリブートして動作確認を行うという方法が使えません。
代替方法として、動作中に無理やり書き換えて動かす方法もありますが、まずは通常の方法で動かすため、コンフィグROMにコンフィグレーションを書き込みます。
コンフィグROMへ書き込むために必要なMCSファイルの作り方は、UG1371の “Creating an MCS File and Programming the Alveo Card” の項に記載されています。
ビットストリームの生成まで完了したのち、Tools -> Generate Memory Configuration File を選択し、Write Memory Configuration Fileダイアログで以下の通り設定します。Filename や Bitfile はMCS出力先のパスとビットストリームのパスを指定します。
パラメータ名 | 値 |
Format | MCS |
Memory Part | mt25qu01g-spi-x1_x2_x4 |
Interface | SPIx4 |
Load bitstream files | チェックする |
Start address | 01002000 |
Direction | up |
Bitfile | 合成済みビットストリームのパス |
MCSを作成したら、Hardware Managerを開いてデバイスに接続します。
コンフィグROMがいない場合は、Add Program Memory Device で mt25qu01g-spi-x1_x2_x4
を追加します。 mt25qu01g-spi-x1_x2_x4
を選んで右クリックし、 Program Configuration Memory Device を選択します。
その後、 以下のとおり設定をしてOKを押します。10分くらいでコンフィグROMへの書き込みが終わります。
パラメータ名 | 値 |
Configuration File | 作成したMCSファイルのパス |
PRM File | 作成したMCSファイルと同じディレクトリにできているprmファイルのパス |
State of non-config mem I/O pins | Pull-up |
Address Range | Configuration File Only |
Erase | あり |
Blank Check | なし |
Program | あり |
Verify | あり |
Verify Checksum | なし |
このとき、Vivado 2019.1 ではなく Vivado 2019.1.3 を使っている場合、以下のような「ビットストリームが見つからない」というエラーが出てコンフィグROMへの書き込みに失敗します。
ERROR: [Labtools 27-2149] File /opt/Xilinx/Vivado/2019.1/data/xicom/cfgmem/bitfile/spi_xcu50_pullup.bit not found. Check file name and file permissions.
JTAG経由でコンフィグROMを書き込むとき、まずターゲットのFPGAにJTAGからコンフィグROMへアクセスする論理がコンフィグレーションされます。このときに書き込まれるビットストリームは (Vivadoのインストール・ディレクトリ)/data/xicon/cfgmem/bitfile.zip
に格納されています。
Vivado 2019.1の場合、このファイルの中にU50用のビットストリーム spi_xcu50_pullup.bit
が含まれているのですが、Vivado 2019.1.3の更新を適用すると spi_xcu50_pullup.bit
が無くなってしまいます。
よって、現時点でAlveo U50を使う場合は Vivado 2019.1.3 のアップデートを適用しないようにします。
コンフィグROMへの書き込みが終わったら、 Alveo Programming Cable のUSBケーブルを抜いてから S822LCを再起動します。
ここで Alveo Programming Cable のUSBケーブルを抜かないと、コンフィグROMからのコンフィグレーションが行われず、何もコンフィグレーションされていない状態で S822LC が起動してしまいます。
この挙動については、 UG1371 の FPGA Configuration の項に以下の通り記載されています。
If the JTAG cable is plugged in, QSPI configuration might not occur. JTAG mode is always available independent of the mode pin settings.
UG1371
If the JTAG cable is plugged in
の条件については、Alveo Programming Cable の場合は、Alveo Programming CableがUSBデバイスとして動作している場合のみ該当するようですので、Alveo Programming Cable User Guide (UG1377) には、USBケーブルを抜くように手順の中で記載されています。
弊社では Alveo Programming Cable をWindows機につないでいるため、試しにデバイスマネージャからAlveo Programming Cableに対応するデバイスを無効化してみたところ、USBケーブルを抜いた場合と同じ効果があることを確認しています。
Linux機にAlveo Programming Cableをつないだ場合に、Windows機と同様にソフトウェア的にUSBケーブルを抜いた場合と同じ状態にすることが可能かどうかは未確認ですが、stack overflowでの回答を見る限り、sysfsのbind/unbindを使えばできるのではないかと思います。
ここまででU50のハードウェアの準備ができたので、次はS822LC側のドライバなどを準備します。
今回使用している DMA/Bridge Subsystem for PCI Express のLinux向けドライバは、Xilinxが管理しているGitHubリポジトリ (https://github.com/Xilinx/dma_ip_drivers) にで公開されています。
このドライバを S822LC 上でビルドしてインストールします。Power8上での動作は公式ではサポートされていませんが、特にエラーなども発生せずビルドできます。
git clone https://github.com/Xilinx/dma_ip_drivers
cd dma_ip_drivers
git checkout 53a8d0757c60aa2b0c680ac4256a6e3fbfd42871
cd XDMA/linux-kernel/xdma
make -j12
sudo make install
cd ../tools
make -j12
cd ..
export driver_dir=`pwd`
ドライバの準備ができたので、U50がPCIデバイスとして認識されていることを確認して、ドライバをロードします。
lspciコマンドの出力は次のようになっており、U50が正常にPCIデバイスとして認識されていることが分かります。
$ lspci
0000:00:00.0 PCI bridge: IBM Device 03dc
0001:00:00.0 PCI bridge: IBM Device 03dc
0001:01:00.0 Serial controller: Xilinx Corporation Device 903f // <- Alveo U50
0002:00:00.0 PCI bridge: IBM Device 03dc
0003:00:00.0 PCI bridge: IBM Device 03dc
0003:01:00.0 PCI bridge: PLX Technology, Inc. Device 8725 (rev ca)
0003:01:00.1 System peripheral: PLX Technology, Inc. Device 87d0 (rev ca)
0003:01:00.2 System peripheral: PLX Technology, Inc. Device 87d0 (rev ca)
0003:01:00.3 System peripheral: PLX Technology, Inc. Device 87d0 (rev ca)
0003:01:00.4 System peripheral: PLX Technology, Inc. Device 87d0 (rev ca)
(省略...)
$ sudo lspci -vvv -s "0001:01:00.0"
0001:01:00.0 Serial controller: Xilinx Corporation Device 903f (prog-if 01 [16450])
Subsystem: Xilinx Corporation Device 0007
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 504
NUMA node: 0
Region 0: Memory at 3fe080010000 (32-bit, non-prefetchable) [size=8K]
Region 1: Memory at 3fe080000000 (32-bit, non-prefetchable) [size=64K]
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [48] MSI: Enable- Count=1/1 Maskable- 64bit+
Address: 0000000000000000 Data: 0000
Capabilities: [70] Express (v2) Endpoint, MSI 00
DevCap: MaxPayload 1024 bytes, PhantFunc 0, Latency L0s <64ns, L1 <1us
ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset- SlotPowerLimit 0.000W
DevCtl: Report errors: Correctable- Non-Fatal+ Fatal+ Unsupported+
RlxdOrd+ ExtTag+ PhantFunc- AuxPwr- NoSnoop+
MaxPayload 512 bytes, MaxReadReq 1024 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #0, Speed 8GT/s, Width x16, ASPM not supported, Exit Latency L0s unlimited, L1 unlimited
ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 8GT/s, Width x16, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Range BC, TimeoutDis+, LTR-, OBFF Not Supported
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis+, LTR-, OBFF Disabled
LnkCtl2: Target Link Speed: 8GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete+, EqualizationPhase1+
EqualizationPhase2+, EqualizationPhase3+, LinkEqualizationRequest-
(省略...)
Kernel modules: xdma
modprobeコマンドを使って先ほどビルドしたドライバをロードすると、カーネルのログに次のようなメッセージが出力され、ドライバが正常にロードできることが確認できました。
$ sudo dmesg --clear
$ modprobe xdma
$ dmesg
[ 2628.198920] xdma:xdma_mod_init: Xilinx XDMA Reference Driver xdma v2018.3.50
[ 2628.199160] xdma:xdma_mod_init: desc_blen_max: 0xfffffff/268435455, sgdma_timeout: 10 sec.
[ 2628.199234] xdma:xdma_device_open: xdma device 0001:01:00.0, 0xc000001ffe10e000.
[ 2628.199319] xdma:map_single_bar: BAR0 at 0x3fe080000000 mapped at 0xd000080083000000, length=4194304(/4194304)
[ 2628.199397] xdma:map_single_bar: BAR1 at 0x3fe080400000 mapped at 0xd0000800817e0000, length=65536(/65536)
[ 2628.199461] xdma:map_bars: config bar 1, pos 1.
[ 2628.199498] xdma:identify_bars: 2 BARs: config 1, user 0, bypass -1.
[ 2628.199545] xdma 0001:01:00.0: Using 64-bit DMA iommu bypass
[ 2628.199673] xdma:probe_one: 0001:01:00.0 xdma0, pdev 0xc000001ffe10e000, xdev 0xc000001fcd006000, 0xc000001f91b3e000, usr 16, ch 4,4.
[ 2628.200619] xdma:cdev_xvc_init: xcdev 0xc000001fcd007958, bar 0, offset 0x40000.
ドライバの準備ができましたので、実際にFPGAボードとホスト間で転送処理を行いました。
dma_ip_drivers/XDMA/linux-kernel/tools
ディレクトリに、FPGAボードとホスト間で転送を行うプログラムとして dma_from_device
と dma_to_device
が含まれています。
dma_from_deviceは FPGAボードからホストへの転送 (Card to Host = C2H転送) を行います。dma_to_deviceは ホストからFPGAボードへの転送 (Host to Card = H2C転送) を行います。
動作確認用に以下の処理を行うスクリプト test_8M_1ch.sh
を作成して、$driver_dir/tests
に保存します。
#!/bin/bash
tool_path=../tools
size=$((8*1024*1024))
size_hr=8M
echo Prepare test data
if [ ! -f data/datafile_8M_0.bin ]; then dd if=/dev/urandom of=data/datafile_${size_hr}_0.bin bs=1048576 count=8; fi
echo Write Performance
$tool_path/dma_to_device -d /dev/xdma0_h2c_0 -f data/datafile_${size_hr}_0.bin -s $size -a $((0x00000000)) -c 128 &
wait
echo Readback and Compare
$tool_path/dma_from_device -d /dev/xdma0_c2h_0 -s $size -f data/readback_${size_hr}_0.bin -a $((0x00000000)) -c 1 &
wait
cmp data/datafile_${size_hr}_0.bin data/readback_${size_hr}_0.bin || echo Channel 0 read result is wrong
echo Read Performance
$tool_path/dma_from_device -d /dev/xdma0_c2h_0 -s $size -a $((0x00000000)) -c 128 &
wait
このスクリプトを実行した結果は次の通りとなりました。
$ cd $driver_dir/tests
$ sudo ./test_8M_1ch.sh
Prepare test data
Write Performance
** Average BW = 8388608, 12086.461914
Readback and Compare
** Average BW = 8388608, 8544.833008
Read Performance
** Average BW = 8388608, 13047.431641
PCI Express Gen3 x16の理論帯域は15.75[GB/s] ですが、Read Performance (C2H転送)のパフォーマンスは13[GB/s]となっており、若干低い目の値となっています。このあたりは今後調整するとして、今回確認したいのは、S822LCとAlveo U50の相性が悪く、PCI Express自体の通信速度が低下するといったことが起きないことです。よって、ホスト側のオーバーヘッドを除いた転送能力を測定してみることにしました。
DMA/Bridge Subsystem for PCI Express にはパフォーマンスカウンタ機能があり、転送処理にかかった総サイクル数を計測することができます。詳しくはPG195の H2C Channel Performance Monitor Control あたりの説明をご確認ください。
パフォーマンスカウンタ機能を使って測定した結果は次の通りです。
Prepare test data
Write Performance
** Average BW = 8388608, 12138.236328
Ch0:
Number of cycles elapsed = 164335
Duty ratio = 79%
Number of bytes transferred = 8388608
Throughput = 12761444610
Readback and Compare
** Average BW = 8388608, 7557.229492
Read Performance
** Average BW = 8388608, 13166.530273
Ch0:
Number of cycles elapsed = 148003
Duty ratio = 88%
Number of bytes transferred = 8388608
Throughput = 14169658723
Number of cycles elapsed
はXDMAコアが実際に転送処理を開始してから完了するまでにかかったサイクル数です。今回のデザインではXDMAは250[MHz]で動作しているため、転送サイズ/(サイクル数/250*1.0^6)
でスループットを計算することができます。
計算した結果は、H2C, C2Hそれぞれ
となり、転送時のPCIeの低レイヤでの通信のオーバーヘッドなどを考えると、これくらい出ていれば十分かなという結果となりました。
次回があれば、Alveo U50にてHBM IPを使う場合に気を付けなければいけない点などを紹介できればと思います。
[…] U50を動かすお話です。以前、FixstarsさんのところでU50のXDMAを動かした記事 を拝見して、同じように UltraRAM を接続して Core i3-6100 + DDR4-2133 Dual […]