ZYNQ に GPIO 回路を接続し Linux 上で割込みを受け付けてみる(後編)

前編で作成した回路を確認するためのソフトウェア環境(Linux)を作成する。 尚、今回はVivado に含まれる hsi (Hardware Software Interface)という CUI ツールを使用し、一環してコマンドラインによる手順をとってみた。

準備

確認した環境は Ubuntu の 64 bit だが、Xilinx の開発ツールは 32 bit 向けのため、予め以下を実施。

% sudo apt-get install ia32-lib

また、開発ツールのパスを通すため、

% source  /opt/Xilinx/Vivado/2015.2/settings64.sh

を行っておく。

更に、開発ツールは gmake を期待するため、以下のように対処する。

% sudo ln -s /usr/bin/make /usr/bin/gmake
ZYBO 用 Linux イメージの取得

今回は、公開されているビルド済みイメージを拝借・流用する。

% git clone https://github.com/ucb-bar/fpga-images-zybo.git

clone したリポジトリから使用するファイルは以下の3点。

fpga-images-zybo/boot_image/u-boot.elf
fpga-images-zybo/uImage
fpga-images-zybo/uramdisk.image.gz
FSBL の作成

ZYNQ 用のブートローダである FSBL (Fast Stage Boot Loader) を作成する。

% mkdir fsbl-build && cd fsbl-build
% hsi

 ****** hsi v2015.2 (64-bit)
   **** SW Build 1266856 on Fri Jun 26 16:35:25 MDT 2015
     ** Copyright 1986-2015 Xilinx, Inc. All Rights Reserved.

hsi% open_hw_design /path/to/gpio-sample/gpio-sample.sdk/zybo_wrapper.hdf
zybo_wrapper
hsi% generate_app -hw zybo_wrapper -os standalone -proc ps7_cortexa9_0 -app zynq_fsbl -compile -sw fsbl -dir fsbl-build
hsi% exit

zybo_wrapper.hdf は前編の最後でエクスポートした H/W 情報のファイルで、/path/to/gpio-sample/ は Vivado で作成したプロジェクトのディレクトリ。

また、生成された FSBL は fsbl-build/executable.elf (ELF フォーマット)で、後ほど使用する。

Device tree の作成

前編でエクスポートした H/W 情報を基に、Device tree を作成する。

% mkdir device-tree && cd device-tree
% git clone git://github.com/Xilinx/device-tree-xlnx.git
% hsi
hsi% open_hw_design /path/to/gpio-sample/gpio-sample.sdk/zybo_wrapper.hdf
hsi% set_repo_path ./device-tree-xlnx
hsi% create_sw_design device-tree -os device_tree -proc ps7_cortexa9_0
device-tree
hsi% generate_target -dir ./
WARNING: ps7_ethernet_0: No reset found
WARNING: ps7_i2c_0: No reset found
WARNING: ps7_usb_0: No reset found
hsi% exit
% dtc -I dts -O dtb -o devicetree.dtb ./system.dts

今回は不要だが、必要に応じて、dtc をかける前に system.dts を編集する(bootargs 等)。 尚、dtc は Ubuntu パッケージのものでOK。

boot.bin の作成

boot.bin は FSBL、FPGA の bit ファイル、ユーザプログラムの3つをパックしたバイナリファイル。ZYNQ は起動デバイスから boot.bin をロードしてジャンプする。

まず、boot.bin の構成を定義するファイルを以下の内容で作成する。

the_ROM_image:
{
    [bootloader]/path/to/fsbl-build/executable.elf
    /path/to/gpio-sample/gpio-sample.sdk/zybo_wrapper.bit
    /path/to/fpga-images-zybo/boot_image/u-boot.elf
}

1行目に最初に作成した FSBL、2行目に前編で生成した FPGA 用の bit ファイル、3行目には u-boot (ELFフォーマット)を指定。

上記ファイルを boot.bif として保存し、以下のようにして boot.bin の作成。

% bootgen -image boot.bif -w on -o i boot.bin
microSD カードにコピーして起動

作成した boot.bin 及び devicetree.dtb と、流用する uImage 及び ramdisk-image を microSD カード(FATフォーマット)にコピーする。

% cp /path/to/boot.bin SDCARD
% cp /path/to/devicetree.dtb SDCARD
% cp /path/to/fpga-images-zybo/uImage SDCARD
% cp /path/to/fpga-images-zybo/uramdisk.image.gz SDCARD

microSD カードを ZYBO に挿入して、JP5(起動方法を選択するジャンパ)を SD にショートし、電源を投入する。 ホストから /dev/ttyUSB* を 115200bps で開き、root (パスワード: root) でログイン可能。 (当然ながら電源投入後でないと ttyUSB が見えないので注意)

GPIO の設定

起動した Linux カーネルには、既にドライバを含む GPIO サブシステムが備わっており、sysfs 経由で設定と操作が行える。

LED の端子一覧と、Linux カーネルが管理するGPIO 番号の対応は以下のとおり。

端子 GPIO 番号
LD0(M14) 898
LD1(M15) 899
LD2(G14) 900
LD3(D18) 901

上記を基に、設定を行う。

for i in `seq 898 901`; do
echo $i > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio$i/direction
done

正しく設定できたか確認するため、LED を全て点灯してみる。(0 で消灯)

for i in `seq 898 901`; do
echo 1 > /sys/class/gpio/gpio$i/value
done

プッシュボタンは以下のとおり。

端子 GPIO 番号
BTN0(R18) 902
BTN1(P16) 903
BTN2(V16) 904
BTN3(Y16) 905

プッシュボタンの設定。割り込み設定も行う。

for i in `seq 902 905`; do
echo $i > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio$i/direction
echo rising > /sys/class/gpio/gpio$i/edge
done

正しく設定できていれば、ボタンを押している間に以下の値が1になる。

for i in `seq 902 905`; do
cat /sys/class/gpio/gpio$i/value
done
割り込みの確認

最終目的である、追加した GPIO の割り込みを Linux から確認する。

sysfs 経由の GPIO インターフェイスは、poll によって割り込みを確認することができる。 BTN0 の割り込みを待ち受ける以下のプログラムを作成した。(エラー処理などは省略)

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>

int main(void)
{
        struct pollfd sw;
        char buf[1];

        sw.fd = open("/sys/class/gpio/gpio902/value", O_RDONLY);
        sw.events = POLLPRI;

        while (1) {
                poll(&sw, 1, -1);
                lseek(sw.fd, 0, SEEK_SET);
                read(sw.fd, buf, 1);
                printf("pushed!\n");
        }
}

コンパイル:

% arm-xilinx-linux-gnueabi-gcc gpio-int.c -o gpio-int

これを ZYBO 上で実行すると、BTN0 を押したタイミングで pushd! が無事に表示された。

おしまい

Xilinx の開発ツールと、ZYNQ の仕組みがだんだんわかってきた。次は、Verilog で 何らかの IP を新たに作成して動かしてみたい。