« ST39VF801CというROMのCFIは間違っている | トップページ | 1GspsのADCボードのひずみ »

2017.10.09

ZYNQのPSからのDDR3メモリアクセス速度

ZYNQ搭載のADCボード「Cosmo-Z」用の、汎用計測ファームウェア V2.0を開発しています。

いままでは、root権限でプログラムを動かして、/dev/memを開いてメインメモリを直接操作していたのですが、これでは一般ユーザが使えません。

そこで、デバイスドライバをちゃんと作って、一般ユーザの権限でもソフトウェアを使えるようにしようとしています。

Linuxのデバイスドライバでは、FPGAやDDR3メモリなどのリソースの物理アドレスがわかっていれば、

request_mem_region(物理アドレス, サイズ ,name);

でメモリマップI/Oの空間を予約して、

void *__iomem vaddr = ioremap(物理アドレス, サイズ);

で仮想アドレスを割り当てます。

割り当てられた仮想アドレスに対して読み書きすれば、指定した物理アドレスに値が読み書きされます。

ぶっちゃけ、まどろっこしいことせずに memcpy(vaddr, user_buf, 100); とやれば、DDR3メモリやFPGAのレジスタに書き込みができてしまうのですが、割り当てた物理アドレスに存在したリソースがページアウトされている可能性があるので、安全なプログラムとはいえません。

まぁ、OS管理外のDMA用メモリなのでページアウトされることはないのですが、カーネルではmemcpyは使ってはいけないことになっているようなので、正しく作らなければなりません。

カーネルモードで、ユーザバッファとの間でデータをコピーするには、copy_from_user()や、copy_to_user()といった関数を使います。

copy_to_userは、カーネルのバッファからユーザのバッファにデータをコピーする際に使います。

remain = copy_to_user(buf, p_dev->mapped_capture_addr + p_dev->offset, count);

この関数の戻り値は0なら成功です。0でなければ、コピーできなかったバイト数を返すという仕様なのですが、今日、実験しているときに0でない値が帰ってくることがありました

ネットを探しても、copy_to_userで失敗する人はそういません。原因は単純なミスでした。

newで確保したバッファのサイズ以上の転送をしようとしたのです。具体的には、

unsigned char buf = new unsigned char[4096];

で確保したバッファに対して16Mバイトくらいの転送をしようとしていたのです。

この単純なミスに気が付かずに、ページサイズの関係は原因かも、とか、copy_to_userのアセンブラソースを読んだりして8時間くらい無駄に悩んでいました。

sun

newでユーザメモリ空間に256Mバイトのバッファを確保し、物理アドレス0x20000000に対して転送した場合の速度を測りました。

Cosmozddr3

ZYNQのCPUからみれば、Linux管理下のメモリから読み出して、Linux管理外のメモリに転送するという、メモリ間の転送なのですが、書き込みは550~600MBytes/s、読み出しは200~240MByte/secという速度が出ました。

つまり、同じDDR3メモリから読み出して書いているので、実際にはこの2倍の速度で動いていることになるのでしょう。

なぜ読み書きで速度が異なるのかはわかりません。また、書き込む内容が全部ゼロよりも、乱数を書いておいたほうが読み出しが早くなる、という傾向がみられました。原因はわかりません。

|

« ST39VF801CというROMのCFIは間違っている | トップページ | 1GspsのADCボードのひずみ »

コメント

コメントを書く



(ウェブ上には掲載しません)




« ST39VF801CというROMのCFIは間違っている | トップページ | 1GspsのADCボードのひずみ »