#author("2022-05-28T01:13:26+00:00","","")
#author("2022-05-28T01:15:25+00:00","","")
[[zFIFO - an AXI DMA driver for Zynq and ZynqMP]]

* 性能チューニング [#x6b0a09e]

** クロック周波数の調整 [#zc4c0b77]

ZynqMP では、PS から供給される PL のクロックが fclk ドライバによって制御されており、sysfs から変更することができます。たとえば、
 $ echo 200000000 | sudo tee /sys/devices/platform/fclk0/set_rate
 $ cat /sys/devices/platform/fclk0/set_rate
 187499999
のような感じです。この例では 200MHz にセットして、実際には 187MHz ですが、これは PS の PLL の事情などもありますので、必ずしも希望したクロック周波数ぴったりになるとは限らないためです。周波数の調整は、PL の回路がアイドル状態ならいつ行っても大丈夫なようですが、気になる場合はリセットできるような仕掛けを用意しておいたり、周波数変更後に JTAG から書き込み直したりするといいかもしれません。

** AXI Stream バンド幅の変更 [#r55b593d]

PS の Slave AXI インタフェイスは ZynqMP では 128bit, Zynq-7000 では 64bit です。例題として配布している DMA loopback のデザインでは、ループバック用の FIFO のデータ幅が 32bit なので、ブロックデザインを開いて AXI DMA の Stream Data Width を 64bit や 128bit に変更すれば、理論上はより速い転送レートを得ることができます。このあたりは、下記の性能評価の結果なども参考にしつつ、実装するアプリケーションで使いやすいストリーム幅にするのがよいでしょう。

* 性能測定 [#qdbfc8a1]
 
#ref(bw.png);

この図は以下のようなコードを用いて、Ultra96 (無印) で測定を行ったものです。往復のバンド幅ですから、片方向の場合はグラフの縦軸の値を2で割ることになります。ピークのバンド幅はおおよそ、
- 100MHz 32bit: 680MB/s
- 100MHz 64bit: 970MB/s
- 150MHz 32bit: 1050MB/s
- 150MHz 64bit: 1110MB/s
- 250MHz 64bit: 1350MB/s

となります。クロック周波数を上げてもあまり性能が伸びないのは、Linux カーネル内部での仮想メモリ操作に関わるオーバーヘッドが大きいからのようです。設計のご参考にどうぞ。

#geshi(c,number){{
// Bandwidth tester

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <omp.h>

#include "zfifo.h"
#include "mysecond.c"

int main(){
  int fd = open("/dev/zfifo0", O_RDWR | O_SYNC);
  if (fd<0) {
    printf("Can't open /dev/zfifo0!\n");
    return -1;
  }

  unsigned maxsize = 8*1024*1024;
  unsigned *send = (unsigned*)malloc(maxsize * sizeof(unsigned));
  unsigned *recv = (unsigned*)malloc(maxsize * sizeof(unsigned));
  
  for (unsigned i=0; i<maxsize; i++){
    send[i] = i;
    recv[i] = 0;
  }

  for (int size=4; size<=maxsize; size*=2){
    int ntimes = maxsize / size;
    if (ntimes<  100) ntimes =  100;
    if (ntimes> 1000) ntimes = 1000;

    printf("size %d ntimes %d ", (int)size*sizeof(unsigned), (int)ntimes);

    double start = mysecond();
    for (int n=0; n<ntimes; n++){
    
      // Send & recv in parallel because FIFO deadlocks
#pragma omp parallel for
      for(int i=0; i<2; i++){
        if (i==0){
          // printf("Send on thread %d\n", omp_get_thread_num());
          zf_send(fd, (char*)send, sizeof(unsigned)*size);
        }
        if (i==1){
          // printf("Recv on thread %d\n", omp_get_thread_num());
          zf_recv(fd, (char*)recv, sizeof(unsigned)*size);
        }
      }
    }
    double stop = mysecond();
    double elapsed = stop-start;
  
    size_t total_size = ntimes * size * 2 * sizeof(unsigned);
    double throughput = (double)total_size/(1000*1000*elapsed);

    printf("%lf sec %lf MB/s\n", elapsed, throughput);

  }
  
  close(fd);
  return 0;
}
}}

Front page   Edit Diff History Attach Copy Rename Reload   New Page list Search Recent changes   Help   RSS of recent changes