Ubuntu Weekly Recipe

第475回廉価なFPGA開発ボード「Zybo」Ubuntuからプログラムする

みなさん、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 Z7PYNQ-Z1などおなじZynq-7000ベースの新しいボードも出ていますので、今ならそちらを買う手もあるでしょう。原則として、この記事に書かれている内容は他のZynq系ボードでも適用可能です。

電源投入のテスト

さっそく電源をいれてみましょう。QSPI Flash ROM[2]には最初から「ちゃんと動く」データが書き込まれているため、何もプログラムしていなくても動くようになっています。

まずは電源ソースを設定するJ15ピンを、USBとVU5V0がショートするようにつなぎます。ACアダプターを使う場合は、USBではなくWALLをショートさせます。ちなみにVU5V0とGNDを利用してバッテリーを接続することも可能です。アイドル状態で5V0.2A消費とのことなので、かんたんなものを動かすうちはUSB給電で十分ですが、本格的に使う場合はACアダプターを用意しましょう。

図1 USB給電に変更した状態
画像

次にJP5ピンを使ってブートデバイスを変更します。シルク印刷にあわせて真ん中の2ピンをショートさせれば、QSPI Flash ROMからのブートになります。

図2 SPIブートに変更した状態
画像

「PROG UART」と書かれたmicroUSB端子にUSBケーブルを接続し、SW4のスイッチを「ON」に変更しましょう。PGOOD LED(LD11)が赤点灯し、DONE LED(LD10)が緑点灯したら起動完了です。HDMIケーブルをディスプレイにつなぐと、カラフルなテストパターンが表示されます。

そのままPCからシリアルコンソールに接続してみましょう。PCにUSBケーブルを接続し、電源を入れたタイミングで2つのシリアルコンソールデバイスが作られているはずです。以下で言うところの、ftdi_sioドライバーが使われているデバイスがそれにあたります。

$ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/8p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/16p, 480M
    |__ Port 3: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 6: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 6: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 8: Dev 7, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 480M
    |__ Port 8: Dev 7, If 1, Class=Vendor Specific Class, Driver=ftdi_sio, 480M
    |__ Port 9: Dev 4, If 0, Class=Vendor Specific Class, Driver=pl2303, 12M

UART/JTAG用のICとしてFTDIのデュアルチャンネルUART/FIFO ICであるFT2232Hが搭載されています。他にUSB接続のUARTデバイスを繋いでいない環境であれば、おそらく/dev/ttyUSB0/dev/ttyUSB1が作られていることでしょう。前者がJTAG経由のプログラミング用で、後者がUART用のデバイスです。udevadmコマンドでプロパティを見れば、⁠Digilent」と書かれているはずです。

$ udevadm info --name=/dev/ttyUSB0 | grep Digilent
S: serial/by-id/usb-Digilent_Digilent_Adept_USB_Device_210279A42C65-if00-port0
E: DEVLINKS=/dev/serial/by-path/pci-0000:00:14.0-usb-0:8:1.0-port0 /dev/serial/by-id/usb-Digilent_Digilent_Adept_USB_Device_210279A42C65-if00-port0
E: ID_MODEL=Digilent_Adept_USB_Device
E: ID_MODEL_ENC=Digilent\x20Adept\x20USB\x20Device
E: ID_SERIAL=Digilent_Digilent_Adept_USB_Device_210279A42C65
E: ID_VENDOR=Digilent
E: ID_VENDOR_ENC=Digilent

screenコマンドをインストールしていれば、次のようにシリアルコンソールを開くことができます。

$ sudo screen /dev/ttyUSB1 115200
zynq> uname -a
Linux (none) 3.10.0-xilinx-14974-gb286654-dirty #12 SMP PREEMPT Tue Nov 5 21:58:53 PST 2013 armv7l GNU/Linux
zynq> cat /proc/cpuinfo
processor       : 0
model name      : ARMv7 Processor rev 0 (v7l)
BogoMIPS        : 1299.25
Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x3
CPU part        : 0xc09
CPU revision    : 0

processor       : 1
model name      : ARMv7 Processor rev 0 (v7l)
BogoMIPS        : 1299.25
Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x3
CPU part        : 0xc09
CPU revision    : 0

Hardware        : Xilinx Zynq Platform
Revision        : 0000
Serial          : 0000000000000000

