- 1回目: 開発環境の準備
- 2回目: Hello Worldプロジェクト
- 3回目: PSのGPIOでLチカ
- 4回目: PLのAXI GPIOでPSからLチカ
- 5回目: PLだけでLチカ
- 6回目: 自作IPでLチカ
- 7回目: ブートイメージを作る
- 8回目: Linux起動する
- 9回目: Linuxカーネルを少しカスタマイズする
- 10回目: LinuxのRootFSをカスタマイズする / PythonでHello World
- 11回目: LinuxユーザアプリケーションでLチカ
- 12回目: LinuxカーネルモジュールでLチカ <--- 今回の内容
- 13回目: LAN(Ethernet 0)を使う
- 14回目: Linuxユーザアプリをデバッグする / RootFSに取り込む
- 15回目: Linux起動時にアプリケーションを自動実行させる
- 16回目: Linuxから自作IPをUIOで制御する
- 17回目: Linuxで自作IPのデバイスドライバを作る
- 18回目: IoT化してスマホから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回目と同じです。
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
が必要らしいが、無くても動いた)
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的なメッセージが出力されていることが分かります。
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を再作成するには、以下のようにします(エディタは適当に置き換えてください)。どういうコードにするかは後述。
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()
で物理アドレス->仮想アドレスに変換しています。
#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)がチカチカするはずです。
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を制御します。
#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を有効にする必要はありません。
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