ABEJA Tech Blog

中の人の興味のある情報を発信していきます

低レイヤー探訪記 - ソフトウェア エンジニアがFPGAでLチカするまで

ABEJA でプロダクト開発を行っている森永です。ABEJAアドベントカレンダー2023の22日目の記事です。普段の業務ではフロントエンドからバックエンドやインフラまで幅広く扱っています。今回は、一般的な Web 開発のスタックを飛び越えてより低レイヤーの領域に触れてみたくなり、以前から興味があった FPGA デバイスを触ってみましたのでこちらの記事にやったことをまとめてみました。

注: 筆者 FPGA は超初心者なため、あくまでも私のような初心者の方の参考となる記事に仕立ております。

1. FPGA とは?

1.1 概要

FPGA (Field Programmable Gate Array) は、プログラム可能なデジタル電子回路デバイスの一種です。FPGA は、ハードウェアとソフトウェアの組み合わせによって、 CPU 上で動作するソフトウエアと比較して、特定の領域では高速でかつ高性能な機能を実現できる特徴を持っています。

特にこのような異なるアーキテクチャのコンピューティング デバイスを協調動作させる取り組みは、ヘテロジニアス コンピューティングといい、 HPC (High Performance Computing) 分野では一定の注目を浴びている印象があります。

個人的には、近年のムーアの法則の頭打ちによって、ヘテロジニアス コンピューティングが普及していくのではないかと思います。

実際に CPU と GPU の協調動作環境はすでに PC からスマホまで普及しているので、 この中に FPGA も加わることも近い将来あるのではないかと、個人的には予測していたりします。

今後のコンピューティング技術の発展を支えていくような重要な技術ではある一方、現在ではまだまだニッチな製品市場であるため、世に出回っている製品のほとんどは Xilinx 社と Altera 社の 2 社から供給されているような状況です。

(ちなみに現在は両者ともそれぞれ AMD 社、 Intel 社に買収されています。前述のヘテロジニアス コンピューティング文脈での製品開発を見据えたものであると考えるのが自然かな、と筆者は考えています)

1.2 FPGA が身近に使われているところ

普段ソフトウェアに触れられている方でも、FPGA にはあまり馴染みがないかもしれませんが、実はさまざまな分野で広く活用されていますので、イメージを持っていただくためにも、以下にいくつか例を挙げてみます。

  • テレビやモニターの画像処理部分: FPGA は、画像処理や映像処理のタスクを効率的に実行するために近年よく見かける 4K 以上の解像度のモニターで使用された実績がります。具体的な利用先の機能としては、画像のスケーリング、フィルタ処理、色調補正など、高速な処理が必要な場合に FPGA が活用されることがあります。
  • クラウドのネットワーク処理: Azure のデータセンター上で動作する各サーバーブレードのネットワーク スイッチ部分に FPGA が使用されています *1 。ここでは、ブレード内での各 VM へのパケット ルーティングを FPGA が高速化、かつホスト CPU への負荷をオフロードすることによって、より快適にユーザーがリソースを利用できるように活用されています。
  • ロボティクス: LOVOT の深層学習推論処理のプロセッサとして FPGA が活用されており *2 、よりリアルタイム性のある処理が行えるようにアクセラレータとして利用されています。

2. FPGA を使うと何が良いのか?

ここまでの話で、特定の処理の高速化に良さそうなデバイスという印象を持たれたかと思いますが、実は FPGA を利用するとそのまま独自のチップを開発へのハードルが一気に下がる、という大きな利点もあるのです。

著名なコンピュータ科学者である Alan Kay が「People who are really serious about software should make their own hardware」 (本気でソフトウェアに取り組む人たちは、自分たちでハードウェアを作るべきだ) と言っていたように、 大手 IT 各社は独自ハードウェアの製造に躍起になっており、ほぼ間違いなくどこの企業もどこかの段階で FPGA を利用したハードウェアの実装検証を行っているのだと推測しています。

近年では下記の Google のプロジェクトのように個人的レベルでもチップ開発が行えるような環境が整いつつあり、FPGA を利用すれば比較的大規模なデジタル回路もチップを制作できます。

早速チップを作りたい!と思っても、まずは回路を作成しなければなりません。

上記の Google のサイトを参考に回路設計を行っても良いのですが、正直初心者向けと言ってもいきなり Verilog でインバーターを記述したりと、やや専門性が高いように感じたところがありました。

