ZYBO (Zynq) 初心者ガイド (11) LinuxユーザアプリケーションでLチカ

More than 1 year has passed since last update.


環境


  • 開発用PC: Windows 10 64-bit


    • Vivado 2017.4 WebPACKライセンス

    • Xilinx SDK 2017.4 <- 今回は使わない



  • 開発用PC (Linux): Ubuntu 16.04 本家 (日本語版じゃない) (on VirtualBox 5.2.4)


    • PetaLinux 2017.4



  • ターゲットボード: ZYBO (Z7-20)

Windows環境は1回目、Linux環境は8回目を参照。

使用するハードウェア(hdf)は8回目を参照。


LinuxユーザアプリケーションでLチカ

PetaLinuxツールで作られるLinux内に、ユーザアプリケーションを追加します。そのアプリケーションでLED(LD4)をチカチカさせます。内容的には、3回目: PSのGPIOでLチカと同じことを、Linuxユーザアプリからやります。

Linuxユーザアプリケーションの作り方は、基本的には、ug1144-petalinux-tools-reference-guide.pdfのガイド通りです。ただし、このガイドにも書いてあるのですが、アプリケーション自体は他の環境(XilinxSDK等)で作成して、完成したら、ここに示す手順でプロジェクトに取り込む、というのが推奨フローのようです。実際、ここで示す手順だと、非常に効率が悪いです。が、現状僕の環境だとLANが使えず、まともなデバッグもできない状況なので、ひとまずこの記事の手順でやろうと思います。


プロジェクトの準備

新規PetaLinuxプロジェクトの作成からやります。とりあえず、下記操作でBOOT.binとimage.ubを作成して、ちゃんと起動できることを確認します。これはいつも通りの手順で、8回目と同じです。


開発PCターミナル

cd ~/work/peta

petalinux-create --type project --template zynq --name SimplePS
cd SimplePS/
petalinux-config --get-hw-description=../project_1.sdk
petalinux-build
petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/design_1_wrapper.bit --u-boot


シェルからLEDチカチカ

