2015年6月9日火曜日

mmapでドライバ的なコードを書くのもアリなのでは?

UIOをあきらめたので素直に通常のドライバを勉強しよう、と思いつつも、単なるIOポーリングくらいだとあちこちで紹介されているmmapを使った手抜きコードでもいいような気がしてきます。

http://www.sweetcafe.jp/?p=171

http://www.wiki.xilinx.com/Linux+User+Mode+Pseudo+Driver

windowsの感覚から考えるととんでもない機能ですが、しかしそもそもユーザー空間とカーネル空間というのはMMU (とCPUの特権レベル)で作り出している概念なので、MMUを使って正式に両者を繋げるようなアクセスはいんちき技というわけでもないようです。


もちろん、ドライバの枠組みを通らずにユーザーコードからメモリマップドIOを操作する(=ハードウェアにアクセスする)のは、ソフトの移植性やハードウェアの隠蔽レベルを下げてしまう可能性はあります。

が、そこはソフトを書く人次第ですから、mmapを使うコードをきちんとカプセル化して"ドライバ"であると意識してプログラミングすれば保守性は問題ありません。

というか、ユーザーコードとハードウェア制御が混ざってカプセル化が崩れるという心配は全く同じことがUIOを使った場合にも言えるので、UIOがアリなんだったらもはやmmapで作ったドライバを邪道扱いする理由は無い気がしてきました。




・・・と、言い訳を並べたところで性能計測

DigilentのチュートリアルでPL側に作ったLEDレジスタ(0x43C30000)をmmapで100万回リード、ライトして時間を計測しました。

  • mmap経由で8ビットライト x100万回 = 0.23sec  ⇒ 1アクセス 230nsec
  • mmap経由で8ビットリード x100万回 = 0.21sec  ⇒ 1アクセス 210nsec

と、なかなかの速度です。1バイトアクセスならPCIexよりはるかに早い!


このままPIOでバカみたいにデータ転送しても数MB/sec出るわけなので通信系ドライバ程度ならこれで十分な場合もありそう。


書き込み100万回のコードを貼り付けておきます(ひでみさんのコード丸パクリです)
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <fcntl.h>
#define SIZE 0x1000
#define BASE_ADRS 0x43C30000
#define REG_DATA 0x00
#define REG_TRI  0x01

void main(){
  int fd;
  unsigned int *buf;

  fd = open("/dev/mem", O_RDWR | O_SYNC);
  if (fd == -1) {
    printf("open failed");
    return;
  }

  buf = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, BASE_ADRS);
  if (buf == MAP_FAILED) {
    printf("mmap failed");
    return;
  }

  int cnt=0;
  int dat;
  while(cnt++ < 1000000){
    buf[REG_DATA] = 0x00;
    //sleep(1);
    buf[REG_DATA] = 0xFF;
    //sleep(1);
  }

  munmap(buf, SIZE);
  close(fd);
}
念のためopenのときにO_SYNCフラグを付けておきましたがこれの有無で特に動作、実行時間に変化は見られませんでした。

これを普通にコンパイルして、実行時に
time ./test2
で実行すると時間が計れます。

0 件のコメント:

コメントを投稿