そこで、まずは FPGA を用いてデジタル回路の設計に慣れてから、チップの作成へと進むのが良いかと考えています。

ここで、一般的にチップ作成に必要な手順を紹介します。デジタル回路のチップ設計では、回路規模にもよりますが、我々が普段利用する機器に入っているようなチップの多くは、大規模集積回路 (LSI: Large Scale Integration) に大別され、以下のような流れで作成することが多いです。 (一部細かい手順は簡略化しています)

  1. 仕様レベルでの回路設計
  2. 回路図や HDL*3 での論理設計
  3. 論理設計が想定通り動作するかのテストやデバッグを実施
  4. 論理設計を実際の物理的な回路として配線配置を行ったレイアウトを作成する
  5. レイアウトを元に、チップの製造を行う
  6. 製造したチップを利用して何か作る

ここで、最も手間とコストがかかるのが 4. と 5. の工程になり、以前まではある程度の規模の企業にお願いしなければ製作は困難でした。

先述の Google のプロジェクトを利用することで、この 4. と 5. の工程を個人でも行えるようになったことのメリットは非常に大きいとは思うのですが、そもそも回路を設計したことがない人が取り組むにはあまりにハードルが高いように感じたので、私のような初心者は FPGA への実装で 1. ~ 3. までの工程を練習するのが良いと考えています。

3. FPGA を始めるには何の言語を学べばいいのか?

FPGA を使うモチベーションがわかったところで、FPGA の開発で利用する言語を整理します。

FPGA 入門に必要な言語は大別すると、ハードウェア記述言語と高位合成を用いたソフトウェア記述言語の2つの選択肢があります。

  • ハードウェア記述言語 (HDL): ハードウェア記述言語は、FPGA の回路を設計する専門の言語となります。代表的なハードウェア記述言語としては、 Verilog と VHDL があります。これらの言語は、回路の振る舞いを記述するため専用の言語となっているため、
  • 高位合成を用いたソフトウェア記述言語:高位合成は、ソフトウェア記述言語(主に C または C++)を使用して FPGA を設計する手法です。この手法では、ソフトウェア コードを FPGA の回路に変換するための高位合成ツールを使用します。これにより、ハードウェア記述言語に比べてより大規模な回路を短い時間で FPGA の設計を行うことができます。高位合成を行うにあたって、通常のソフトウェア開発とは異なる設計手法をとらないといけなかったりもするのですが、 C や C++ に自信がある方であれば、この方法で FPGA を始めることもできると思います。

この記事では簡単な回路を扱うので、一番簡単に動かせる (と筆者は思っている) Verilog でちょっとした検証をやってみます。 

4. 実際に簡単な回路を FPGA 上で動作させてみる

今回の検証で用いたのは Papilio Pro という Gaget Factory LLC が提供している教育目的向けのオープン ソース FPGA デバイスです。

Papilio Pro のボード写真 中央にある大きなチップが FPGA 本体です

Gaget Factory LLC の PapilioPro 紹介ページ

比較的安価で、尚且つ使い勝手の良い Xilinx 社の Spartan LX 6 の FPGA チップが搭載されていて、64 Mbit の SDRAM が搭載されているので、いろいろと実験するのに最適なデバイスだと思います。

難点なのが、現在あまり積極的な生産が行われている様子がなく、入手性がかなり怪しいところでしょうか。 (私はたまたま在庫があるお店を見つけたので手に入ったのですが、他の方におすすめするのが難しいなと感じてます...)

まずはこちらの Papilio Pro を使って電子工作界の Hello World! である L チカを行ってみましょう!

4.1 環境構築手順

Verilog で記述した回路の検証と、 FPGA へ転送する bitstream ファイルを Spartan LX 6 向けに作成するために Xilinx ISE というツールを利用するので、まずはこちらをインストールします。

筆者の環境は Ubuntu 22.04 で、異なる環境の方ですと下記の手順だと上手くいかない可能性もありますので、ご了承いただきますようお願いいたします。

4.1.1 Xilinx ISE Design Suite Full Installer のインストール

