Article

2019年10月30日

概要

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つの項目について問題がないかどうか確認することを目標としました。

  • S822LCにAlveo U50を物理的に接続できるかどうか
  • S822LCで PCI Express用のLinuxドライバを動かすことができるか
  • S822LCでホスト-FPGA間通信を行ったときに十分な速度で通信できるかどうか

環境

以下の環境で動作確認を行いました。

  • IBM Power System S822LC
    • Red Hat Enterprise Linux 7.3
  • Xilinx Alveo U50 (U50DD ES3) SKU:A-U50DD-P00G-ES
  • Vivado 2019.1 Design Edition

対象とする開発フロー

XilinxのサイトのAlveo U50のページに記載されている通り、Alveoの開発フローとしては大きく分けて

  1. SDAccelを使った開発フロー
  2. Vivadoを使った開発フロー

の2つが用意されています。

実験のためになるべくFPGA側の構成を自分で把握しやすいほうが便利なので、今回はVivadoを使った開発フローを対象としました。

S822LCへインストール

S822LCのマニュアルによると、プロセッサ・モジュールに直結しているPCIeインターフェースに対応するスロットは、 Slot1Slot2Slot4Slot5の4つあります。

このうち Slot2はハーフハイト・ハーフレングスのカードのみ挿せるスロットとなっており、元々はネットワークカードが挿してありました。今回はネットワークカードを使用しないので、ネットワークカードをSlot2から取り外し、U50をSlot2に取り付けました。

JTAGアダプターの配線

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のインストール

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を読み書きするデザインを作成しました。

PCIe経由でURAMを読み書きするデザイン

Hierarchy ram_hier_n の中身はすべて ram_hier_0 の中身と同じ構造になっており、単にAXI BRAM ControllerとBlock Memory Generatorがつながっているだけです。ただし、以下の通りBlock Memory GeneratorはBRAMではなくURAMを使う設定に変更してあります。

Block Memory Generatorの設定
Block Memory Generatorの設定

Address Editorで各AXI BRAM Controllerの領域を2[MiB]に設定して、合計8[MiB]分のURAMがホストから見えるようにします。

Address Editorの設定

DMA/Bridge Subsystem for PCI ExpressのインスタンスやPCIe用のクロックは、Alveo U50用のプリセット構成が用意されていますので、Block Designの画面の左側にあるBoardタブからドラッグ&ドロップしてインスタンスを作成しておきます。

BoardタブのU50プリセット

プリセットから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]
# ------------------------------------------------------------------------

コンフィグROMへの書き込み

デスクトップPCの場合、リブート時はPCI Expressへの電源は落ちずにファームウェアの実行から処理を再開します。一方、今回使用した S822LC はリブート時もPCI Expressへの電源を切断したのち、再度電源を投入するという動作をします。 このため、PCI Express用のデザインの動作確認に、JTAGでコンフィグレーション後にリブートして動作確認を行うという方法が使えません。

代替方法として、動作中に無理やり書き換えて動かす方法もありますが、まずは通常の方法で動かすため、コンフィグROMにコンフィグレーションを書き込みます。

MCSファイルの作成

コンフィグROMへ書き込むために必要なMCSファイルの作り方は、UG1371の “Creating an MCS File and Programming the Alveo Card” の項に記載されています。

ビットストリームの生成まで完了したのち、Tools -> Generate Memory Configuration File を選択し、Write Memory Configuration Fileダイアログで以下の通り設定します。Filename や Bitfile はMCS出力先のパスとビットストリームのパスを指定します。

Write Memory Configuration Fileダイアログ
パラメータ名
FormatMCS
Memory Part mt25qu01g-spi-x1_x2_x4
InterfaceSPIx4
Load bitstream filesチェックする
Start address01002000
Directionup
Bitfile合成済みビットストリームのパス

書き込み

MCSを作成したら、Hardware Managerを開いてデバイスに接続します。

コンフィグROMがいない場合は、Add Program Memory Device で mt25qu01g-spi-x1_x2_x4 を追加します。 mt25qu01g-spi-x1_x2_x4 を選んで右クリックし、 Program Configuration Memory Device を選択します。

Program Configuration Memory Deviceダイアログ
Program Configuration Memory Deviceダイアログ

その後、 以下のとおり設定をしてOKを押します。10分くらいでコンフィグROMへの書き込みが終わります。

パラメータ名
Configuration File作成したMCSファイルのパス
PRM File作成したMCSファイルと同じディレクトリにできているprmファイルのパス
State of non-config mem I/O pinsPull-up
Address RangeConfiguration 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 のアップデートを適用しないようにします。

S822LCの再起動

コンフィグ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ケーブルを抜いた場合と同じ効果があることを確認しています。

デバイスマネージャでAlveo Programming Cableを無効化

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 に保存します。

  1. ホストで8[MiB]分のランダム・データを生成します。
  2. ホストからU50に(1)のデータを128回転送します。
  3. U50からホストに8[MiB]分のデータを転送します。
  4. (3)の転送結果が(1)と一致するか比較します。
  5. U50からホストに8[MiB]分のデータを128回転送します。
#!/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 にはパフォーマンスカウンタ機能があり、転送処理にかかった総サイクル数を計測することができます。詳しくはPG195H2C 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それぞれ

  • H2C: 12761444610 = 12.76[GB/s]
  • C2H: 14169658723 = 14.17[GB/s]

となり、転送時のPCIeの低レイヤでの通信のオーバーヘッドなどを考えると、これくらい出ていれば十分かなという結果となりました。

まとめ

  • Power8マシンであるS822LCにAlveo U50を接続できることを確認した
  • Xilinxのリファレンス・ドライバを用いて、ホストとFPGA間での通信が行えることを確認した。
  • Xilinx IPのパフォーマンス測定機能を使って、十分なスループットが出ることを確認した。

次回があれば、Alveo U50にてHBM IPを使う場合に気を付けなければいけない点などを紹介できればと思います。

Tags

About Author

idakenta

Leave a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Recent Comments

Social Media