この状態でボード上の「PS-SRST」ボタンを押すと、システムがリセットします。その結果、次のようにU-Bootとカーネルの起動ログが表示されます[3]⁠。

U-Boot 2013.04-dirty (Nov 14 2013 - 11:56:14)

Memory: ECC disabled
DRAM:  512 MiB
WARNING: Caches not enabled
MMC:   zynq_sdhci: 0
SF: Detected S25FL129P_64K with page size 64 KiB, total 16 MiB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   Gem.e000b000
Hit any key to stop autoboot:  0
Copying Linux from QSPI flash to RAM...
SF: Detected S25FL129P_64K with page size 64 KiB, total 16 MiB
## Booting kernel from Legacy Image at 03000000 ...
   Image Name:   Linux-3.10.0-xilinx-14974-gb2866
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    3126272 Bytes = 3 MiB
   Load Address: 00008000
   Entry Point:  00008000
   Verifying Checksum ... OK
## Loading init Ramdisk from Legacy Image at 02000000 ...
   Image Name:
   Image Type:   ARM Linux RAMDisk Image (gzip compressed)
   Data Size:    3692769 Bytes = 3.5 MiB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 02a00000
   Booting using the fdt blob at 0x02a00000
   Loading Kernel Image ... OK
OK
   Loading Ramdisk to 1f7cf000, end 1fb548e1 ... OK
   Loading Device Tree to 1f7c9000, end 1f7ce1e9 ... OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
Linux version 3.10.0-xilinx-14974-gb286654-dirty (Sham@TheFootball) (gcc version 4.6.1 (Sourcery CodeBench Lite 2011.09-50) ) #12 SMP PREEMPT Tue Nov 5 21:58:53 PST 2013
CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
Machine: Xilinx Zynq Platform, model: Xilinx Zynq Platform
(中略)
EXT2-fs (ram0): warning: mounting unchecked fs, running e2fsck is recommended
VFS: Mounted root (ext2 filesystem) on device 1:0.
devtmpfs: mounted
Freeing unused kernel memory: 172K (c0552000 - c057d000)
Starting rcS...
++ Mounting filesystem
++ Setting up mdev
++ Configure static IP 192.168.1.10
++ Starting telnet daemon
++ Starting http daemon
++ Starting ftp daemon
++ Starting dropbear (ssh) daemon
++ Exporting LEDs & SWs
rcS Complete
zynq>

上記ログにもあるようにルートファイルシステム(ramdisk)は3MiB程度になっています。ほぼBusyBoxだけで構成されているものの、ちゃんとしたLinuxシステムです。ちなみにpsコマンドを実行すると、起動ログにもあるようにtelnetdやHTTPサーバー、ftpdにSSHサーバー(dropbear)が動いていることもわかります。

  525 root       0:00 telnetd -l /bin/sh
  531 root       0:00 httpd -h /var/www
  532 root       0:00 tcpsvd 0:21 ftpd ftpd -w /
  538 root       0:00 dropbear

個々のパーツやその機能については、Zyboのリファレンスマニュアル(PDF)を参照してください。

LXD上にVivadoをインストール

FPGAの回路をデザインしプログラムするために、Xilinx製の統合開発環境VivadoXSDKを導入します。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以降を使いたい場合は、バックポートリポジトリを指定してください。

$ sudo apt -t xenial-backports install lxd

もし初めてLXDをインストールした場合は、初期設定を行っておきましょう。

$ sudo lxd init
(基本的にすべての既定の選択肢をそのまま選ぶ)

設定が終わったら、一度ログインしなおしてください。Vivadoはi386アーキテクチャのバイナリが必要になるケースがあるため、次のような内容のファイルをxenial.cfgとして作っておきます。

#cloud-config

apt:
  primary:
    - arches: [amd64, i386, default]
      uri: http://jp.archive.ubuntu.com/ubuntu
  security:
    - arches: [amd64, i386, default]
      uri: http://security.ubuntu.com/ubuntu

package_upgrade: true

packages:
  - language-pack-ja
  - fonts-takao

locale: ja_JP.UTF-8

timezone: Asia/Tokyo

runcmd:
  - [ dpkg, --add-architecture, i386 ]
  - [ apt-get, update ]

ではVivadoをインストールするためのコンテナを作ります。