Xilinx の Downloads ページにアクセスして、 Xilinx ISE 14.7 の ISE Design Suite Full Installer を入手します。 上記画面で [14.7] をクリックした時に表示されるリストから、下記の [Full Installer for Linux] の項目を探します。 (ファイルサイズが 6.09 GB とやや大きめ目なのでご注意ください) 手元にダウンロード後、正常にダウンロードできたかの確認のためにファイルの MD5 Sum が上記サイトに記載の値を一致するか確認しましよう。

$ md5sum Xilinx_ISE_DS_Lin_14.7_1015_1.tar
e8065b2ffb411bb74ae32efa475f9817  Xilinx_ISE_DS_Lin_14.7_1015_1.tar

MD5 Sum が一致することを確認したら、依存関係のあるパッケージをインストールします。

$ sudo apt install \
    libncurses5 \
    libstdc++5 \
    libmotif-dev \
    xfonts-75dpi \
    xfonts-100dpi

上記ライブラリのインストール完了後、早速ファイルを展開して、必要なインストーラを実行します。

$ tar -xvf Xilinx_ISE_DS_Lin_14.7_1015_1.tar

$ cd Xilinx_ISE_DS_Lin_14.7_1015_1
$ sudo chmod +x ./xsetup
$ ./xsetup

下記のようにインストーラが立ち上がるので、 [Next] をクリックします。

ライセンスと利用規約に同意するか求められるので、両方とも確認して、問題なければ同意のチェック ボックスを埋めて、 [Next] をクリックします。

ライセンス同意書の続きが表示されるので、こちらも確認して、問題なければ同意のチェック ボックスを埋めて、 [Next] をクリックします。

インストールする ISE を選択できる画面が表示されます。

ここでは [ISE WebPACK] を選択して、 [Next] をクリックします。

ここではデフォルトの選択のまま、 [Next] をクリックします。

インストール ディレクトリの選択画面ですが、デフォルトの /opt/Xilinx 以外を選択するとしばしば動作上に問題が発生することがあるので、このまま [Next] をクリックします。

これからインストールする内容の一覧が表示されるので、確認して問題なければ [Install] をクリックしましょう。

インストールが完了するまで待ちます。

下記の画面が表示され、正常にインストールが完了したら、最後に [Finish] をクリックしてインストーラを終了します。

(インストールパスが /opt/Xilinx/ ではないのは、この手順を作るために再実行した時にインストール ディレクトリを変更したためなので、気になさらないでください)

最後にパスを通して ise コマンド実行することで、どのディレクトリから簡単に Xilinx ISE を起動できるようにしましょう。

$ echo PATH=\$PATH:/opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64 >> ~/.bashrc
$ source ~/.bashrc

4.1.2 ライセンスのインポート

このまま Xilinx ISE を起動すると、「ライセンスがないよ」 と言われてしまいます。

ただ、 Xilinx ISE のライセンスは Xilinx のサイトから無償で入手できてしまうので、サクッと入手してインポートしましよう。

AMD のアカウントにログインした状態で下記のライセンス取得ページにアクセスします。 アカウント作成はこちらから行えます。

Xilinx の ライセンス取得ページ

ライセンス ページのリンクを開くと、住所等の入力欄が表示されるので、適切な値を入力した後に [Next] をクリックします。

下記の画面が開くので、 [ISE WebPACK Licence] にチェックをつけ、 [Generate Node-Locked License] をクリックします。

ライセンスが発行されますので、画面からダウンロードするか、登録したメールアドレスに送付されている Xilinx.lic ファイルを取得しておきます。

ここで、ライセンス登録プログラムである xlcm を起動して取得したライセンスを登録するのですが、私の環境では libQt_Network.so が見つからないよと言われて立ち上がりませんでした。

そこで、下記のコマンドで別のディレクトリに配置されていた libQt_Network.so を、 xlcm が配置されているディレクトリにコピーしたところ無事に立ち上がりました。

$ cp /opt/Xilinx/14.7/ISE_DS/ISE/lib/lin64/libQt_Network.so /opt/Xilinx/14.7/ISE_DS/common/bin/lin64/

ようやく準備が整ったところで、以下のコマンドで xlcm を実行しましょう

$ /opt/Xilinx/14.7/ISE_DS/common/bin/lin64/xlcm

[Manage Licenses] タブから、 [Load Licenses] をクリックして、先ほどの Xilinx.lic を開くと、 xlcm がライセンスを読み込んでインポートしてくれます。

