みなさん、FPGA使ってますか?
スマートフォンの普及によって、ARMデバイスは本当に身近な存在になりました。近頃のスマートフォンは一昔前では考えられないほどいいCPU/SoCを搭載しているため、Twitterから3Dゲームに至るまで、手のひらにおさまる大抵の用途において十分に真価を発揮します。ところが「ひたすら行列計算を行いたい」「画像認識や機械学習をより低消費電力で回したい」といった、ちょっと特殊なシチュエーションになると、スマホに搭載されているARM CPU/SoCでは遠からず「性能の限界」がやってきます。この限界を突破するために、FPGAに手を出すのは生き物のサガと言っても差し支えないでしょう。すみません、ちょっと言い過ぎました。
今回はそんな「ちょっと特殊なシチュエーション」に憧れる人に向けて、廉価なFPGA開発ボードの使い方を紹介します。
Digilent製FPGA開発ボード「Zybo」
Digilent製FPGA開発ボード「Zybo」は2万円強で買える初心者向けのFPGA開発ボードです[1]。Zyboにはメインプロセッサとして「謎の半導体メーカー」Xilinx社のZynq-7000シリーズが載っています。Zynqはいわゆる「APSoC」とか「SoC FPGA」ともいわれるカテゴリのチップで、Atrix-7相当のFPGAとARMのCPUコアであるCortex-A9がひとつにまとめられたSoCです。これにより一般的なARMボードのように使いながら、周辺機器からの信号をFPGAに処理させるといった使い方が可能になります。
- CPU:650MHz デュアルコア Cortex-A9
- ロジックスライス:4400個(1個あたり6入力LUTと8個のフリップフロップのロジックセルが4つ)
- ブロックRAM:240KB
- メモリ:DDR3 512MiB
- ストレージ:128Mb QSPI Flash ROM、microSDカードコントローラー
- Ethernet:1Gbit/100Mbit/10Mbit
- オーディオ:ヘッドフォン出力、マイクロフォン・ライン入力ジャック
- インターフェース:HDMI(sink/source)、VGA、USB 2.0(OTG)、UART、JTAG
- その他:スイッチ4個、ボタン6個、LED5個
廉価版のFPGAがベースになっているためロジックスライス数は4400個と少なめです。「ぼくのかんがえたさいきょうのロジックかいろ」をプログラミングしたいのであれば、より高機能・高価格のFPGAが必要になるかもしれません。あくまでFPGAの学習用だと割りきりましょう。
Zyboには、microUSBのJTAG・UART共用端子がついています。つまり「お高い」ダウンロードケーブルを購入しなくても、周囲に転がっているUSBケーブルを使えばFPGAへの書き込みができるというわけです。Zybo以外に用意すべきものは、次のとおりです。
- PCとZyboをつなぐためのUSBケーブル(Zybo側がmicroUSB端子)
- HDMI・VGAケーブル(ディスプレイを使う場合のみ)
- microSDカード(microSDカードから起動する場合)
- 5V 2.5Aの内径2.1mm・center-positiveなプラグのついたACアダプター(USBから給電しない場合)
USBケーブル以外は「あると便利」程度であり、とりあえず動かすだけであれば不要です。電源はUSBからも給電できますし、JTAGを使ったりSPI Flashに書き込むことでmicroSDがなくてもシステムを起動できます。
ちなみにZybo自体は比較的古い製品です。Arty Z7やPYNQ-Z1などおなじZynq-7000ベースの新しいボードも出ていますので、今ならそちらを買う手もあるでしょう。原則として、この記事に書かれている内容は他のZynq系ボードでも適用可能です。
電源投入のテスト
さっそく電源をいれてみましょう。QSPI Flash ROM[2]には最初から「ちゃんと動く」データが書き込まれているため、何もプログラムしていなくても動くようになっています。
まずは電源ソースを設定するJ15ピンを、USBとVU5V0がショートするようにつなぎます。ACアダプターを使う場合は、USBではなくWALLをショートさせます。ちなみにVU5V0とGNDを利用してバッテリーを接続することも可能です。アイドル状態で5V0.2A消費とのことなので、かんたんなものを動かすうちはUSB給電で十分ですが、本格的に使う場合はACアダプターを用意しましょう。
次にJP5ピンを使ってブートデバイスを変更します。シルク印刷にあわせて真ん中の2ピンをショートさせれば、QSPI Flash ROMからのブートになります。
「PROG UART」と書かれたmicroUSB端子にUSBケーブルを接続し、SW4のスイッチを「ON」に変更しましょう。PGOOD LED(LD11)が赤点灯し、DONE LED(LD10)が緑点灯したら起動完了です。HDMIケーブルをディスプレイにつなぐと、カラフルなテストパターンが表示されます。
そのままPCからシリアルコンソールに接続してみましょう。PCにUSBケーブルを接続し、電源を入れたタイミングで2つのシリアルコンソールデバイスが作られているはずです。以下で言うところの、「ftdi_sio
」ドライバーが使われているデバイスがそれにあたります。
UART/JTAG用のICとしてFTDIのデュアルチャンネルUART/FIFO ICであるFT2232Hが搭載されています。他にUSB接続のUARTデバイスを繋いでいない環境であれば、おそらく「/dev/ttyUSB0
」と「/dev/ttyUSB1
」が作られていることでしょう。前者がJTAG経由のプログラミング用で、後者がUART用のデバイスです。udevadm
コマンドでプロパティを見れば、「Digilent」と書かれているはずです。
screen
コマンドをインストールしていれば、次のようにシリアルコンソールを開くことができます。
この状態でボード上の「PS-SRST」ボタンを押すと、システムがリセットします。その結果、次のようにU-Bootとカーネルの起動ログが表示されます[3]。
上記ログにもあるようにルートファイルシステム(ramdisk)は3MiB程度になっています。ほぼBusyBoxだけで構成されているものの、ちゃんとしたLinuxシステムです。ちなみにps
コマンドを実行すると、起動ログにもあるようにtelnetdやHTTPサーバー、ftpdにSSHサーバー(dropbear)が動いていることもわかります。
個々のパーツやその機能については、Zyboのリファレンスマニュアル(PDF)を参照してください。
LXD上にVivadoをインストール
FPGAの回路をデザインしプログラムするために、Xilinx製の統合開発環境「Vivado」と「XSDK」を導入します。Vivadoは有償ソフトウェアですが、機能を制限した無償版のWebPACKがあるのでこちらを使うことにしましょう。いずれもLinux向けのバイナリも提供されています。
そこでまずはXilinxのサイトからVivadoのインストーラーをダウンロードしてください。サイトのリンクをたどって「Vivado HLx 2017.1: WebPACK および Editions - Linux 用自己解凍型ウェブ インストーラー」を選択します。Vivadoのインストーラーを使えば、XSDKもセットでインストールできます。ダウンロードにはアカウント登録が必要です。またダウンロードの際に「米国政府輸出承認」というフォームに個人情報を記録する必要があります。たとえばアメリカ合衆国の商務省産業安全保障局から輸出禁止対象になっているような個人・会社は何がしかのおとがめがあるかもしれません。
インストーラー自体は86MB程度ですが、インストール時に数GBのデータをダウンロードすることになりますのでネットワークの帯域には注意してください。
xilinxコンテナの作成
今回はこれらのプロプライエタリのバイナリをLXD上のコンテナにインストールして使うことにします。ちなみにLXD上である必要性はなく、実機に直接インストールしてもかまいません。実機に直接インストールする場合は、この節を飛ばしてください。
LXDを使う場合に気をつけなければいけないのは、XSDKがUSB-JTAG経由でFPGAをプログラムすることです。つまりLXDコンテナの中からUSBデバイスが見えなくてはなりません。そこでLXD 2.1以降で機能が充実した、usbデバイスタイプのホットプラグ機能を使うことにします[4]。Ubuntu 16.04 LTSの場合、普通にLXDをインストールすると2.0.x系がインストールされます。2.1以降を使いたい場合は、バックポートリポジトリを指定してください。
もし初めてLXDをインストールした場合は、初期設定を行っておきましょう。
設定が終わったら、一度ログインしなおしてください。Vivadoはi386アーキテクチャのバイナリが必要になるケースがあるため、次のような内容のファイルを「xenial.cfg
」として作っておきます。
ではVivadoをインストールするためのコンテナを作ります。
UbuntuのコンテナのSSHサーバーは公開鍵認証のみ可能な設定で起動しているため、ssh-import-id
コマンドで鍵を登録しています。LaunchpadにSSH公開鍵を登録済みであれば「lp:(Launchpadのアカウント)」でインターネットから公開鍵をダウンロードしますし、「gh:(GitHubのアカウント)」を実行すればGitHubから取得します。
最後にインストールしているパッケージは、VivadoやXSDKを動かすために必要なパッケージです。もちろんこれらの設定はxenial.cfg
に書いてもかまいません。
あとは「lxc list
」で表示されるxilinxコンテナのアドレスに対して、X転送を有効化した上でSSHログインしましょう。
これ以降は、コンテナの上でコマンドを実行するものとします[5]。
Vivadoインストーラーの起動
Vivadoインストーラーは「シェルスクリプト+アーカイブバイナリデータ」タイプのただのスクリプトファイルです。スクリプトを実行するとアーカイブバイナリを展開し、その中のJavaプログラムを起動します。「--help
」オプションを付けて実行すると、使い方が表示されます。
インストール先を「/opt/Xilinx
」にするために、あらかじめ一般ユーザーが書き込めるように作っておきます[6]。その後、インストーラーを起動してください。
しばらく待つと、インストーラーのウィンドウが立ち上がります。以降、スクリーンショットを元にステップバイステップで説明します。
一通り入力が終わったら、ダウンロードとインストールを開始します。しばらく時間がかかるので放置しておきましょう。
インストールが完了したら環境変数設定ファイルを読み込み、vivado
コマンドで起動します。
無事に以下の画面が表示されたらインストール完了です。実際に使う前に一度Vivadoを終了して、他のファイルをインストール手順に移ります。
ボードファイルのインストール
「ボードファイル」は各開発ボードの各種インターフェースを定義したファイルです。VivadoにはXilinx製の開発ボードのボードファイルが最初から入っていますが、Zyboについては別途DigilentのGitHubサイトからダウンロードしてインストールする必要があります。といってもzipアーカイブを展開するだけなので、手順は至極シンプルです。
これによりVivado上でZyboの各種インターフェースを簡単に利用できるようになります。
「ケーブルドライバー」のインストールとコンテナの設定
一般的にFPGAの書き込みには、FPGAのベンダーが用意している「書き込みケーブル」を利用します。最近はFPGA側のインターフェースがJTAGで、PC側がUSBであることが多いため「USBケーブルドライバー」をPCにインストールする必要があります[7]。
Zyboの場合は普通のUSBケーブルをそのまま使えるため、実は専用のUSBケーブルドライバーは不要です。ただしVivadoには最初からケーブルドライバーのインストールスクリプト[8]が用意されています。これは何をするものかと言うと、実質的にはDigilent製のシリアルコンソールデバイスが接続された場合に、デバイスファイルのパーミッションを「666」にする(誰でも読み書き可能にする)だけです。要するにVivadoやXSDKからJTAG経由でFPGAのデータなどを書き込むことを考慮して、VivadoやXSDKを起動したユーザーの権限でそのデバイスに書き込めるようにしているわけです。
そこでインストールスクリプトを実行する代わりにホスト側において「/etc/udev/rules.d/55-zybo.rules
」というファイル名で、次の内容の設定ファイルを用意します。ホスト側としているのは、LXDコンテナの内部のsystemd-udevdの権限が制限されているためです。
なお、上記はZyboの場合のみを考慮しています。他のXilinxのケーブルを使う場合は、インストールスクリプトを参考に適宜設定してください。ちなみにVivadoに同梱されているファイルのudevルールは若干古いようで、Ubuntu 16.04 LTSだと多少調整が必要かもしれません。
設定ファイルを作成したら、ホスト上で次のコマンドで設定を再読み込みします。
さらにZyboの電源を入れて、UARTデバイスが認識された状態にした上で、それをxilinxコンテナの中から見えるように設定を行います。
これでUARTデバイスを認識したら自動的に、xilinxコンテナの中からも見えるようになりました。FPGAのオンオフにあわせて「/dev/bus/usb
」以下の内容が変化するはずです。
もし「/dev/ttyUSB0
」などもコンテナの中から見えるようにしたい場合は、ホスト上で次のコマンドを実行してください。
「ttyUSB0
」などは認識順に移動します。必要であればudevルールのほうで固定の名前をつけましょう。
FPGAサンプルプログラムを動かす
最後にVivadoを使って実際にFPGAの回路を作成し、XSDKを用いてFPGAと協調して動作するプログラムを作成して、両方のデータをJTAGからボードに送信して起動してみましょう。
Zyboの場合、CPU側を「PS:Processing System」と呼び、FPGA側を「PL:Programmable Logic」と呼びます。PSとPLの間はARM社が提唱するインターコネクト「AMBA」のプロトコルのひとつである「AMBA AXI」で通信します。PL側が周辺機器(I/O Peripheral)とやり取りするためには、AXIインターコネクト経由で接続するか、Xilinx独自のEMIO(Extended
Multiplexed I/O)を使って直接接続するかの二択になります。
一番わかりやすいのは、Digilentのチュートリアルを実施することです。このチュートリアルを通して、ボード上のプッシュボタンやスライドスイッチ、LEDの値をGPIOを経由して読み書きする回路を構築できます。GPIOの操作はAXIインターコネクトを用いて行います。PS側がマスターで、PL側がスレーブです。PL側、つまりFPGA側の回路は自分で1から設計することも可能ですが、このチュートリアルではXilinxが提供するIPコア(Intellectual
Property Core)を流用しています。たとえばGPIOを操作する部分はAXI GPIO(PDF)を使っています。Vivadoを使うとVHDLなどを記述しなくても、マウスの操作だけでこれらのIPコアを接続することが可能です。
- PL側に2つのAXI GPIOを用意する
- 1つのAXI GPIOはデュアルチャンネル設定とし、4つのボタン(
btns_4bits
)と4つのスイッチ(sws_4bits
)に接続する
- 1つのAXI GPIOはシングルチャネル設定とし、4つのLED(
leds_4bits
)に接続する
- PL側にAXI Interconnectを用意する(IPコアのデータシートPDF)
- AXI InterconnectをPS側のAXIの汎用ポートの1つ(
M_AXI_GP0
)に接続する(PS側がマスター)
- AXI InterconnectとAXI GPIOを接続する(AXI Interconnect側がマスター)
回路をデザインしたら、チュートリアルの手順に従って検証し、論理合成を行ってBitstreamを含むアーカイブ(sysdefファイル)を生成します。このBitstreamがFPGAにプログラムされるデータの実体です。今回はXSDKからJTAGを使ってデータを送るため、次の手順でこのZIPアーカイブをhdfファイル(拡張子が異なるだけで中身は同じ)としてSDKへエクスポートしています。なおBitstreamの生成にはそれなりに時間がかかります。完了前にSDKへのエクスポートを実行しようとすると、「sysdefファイルがない」と怒られます。「Bitstream
Generation Completed」ダイアログが表示されるまでおとなしく待ちましょう。
SDKへのエクスポートが完了したら、PS側のロジックをSDKで記述します。
- スイッチのGPIOの値を読む
- その値をLEDのGPIOにセットする
- ボタンのGPIOの値を読む
- 値にあわせてコンソールに押されたボタンの番号を表示する
- 上記を200ミリ秒周期で繰り返す
PS側のロジックはこれだけです。「OS Platform」を「standalone」として構築するため、本来であればLinuxカーネルがやるであろうハードウェアの初期化などもすべて上記ロジックを含むシングルバイナリで行います。
最後にJTAG経由でZyboにデータを送ります。まずZybo上のJP5を「JTAG」がショートするようにジャンパピンの位置を変更しましょう。変更したら、ZyboとPCをUSBケーブルで接続した上で、電源を投入してください。PGOOD LEDが赤色で点灯し、DONE LEDは消灯したままになるはずです。
XSDKのXilinx Toolsメニューから「Program FPGA」を選ぶとBitstreamをFPGAに書き込みます。
Deviceが「Auto Detect」になっている場合、「hw_server
」が起動し存在するUSBデバイスから適切な書き込みデバイスを検知した上でデータをプログラムします。Zyboの電源が入っていなかったり、USBケーブルがつながっていないとプログラムに失敗するので注意してください。プログラムが成功すれば、Zybo上のDONE LEDが緑色に点灯します。
最後にPS側のロジックをビルド&実行しましょう。Project Explorerのプロジェクト名(チュートリアル通りに実施しているなら「getting_started_with_ZYBO
」)を右クリックし「Run As」から「Launch on Hardware (System Debugger)」を選択すれば、ソースコードをビルドし、ELFバイナリがPS側に送られ、実行されます。
今回は押されたボタンの番号がコンソールに表示されるため、/dev/ttyUSB1
はscreen
で接続しておきます[9]。
あとはスイッチ(SW0からSW3まで)やボタン(BTN0からBTN3まで)をガチャガチャと動かして、Vivadoで設計した論理回路とXSDKで書いたコードのとおりに動作することを確認すれば完成です。