$ lxc init ubuntu:16.04 xilinx
xilinx を作成中
$ lxc config set xilinx user.user-data - < xenial.cfg
$ lxc start xilinx
$ lxc exec xilinx -- sudo -iu ubuntu ssh-import-id lp:(Launchpadのアカウント)
$ lxc file push ~/ダウンロード/Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin xilinx/home/ubuntu/
$ lxc exec xilinx -- chown ubuntu: /home/ubuntu/Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin
$ lxc exec xilinx -- chmod +x /home/ubuntu/Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin
$ lxc exec xilinx -- apt install -y xterm libgtk2.0-0 unzip

UbuntuのコンテナのSSHサーバーは公開鍵認証のみ可能な設定で起動しているため、ssh-import-idコマンドで鍵を登録しています。LaunchpadにSSH公開鍵を登録済みであれば「lp:(Launchpadのアカウント⁠⁠」でインターネットから公開鍵をダウンロードしますし、⁠gh:(GitHubのアカウント⁠⁠」を実行すればGitHubから取得します。

最後にインストールしているパッケージは、VivadoやXSDKを動かすために必要なパッケージです。もちろんこれらの設定はxenial.cfgに書いてもかまいません。

あとはlxc listで表示されるxilinxコンテナのアドレスに対して、X転送を有効化した上でSSHログインしましょう。

$ ssh -X ubuntu@(xilinxコンテナのアドレス)

これ以降は、コンテナの上でコマンドを実行するものとします[5]⁠。

Vivadoインストーラーの起動

Vivadoインストーラーは「シェルスクリプト+アーカイブバイナリデータ」タイプのただのスクリプトファイルです。スクリプトを実行するとアーカイブバイナリを展開し、その中のJavaプログラムを起動します。--helpオプションを付けて実行すると、使い方が表示されます。

$ ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --help
Makeself version 2.1.5
 1) Getting help or info about ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin :
  ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --help   Print this message
  ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --info   Print embedded info : title, default target directory, embedded script ...
  ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --lsm    Print embedded lsm entry (or no LSM)
  ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --list   Print the list of files in the archive
  ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --check  Checks integrity of the archive

 2) Running ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin :
  ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin [options] [--] [additional arguments to embedded script]
  with following options (in that order)
  --confirm             Ask before running embedded script
  --noexec              Do not run embedded script
  --keep                Do not erase target directory after running
                        the embedded script
  --nox11               Do not spawn an xterm
  --nochown             Do not give the extracted files to the current user
  --target NewDirectory Extract in NewDirectory
  --tar arg1 [arg2 ...] Access the contents of the archive through the tar command
  --                    Following arguments will be passed to the embedded script

$ ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --info
Identification: Xilinx Installer
Target directory: removeLin64
Uncompressed size: 16 KB
Compression: gzip
Date of packaging: Fri Apr 14 21:56:37 MDT 2017
Built with Makeself version 2.1.5 on linux
Build command was: /proj/rdi-xco/scratch/builds/2017.1/nightly/RUNNING_BUILD/prep/rdi/installer/ict/bin/../../tools/makeself.sh \
    "--follow" \
    "/proj/xbuilds/2017.1_0415_1/images/ict_data/Xilinx_Vivado_SDK_2017.1_0415_1/removeLin64" \
    "/proj/xbuilds/2017.1_0415_1/images//Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin" \
    "Xilinx Installer" \
    "./xsetup"
Script run after extraction:
     ./xsetup
removeLin64 will be removed after extraction

$ ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin --check
Verifying archive integrity... MD5 checksums are OK. All good.

インストール先を/opt/Xilinxにするために、あらかじめ一般ユーザーが書き込めるように作っておきます[6]⁠。その後、インストーラーを起動してください。

$ sudo mkdir -p /opt/Xilinx
$ sudo chown -R ubuntu: /opt/Xilinx
$ ./Xilinx_Vivado_SDK_2017.1_0415_1_Lin64.bin

しばらく待つと、インストーラーのウィンドウが立ち上がります。以降、スクリーンショットを元にステップバイステップで説明します。

図3 ダウンロード時に登録したアカウント情報を入力
画像
図4 License Agreementsに同意する
画像
図5 Vivado HL WebPACKを選択
画像
図6 ⁠Software Development Kit (SDK)」もチェックする
画像
図7 インストール先の選択
画像

一通り入力が終わったら、ダウンロードとインストールを開始します。しばらく時間がかかるので放置しておきましょう。