(以下のスクリーンショットは、インポート後の画面となります)

4.1.3 Cable Driver のインストール

続いて、デバイスと接続するための Cable Driver をインストールします。 こちらもまずは依存関係のあるパッケージを入れます。

$ sudo apt install libusb-dev libftdi-dev build-essential libc6-dev fxload

早速 USB Driver のインストール プログラムを /opt/Xilinx/ 配下に clone します。

$ cd /opt/Xilinx
$ git clone https://git.zerfleddert.de/git/usb-driver

Clone したディレクトリに移動して、おもむろに make コマンドを実行します。

$ cd /opt/Xilinx/usb-driver
$ sudo make

make コマンドが実行完了したら、 ./setup_pcusb を実行して Driver をインストールして完了です。

$ sudo ./setup_pcusb /opt/Xilinx/14.7/ISE_DS/ISE/

4.1.4 Papilio Pro Loader のインストール

Xilinx ISE で生成した bitstream ファイル (FPGA の構成ファイル) を、 Papilio Pro 上の FPGA に流し込むためのツールになります。

Gadget Factory のこちらのサイト で [Donwload this file] をクリックして、表示されたファイルの中の Papilio-Loader-V2.8.zip をダウンロードしようとしましたが、公開が終了したためかダウンロードできなかったため、 GitHub 上にあるスクリプトからインストールします。

(ここまで開きますが、ボタンをクリックしても反応がない.....)

下記の GitHub レポジトリを clone します。

$ git clone https://github.com/GadgetFactory/Papilio-Loader.git
$ cd Papilio-Loader

依存関係のあるパッケージをまた入れます。

$ sudo apt install default-jdk
$ sudo apt install libftdi-dev

下記のスクリプトを実行して、Papilio Loader をインストールします

./linux-installer.sh

インストールに成功すると、 papilio-prog コマンドを実行すれば下記のような出力が確認できます。

$ papilio-prog
No or ambiguous options specified.

Usage:papilio-prog [-v] [-j] [-f <bitfile>] [-b <bitfile>] [-s e|v|p|a] [-c] [-C] [-r] [-A <addr>:<binfile>]
   -h                   print this help
   -v                   verbose output
   -j                   Detect JTAG chain, nothing else
   -d                   FTDI device name
   -f <bitfile>         Main bit file
   -b <bitfile>         bscan_spi bit file (enables spi access via JTAG)
   -s [e|v|p|a]         SPI Flash options: e=Erase Only, v=Verify Only,
                        p=Program Only or a=ALL (Default)
   -c                   Display current status of FPGA
   -C                   Display STAT Register of FPGA
   -r                   Trigger a reconfiguration of FPGA
   -a <addr>:<binfile>  Append binary file at addr (in hex)
   -A <addr>:<binfile>  Append binary file at addr, bit reversed

4.2 L チカ回路の実装

ようやくツール群をインストールが完了したので、 L チカを行ってみましょう。

Papilio Pro 上にある LED ランプを点灯させることができれば目的達成です。

こちらが光らせる対象の LED の位置です。

4.2.1 Xlinix ISE 上での実装

Xilinx ISE を立ち上げます。

$ ise

[open project] をクリックして、新規プロジェクトを作成します。

Name 欄に L_chika と入力して、 Top-level source type が HDL になっていることを確認して、 [Next] をクリックします。

以下の入力値になるように設定して、 [Next] をクリックします。

設定値を確認して問題がなければ [Finish] をクリックします。

ここから Verilog のファイルを作成していきます。

[Project] > [New Source] をクリックします。

左のペインで Verilog Module を選択して、 File name には led と入力して、[Next] をクリックします。

この画面ではこのまま [Next] をクリックします。

設定値を確認して問題がなければ [Finish] をクリックします。

テンプレート的に、自動で Verilog のファイルが作成されます

下記のコードを参考に、 作成された Verilog のファイルを編集します。

このコードでは、クロック信号 (CLK) の立ち上がりに常に LED1 に 1 (ON) の値を渡す動作となります。 すなわち、 LED が常に点灯し続けるような動作となります。

ここで使われている CLK と LED1 は、次の手順でインポートする UCF (User Constraint File) ファイルで記述されている変数となります。

(余談ですがこちらの記事に記載されている通り、 UCF ファイルは古い規格となるので、最新の FPGA チップを利用したボードを使う時には XDC ファイルという異なる規格のファイルを利用します。)

