LinuxにおいてZynqのPL部に作成したレジスタを操作するための方法について調査したところ、カーネルドライバを作成する以外に、UIOという枠組みを使用することで簡易に行うことができるということが分かった。これを使用すると、ユーザプログラムでレジスタの読み書きだけではなく、割り込みの検出も行うことが出来る。

まず、カーネルコンフィグにおいてUIOを有効にしてビルドする必要がある。

CONFIG_UIO=y
CONFIG_UIO_PDRV_GENIRQ=y

あたりが最低限必要と思われる。(自分の場合は元から有効になっていたので、カーネルの再ビルドは不要だった。)

次に、デバイスツリーに次のような記述を追加する。自分の場合では、arch/arm/boot/dts/zynq-zed.dtsのamba@0ノードの一番下に追加した。

foo_bar_device: foo-bar-device@44000000 {
                compatible = "generic-uio";
                reg = < 0x44000000 0x4000 >;
                interrupts = < 0 29 4 >;
                interrupt-parent = <&ps7_scugic_0>;
};

まず、compatibleでgeneric-uioと記述する。これが、あらかじめ用意されているカーネル側のUIOドライバを使用するための文字列。こうすることで、drivers/uio/uio_pdrv_genirq.cが使用される。

次に、regにおける、0x44000000というのが、ARMから見えるレジスタの先頭アドレス(IP IntegratorのAddress Mapで定義するアドレス)。0x4000というのはレジスタ空間のサイズ。ここでは4KB。

デバイスが割り込みを生成する場合、interrupts, interrupt-parentの記述を行う。< 0 29 4 >というのは、それぞれSPI(CPU共有割り込み。非0でPPI)、割り込み番号61(SPIの場合、この値に32を加えたものが実際の割り込み番号なので、29+32=61となる。これはFPGA0割り込み)、 Highのレベル割り込み、を意味しているらしい。

最初に試したときは、先頭を1にしていたが、これだとuio_register_deviceに失敗してデバイスドライバが登録されていなかった。多分割り込み番号が範囲外になってしまうからだろう。多くの場合では、2番目の割り込み番号だけの変更で済むはず。

interrupt-parentは別のところで定義しているGICを示す。

以上のDTSをコンパイルしDTBにして、カーネルに読ませれば、起動時に/dev/uio0のようなデバイスファイルができるはず。これでユーザプログラムからアクセスする準備が出来た。

長くなってきたので、まずはここまで。次の記事でユーザ側プログラムの例を示す。

参考にしたURL:

How to Design and Access a Memory-Mapped Device in Programmable Logic from Linaro Ubuntu Linux on Xilinx Zynq on the ZedBoard, Without Writing a Device Driver – Part One

How to Design and Access a Memory-Mapped Device in Programmable Logic from Linaro Ubuntu Linux on Xilinx Zynq on the ZedBoard, Without Writing a Device Driver — Part Two
 
Interrupt definitions in DTS (device tree) files for Xilinx Zynq-7000 / ARM

The Userspace I/O HOWTO
 

Comments