インストールが完了したら環境変数設定ファイルを読み込み、vivadoコマンドで起動します。

$ source /opt/Xilinx/Vivado/2017.1/settings64.sh
$ vivado

無事に以下の画面が表示されたらインストール完了です。実際に使う前に一度Vivadoを終了して、他のファイルをインストール手順に移ります。

図8 Vivadoの画面
画像

ボードファイルのインストール

ボードファイルは各開発ボードの各種インターフェースを定義したファイルです。VivadoにはXilinx製の開発ボードのボードファイルが最初から入っていますが、Zyboについては別途DigilentのGitHubサイトからダウンロードしてインストールする必要があります。といってもzipアーカイブを展開するだけなので、手順は至極シンプルです。

$ wget https://github.com/Digilent/vivado-boards/archive/master.zip
$ unzip master.zip
$ cp -a vivado-boards-master/new/board_files/* /opt/Xilinx/Vivado/2017.1/data/boards/board_files/

これにより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の権限が制限されているためです。

ACTION=="add", ATTRS{idVendor}=="0403", ATTRS{manufacturer}=="Digilent", MODE:="666"

なお、上記はZyboの場合のみを考慮しています。他のXilinxのケーブルを使う場合は、インストールスクリプトを参考に適宜設定してください。ちなみにVivadoに同梱されているファイルのudevルールは若干古いようで、Ubuntu 16.04 LTSだと多少調整が必要かもしれません。

設定ファイルを作成したら、ホスト上で次のコマンドで設定を再読み込みします。

$ sudo udevadm control --reload

さらにZyboの電源を入れて、UARTデバイスが認識された状態にした上で、それをxilinxコンテナの中から見えるように設定を行います。

$ lsusb
Bus 001 Device 005: ID 0403:6010 Future Technology Devices International, Ltd FT2232C Dual USB-UART/FIFO IC
$ lxc config device add xilinx zybo usb vendorid=0403 productid=6010 mode=0666

これでUARTデバイスを認識したら自動的に、xilinxコンテナの中からも見えるようになりました。FPGAのオンオフにあわせて/dev/bus/usb以下の内容が変化するはずです。

もし/dev/ttyUSB0などもコンテナの中から見えるようにしたい場合は、ホスト上で次のコマンドを実行してください。

$ lxc config device add xilinx ttyUSB0 unix-char path=/dev/ttyUSB0
$ lxc config device set xilinx ttyUSB0 mode 666
$ lxc config device add xilinx ttyUSB1 unix-char path=/dev/ttyUSB1
$ lxc config device set xilinx ttyUSB1 mode 666

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つのLEDleds_4bitsに接続する
  • PL側にAXI Interconnectを用意するIPコアのデータシートPDF
  • AXI InterconnectをPS側のAXIの汎用ポートの1つM_AXI_GP0に接続する(PS側がマスター)
  • AXI InterconnectとAXI GPIOを接続する(AXI Interconnect側がマスター)
図9 Block DesignでPSを配置したところ
画像
図10 Block DesignでAXI GPIOを配置したところ
画像
図11 AXI GPIOのデュアルチャンネル設定
画像
図12 AXI GPIOの個々のチャンネルに接続するデバイスの選択
画像
図13 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カーネルがやるであろうハードウェアの初期化などもすべて上記ロジックを含むシングルバイナリで行います。

図14 ⁠standalone」でプロジェクトを作成する
画像
図15 チュートリアルにあわせてhelloworld.cを書き換える
画像

最後にJTAG経由でZyboにデータを送ります。まずZybo上のJP5を「JTAG」がショートするようにジャンパピンの位置を変更しましょう。変更したら、ZyboとPCをUSBケーブルで接続した上で、電源を投入してください。PGOOD LEDが赤色で点灯し、DONE LEDは消灯したままになるはずです。

XSDKのXilinx Toolsメニューから「Program FPGA」を選ぶとBitstreamをFPGAに書き込みます。

図16 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/ttyUSB1screenで接続しておきます[9]⁠。

$ screen /dev/ttyUSB1 115200

あとはスイッチ(SW0からSW3まで)やボタン(BTN0からBTN3まで)をガチャガチャと動かして、Vivadoで設計した論理回路とXSDKで書いたコードのとおりに動作することを確認すれば完成です。

おすすめ記事

記事・ニュース一覧