ZynqのPLを操作するLinuxドライバ (ユーザプログラム編)
ZynqのPLを操作するLinuxドライバ (DTS設定編)の続き。これは見た方が早いと思うので、コードを示す。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int uiofd;
int err;
int i;
unsigned icount;
unsigned int* map_addr;
unsigned int cnt;
unsigned int one = 1;
uiofd = open("/dev/uio0", O_RDWR);
if (uiofd < 0) {
perror("uio open:");
return errno;
}
map_addr = (unsigned int*)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, uiofd, 0);
if (!map_addr){
fprintf(stderr, "mmap failed\n");
return errno;
}
write(uiofd, &one, 4); // enable interrupt to uio_pdrv_genirq
map_addr[0] = 0x0BEBC200; // 200M
map_addr[1] = 0; // disable
map_addr[2] = 1; // clear interrupt
map_addr[1] = 1; // enable
for(i = 0;; ++i) {
/* Print out a message, for debugging. */
if (i == 0)
fprintf(stderr, "Started uio test driver.\n");
else
fprintf(stderr, "Interrupts: %d,%x\n", icount, cnt);
/* Wait for next interrupt. */
err = read(uiofd, &icount, 4);
if (err != 4) {
perror("uio read:");
break;
}
cnt = map_addr[3];
map_addr[2] = 1; // clear interrupt
write(uiofd, &one, 4); // enable interrupt again
}
munmap(map_addr, 4096);
close(uiofd);
return errno;
}
要点は次の通り:
- /dev/uio0をオープンする
- デバイスレジスタにアクセスするためにuiofdに対してmmapする
- uiofdに対するreadで割り込み待ちが可能。読み込むバイト数は4にしなければならない
- readが戻ったときには、ドライバが割り込み禁止にしているので、write(uiofd, &one, 4)をすることで、再度割り込みを有効にする。
- mmapで得られた領域の先頭要素が、デバイスレジスタ領域の先頭に対応しているので、普通のメモリのように読み書きすると、デバイスレジスタを読み出し/書き込みできる。
以上を例によってZYBOにて実験してみたところ、デバイスの割り込みがHighになってから、おおよそ75マイクロ秒程度でreadが完了した(CPU負荷はデバッガを除いてほとんど無い状態。アプリケーションはデバッグビルド)。それほど厳しい制御が必要でない場合は、この程度の応答性でも十分実用になりそうだ。