この状態で既に、GPIOにアクセスするためのsysfsインターフェースが用意されています。今回は、PSのGPIOに接続されているLED(LD4)を制御します。これはMIO7に接続されています。もともと、/sys/class/gpio/gpiochip906が用意されているので、この906を基準に、906+7=913がMIO7用のGPIO番号になるようです。(https://stackoverflow.com/questions/40131771/sysfs-interface-i-cant-export-gpio-pins-in-a-zybo-board)


Zyboターミナル

echo 913 > /sys/class/gpio/export

echo out > /sys/class/gpio/gpio913/direction
echo 1 > /sys/class/gpio/gpio913/value
echo 0 > /sys/class/gpio/gpio913/value

または、レジスタ(メモリ)に直書きでも制御できます。理由は後述しますが、GPIOへのクロック供給をONにするために、echo 913 > /sys/class/gpio/exportは必要です。レジスタと設定値については3回目をご参考ください。


Zyboターミナル

echo 913 > /sys/class/gpio/export

devmem 0xE000A204 32 0x0080
devmem 0xE000A208 32 0x0080
devmem 0xE000A040 32 0x0080
devmem 0xE000A040 32 0x0000


ユーザアプリケーションを作成する

先ほどシェルからやった、LEDチカチカ制御を行うアプリケーション(Cプログラム)を作ります。下記コマンドで、myappというLinuxのユーザ空間で動くアプリケーションを作成します。コードは、project-spec/meta-user/recipes-apps/myapp/に作られます。後で編集しますが、ひとまずはこのままにします。petalinux-build -x packageでこのアプリケーションを含むimage.ubが再作成されます。


開発PCターミナル

petalinux-create -t apps --template c --name myapp --enable

petalinux-build -x package

作成されたimage/linux/image.ubをSDカードに上書きコピーしてZyboを起動します。(BOOT.binはそのまま)

すると、/usr/bin/myappがインストールされており、実行するとHello Worldが出力されていることが分かります。


Zyboターミナル

root@SimplePS:~# ls /usr/bin/myapp

/usr/bin/myapp
root@SimplePS:~# myapp
Hello World!


コードを編集して、再ビルドする

コードを編集して、再度ビルドするには、以下のようにします(エディタは適当に置き換えてください)。どういうコードにするかは後述。


開発PCターミナル

code project-spec/meta-user/recipes-apps/myapp/files/myapp.c &

petalinux-build -c myapp -x do_clean
petalinux-build -c myapp -x do_install
petalinux-build -x package

ug1144-petalinux-tools-reference-guide.pdfには、最初に一度だけ、petalinux-build -x do_populate_sysrootpetalinux-build -c rootfsをやるように書いてありましたが、無くても問題ありませんでした。(影響は不明)


メモ

再ビルドする前にpetalinux-build -c myapp -x do_cleanをしないと、ビルドエラーが発生しました(原因不明)。

なお、petalinux-build -c myapp -x do_installを省略して、petalinux-build -x packageだけでも自動的にmyappをビルドしてくれるようです。


メモ

今回は、毎回、パッケージ化されたimage/linux/image.ubを置き換えて動作確認します。後々、myappのバイナリだけを差し替えて開発できるようにしたいと思っています(TCFやNFSなど)。その時は、petalinux-build -c myapp -x do_installだけやればmyappのバイナリ作成が完了します。(petalinux-build -x packageもそれなりに時間がかかるので、だいぶ時間を節約できます。)

myappのバイナリ自体は、下記の場所に作られていました。ファイルが異なるものが2つ出来ていたのですが、おそらくstripされているかどうかだと思います。9回目にやったように、RootFSをSDカードにしている場合は、このバイナリをSDカードに書き込むことでも動作確認ができると思います。


  • build/tmp/work/cortexa9hf-neon-xilinx-linux-gnueabi/myapp/1.0-r0/myapp

  • build/tmp/work/cortexa9hf-neon-xilinx-linux-gnueabi/myapp/1.0-r0/package/usr/bin/myapp


レジスタ直叩きでLチカ

3回目でやったように、PS GPIO(MIO7)のレジスタを直叩きして制御します。コードはほとんど同じですが、Linuxユーザ空間からのアクセスになるので、mmapが必要になります。


project-spec/meta-user/recipes-apps/myapp/files/myapp.c

#include <stdio.h>

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#define REG(address) *(volatile unsigned int*)(address)
#define GPIOPS_BASE (0xE000A000)
#define GPIOPS_DATA_0 (0x0040)
#define GPIOPS_DIRM_0 (0x0204)
#define GPIOPS_OEN_0 (0x0208)

int main(int argc, char **argv)
{
printf("Hello World!\n");

int address; /* GPIOレジスタへの仮想アドレス(ユーザ空間) */
int fd;

/* メモリアクセス用のデバイスファイルを開く */
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
perror("open");
return -1;
}

/* ARM(CPU)から見た物理アドレス → 仮想アドレスへのマッピング */
address = (int)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIOPS_BASE);
if (address == MAP_FAILED) {
perror("mmap");
close(fd);
return -1;
}

/* Set MIO7 as output */
REG(address + GPIOPS_DIRM_0) |= 1 << 7;
REG(address + GPIOPS_OEN_0) |= 1 << 7;
while(1) {
/* Set MIO7 as High */
REG(address + GPIOPS_DATA_0) |= 1 << 7;
usleep(1*1000*1000);
/* Set MIO7 as Low */
REG(address + GPIOPS_DATA_0) &= ~(1 << 7);
usleep(1*1000*1000);
}

/* 使い終わったリソースを解放する */
munmap((void*)address, 0x1000);
close(fd);

return 0;
}


