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時間くらい無駄に悩んでいました。
![]()
newでユーザメモリ空間に256Mバイトのバッファを確保し、物理アドレス0x20000000に対して転送した場合の速度を測りました。
ZYNQのCPUからみれば、Linux管理下のメモリから読み出して、Linux管理外のメモリに転送するという、メモリ間の転送なのですが、書き込みは550~600MBytes/s、読み出しは200~240MByte/secという速度が出ました。
つまり、同じDDR3メモリから読み出して書いているので、実際にはこの2倍の速度で動いていることになるのでしょう。
なぜ読み書きで速度が異なるのかはわかりません。また、書き込む内容が全部ゼロよりも、乱数を書いておいたほうが読み出しが早くなる、という傾向がみられました。原因はわかりません。
| 固定リンク



コメント