ZYBO (Zynq) 初心者ガイド (12) LinuxカーネルモジュールでLチカ


環境

  • 開発用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チカ

前回は、Linuxユーザ空間で動くアプリケーションを作成して、そこからLチカを行いました。今回は、Linuxのカーネル空間で動くモジュールを作成して、そこからLチカを行います。
面倒なので、/devや/sysといったインターフェースは作りませんが、デバイスドライバとして使う際には必要になります。詳しくはこちら(https://qiita.com/take-iwiw/items/1fdd2e0faaaa868a2db2 )の記事をご参考ください。
「Lチカ」と言いましたが、実際には、ロード時にLED点灯、アンロード時にLED消灯、という簡単な仕様のカーネルモジュールを作ります。

プロジェクトの準備

新規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

カーネルモジュールを作成する

下記コマンドで、mymoduleというLinuxのカーネル空間で動くモジュールを作成します。コードは、project-spec/meta-user/recipes-modules/mymoduleに作られます。後で編集しますが、ひとまずはこのままにします。petalinux-build -x packageでこのアプリケーションを含むimage.ubが再作成されます。

(ug1144-petalinux-tools-reference-guide.pdfによると、petalinux-build -x packageの前にpetalinux-build -c rootfsが必要らしいが、無くても動いた)

開発PCのターミナル
petalinux-create -t modules --name mymodule --enable
petalinux-build -x package

作成されたimage/linux/image.ubをSDカードに上書きコピーしてZyboを起動します。(BOOT.binはそのまま)
すると、作成したカーネルモジュールが/lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.koにインストールされており、modprobe(ロード)すると、Hello world的なメッセージが出力されていることが分かります。

Zyboのターミナル
root@SimplePS:~# ls /lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.ko
/lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.ko
root@SimplePS:~# modprobe mymodule
mymodule: loading out-of-tree module taints kernel.
<1>Hello module world.
<1>Module parameters were (0xdeadbeef) and "default"

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

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

開発PCのターミナル
code project-spec/meta-user/recipes-modules/mymodule/files/mymodule.c &
petalinux-build -c mymodule -x do_install
petalinux-build -x package

petalinux-build -c mymodule -x do_installは省略可能。バイナリ(.ko)自体は、下記に作成される。違いはおそらくstripの有無

  • build/tmp/work/plnx_arm-xilinx-linux-gnueabi/mymodule/1.0-r0/mymodule.ko
  • build/tmp/work/plnx_arm-xilinx-linux-gnueabi/mymodule/1.0-r0/package/lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.ko

レジスタ直叩きでLチカ

モジュールロード(mymodule_init)のタイミングで、GPIOの設定と、High出力をします。モジュールアンロード(mymodule_exit)のタイミングで、Low出力をします。レジスタを叩くために、ioremap_nocache()で物理アドレス->仮想アドレスに変換しています。

project-spec/meta-user/recipes-modules/mymodule/files/mymodule.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>

/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");
MODULE_DESCRIPTION
    ("mymodule - loadable module template generated by petalinux-create -t modules");


#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)

static int __init mymodule_init(void)
{
    printk("mymodule_init\n");
    int address;
    address = (int)ioremap_nocache(GPIOPS_BASE, 0x1000);

    /* Set MIO7 as output */
    REG(address + GPIOPS_DIRM_0) |= 1 << 7;
    REG(address + GPIOPS_OEN_0)  |= 1 << 7;

    /* Set MIO7 as High */
    REG(address + GPIOPS_DATA_0) |= 1 << 7;

    iounmap((void*)address);
    return 0;
}


static void __exit mymodule_exit(void)
{
    printk("mymodule_exit\n");

    int address;
    address = (int)ioremap_nocache(GPIOPS_BASE, 0x1000);

    /* Set MIO7 as Low */
    REG(address + GPIOPS_DATA_0) &= ~(1 << 7);

    iounmap((void*)address);
}

module_init(mymodule_init);
module_exit(mymodule_exit);

image.ubを再作成して、SDカードに入れて起動します。そして、MIO7に相当するGPIOを有効にします。その後、mymodule.koをロード、アンロードします。そのタイミングでLED(LD4)がチカチカするはずです。

Zyboターミナル
root@SimplePS:~# echo 913 > /sys/class/gpio/export
root@SimplePS:~# modprobe mymodule
mymodule: loading out-of-tree module taints kernel.
mymodule_init
root@SimplePS:~# modprobe -r mymodule
mymodule_exit
root@SimplePS:~# modprobe mymodule
mymodule_init

GPIO制御関数でLチカ

カーネル空間で使用できる、GPIO制御関数を使用してLEDを制御します。

project-spec/meta-user/recipes-modules/mymodule/files/mymodule.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/gpio.h>

/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");
MODULE_DESCRIPTION
    ("mymodule - loadable module template generated by petalinux-create -t modules");

static int __init mymodule_init(void)
{
    printk("mymodule_init\n");

    /* LED用のGPIO913(MIO7)を使用する */
    gpio_request(913, NULL);
    gpio_export(913, true);

    /* LED用のGPIO913(MIO7)を出力にする。初期値は1(High) */
    gpio_direction_output(913, 1);

    /* LED用のGPIO913(MIO7)に1(High)を出力にする */
    gpio_set_value(913, 1);

    return 0;
}


static void __exit mymodule_exit(void)
{
    printk("mymodule_exit\n");

    /* LED用のGPIO913(MIO7)に0(Low)を出力にする */
    gpio_set_value(913, 0);
}

module_init(mymodule_init);
module_exit(mymodule_exit);

image.ubを再作成して、SDカードに入れて起動します。mymodule.koをロード、アンロードします。そのタイミングでLED(LD4)がチカチカするはずです。今回は、手動でGPIOを有効にする必要はありません。

Zyboターミナル
root@SimplePS:~# modprobe mymodule
mymodule: loading out-of-tree module taints kernel.
mymodule_init
root@SimplePS:~# modprobe -r mymodule
mymodule_exit
root@SimplePS:~# modprobe mymodule
mymodule_init

おわりに

ここまでで、Zybo(Zynq)の基本的な使い方を理解できました。ひとまずこの連載を終わりにしようと思います。引き続きやるべき内容としては以下がありますが、どれもハマりそうなので、この「初心者ガイド」のスコープ外としました。

  • LANを使えるようにする
  • Linuxアプリのデバッグ環境
  • デバイスツリーの設定

LAN設定が出来たので、やっぱり続く ⇒ https://qiita.com/take-iwiw/items/ac489acaca1ab54d7ce8