led.v

module led(
    input CLK,
     output LED1
    );
    reg LED1;
     
     always @(posedge CLK)
         LED1 <= 1;

endmodule

ここで、下記の Gadget Factory のサイトより Papilio Pro 向けの UCF ファイルをダウンロードします。

ダウンロードしたファイルをインポートするために、 [Project] > [Add Copy of Source] をクリックします。

ファイルを選択すると、以下の画面が表示されますが、このまま [OK] をクリックしてインポートを完了させましょう。

led.v 配下に UCF ファイルが追加されたことを確認しました。 これで、 led.v で UCF ファイルが参照されて回路が作成されるようになります。

このまま UCF ファイルを利用すると問題が発生するので、追加した UCF ファイルの 31 行目と 127 行目 (TX と FLASH_SO の記載がある行) を 「#」 でコメントしておきます。

以上でファイルの準備は整いましたので、最後に bitstream 生成時に必要な設定を行いましょう。 [Implement Design] を右クリックして、[Process Properties] を開きます。

(以下のスクリーン ショットで [Implement Design] をハイライトしています。)

Switch Name: -aul のチェック ボックスを埋めて、 [Apply] をクリックして有効化します。 この項目を有効化することで、先ほど UCF ファイルに記載していた変数の内、 led.v 内で利用されていないものがあっても自動的に無視して bitstream の生成を行ってくれるようになるので、余計なコメント アウト作業をしなくて済みます。

いよいよ bitstream を生成します。 [Generate Programming File] をダブル クリックすると、 [Synthesize - XST] から徐々にされていきますので、結果をゆっくり待ちましょう。

無事にファイルが生成されると下記の画面のように、[Generate Programming File] の左側に緑のチェック マークが表示されます。ここまで来れば一安心です、お疲れ様でした。

もし、 [Synthesize - XST] [Implement Design] [Generate Programming File] のいずれかの工程で赤のバツ マークが表示されている場合には、どこかしらで記述ミスや設定ミスがあるので、画面下部の [Errors] タブを開いて原因を確認して修正しましょう。

4.2.2 実機での動作確認

生成した bitstream ファイルを Papilio Pro の FPGA に転送しましょう。

Xilinx ISE で作成したプロジェクト フォルダ (ウィンドウ上部に表示されているファイル名が配置されているディレクトリ) に、 led.bit という名称のファイルがあり、こちらが今回記述した Verilog から生成した bitstream ファイルとなります。

下記のコマンドでこの bitstream ファイルを転送し、無事に転送が完了すると下記のような表示になります。

$ sudo papilio-prog -v -f led.bit 
Using built-in device list
JTAG chainpos: 0 Device IDCODE = 0x24001093  Desc: XC6SLX9
Created from NCD file: led.ncd;UserID=0xFFFFFFFF
Target device: 6slx9tqg144
Created: 2023/12/19 23:30:09
Bitstream length: 2724832 bits

Uploading "led.bit". DNA is 0x99665c5d2ea170ff
Done.
Programming time 541.3 ms
USB transactions: Write 180 read 12 retries 4

早速ボードを確認してみますと......

無事にピカりと光らせることができました!

これにて L チカは無事に完了です。

まとめ

FPGA の概要とその利用用途を確認して、実際に簡単な Verilog を記述して LED を光らせることができました。 利用するツールに癖があったり、開発フローが一般的な Web 開発とは異なりましたので、少々慣れるまで時間がかかりましたが、実際に回路が動作するところを確認できた時の喜びは非常に大きいものでした。

まだまだ Papilio Pro を味わい尽くせていないと感じたので、次はこのボードに搭載された SDRAM を利用してもう少し複雑な動作を実現したくなりました。

こちらの記事を読まれた方にも FPGA の魅力が少しだけ伝われば大変嬉しく思いますし、ABEJA では IoT デバイスを利用した開発プロジェクトもありますので、こういったガジェットが大好きな仲間が来てくださると幸いです!!

We Are Hiring!

ABEJAは、テクノロジーの社会実装に取り組んでいます。 技術はもちろん、技術をどのようにして社会やビジネスに組み込んでいくかを考えるのが好きな方は、下記採用ページからエントリーください! (新卒の方のエントリーもお待ちしております) careers.abejainc.com