UIOを用いてLinuxからIPコアへアクセスする

LinuxからIPコアへアクセスする方法は3つある。そのうち上位2つの方法は簡単に使用できるので重宝されているらしい。

  1. dev/memにより物理アドレスに直接アクセス
  2. UIOにより仮想アドレス
  3. デバイスドライバの作成し、レジスタにアクセスする

ただ、dev/memを用いる方法は、物理アドレスに直接アクセスできることから、予期せぬメモリ領域にデータを書き込んでしまい、カーネルやプロセスのメモリを破壊する恐れがある。実際に使ったことないのでわからないが。

そのため、上記の中ではUIOを使用する方法が良いらしいので、UIOの使い方の調査と実機を使った動作確認を行う。調査内容と実機確認内容を記載するとブログが長くなるので、理解編と実践編と2回に分ける。この記事は理解編である。

目次

UIOとは

UIO(User space I/O)は物理アドレスへアクセスするための仕組みで、Linux2.6.22から追加された機能らしい。UIOは割り込みハンドリングが可能であり、かつデバイスドライバ開発が不要であるという利点がある。その一方で、DMA転送には対応していないのが欠点らしい。

使用手順

UIOを用いてのIPコアアクセスは、以下の手順が必要となる。

  1. UIOドライバを有効化する
  2. IPコアをUIOデバイスに設定する
  3. Linuxから実行できるプログラム作成する

UIOドライバを有効化する

コンフィグのデフォルト設定ではUIOドライバが有効であるので、ここは読み飛ばしてもよい。

  • 以下のコマンドを実行し、カーネルコンフィグレーション画面を表示
make xilinx_zynq_deconfig
#MPSocの場合は、下記コマンド
#make xilinx_zynqmp_deconfig
make menuconfig
  • UserspcaeI/O platform driverwith IRQ handoling を有効化にする Device Drives > Userspace I/O drivers まで移動する。UserspaceI/O platform driver with generic IRQ handling にチェック、またはM<モジュール>が付いていればよい。

IPコアをUIOデバイスに設定する

今回は既存の.dtbファイルを.dtsファイルに逆変換し、UIOデバイスの設定を行う。そして、設定した.dtsファイルを.dtbファイルに変換し、デバイスツリーを再生成する 。

  • .dtbファイルを.dtsファイルへ逆変換 以下のコマンドを実行し、.dtsファイルをdtsファイルに変換する。
$ dtc -I dtb -O dts -o system.dts system.dtb
  • UIOデバイスの設定 下記のように、IPコアのcompatiblegeneric-uioに変更する。
Gpio@a0020000{
-  compatible  = "xlnx,GpioIP-1.1";
+  generic-uio = "generic-uio";
   reg = <0x 0xa0020000 0x0 0x1000>;
};
  • 編集した.dtsファイルを.dtbファイルに再変換 以下のコマンドを実行し、編集した.dtsファイルを.dtbファイルに再変換する。
$ dtc -I dts -O dtb -o system.dtb system.dts

Linuxから実行できるプログラム作成する

mmapUNIX OSシステムコールの一つで、ファイルに連続した仮想アドレス空間マッピングする関数である。LinuzからFPGAアドレス空間にアクセスするには、mmap関数とUIOを使用して、FPGAアドレス空間を仮想アドレス空間マッピングするプログラムを作成する。

手順
1. open関数を用いてデバイスをオープンする  open関数の戻り値と引数は以下である。

int open (const char *pathname, int flags)
/*
* @param pathname :オープンするファイルを指定する文字列
* @param flags :  O_RDONLY(0x),O_WRONLY(0x), O_RDWR(0x) のいづれかが入る。
* @return
*  - 正常:正の整数を返す
*  - 異常:-1を返す
/*/
  1. mmap関数により、物理アドレスから仮想アドレスへマッピングする。
void *mmap (void  *addr, size_t len,int prot, int flags, int fileds, off_t off);
/*
* @param  *addr : マッピングするアドレスを指定する。NULLを指定した場合、
*         カーネルがマッピングするアドレスを決める。
* @param  size_t len :マッピングするデータサイズを指定する。
* @param  int prot : マップ領域のアクセス権限を設定する。
* @param  int flags : マップ領域の扱いを設定する。MAP_PRIVATE とMAP_SHAREDの2つのフラグがある。
*            MAP_SHAREDがよく使用される。
* @param   int fileds : opne関数でオープンしたファイルを指定する。
* @param   off_t off : ファイルのどこからマッピングしたいのか、オフセットを指定
* @return
*  - 正常:マップ領域のポインタを返す
*  - 異常:MAP_FAILEDを返す
/*/
  1. close関数により、オープンしたファイルディスクリプタを閉じる。
close(fd);
/*
* @return
* - 正常:0を返す
* - 異常:-1を返す
*/
  1. munmap関数により、mmapでマップしたメモリを解放する。 munmap関数の引数と戻り値は以下である。
munmap(void *addr, size_t len);
/*
* @return
* - 正常:0を返す
* - 異常:-1を返す
*/

以上が理解編である。次回は、実践編でUIOを用いてLinuxからIPコアへアクセスできるか動作確認する。