image.ubを再作成して、SDカードに入れて起動します。そして、MIO7に相当するGPIOを有効にしてから、myappを実行します。すると、LED(LD4)が1秒間隔でチカチカするはずです。


Zyboターミナル

root@SimplePS:~# echo 913 > /sys/class/gpio/export

root@SimplePS:~# myapp
Hello World!

1点注意点ですが、myappを実行する前には、echo 913 > /sys/class/gpio/exportでGPIOを有効化しておく必要があります。これは、デフォルトだと省電力のために、GPIOのクロックがOFFになっているためだそうです。(https://forums.xilinx.com/t5/Embedded-Linux/GPIO-access-via-mmap/td-p/672045)

ここではレジスタ直叩きしてみましたが、実際には、デバイスドライバが用意したデバイスファイル/SysFSインターフェースを使うか(次に紹介)、UIOというものを使うのが一般的なようです。


GPIOのSysFSインターフェースでLチカ

以前、他の記事で紹介しましたが、以下のようなコードでGPIOのSysFSにアクセスします。


project-spec/meta-user/recipes-apps/myapp/files/myapp.c

#include <stdio.h>

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

int main(int argc, char **argv)
{
printf("Hello World!\n");

int fd;

/* 1. GPIO913を使用可能にする */
fd = open("/sys/class/gpio/export", O_WRONLY);
write(fd, "913", 3);
close(fd);

/* 2. GPIO913を出力設定する */
fd = open("/sys/class/gpio/gpio913/direction", O_WRONLY);
write(fd, "out", 3);
close(fd);

/* 3. GPIO913に1(High)を出力する */
fd = open("/sys/class/gpio/gpio913/value", O_WRONLY);
write(fd, "1", 1);

while(1) {
write(fd, "0", 1);
usleep(1*1000*1000);
write(fd, "1", 1);
usleep(1*1000*1000);
}

/* 4. 使い終わったので閉じる */
close(fd);

return 0;
}


image.ubを再作成して、SDカードに入れて起動します。myappを実行すると、LED(LD4)が1秒間隔でチカチカするはずです。このコードの場合は、手動でGPIOを有効にする必要はありません。


Zyboターミナル

root@SimplePS:~# myapp

Hello World!


アプリケーションを取り除く

このユーザアプリケーションを、作成するLinuxイメージ(image.ub)に含めたくないときは、petalinux-config -c rootfsのapps設定で、myappのチェックを外します。

image.png


メモ: PL GPIOの場合

PL側のGPIOに接続されたLED(M14、M15、G14、D18)を制御する場合の方法です。

ハードウェアは、4回目で作ったものと同じ、AXI GPIOをLED(M14、M15、G14、D18)に接続したものがあるとします。AXI GPIOのBase Addressを0x41200000とします。これをベースにPetaLinuxで作成したLinuxを起動します。

まずは、レジスタ直叩きバージョンです。PLのAXI GPIOに関しては、OSで電源管理されていないので、レジスタを叩くだけで使えます。


レジスタ直叩きの場合

# set GPIO as output

devmem 0x41200004 32 0x0000
# Output High to GPIO (4-bit)
devmem 0x41200000 32 0x000F
# Output Low to GPIO (4-bit)
devmem 0x41200000 32 0x0000

SysFS経由の場合です。/sys/class/gpio/gpiochip1020が用意されているので、それがAXI GPIOになります。念のため、more /sys/class/gpio/gpiochip1020/labelで中身を確認してみてください。オフセットをつける必要がないので、1020番をそのまま使えます。


sysfsを使う場合

echo 1020 > /sys/class/gpio/export

echo out > /sys/class/gpio/gpio1020/direction
echo 1 > /sys/class/gpio/gpio1020/value
echo 0 > /sys/class/gpio/gpio1020/value

アドレスや番号が違うだけで、プログラムからはPS GPIOと同様に制御できます。


メモ: ライブラリを作りたい場合

petalinux-create -t libs --template c --name mylib --enable