ユーザー回路からインタラプト信号は、Qsysを利用してCyclone VのHPSに入力することができました。次に行うのは、インタラプト・ハンドラの動作確認です。当初、ベアメタルでインタラプトの動作を確認しようと考えていました。しかし、Cyclone VではNios IIの時のようなベアメタル開発環境(Eclipse Nios II + Altera API)が見当たらなかったので、Linuxで確認することにしました。Linuxのバージョンは3.13.0-00298-g3c7cbb9-dirtyです。これはDE0-Nano-SoCに付属のSDカードに格納されているLinuxのバージョンです。具体的な手順は次の通りです。
デバイスドライバの開発環境について
今回、Linuxデバイスドライバの開発はubuntu 14.04LTS 64bitで行いました。デバイスドライバの開発にはARM用のクロスコンパイラが必要になりますが、ubuntuではapt-getで簡単にインストールできるようです。具体的には、次のコマンドでクロスコンパイラをインストールできます。
$sudo apt-get install gcc-arm-linux-gnueabi
Linuxソースコードのダウンロード
Linuxのデバイスドライバを開発するには、ソースコードが必要になります。ソースコードはRocketBoard.orgからダウンロードできます。DE0-Nano-SoCに付属しているLinuxのバージョンは3.13.0-00298-g3c7cbb9-dirtyなので、同じバージョンのソースコードが欲しいのですが、見つからないので3.13.0-00298-g3c7cbb9をダウンロードしました。具体的な手順は次の通りです。
git clone git://git.rocketboards.org/linux-socfpga.git cd linux-socfpga git checkout -b socfpga origin/socfpga-3.13-rel14.0
次にLinuxをコンパイルします。
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm socfpga_defconfig make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm zImage
コンパイルしたLinuxのバイナリは今回は使用していません。ただ、コンパイルを行わないとデバイスドライバのコンパイルに必要なインクルードファイルなどが生成されないようなのでLinuxのコンパイルを行いました。
デバイスドライバの作成
次のような簡単なドライバでインタラプトの動作を確認します。72番のIRQが発生すると、”Interrupt 72 occured”と表示するだけのドライバです。
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #define INT_NUM 72 MODULE_LICENSE("Dual BSD/GPL"); static irqreturn_t int_test_interrupt(int irq, void *dev_id){ if (irq != INT_NUM) return IRQ_NONE; printk(KERN_ALERT "Interrupt %d occured\n",INT_NUM); return IRQ_HANDLED; } static int __init int_test_init(void){ int ret; ret = request_irq(INT_NUM, int_test_interrupt, 0, "int_test", NULL); if (ret < 0) return ret; printk(KERN_ALERT "int_test registed\n"); return 0; } static void int_test_exit(void){ free_irq(INT_NUM, NULL); printk(KERN_ALERT "exit from int_test\n"); } module_init(int_test_init); module_exit(int_test_exit);
コンパイルの前にLinuxのソースコードを一部書き換えます。これは、DE0-Nano-SoCのLinuxのバージョンとソースコードのバージョンが異なるためです。本当は、DE0-Nano-SoC用のLinuxのバージョンを合わせる方が正しいと思いますが、インタラプトハンドラの動作を確認したいだけなので、ソースコードを書き換えました。具体的には、linux-socfpga/include/generated/utsrelease.hを次のように書き換えます。
//#define UTS_RELEASE "3.13.0-00298-g3c7cbb9" #define UTS_RELEASE "3.13.0-00298-g3c7cbb9-dirty"
ソースコードのバージョンが異なると、insmodでデバイスドライバを登録する時に次のようなエラーが発生します。
root@socfpga:~# insmod int_test.ko int_test: version magic '3.13.0-00298-g3c7cbb9 SMP mod_unload ARMv7 p2v8 ' should be '3.13.0-00298-g3c7cbb9-dirty SMP mod_unload ARMv7 p2v8 ' Error: could not insert module int_test.ko: Invalid module
ドライバは、次のようなMakefileでコンパイルできます。この例ではLinuxのソースコードは~linux-socfpgaに格納されています。
obj-m := int_test.o all: make -C ~/linux-socfpga ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- M=$(PWD) modules clean: make -C ~/linux-socfpga ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- M=$(PWD) clean
テスト用アプリケーションの作成
次のようなアプリケーションでインタラプトの動作を確認しました。Qsysでユーザー回路からのインタラプトを入力したPIOのインタラプトマスクを解除後、my_system_init()でユーザー回路を初期化して無限ループを行うだけのアプリケーションです。Qsys PIOのレジスタ詳細は、Embedded Peripherals IP User Guideに記載があります。my_system_init()の内容は省略していますが、ユーザー回路を初期化してLCD表示用のV-syncのタイミングでインタラプトが発生するように設定を行っています。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <time.h> #include <sys/mman.h> #include "hwlib.h" #include "socal/socal.h" #include "socal/hps.h" #include "socal/alt_gpio.h" #include "hps_0.h" #include <stdbool.h> #include <errno.h> #define HW_REGS_BASE ( ALT_STM_OFST ) #define HW_REGS_SPAN ( 0x04000000 ) #define HW_REGS_MASK ( HW_REGS_SPAN - 1 ) volatile unsigned long *h2p_addr=NULL; volatile unsigned long *pio_addr=NULL; int main(int argc, char **argv) { void *virtual_base; int fd; printf("interrupt test\n"); if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) return 1; virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE ); if( virtual_base == MAP_FAILED ) { close( fd ); return 1; } pio_addr=virtual_base + ( ( unsigned long )( ALT_LWFPGASLVS_OFST + PIO_EXT_BASE ) & ( unsigned long)( HW_REGS_MASK ) ); alt_write_word(pio_addr+2, 0xff ); // PIOのマスク解除 my_system_init(); //ユーザー回路の初期化(VINTの発生) printf("start VGA\n"); while(1); return 0; }
Qsysで設定したPIOのアドレス・マップは、hps_0.hに格納されています。このファイルは、DE0-Nano-SoC付属のデモプロジェクトに格納されているgenerate_hps_qsys_header.shを実行することで生成されます。
DE0-Nano-SoCへの転送
DE0-Nano-SoCでLinuxを起動後、ユーザー回路のコンフィグレーションを行います。その後、ネットワーク経由でドライバやアプリケーションデータをDE0-Nano-SoCに転送します。
$ scp int_test.ko root@192.168.10.6:/home/root (DE0-Nano-SoCのIPが192.168.10.6の場合)
デバイスドライバの登録とアプリケーションの実行
insmodでデバイスドライバを登録後、テスト用のアプリケーションを実行します。72番のインタラプトの発生を確認できます。
socfpga login: root root@socfpga:~# insmod int_test.ko int_test registed root@socfpga:~#./int_test interrupt test start VGA Interrupt 72 occured Interrupt 72 occured :
まとめ
QsysでCyclone V HPSのf2h_irq0[0]に入力したインタラプト信号は、IRQ72のインタラプト・ハンドラで認識できることが確認できました。