ユーザレジスタの操作方法

ユーザモジュールの追加

次にユーザ作成のモジュールを増設されたAXIポートに追加します。

ここでは、ユーザ作成モジュールの代わりにaxi_gpioという汎用的なIPコアで説明します。

 

 

追加されたモジュールのAXI_ACLKとAXI_ARESETNは、インタコネクトのaclkとaresetnに合わせておきます。上の図ではAXI GPIOはインタコネクトのM01_AXIに接続されているので、インタコネクトのM01_ACLKとAXI GPIOのs_axi_aclkは同じものを使用します。

 

アドレスの割り当て

AXI GP1は0x80000000~0xBFFFFFFFの1Gバイトのアドレス空間を使用できます。この広大な空間にはレジスタを配置することもメモリを配置することもできます。

ユーザが作成したモジュールをどのアドレスに割り当てるかは基本的に自由です。VivadoのAddress Editorで自動配置を行うと、使用されていないアドレスの範囲に適当に割り当てられます。

ユーザ作成モジュールをソフトウェアから操作するにはデバイスドライバが必要になりますが、デバイスドライバに新規モジュールを認識させるにはデバイスツリーの書き換えなど、面倒な作業が必要になります。

 

ところで、Cosmo-Zのレジスタ(reg_files)は0xB8800000~0xB8BFFFFFの4Mバイトを割り当てていますが、基本機能では先頭の512バイト程度しか使用していません。

そこで、Cosmo-Zのレジスタで使用している範囲を減らし、その空いた領域にユーザ作成モジュールを配置するとデバイスツリーの設定を変更することなくユーザ作成モジュールを使用することができるようになります。

 

 

 

次の図に示すAddress Editorの設定では、Cosmo-Zのレジスタファイルを先頭0xB8800000からの64kバイトに減らし、続く0xB8810000からの64kバイトにaxi_gpioを配置しています。

 

デバイスドライバの構成

ユーザ追加モジュールを扱うにはUIOという汎用ドライバを使います。

UIOを使用するにはデバイスツリーでメモリの先頭アドレスと範囲を書いておく必要がありますが、あらかじめCosmo-ZではUIO用に2つの範囲を確保しています。

 

amba_pl {
                #address-cells = <0x01>;
                #size-cells = <0x01>;
                compatible = "simple-bus";
                ranges;

                cosmoz-reg@b8800000 {
                        compatible = "generic-uio";
                        reg = <0xb8800000 0x400000>;
                };

                cosmoz-mem@20000000 {
                        compatible = "generic-uio";
                        reg = <0x20000000 0x20000000>;
                };
        };
};

 

1つ目が、0xb8800000からの4MByteで、これは/dev/uio0に割り当てられています。

2つ目が、0x20000000からの512MByteで、これは/dev/uio1に割り当てられています。

 

ユーザ定義モジュールを追加する場合は、デバイスツリーに3個目の範囲を指定すればよい(dev/uio2となる)のですが、デバイスツリーの書き換えはそれなりに面倒な作業となります。

cosmoz-reg@b8800000からの範囲にユーザモジュールを配置すれば、デバイスツリーの書き換えは不要となります。もともとCosmo-Zのレジスタは先頭の1kバイト程度しか使用していないので、b8800000~b880ffffをCosmo-Z用とし、b8810000~b881ffffをユーザ拡張モジュール用とすればデバイスドライバの書き換えさえ不要となります。

 

そのため、このページの手順ではデバイスツリーの書き換えは不要です。

 

ソフトウェアの書き方

UIOを使うサンプルプログラムの書き方を示します

#include
#include
#include
#include
#include
#include
#include
#include

int main() {
    int fd_regs = open("/dev/uio0",O_RDWR);
    unsigned long *regs;
    if(!fd_regs) {
        printf("Can not open /dev/uio0\n");
        return 1;
    }
    regs = (unsigned long *)mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd_regs, 0);
    for(int i=0;i<6;i++) {
        printf("FPGA reg(%x) data=%08lx\n",i,regs[i]);
    }
    regs[0] = 0x123;
    munmap(regs,0x10000);
    close(fd_regs);
    return 0;
}

 

UIOはopen("/dev/uio0",O_RDWR)で開きます。

開いたハンドル(fd_regs)でmmapを呼び出してポインタ(regs)を得ます。

mmapの第二引数(0x10000)は確保したいアドレスのサイズを示します。

確保される先頭アドレスはデバイスツリーで設定した値で、0xb8800000となります。

mmapの戻り値のregsはポインタであって、regs[]や*regsを通じて読み書きすることでAXIバスに読み書きトランザクションが発生します。

例えば、regs[0]は0xb8800000をアクセスし、regs[1]は0xb8800004をアクセスします。

 

ユーザ拡張モジュールを0xb8810000に配置したのであれば、ユーザ拡張モジュールの先頭にあるレジスタはregs[16384]でアクセスすることになります。このアドレスのずれを避けるには、uioの設定変更を行います。

 

UIOの設定変更

uioの3番目である/dev/uio2を設定するには、デバイスツリーの書き換えが必要です。

Cosmo-Zのデバイスツリーはバイナリファイルで、/mnt/devicetree.dtb です。

このファイルは直接変更できないので、Cosmo-Z上でdtcコマンドを使用して、dtbファイルをソースファイルに戻します。

 

まず、適当なディレクトリへ移動して、

dtc -I dtb -O dts /mnt/devicetree.dtb > devicetree.dts

とコマンドを打ち、dtsを生成します。

 

次に、devicetree.dtsを編集して以下の記述を追加します。(ソースの最後のほうにある)

 

                cosmoz-user@0xb8810000 {
                        compatible = "generic-uio";
                        reg = <0xb8810000 0x00010000>;
                };

 

それから、dtcコマンドを使用してデバイスツリーソースをコンパイルしてdtbを作ります。

dtc -I dts -O dtb devicetree.dts > devicetree.dtb

 

バイナリからソースへの変換と、ソースからバイナリへの変換は、-Iオプションと-Oオプションの引数が変わるので注意してください。dtbファイルの生成に失敗して壊れたdtbを書き込むとシステムが起動しなくなるので注意してください。

確実にdtbファイルが生成できたと思ったら、

cp devicetree.dtb /mnt/devicetree.dtb

で書き込んで再起動してください。