FC2カウンター FPGAの部屋 2015年06月
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較

ラプラシアンフィルタのVivado HLS 2014.4 でのCソースコードの最適化、ディレクティブの追加による速度の違いについては”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化14(性能が最大になる設定を探る7、まとめ)”でまとめてある。これはZYBOを使用している

ZYBOのソフトウェアで実装したラプラシアンフィルタの実行速度は”ZYBO用Ubuntu Linux のカメラで撮った画像にラプラシアンフィルタをかける”で紹介したが、455 ms 程度だった。
しかし、これはソフトウェアの実装したCソースコードを使用している。Vivado HLS 2014.4 で使用したCソースコードは異なるものだ。
そこで、なるべくVivado HLSで使用したCソースコードを使用したらソフトウェアでは、どのくらい速くなるのかやってみた。
なお、”RTLを語る会(9) ~サヨナラHDL、Alteraよさらば!~”でご指摘を受けて、なるほど、そうだな。。。ということでやって見ることにした。ソフトウェアも高位合成のCソースコードに進化に合わせて比較してみる必要があるはず。。。
hanetsuki_y さん、ご指摘ありがとうございました。

なお、初期設定時間の違いを除いたラプラシアンフィルタの処理のみの経過時間を比較する。

ZYBO用Ubuntu Linux のカメラで撮った画像にラプラシアンフィルタをかける”の計測時間は初期設定時間も入っているので、初期設定時間を除いたラプラシアンフィルタ処理のみの時間を測定した。下に示す。
laplacian_filter がソフトウェア全体の処理時間、laplacian_filter1 が初期設定時間を除いたラプラシアンフィルタ処理のみの時間だ。
laplacian_filter1 はlaplacian_filter よりもわずかに短い。laplacian_filter は449 ms 程度、laplacian_filter1 は448 ms程度だ。
(2015/07/03: 値を修正)
soft_hard_comp_1_150629.png

最初に”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化14(性能が最大になる設定を探る7、まとめ)”の”1.ディレクティブを与えていない状態でのラプラシアンフィルタの処理時間を示す。この時には、FCLK_CLK0からすべてのIP にクロックが供給されている。”の高位合成用のCソースコードをソフトウェア用Cソースコードに組み込んだ。下にその全ソースコードを示す。
2015/07/12:修正 時間計測にバグがあったので、修正しました。miyox さん、ありがとうございました)

// laplacian_filter2.c
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/09/16
// 2014/12/04 : ZYBO用Ubuntu Linux のUIO用に変更
// Vivado HLS 2014.4 のプロジェクト http://marsee101.blog19.fc2.com/blog-entry-3102.html

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define CMA_START_ADDRESS           0x17800000
#define VIDEO_BUFFER_START_ADDRESS  0x18000000  // Limit 0x18800000, 800*600*4 = 2MBytes * 2
#define LAPLACIAN_FILTER_ADDRESS    0x18200000  // 800*600*4 = 0x1d4c00

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);
int chkhex(char *str);

int main()
{
    volatile unsigned int *cam_fb = 0;
    volatile unsigned int *lap_fb = 0;
    volatile unsigned int *cam_addr;
    volatile unsigned int *lap_addr;
    int lap_fil_val;
    int x, y;
    struct timeval start_time, temp1, temp2, end_time;
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    int a, b;
    int fl, sl, tl;
    int fd0, fd3;
      unsigned int offset_cam_addr, offset_lap_addr;
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    volatile unsigned int *cam_fb_addr, *lap_fb_addr;
    int line_sel;
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *frame_buffer;

    // gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録

    // frame_buffer にマップする
    fd3 = open("/dev/uio3", O_RDWR); // Frame Buffer
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }
    cam_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(VIDEO_BUFFER_START_ADDRESS-CMA_START_ADDRESS));

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    lap_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(LAPLACIAN_FILTER_ADDRESS-CMA_START_ADDRESS));

    offset_cam_addr = (volatile unsigned int)((unsigned int)cam_addr/sizeof(int));
    offset_lap_addr = (volatile unsigned int)((unsigned int)lap_addr/sizeof(int));
    
    gettimeofday(&start_time, NULL);
    
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
   for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        sl = y%3;        // 2番めのライン
        tl = (y+1)%3;    // 3番目のライン
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                 if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            cam_fb_addr = (int*)(cam_fb+offset_cam_addr+(a*(HORIZONTAL_PIXEL_WIDTH)));
                            memcpy(&line_buf[a][0], (const int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                        cam_fb_addr = (int*)(cam_fb+offset_cam_addr+((y+1)*(HORIZONTAL_PIXEL_WIDTH)));
                         memcpy(line_buf[(y+1)%3], (const int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                            line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                        }
                    }
                }
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(y*(HORIZONTAL_PIXEL_WIDTH)));
        memcpy((void *)lap_fb_addr, (const int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
    
    gettimeofday(&end_time, NULL);
    
    munmap((void *)frame_buffer, 0x1000000);
 
   // ラプラシアンフィルタ表示画面に切り替え
    // Bitmap Display Controller AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // bitmap_display_controller axi4 lite
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 open error\n");
        exit(-1);
    }
    bmdc_axi_lites = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!bmdc_axi_lites){
        fprintf(stderr, "bmdc_axi_lites mmap error\n");
        exit(-1);
    }
    bmdc_axi_lites[0] = (unsigned int)LAPLACIAN_FILTER_ADDRESS; // Bitmap Display Controller start (ラプラシアンフィルタ表示画面のアドレス)
    munmap((void *)bmdc_axi_lites, 0x10000);
    
    //gettimeofday(&end_time, NULL);
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}


./laplacian_filter2 を実行した時に実行時間は 313 ms だった。Vivado HLS 2014.4 を使用してハードウェア化した時のラプラシアンフィルタ処理のみの時間は、80 ms だった。
よって、ハードウェア化シタ場合のラプラシアンフィルタの実行速度は、ソフトウェアで実行した場合の 313 ms / 80.0 ms ≒ 3.91 倍になったことがわかった。
soft_hard_comp_2_150629.png

次に、2.ラプラシアンフィルタのCソース中の割り算をカウンタで置き換えて、2箇所にPIPELINEディレクティブを追加した状態でのラプラシアンフィルタの処理時間を示す。この時にも、FCLK_CLK0からすべてのIP にクロックが供給されている。”場合との比較をしてみよう。ソフトウェアでは、単にラプラシアンフィルタのCソース中の割り算をカウンタで置き換えただけになる。これを laplacian_fitelr3.c とした。
これをgcc でコンパイルして、実行し、ラプラシアンフィルタ処理のみの時間を測定した。上の図の ./laplacian_filter3 を実行した時の時間がそれになる。その実行時間は、279 ms となり、laplacian_filter2 よりも 34 ms 高速になった。
Vivado HLS 2014.4 を使用してハードウェア化した時のラプラシアンフィルタ処理のみの時間は、60 ms だった。但し、PIPELINEディレクティブを追加してある。
よって、ハードウェア化した場合のラプラシアンフィルタの実行速度は、ソフトウェアで実行した場合の 279 ms / 60.0 ms = 4.65 倍になった。

laplacian_filter3.c を下に示す。
2015/07/12:修正 時間計測にバグがあったので、修正しました。miyox さん、ありがとうございました)

// laplacian_filter3.c
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/09/16
// 2014/12/04 : ZYBO用Ubuntu Linux のUIO用に変更
// Vivado HLS 2014.4 のプロジェクト ZYBO/lap_filter_axim_2014_4を使用したソースコードと同じものを使用する

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define CMA_START_ADDRESS           0x17800000
#define VIDEO_BUFFER_START_ADDRESS  0x18000000  // Limit 0x18800000, 800*600*4 = 2MBytes * 2
#define LAPLACIAN_FILTER_ADDRESS    0x18200000  // 800*600*4 = 0x1d4c00

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);
int chkhex(char *str);

int main()
{
    volatile unsigned int *cam_fb = 0;
    volatile unsigned int *lap_fb = 0;
    volatile unsigned int *cam_addr;
    volatile unsigned int *lap_addr;
    int lap_fil_val;
    int x, y;
    struct timeval start_time, temp1, temp2, end_time;
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    int a, b;
    int fl, sl, tl;
    int fd0, fd3;
      unsigned int offset_cam_addr, offset_lap_addr;
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    volatile unsigned int *cam_fb_addr, *lap_fb_addr;
    int line_sel;
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *frame_buffer;

    // gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録

    // frame_buffer にマップする
    fd3 = open("/dev/uio3", O_RDWR); // Frame Buffer
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }
    cam_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(VIDEO_BUFFER_START_ADDRESS-CMA_START_ADDRESS));

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    lap_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(LAPLACIAN_FILTER_ADDRESS-CMA_START_ADDRESS));

    offset_cam_addr = (volatile unsigned int)((unsigned int)cam_addr/sizeof(int));
    offset_lap_addr = (volatile unsigned int)((unsigned int)lap_addr/sizeof(int));
    
    gettimeofday(&start_time, NULL);
    
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0, line_sel=0; y<VERTICAL_PIXEL_WIDTH; y++){
        // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        switch(line_sel){
            case 1 :
                fl = 0; sl = 1; tl = 2;
                break;
            case 2 :
                fl = 1; sl = 2; tl = 0;
                break;
            case 3 :
                fl = 2; sl = 0; tl = 1;
                break;
            default :
                fl = 0; sl = 1; tl = 2;
        }

        //fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        //sl = y%3;        // 2番めのライン
        //tl = (y+1)%3;    // 3番目のライン
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                 if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            cam_fb_addr = (int*)(cam_fb+offset_cam_addr+(a*(HORIZONTAL_PIXEL_WIDTH)));
                            memcpy(&line_buf[a][0], (const int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){
#pragma HLS PIPELINE
 // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                        cam_fb_addr = (int*)(cam_fb+offset_cam_addr+((y+1)*(HORIZONTAL_PIXEL_WIDTH)));
                         memcpy(line_buf[tl], (const int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){
#pragma HLS PIPELINE
 // ライン
                            line_buf[tl][b] = conv_rgb2y(line_buf[tl][b]);    // カラーから白黒へ
                        }
                    }
                }
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(y*(HORIZONTAL_PIXEL_WIDTH)));
        memcpy((void *)lap_fb_addr, (const int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));

        line_sel++;
        if (line_sel > 3){
            line_sel = 1;
        }
    }
    
    gettimeofday(&end_time, NULL);
    
    munmap((void *)frame_buffer, 0x1000000);
 
   // ラプラシアンフィルタ表示画面に切り替え
    // Bitmap Display Controller AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // bitmap_display_controller axi4 lite
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 open error\n");
        exit(-1);
    }
    bmdc_axi_lites = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!bmdc_axi_lites){
        fprintf(stderr, "bmdc_axi_lites mmap error\n");
        exit(-1);
    }
    bmdc_axi_lites[0] = (unsigned int)LAPLACIAN_FILTER_ADDRESS; // Bitmap Display Controller start (ラプラシアンフィルタ表示画面のアドレス)
    munmap((void *)bmdc_axi_lites, 0x10000);
    
    //gettimeofday(&end_time, NULL);
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}


Vivado HLS 2014.4 で高位合成する用のCソースコードはソフトウェアのCソースコードとは明らかに違うと思う。普通、memcpy() とか使うと思わないよね?
だから、ソフトウェアのCソースコードのラプラシアンフィルタのみの処理時間とVivado HLS 2014.4 でIP 化したハードウェアのラプラシアンフィルタのみの処理時間を比較してもよいのじゃないかと車を運転しながら、そう思った。比較してみよう。
ソフトウェアのCソースコードのラプラシアンフィルタのみの処理時間は、laplacian_filter1 の処理時間と laplacian_filter2, laplacian_filter3 の処理時間を比較してみよう。

・laplacian_filter1 の処理時間と laplacian_filter2 の処理時間の比較
448 ms / 80.0 ms = 5.60 倍

・laplacian_filter1 の処理時間と laplacian_filter3 の処理時間の比較
448 ms / 60.0 ms ≒ 7.47 倍

memcpy() を使わないとAXI4バスのバースト・アクセスが使えないので、当然Vivado HLS では、それを使ってCソースコードを書く。それをソフトウェアとしてみた時に、私は違和感を覚える。でも、それはソフトウェアとしても動くし、ラプラシアンフィルタの処理が正常にできる。
ソフトウェアとハードウェアの比較はどのレベルで行うのが良いのか?悩む。
そう言えば、やっていないことがあった。”ZYBO用Ubuntu Linux のカメラで撮った画像にラプラシアンフィルタをかける”のCソースコードでVivado HLS 2014.4 でIP 化してみることだ。memcpy() を使っていないので、AXI4バスはバースト・アクセスが出来ないと思うが、果たしてどのくらいの性能が出るものか?とっても興味がある。
  1. 2015年06月29日 06:45 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのドライバによるレジスタの設定値2

AXI VDMAのドライバによるレジスタの設定値”の続きというか、前回はディスプレイに画像が表示されない状態でデバックのためにドライバを使用してAXI VDMA を設定した時のWrite/Read したレジスタ番地とその値を調べた。
今回は、動作する状態になった時のWrite/Read したAXI VDMAのレジスタ番地とその値を調べる。

今回は、”#define NUMBER_OF_WRITE_FRAMES 3”になっていて、フレームバッファが3面になっている。

最初にAXI VDMAのレジスタ設定波形の全体像を示す。
V_ZYBO_CAMD_151_94_150628.png

波形を拡大する。最初の波形を示す。
V_ZYBO_CAMD_151_95_150628.png

Write : 0x30 番地に 0x4 をWrite した。これは、S2MM_VDMACR (S2MM VDMA Control Register – Offset 30h) が 0x4 なので、Reset のビットのみ立っている。リセットしているようだ。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Write : 0x30 番地に 0x4 をWrite した。これは、S2MM_VDMACR (S2MM VDMA Control Register – Offset 30h) が 0x4 なので、Reset のビットのみ立っている。リセットしているようだ。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

V_ZYBO_CAMD_151_96_150628.png

Write : 0x48 に 0x1 をWrite した。0x48は、”AXI Video Direct Memory Access v6.2 LogiCORE IP Product Guide Vivado Design Suite PG020 April 1, 2015”では、Reserved となっているが、古いAXI VDMAのPG020、v5.00.a では、S2MM Frame Store Register (S2MM_FRMSTORE) と設定されていたので、その名残なのかな?

V_ZYBO_CAMD_151_97_150628.png

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Write : 0x30 番地に 0x00010002 をWrite した。これは、S2MM_VDMACR のIRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。リセット解除?

Write : 0xA4 番地に 0x00000c80 を Write した。 S2MM Horizontal Size Register(S2MM_HSIZE)。10進数で3200 = 800 x 4バイト。

Write : 0xA8 番地に 0x00000c80 を Write した。 S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)。10進数で3200 = 800 x 4バイト。

V_ZYBO_CAMD_151_98_150628.png

Write : 0xAC 番地に 0x10000000 をWrite した。S2MM Start Address (1 to 16) の最初のスタートアドレスに 0x10000000 を書いた。これは自分で定義した値でDDR3 SDRAMのアドレス範囲の真ん中辺りだ。

Write : 0xBO 番地に 0x101D4C00 をWrite した。S2MM Start Address (1 to 16) の2番目のスタートアドレスに次のフレームバッファのアドレスの 0x101D4C00 を書いた。

Write : 0xB4 番地に 0x103A9800 をWrite した。S2MM Start Address (1 to 16) の2番目のスタートアドレスに次のフレームバッファのアドレスの 0x103A9800 を書いた。

V_ZYBO_CAMD_151_99_150628.png

この区間にはAXI VDMAへのアクセスが無い。

V_ZYBO_CAMD_151_100_150628.png
Read : 0x34番地をRead した。 S2MM VDMA Status Register(S2MM_VDMASR)。値は0x00010001 で、IRQFrameCntSts が 0x1、 Halted が 1 となっている。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Write : 0x30 番地(S2MM_VDMACR)に 0x00010003 をWrite した。これは、IRQFrameCount が 0x1、Circular_Park が 1 、Run/Stop が 1 で後は全て 0 だった。

Read : 0x34番地をRead した。 S2MM VDMA Status Register(S2MM_VDMASR)。値は0x00010000 で、IRQFrameCntSts が 0x1のみだった。 Halted の 1 が解除されていた。

V_ZYBO_CAMD_151_101_150628.png

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010003 で、IRQFrameCount が 0x1、Circular_Park が 1 、Run/Stop が 1 で後は全て 0 だった。

Write : 0xA0番地(S2MM Vertical Size (S2MM_VSIZE – Offset 0xA0))をWrite した。値は0x00000258 で10進数で 600 ラインだ。

V_ZYBO_CAMD_151_102_150628.png

この区間にはAXI VDMAへのアクセスが無い。

結局、S2MM Start Addressに3回書いて、3つのフレームバッファのアドレスを書いただけの差だった。
S2MM Frame Store Register は廃止されていて、フレームバッファ・アドレスに書いたレーム・スタート番地の数が画像フレームの数になるのかもしれない?

AXI VDMAの設定値サンプル
sentVDMA.txt
video_frame_buffer.c
Lab 3: Rendering Video for Space Invaders via the VGA Controller
PASTEBIN
  1. 2015年06月28日 06:04 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム11(ベアメタル・アプリ版完成)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム10(ラプラシアンフィルタ処理)”の続き。

前回はAXI4-Stream版のラプラシアンフィルタ処理を行ったところ、エッジが青く表示されてしまった。これはAXI4-Stream版のラプラシアンフィルタIP のバグだったので、修正を行った。

修正したのは、以下の記事だ。
Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する1(C++ ソースコードの公開)
Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する2(C++シミュレーション、高位合成)
Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する3(RTLシミュレーション)
Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する4(RTLシミュレーション2)
Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する5(IP化)

計算したラプラシアンフィルタの値をRGBに振り分けるようにした。
これでV_ZYBO_CAMDS_151 のlap_filter_axis_0 のIP を書き換えて、Vivado 2015.1を起動して、IPをアップデートして論理合成、インプリメント、ビットストリームの生成を行った。
ハードウェアをエクスポートして、SDKを起動した。
SDKからビットストリームをZYBOにダウンロードし、cam_disp アプリケーションを起動したところ、無事にカメラ画像が表示できた。
次に、lap_fil_on アプリケーションを起動したところ、白いエッジのラプラシアンフィルタ画像になった。
V_ZYBO_CAMD_151_94_150627.jpg

元画像は例によって、これだ。
V_ZYBO_CAMD_151_95_150627.jpg

これでベアメタル・アプリケーション版のAXI4-Stream版ラプラシアンフィルタIPのカメラ表示システムは完成だ。
  1. 2015年06月27日 05:26 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム10(ラプラシアンフィルタ処理)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム9(カメラ画像表示は完成)”の続き。

AXI VDMAを使用してカメラ画像をディスプレイに表示することができた。今回は2つのAXI4-Stream Switch を切り替え、ラプラシアンフィルタIPの連続処理を設定して、ラプラシアンフィルタ処理後の画像をディスプレイに表示する。

サブモジュールの camera_interface を示す。
V_ZYBO_CAMD_151_45_150604.png

カメラ画像を表示する場合は、axis_switch_1 でカメラ画像のストリームをM00_AXISへスイッチして、そのままaixs_switch_0 でS00_AXIS をスイッチし、AXI VDMAへ渡しているわけだ。
ラプラシアンフィルタIPを通す時は、axis_switch_1 でカメラ画像のストリームをM01_AXISへスイッチして、lap_filter_axis_0 を通ってから、aixs_switch_0 でS01_AXIS をスイッチし、AXI VDMAへ渡す。

ラプラシアンフィルタ処理を行うためのCソースファイル、lap_fil_on.c を下に示す。

// lap_fil_on.c
// 2015/06/17 by marsee
//
// Refered to http://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2014_4/ug902-vivado-high-level-synthesis.pdf
//

#include <stdio.h>
#include "xlap_filter_axis.h"
#include "xparameters.h"

int main(){
    XLap_filter_axis Xlap_fil_axis;
    XLap_filter_axis_Config *Xlap_fil_axisPtr;

    // Look Up the device configuration
    Xlap_fil_axisPtr = XLap_filter_axis_LookupConfig(0);
    if (!Xlap_fil_axisPtr){
        fprintf(stderr, "XLap_filter_axis configuration failed.\n");
        return(-1);
    }

    // Initialize the Device
    int Xlap_status = XLap_filter_axis_CfgInitialize(&Xlap_fil_axis, Xlap_fil_axisPtr);
    if (Xlap_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XLap_filter_axis\n");
        return(-1);
    }

    // axis_switch_1, 1to2 ,Select M01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // laplacian filter AXIS Start
    XLap_filter_axis_Start(&Xlap_fil_axis);
    XLap_filter_axis_EnableAutoRestart(&Xlap_fil_axis);

    // axis_switch_0, 2to1, Select S01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x1);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    return(0);
}


これを実行したところ、青い色でエッジが見えた。
V_ZYBO_CAMD_151_91_150625.jpg

元画像はこれ。
V_ZYBO_CAMD_151_92_150625.jpg

青いエッジになってしまうのは何故か?というと、AXI4-Stream 版ラプラシアンフィルタIPのCソースファイルで、ラプラシアンフィルタ処理を行ったデータを各色RGBに振り分けなかったからだ。
下のAXI4-Stream 版ラプラシアンフィルタIPのVivado HLS 2014.4 のプロジェクトの lap_filter_axis.cpp を見ると、lap.data = の部分があるがラプラシアンフィルタ処理をした値をそのまま入れているからだ。
V_ZYBO_CAMD_151_93_150624.png

次はこのAXI4-Stream 版ラプラシアンフィルタIPのバグをFIXする。

最後に、ラプラシアンフィルタ処理から、カメラ画像表示に戻るソフトウェアの cam_return.c を貼っておく。

// cam_return.c
// 2015/06/25 by marsee
//

#include <stdio.h>
#include "xlap_filter_axis.h"
#include "xparameters.h"

int main(){
    XLap_filter_axis Xlap_fil_axis;
    XLap_filter_axis_Config *Xlap_fil_axisPtr;

    // Look Up the device configuration
    Xlap_fil_axisPtr = XLap_filter_axis_LookupConfig(0);
    if (!Xlap_fil_axisPtr){
        fprintf(stderr, "XLap_filter_axis configuration failed.\n");
        return(-1);
    }

    // Initialize the Device
    int Xlap_status = XLap_filter_axis_CfgInitialize(&Xlap_fil_axis, Xlap_fil_axisPtr);
    if (Xlap_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XLap_filter_axis\n");
        return(-1);
    }

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // laplacian filter AXIS AutoRestart disable
    XLap_filter_axis_DisableAutoRestart(&Xlap_fil_axis);

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    return(0);
}

  1. 2015年06月25日 04:49 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム9(カメラ画像表示は完成)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム8(デバック)”の続き。

AXI VDMAのドライバによるレジスタの設定値”で画像のフレームバッファのアドレスが 0 番地から始まっているのはまずいと思った。何故ならば、Zynqの 0x00000000番地から0x0003FFFF番地まではAXI_HPポートからはOCM (On Chip Memory) に割りあたっていて、0x0004000番地から0x0007FFFF番地までは何も割り当てられていないからだ。malloc() で取ったメモリのアドレスがおかしいようだ。
これを0x10000000 とかDDR3 SDRAMのアドレスを決め打ちで設定すれば良い。

実際にやってみたが、カメラ画像は1回は表示できたが、サイクリックに連続して表示されない。もしやと思い、NUMBER_OF_WRITE_FRAMES を 2 にしてみたが変化がない。3 にしてみたらカメラを振ると連続して表示された。4 にしても同様に連続して表示されている。3 以上にする必要がありそうだ。但し、自作のビットマップ・ディスプレイ・コントローラは1つのフレームバッファにしか対応していないので、NUMBER_OF_WRITE_FRAMESの数を増やすとフレームレートが遅くなるはずだ。

下にディスプレイに表示されたカメラ画像を示す。
V_ZYBO_CAMD_151_90_150624.jpg

Vivado Logic Analyzer の波形を示す。
V_ZYBO_CAMD_151_89_150624.png

最後に完成版のソフトウェア cam_disp_axis.c を貼っておく。

// cam_disp_axis.c
// 2015/06/14 by marsee
//
// Refered to Xilinx\SDK\2015.1\data\embeddedsw\XilinxProcessorIPLib\drivers\axivdma_v5_1\doc\html\api
// Refered to https://github.com/elitezhe/Atyls-VDMA-one-in-one-out/blob/master/SDK/colorbar/src/helloworld.c
// Refered to http://www.xilinx.com/support/documentation/ip_documentation/axi_vdma/v6_2/pg020_axi_vdma.pdf
// Refered to http://forums.xilinx.com/t5/Embedded-Processor-System-Design/Axi-VDMA-on-Digilent-Atlys/td-p/297019/page/2
//
// normal camera out
//

#include <stdio.h>
#include <stdlib.h>
#include "xaxivdma.h"
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"

#define NUMBER_OF_WRITE_FRAMES    3 // Note: If not at least 3 or more, the image is not displayed in succession.

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

#define FRAME_BUFFER_ADDRESS 0x10000000

static XAxiVdma_DmaSetup Vdma0_WriteCfg;

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main(){
    // malloc frame buffer
    // unsigned int *frame_buffer = (unsigned int *)malloc(HORIZONTAL_PIXELS * VERTICAL_LINES * PIXEL_NUM_OF_BYTES * NUMBER_OF_WRITE_FRAMES);

    // AXI VDMA Initialization sequence
    XAxiVdma_Config *XAxiVdma0_Config;
    XAxiVdma XAxiVdma0;
    int XAxiVdma0_Status;

    XAxiVdma0_Config = XAxiVdma_LookupConfig(XPAR_CAMERA_INTERFACE_AXI_VDMA_0_DEVICE_ID); // Look up the hardware configuration for a device instance
    if (XAxiVdma0_Config == NULL){
        fprintf(stderr, "No AXI VDMA found\n");
        return(-1);
    }

    XAxiVdma0_Status = XAxiVdma_CfgInitialize(&XAxiVdma0, XAxiVdma0_Config, XAxiVdma0_Config->BaseAddress); // Initialize the driver with hardware configuration
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_CfgInitialize() failed\n");
        return(-1);
    }

    XAxiVdma_Reset(&XAxiVdma0, XAXIVDMA_WRITE);
    while(XAxiVdma_ResetNotDone(&XAxiVdma0, XAXIVDMA_WRITE)) ;

    XAxiVdma0_Status = XAxiVdma_SetFrmStore(&XAxiVdma0, NUMBER_OF_WRITE_FRAMES, XAXIVDMA_WRITE); // Set the number of frame store buffers to use.

    Vdma0_WriteCfg.VertSizeInput = VERTICAL_LINES;
    Vdma0_WriteCfg.HoriSizeInput = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES;
    Vdma0_WriteCfg.Stride = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES; // Indicates the number of address bytes between the first pixels of each video line.
    Vdma0_WriteCfg.FrameDelay = 0// Indicates the minimum number of frame buffers the Genlock slave is to be behind the locked master. This field is only used if the channel is enabled for Genlock Slave operations. This field has no meaning in other Genlock modes.
    Vdma0_WriteCfg.EnableCircularBuf = 1// Indicates frame buffer Circular mode or frame buffer Park mode.  1 = Circular Mode Engine continuously circles through frame buffers.
    Vdma0_WriteCfg.EnableSync = 0// Enables Genlock or Dynamic Genlock Synchronization. 0 = Genlock or Dynamic Genlock Synchronization disabled.
    Vdma0_WriteCfg.PointNum = 0// No Gen-Lock
    Vdma0_WriteCfg.EnableFrameCounter = 0// Endless transfers
    Vdma0_WriteCfg.FixedFrameStoreAddr = 0// We are not doing parking

    XAxiVdma0_Status = XAxiVdma_DmaConfig(&XAxiVdma0, XAXIVDMA_WRITE, &Vdma0_WriteCfg);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaConfig() failed\n");
        return(-1);
    }

    // Frame buffer address set
    unsigned int frame_addr = (unsigned int)FRAME_BUFFER_ADDRESS;
    int i;
    for (i=0; i<NUMBER_OF_WRITE_FRAMES; i++){
        Vdma0_WriteCfg.FrameStoreStartAddr[i] = frame_addr;
        frame_addr += HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES * VERTICAL_LINES;
    }

    XAxiVdma0_Status = XAxiVdma_DmaSetBufferAddr(&XAxiVdma0, XAXIVDMA_WRITE, Vdma0_WriteCfg.FrameStoreStartAddr);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaSetBufferAddr() failed\n");
        return(-1);
    }

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    // VDMA start
    XAxiVdma0_Status = XAxiVdma_DmaStart(&XAxiVdma0, XAXIVDMA_WRITE);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaStart() failed\n");
        return(-1);
    }

    // mt9d111_inf_axis_0, axi_iic_0, bitmap_disp_cntrler_axi_master_0
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *mt9d111_axi_lites;
    volatile unsigned int *mt9d111_i2c_axi_lites;

    bmdc_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    mt9d111_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_MT9D111_INF_AXIS_0_BASEADDR;
    mt9d111_i2c_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_AXI_IIC_0_BASEADDR;

    bmdc_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller start
    mt9d111_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Camera Interface start (Address is dummy)

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(mt9d111_i2c_axi_lites);

    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0xf00x1);      // Changed regster map to IFP page 1
    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0x970x20);        // RGB Mode, RGB565

    mt9d111_axi_lites[1] = 0// One_shot_mode is disabled

    return(0);
}


  1. 2015年06月24日 04:23 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのドライバによるレジスタの設定値

まだ、AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システムは表示できるようにならない。デバックは時間がかかってしまい、朝2時間でやるには日数が掛かりそうだ。

今日は、AXI VDMAのドライバを使って書いてみたが、実際にどのようにレジスタを設定しているか?を見ていく。Vivado Logic Analyzer でAXI VDMAの設定用のAXI4 Lite Slave バスを観察して、どのレジスタを設定しているのかを見ていこう。

以前、動作させているソフトウェア cam_disp_axis.c を”AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム8(デバック)”に貼ったが、I2Cの設定に時間がかかって最後の XAxiVdma_DmaStart() まで、1024サンプルで見られないようなので、ソフトウェアを変更した。その cam_disp_axis.c を下に示す。

// cam_disp_axis.c
// 2015/06/14 by marsee
//
// Refered to Xilinx\SDK\2015.1\data\embeddedsw\XilinxProcessorIPLib\drivers\axivdma_v5_1\doc\html\api
// Refered to https://github.com/elitezhe/Atyls-VDMA-one-in-one-out/blob/master/SDK/colorbar/src/helloworld.c
// Refered to http://www.xilinx.com/support/documentation/ip_documentation/axi_vdma/v6_2/pg020_axi_vdma.pdf
// Refered to http://forums.xilinx.com/t5/Embedded-Processor-System-Design/Axi-VDMA-on-Digilent-Atlys/td-p/297019/page/2
//
// normal camera out
//

#include <stdio.h>
#include <stdlib.h>
#include "xaxivdma.h"
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"

#define NUMBER_OF_WRITE_FRAMES    1

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

static XAxiVdma_DmaSetup Vdma0_WriteCfg;

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main(){
    // malloc frame buffer
    unsigned int *frame_buffer = (unsigned int *)malloc(HORIZONTAL_PIXELS * VERTICAL_LINES * PIXEL_NUM_OF_BYTES * NUMBER_OF_WRITE_FRAMES);

    // AXI VDMA Initialization sequence
    XAxiVdma_Config *XAxiVdma0_Config;
    XAxiVdma XAxiVdma0;
    int XAxiVdma0_Status;

    XAxiVdma0_Config = XAxiVdma_LookupConfig(XPAR_CAMERA_INTERFACE_AXI_VDMA_0_DEVICE_ID); // Look up the hardware configuration for a device instance
    if (XAxiVdma0_Config == NULL){
        fprintf(stderr, "No AXI VDMA found\n");
        return(-1);
    }

    XAxiVdma0_Status = XAxiVdma_CfgInitialize(&XAxiVdma0, XAxiVdma0_Config, XAxiVdma0_Config->BaseAddress); // Initialize the driver with hardware configuration
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_CfgInitialize() failed\n");
        return(-1);
    }

    XAxiVdma_Reset(&XAxiVdma0, XAXIVDMA_WRITE);
    while(XAxiVdma_ResetNotDone(&XAxiVdma0, XAXIVDMA_WRITE)) ;

    XAxiVdma0_Status = XAxiVdma_SetFrmStore(&XAxiVdma0, NUMBER_OF_WRITE_FRAMES, XAXIVDMA_WRITE); // Set the number of frame store buffers to use.

    Vdma0_WriteCfg.VertSizeInput = VERTICAL_LINES;
    Vdma0_WriteCfg.HoriSizeInput = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES;
    Vdma0_WriteCfg.Stride = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES; // Indicates the number of address bytes between the first pixels of each video line.
    Vdma0_WriteCfg.FrameDelay = 0// Indicates the minimum number of frame buffers the Genlock slave is to be behind the locked master. This field is only used if the channel is enabled for Genlock Slave operations. This field has no meaning in other Genlock modes.
    Vdma0_WriteCfg.EnableCircularBuf = 1// Indicates frame buffer Circular mode or frame buffer Park mode.  1 = Circular Mode Engine continuously circles through frame buffers.
    Vdma0_WriteCfg.EnableSync = 0// Enables Genlock or Dynamic Genlock Synchronization. 0 = Genlock or Dynamic Genlock Synchronization disabled.
    Vdma0_WriteCfg.PointNum = 0// No Gen-Lock
    Vdma0_WriteCfg.EnableFrameCounter = 0// Endless transfers
    Vdma0_WriteCfg.FixedFrameStoreAddr = 0// We are not doing parking

    XAxiVdma0_Status = XAxiVdma_DmaConfig(&XAxiVdma0, XAXIVDMA_WRITE, &Vdma0_WriteCfg);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaConfig() failed\n");
        return(-1);
    }

    // Frame buffer address set
    unsigned int frame_addr = (unsigned int)frame_buffer;
    int i;
    for (i=0; i<NUMBER_OF_WRITE_FRAMES; i++){
        Vdma0_WriteCfg.FrameStoreStartAddr[i] = frame_addr;
        frame_addr += HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES * VERTICAL_LINES;
    }

    XAxiVdma0_Status = XAxiVdma_DmaSetBufferAddr(&XAxiVdma0, XAXIVDMA_WRITE, Vdma0_WriteCfg.FrameStoreStartAddr);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaSetBufferAddr() failed\n");
        return(-1);
    }

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    // VDMA start
    XAxiVdma0_Status = XAxiVdma_DmaStart(&XAxiVdma0, XAXIVDMA_WRITE);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaStart() failed\n");
        return(-1);
    }

    // mt9d111_inf_axis_0, axi_iic_0, bitmap_disp_cntrler_axi_master_0
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *mt9d111_axi_lites;
    volatile unsigned int *mt9d111_i2c_axi_lites;

    bmdc_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    mt9d111_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_MT9D111_INF_AXIS_0_BASEADDR;
    mt9d111_i2c_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_AXI_IIC_0_BASEADDR;

    bmdc_axi_lites[0] = (volatile unsigned int)frame_buffer; // Bitmap Display Controller start
    mt9d111_axi_lites[0] = (volatile unsigned int)frame_buffer; // Camera Interface start (Address is dummy)

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(mt9d111_i2c_axi_lites);

    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0xf00x1);      // Changed regster map to IFP page 1
    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0x970x20);        // RGB Mode, RGB565

    mt9d111_axi_lites[1] = 0// One_shot_mode is disabled

    // VDMA start
/*    XAxiVdma0_Status = XAxiVdma_DmaStart(&XAxiVdma0, XAXIVDMA_WRITE);    if (XAxiVdma0_Status != XST_SUCCESS){        fprintf(stderr, "XAxiVdma_DmaStart() failed\n");        return(-1);    }    */

    return(0);
}


Vivado Logic Analyzer でキャプチャした波形を下に示す。
V_ZYBO_CAMD_151_79_150622.png

拡大してみていこう。最初の波形を示す。
V_ZYBO_CAMD_151_80_150622.png

Write : 0x30 番地に 0x4 をWrite した。これは、S2MM_VDMACR (S2MM VDMA Control Register – Offset 30h) が 0x4 なので、Reset のビットのみ立っている。リセットしているようだ。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。2ビット目0x4 のResetが 1 の時は、リセット処理が進んでいるので、2ビット目が 0 であることを見ているようだ。

Write : もう一度、0x30 番地に 0x4 をWrite した。これは、S2MM_VDMACR のReset ビットのみ 1 にしている。2回リセットしている。

Read : もう一度、0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。。2ビット目0x4 のResetが 1 の時は、リセット処理が進んでいるので、2ビット目が 0 であることを見ているようだ。

V_ZYBO_CAMD_151_81_150622.png

Read : 0x34をRead した。 S2MM VDMA Status Register(S2MM_VDMASR)。値は0x00010001 で、IRQFrameCntSts が 0x1、 Halted が 1 となっている。

Write : 0x48 に 0x1 をWrite した。0x48は、”AXI Video Direct Memory Access v6.2 LogiCORE IP Product Guide Vivado Design Suite PG020 April 1, 2015”では、Reserved となっている。

V_ZYBO_CAMD_151_82_150622.png

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Write : 0x30 番地に 0x00010002 をWrite した。これは、S2MM_VDMACR のIRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。リセット解除?

Write : 0xA4 番地に 0x00000c80 を Write した。 S2MM Horizontal Size Register(S2MM_HSIZE)。10進数で3200 = 800 x 4バイト。

V_ZYBO_CAMD_151_83_150622.png

Write : 0xA8 番地に 0x00000c80 を Write した。 S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)。10進数で3200 = 800 x 4バイト。

Write : 0xAC 番地に 0x00000000 をWrite した。S2MM Start Address (1 to 16) の最初のスタートアドレスに 0 を書いた。
これって、malloc() で取得したアドレスが 0 ということだけど、アドレス 0x00000000 ~ 0x00003FFF まではOCM だったはず。それで途中で止まってしまったのか? 0x10000000 くらいの適当なアドレスを入れてみよう。

V_ZYBO_CAMD_151_84_150622.png

この区間にはAXI VDMAへのアクセスが無い。

V_ZYBO_CAMD_151_85_150622.png

Read : 0x34番地をRead した。 S2MM VDMA Status Register(S2MM_VDMASR)。値は0x00010001 で、IRQFrameCntSts が 0x1、 Halted が 1 となっている。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010002 で、IRQFrameCount が 0x1、Circular_Park が 1 で後は全て 0 だった。

Write : 0x30 番地(S2MM_VDMACR)に 0x00010003 をWrite した。これは、IRQFrameCount が 0x1、Circular_Park が 1 、Run/Stop が 1 で後は全て 0 だった。

Read : 0x34番地をRead した。 S2MM VDMA Status Register(S2MM_VDMASR)。値は0x00010000 で、IRQFrameCntSts が 0x1のみだった。 Halted の 1 が解除されていた。

Read : 0x30 番地をRead した。S2MM_VDMACR。値は0x00010003 で、IRQFrameCount が 0x1、Circular_Park が 1 、Run/Stop が 1 で後は全て 0 だった。

Write : 0xA0番地(S2MM Vertical Size (S2MM_VSIZE – Offset 0xA0))をWrite した。値は0x00000258 で10進数で 600 ラインだ。

V_ZYBO_CAMD_151_87_150622.png

この区間にはAXI VDMAへのアクセスが無い。

V_ZYBO_CAMD_151_88_150622.png

この区間にはAXI VDMAへのアクセスが無い。
  1. 2015年06月23日 05:00 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム8(デバック)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム7(実機動作)”の続き。

実機動作でうまく動作しなかったので、Vivado Analyzer を使ってデバックを行う。

まずは、ブロックデザインでMark Debug を行った。下の図に示すように、AXI4-Stream とAXI VDMAのAXI4-Master とAXI4-Lite SlaveにMark Debugを行った。
V_ZYBO_CAMD_151_77_150621.png

Hardware Manager を立ちあげた。V_ZYBO_CAMDS_i/camera_interface/mt9d111_inf_axis_0_m_axis_TUSERの立ち上がりをトリガとした。
なお、Zynqはビットストリームをダウンロードしても、PSからクロックが供給されない。PSをコンフィギュレーションする必要があるため、Hello WorldをSDKで作って、それを起動した。

次に、V_ZYBO_CAMDS_i/camera_interface/mt9d111_inf_axis_0_m_axis_TUSERの立ち上がりをトリガとして、トリガを掛けて取得できた波形を示す。
V_ZYBO_CAMD_151_74_150621.png

全体的に、なにかやっているのが分かる。

最初の部分を拡大した。
V_ZYBO_CAMD_151_75_150621.png

mt9d111_inf_axis_0 のtuser が1を出力し、axis_switch_1 に伝搬して、axi_switch_0 に、そこから axi_vdam_0 に入っている。その後で、AXI VDMA の AXI4-Master がDDRメモリにDMAしている。正常に動作している。

AXI VDMA の AXI4-Master 部分を拡大してみた。
V_ZYBO_CAMD_151_76_150621.png

AWLEN は 7 なので 8 バーストとなる。AWSIZE が 2 なので、1回に転送するバイト数は 4 バイトである。AWBURST は 1 なので、アドレスをインクリメントだった。

ここまでは、良いのだが、もう一度トリガを掛けても掛からない。減算の状態はと見ると下のような波形になっている。
V_ZYBO_CAMD_151_78_150621.png

mt9d111_inf_axis_0、axis_switch_1、axi_switch_0 のTVALIDが1 のまま止まっている。AXI VDMAが受けられなくなっているのだろうか?

ベアメタル・アプリケーション用のCソースファイル、cam_disp_axis.c を貼っておくので、何かやらかしていたら教えてください。よろしくお願いします。

// cam_disp_axis.c
// 2015/06/14 by marsee
//
// Refered to Xilinx\SDK\2015.1\data\embeddedsw\XilinxProcessorIPLib\drivers\axivdma_v5_1\doc\html\api
// Refered to https://github.com/elitezhe/Atyls-VDMA-one-in-one-out/blob/master/SDK/colorbar/src/helloworld.c
// Refered to http://www.xilinx.com/support/documentation/ip_documentation/axi_vdma/v6_2/pg020_axi_vdma.pdf
// Refered to http://forums.xilinx.com/t5/Embedded-Processor-System-Design/Axi-VDMA-on-Digilent-Atlys/td-p/297019/page/2
//
// normal camera out
//

#include <stdio.h>
#include <stdlib.h>
#include "xaxivdma.h"
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"

#define NUMBER_OF_WRITE_FRAMES    1

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

static XAxiVdma_DmaSetup Vdma0_WriteCfg;

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main(){
    // malloc frame buffer
    unsigned int *frame_buffer = (unsigned int *)malloc(HORIZONTAL_PIXELS * VERTICAL_LINES * PIXEL_NUM_OF_BYTES * NUMBER_OF_WRITE_FRAMES);

    // AXI VDMA Initialization sequence
    XAxiVdma_Config *XAxiVdma0_Config;
    XAxiVdma XAxiVdma0;
    int XAxiVdma0_Status;

    XAxiVdma0_Config = XAxiVdma_LookupConfig(XPAR_CAMERA_INTERFACE_AXI_VDMA_0_DEVICE_ID); // Look up the hardware configuration for a device instance
    if (XAxiVdma0_Config == NULL){
        fprintf(stderr, "No AXI VDMA found\n");
        return(-1);
    }

    XAxiVdma0_Status = XAxiVdma_CfgInitialize(&XAxiVdma0, XAxiVdma0_Config, XAxiVdma0_Config->BaseAddress); // Initialize the driver with hardware configuration
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_CfgInitialize() failed\n");
        return(-1);
    }

    XAxiVdma_Reset(&XAxiVdma0, XAXIVDMA_WRITE);
    while(XAxiVdma_ResetNotDone(&XAxiVdma0, XAXIVDMA_WRITE)) ;

    XAxiVdma0_Status = XAxiVdma_SetFrmStore(&XAxiVdma0, NUMBER_OF_WRITE_FRAMES, XAXIVDMA_WRITE); // Set the number of frame store buffers to use.

    Vdma0_WriteCfg.VertSizeInput = VERTICAL_LINES;
    Vdma0_WriteCfg.HoriSizeInput = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES;
    Vdma0_WriteCfg.Stride = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES; // Indicates the number of address bytes between the first pixels of each video line.
    Vdma0_WriteCfg.FrameDelay = 0// Indicates the minimum number of frame buffers the Genlock slave is to be behind the locked master. This field is only used if the channel is enabled for Genlock Slave operations. This field has no meaning in other Genlock modes.
    Vdma0_WriteCfg.EnableCircularBuf = 1// Indicates frame buffer Circular mode or frame buffer Park mode.  1 = Circular Mode Engine continuously circles through frame buffers.
    Vdma0_WriteCfg.EnableSync = 0// Enables Genlock or Dynamic Genlock Synchronization. 0 = Genlock or Dynamic Genlock Synchronization disabled.
    Vdma0_WriteCfg.PointNum = 0// No Gen-Lock
    Vdma0_WriteCfg.EnableFrameCounter = 0// Endless transfers
    Vdma0_WriteCfg.FixedFrameStoreAddr = 0// We are not doing parking

    XAxiVdma0_Status = XAxiVdma_DmaConfig(&XAxiVdma0, XAXIVDMA_WRITE, &Vdma0_WriteCfg);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaConfig() failed\n");
        return(-1);
    }

    // Frame buffer address set
    unsigned int frame_addr = (unsigned int)frame_buffer;
    int i;
    for (i=0; i<NUMBER_OF_WRITE_FRAMES; i++){
        Vdma0_WriteCfg.FrameStoreStartAddr[i] = frame_addr;
        frame_addr += HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES * VERTICAL_LINES;
    }

    XAxiVdma0_Status = XAxiVdma_DmaSetBufferAddr(&XAxiVdma0, XAXIVDMA_WRITE, Vdma0_WriteCfg.FrameStoreStartAddr);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaSetBufferAddr() failed\n");
        return(-1);
    }

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    // mt9d111_inf_axis_0, axi_iic_0, bitmap_disp_cntrler_axi_master_0
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *mt9d111_axi_lites;
    volatile unsigned int *mt9d111_i2c_axi_lites;

    bmdc_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    mt9d111_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_MT9D111_INF_AXIS_0_BASEADDR;
    mt9d111_i2c_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_AXI_IIC_0_BASEADDR;

    bmdc_axi_lites[0] = (volatile unsigned int)frame_buffer; // Bitmap Display Controller start
    mt9d111_axi_lites[0] = (volatile unsigned int)frame_buffer; // Camera Interface start (Address is dummy)

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(mt9d111_i2c_axi_lites);

    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0xf00x1);      // Changed regster map to IFP page 1
    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0x970x20);        // RGB Mode, RGB565

    mt9d111_axi_lites[1] = 0// One_shot_mode is disabled

    // VDMA start
    XAxiVdma0_Status = XAxiVdma_DmaStart(&XAxiVdma0, XAXIVDMA_WRITE);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaStart() failed\n");
        return(-1);
    }

    return(0);
}

  1. 2015年06月21日 09:46 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:2

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム7(実機動作)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム6(SDK1)”の続き。

前回は、cam_disp プロジェクトとcam_disp_bsp プロジェクトを作製した。今回は、ソフトウェアを作製して実機動作テストを行う。

カメラ画像を表示されるソフトウェア cam_disp_aixs.c を作った。

ZYBOの電源をONしてSDKからZYBOにビットストリームをダウンロードした。

cam_disp_axis を起動したら、ほんの少しカメラ画像が表示されたが、そこで止まってしまった。

下にSDKの画面を示す。
V_ZYBO_CAMD_151_73_150620.png

下に出力された画像を示す。
V_ZYBO_CAMD_151_74_150620.jpg

デバックをする必要がある。
  1. 2015年06月20日 13:39 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

SDKにVivado HLSで作製したIPのドライバをインポートする

いや~。SDKにVivado HLSで作製したIPのドライバをインポートする方法の検索にだいぶ時間がかかってしまった。

Vivado HLSでIPを作製した時に同時に作成されたドライバのLinuxでの使用方法は以下のブログ記事に書いた。
Vivado 2014.4のCドライバーファイル1
Vivado 2014.4のCドライバーファイル2

今回は、ベアメタル・アプリケーションからVivado HLSでIPを作製した時に同時に作成されたドライバを使うことになる。
使用するSDKプロジェクトは、”AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム6(SDK1)”で生成したSDKプロジェクトだ。
すでに、AXI VDMAを初期化して、カメラ画像を表示する cam_disp_axis プロジェクトは作製してある。
ここに、ラプラシアンフィルタをONするソフトウェアのプロジェクト lap_fil_on を追加した。まだスケルトンに近いが XLap_filter_axis_LookupConfig() や XLap_filter_axis_CfgInitialize() を使用している。
V_ZYBO_CAMD_151_67_150618.png

このプロジェクトをビルドするために、Vivado HLSで作製したIPのドライバをSDKのローカル・リポジトリに追加した。追加してから、BSPを作成しながら、Application Project を新規作成する必要がある。既存のApplication Project があってVivado HLSで作製したIPのドライバをSDKのローカル・リポジトリに追加するという順番ではうまく行かない。何故ならば、BSPを作る時、 libsrc にVivado HLSで作製したIPのドライバが追加されるからだ。必ず順番を守る必要がある。

SDK にVivado HLSで作製したIPのドライバをインポートする方法を見ていこう。

SDK のXilinx Tools メニューからRepositories を選択する。
V_ZYBO_CAMD_151_66_150617.png

Preferences のRepositories 画面が開く。
すでに、Z:\V_ZYBO_CAMDS_151\lap_filter_axis_144 が入っているが、最初は空なのでNew ボタンをクリックして、このフォルダを選択する。
V_ZYBO_CAMD_151_68_150618.png

Z:\V_ZYBO_CAMDS_151\lap_filter_axis_144 フォルダはどうやって作ったかというと、Vivado HLS 2014.4 で作製したラプラシアンフィルタIPのZIPパッケージがVivado_HLS\lap_filter_aixs_2014_4\solution1\impl\ip にできている。Vivado プロジェクトの下に、自分でlap_filter_axis_144 フォルダを作り、そこに、xilinx_com_hls_lap_filter_axis_1_0.zip の中身を解凍した。

C:\Users\Masaaki\Documents\Vivado_HLS\lap_filter_aixs_2014_4\solution1\impl\ip フォルダを下に示す。
V_ZYBO_CAMD_151_69_150618.png

Z:\V_ZYBO_CAMDS_151 フォルダを下に示す。
V_ZYBO_CAMD_151_70_150618.png

Preferences のRepositories 画面は、Apply ボタンをクリックしてから、OKボタンをクリックして閉じておく。

そして、File メニューからNew -> Application Project を選択して、新規作成する。
その際に、前回BSP を作製した時に、IPドライバのリポジトリを追加していない時は必ずBSPを新規作成すること。V_ZYBO_CAMD_151_71_150618.png

すると、BSPの ps7_cortex9_0 -> libsrc の下に、lap_filter_axis_v1_0 が入って、ドライバの関数を使うことができるようになる。
V_ZYBO_CAMD_151_72_150618.png

なお、いつも使うライブラリは、Preferences のRepositories のGlobal Repositories に追加してもよい。
C:\Users\Masaaki\Documents\Vivado\user_repository\MyProcessorIPLib\driversを作製してみた。

参考文献

Importing IP drivers in SDK
Vivado Design Suite Tutorial: High-Level Synthesis UG871 (v 2013.2) June 19, 2013 232ページ ”Step 6: Developing Software and Running it on the ZYNQ System”
sdk 13.2 add custom ip driver
AR# 35443 12.1 EDK - SDK でローカル プロジェクト ディレクトリにあるソフトウェア ドライバー アップデートが検出できない

  1. 2015年06月18日 05:35 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム6(SDK1)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム5(制約ファイル、インプリメント)”の続き。

前回はインプリメントして、ビットストリームの生成まで終了したので、今回はハードウェアをエクスポートして、SDKを立ちあげプロジェクトを生成する。

File メニューから Exprot -> Export Hardware... を選択する。
V_ZYBO_CAMD_151_57_150612.png

Export Hardware ダイアログが表示される。OKボタンをクリックした。
V_ZYBO_CAMD_151_58_150612.png

File メニューから Launch SDKを選択する。
V_ZYBO_CAMD_151_59_150612.png

Launch SDK ダイアログが表示される。OKボタンをクリックした。
V_ZYBO_CAMD_151_60_150612.png

SDKが立ち上がった。(キャプチャするのを忘れてしまったので、先のSDK画面を使っています)
V_ZYBO_CAMD_151_65_150615.png

Application Project と Board Support Package を生成する。

File メニューからNew -> Application Project を選択する。
V_ZYBO_CAMD_151_61_150612.png

Project name に cam_disp と入力した。Next > ボタンをクリックした。
V_ZYBO_CAMD_151_62_150612.png

Available Templates から Empty Application を選択する。Finish ボタンをクリックした。
V_ZYBO_CAMD_151_63_150612.png

cam_disp Application Project と cam_disp_bsp Board Support Package が生成された。
V_ZYBO_CAMD_151_64_150612.png
  1. 2015年06月15日 19:04 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのソフトウェア制御について

AXI VDMAのマニュアルは、”AXI Video Direct Memory Access v6.2 LogiCORE IP Product Guide Vivado Design Suite PG020 April 1, 2015 ”を使用する。

AXI VDMAの使い方のマニュアルとして、”AXI4-Stream Video IP and System Design Guide UG934 April2, 2014”がある。AXI VDMAのドライバ・サンプルも載っている。

AXI VDMAについては以前のバージョンだが、調査したことがある。それが下に示す記事だ。
AXI VDMAのお勉強
AXI VDMAのお勉強2(Register Direct Mode)
AXI VDMAのお勉強3(Scatter Gather Mode)

今回は、ベアメタル・アプリケーションは、”AXI VDMAのお勉強”のドライバを使って書いてみようと思う。

更にAXI VDMAのベアメタル・ドライバの書き方サンプルとして、GitHubの
Atyls-VDMA-one-in-one-out/SDK/colorbar/src/helloworld.c
Atyls-VDMA-one-in-one-out/SDK/edid_api_test/src/helloworld.c
AXI4-Stream Video IP and System Design Guide UG934 April2, 2014”の22ページから
が参考になる。

ベアメタル・アプリケーションとして動作させた後で、Linuxのデバイスリーに各IPのAXI Lite Slave番地を登録して、Linuxのドライバを使用してみたい。
AXI VDMAのLinuxドライバについての情報は、Xilinx Wiki - DMA Drivers - Soft IPs - AXI VDMA にある。

Video capture with VDMA”も /dev/mem の mmap() を使っているが、Linux上からの AXI VDMA の使い方の参考になる。

AXI VDMAの設定値サンプル
sentVDMA.txt
video_frame_buffer.c
Lab 3: Rendering Video for Space Invaders via the VGA Controller
PASTEBIN
  1. 2015年06月13日 04:52 |
  2. IP
  3. | トラックバック:0
  4. | コメント:3

AXI4-Stream Switchについて

今回は、”AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム5(制約ファイル、インプリメント)”で使ったAXI4-Stream Switchについて学んでいく。

AXI4-Stream Switchは、Vivado のIP Catalog に入っているXilinx社のIP である。そのマニュアルは、”AXI4-Stream Infrastructure IP Suite LogiCORE IP Product Guide Vivado Design Suite PG085 April 1, 2015”だった。

このProduct Guide には、AXI4-Stream Broadcaster, AXI4-Stream Clock Converter, AXI4-Stream Combiner, AXI4-Stream Combiner, AXI4-Stream Data FIFO, AXI4-Stream Data Width Converter, AXI4-Stream Register Slice, AXI4-Stream Subset Converter, AXI4-Stream Switch が載っている。
使いたいのは、AXI4-Stream Switch なのだが、これだけ多くのAXI4-Steram のインフラがあるとは驚いた。

AXI4-Stream を1対2 にする方は、AXI4-Stream Switch ではなく、AXI4-Stream Broadcaster でも良いのかな?と思ったが、ラプラシアンフィルタIPを使わない場合でもAXI4-Stream を受けている必要があるかも知れないので、止めておいた。ブロードキャストするためにデータを保証する必要があり、1つのストリームが受け取れなければ、他のストリームは止まってしまうのではないだろうか?

さて、、AXI4-Stream Switch は、AXI4-Lite Slave を生成しているので、アドレスマップされたレジスタに値を書くことによって制御されるという使い方になる。
AXI4-Stream Infrastructure IP Suite LogiCORE IP Product Guide Vivado Design Suite PG085 April 1, 2015”の26ページのTable 2-8: Register Map を引用する。
Xilinx_IP_1_150611.png

オフセット0 のコントロール・レジスタは、1ビットのみの実装で、ビット1にREG_UPDATEが実装されている。
2 をWrite すると、MI_MUX[0-16] レジスタが反映され、ソフトリセットがかかるそうだ。ソフトリセットには16クロックサイクルを必要とする。つまり、MI_MUX[0-16] レジスタを設定してからコントロール・レジスタのREG_UPDATEを1にしてMI_MUX[0-16] レジスタの内容を出力に反映させる必要がある。なお、REG_UPDATEはリセットとレジスタのコミットが終了した時に self-clearing されるので、 0 に書き戻す必要はない。

MI_MUX[0-16] レジスタは、そのアドレスにマップされるAXI4-Stream マスタポートがどのスレーブポートのAXI4-Stream データをスイッチするのかを決定する。
MI_MUX[0-16] レジスタは、32ビットなので、4つのバイト・アドレスを消費する。つまり、MI_MUX0 レジスタは、オフセット40 番地、MI_MUX1 レジスタはオフセット 44 番地となる。
MI_MUX0 レジスタに 0x0 を書くと、S00_AXIS からのAXI4-Stream データをM00_AXIS にスイッチする。スイッチをしないで、AXI4-Stream データを止める時は0x80000000 をWrite すれば良い。
それをAXI4-Stream のマスタポートの数だけ設定すれば良いわけだ。

AXI4-Stream Infrastructure IP Suite LogiCORE IP Product Guide Vivado Design Suite PG085 April 1, 2015”の27ページのTable 2-10: MI Mux Registers を引用する。
Xilinx_IP_2_150611.png

、”AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム5(制約ファイル、インプリメント)”の axis_switch_1 についての設定は、

カメラの画像を表示する時は
 0x40 を 0x0
 0x44 を 0x80000000
 0x0 を 0x2

ラプラシアンフィルタIP を通す場合は
 0x40 を 0x80000000
 0x44 を 0x0
 0x0 を 0x2

にすれば良さそうだ。
  1. 2015年06月11日 05:13 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム5(制約ファイル、インプリメント)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム4(ブロックデザイン2)”の続き。

前回はブロックデザインを完成させたのだが、ブロックデザインの名前が間違っていたので、V_ZYBO_CAMDS を新規作成して、ブロックデザインをコピーした。

V_ZYBO_CAMDS を示す。
V_ZYBO_CAMD_151_44_150604.png

サブモジュールの camera_interface を示す。
V_ZYBO_CAMD_151_45_150604.png

Address Editor を示す。
V_ZYBO_CAMD_151_46_150604.png

次にHDL Wapperファイルを作製する。

Source ウインドウのタブをクリックして、ブロックデザインのV_ZYBO_CAMDS を右クリックし、右クリックメニューからCreate HDL Wapper... を選択する。
V_ZYBO_CAMD_151_47_150610.png

Create HDL Wapper ダイアログが開いた。

Let Vivado manage wrapper and auto-update のラジオボタンが選択されていることを確認して、OKボタンをクリックする。
V_ZYBO_CAMD_151_48_150610.png

V_ZYBO_CAMDS_wapper.v が生成された。
V_ZYBO_CAMD_151_49_150610.png

V_ZYBO_CAMD_151_50_150610.png

次に、制約ファイルを作る。

File メニューからAdd Sources... を選択する。

Add or create constraints のラジオボタンをクリックする。
V_ZYBO_CAMD_151_51_150610.png

Add Sources ダイアログで、左上の+ボタンをクリックして、Create File... を選択する。
V_ZYBO_CAMD_151_52_150610.png

Create Constraints File ダイアログが開く。File name に V_ZYBO_CAMDS と入力してOKボタンをクリックする。
V_ZYBO_CAMD_151_53_150610.png

Add Sources ダイアログに、V_ZYBO_CAMDS.xdc が入った。Finish ボタンをクリックする。
V_ZYBO_CAMD_151_54_150610.png

Sources ウインドウのConstraints にV_ZYBO_CAMDS.xdc が入った。

以前のZYBOのカメラ表示用のXDCをまるごとコピーした。
V_ZYBO_CAMD_151_55_150610.png

忘れずに、制約ファイルをセーブしておく。

これで論理合成、インプリメントの準備が整った。ビットストリームの生成まで一気に行う。

Flow Navigator でProgram and Debug のGenerate Bitstream をクリックした。

ダイアログが出る。OKボタンをクリックした。

ビットストリームの生成まで終了した。成功した。
V_ZYBO_CAMD_151_56_150610.png
  1. 2015年06月10日 05:15 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

古い実家を壊しました

今日はFPGAの話題からちょっと離れて、長年住んだ古い実家を壊したというお話です。

古い実家は私が生まれる前から、現在の場所に移築してきた建物で、土壁でトタン屋根の古い建物です。もう建築後、70年以上は経っているのじゃないでしょうか?
その実家を修繕してもしようがないので、私の親が壊そうということで壊してもらうことにしました。見積を取ると、昔の家を壊す価格の1.7倍くらいらしいです。昔は、一気に家を機械で壊して全部持っていったのですが、今は分別回収するとのことです。

早速、先週、家を壊してもらいました。

さて、これが壊す前の外観です。
IMG_20150524_113051.jpg

IMG_20150524_113040.jpg

1日目です。障子やアルミサッシを取り外して幕を取り付けました。
IMG_20150529_072802.jpg

IMG_20150529_072836.jpg

2日目です。土壁を壊していました。竹で下地が組んでありました。
IMG_20150530_075328.jpg

IMG_20150530_075342.jpg

3日目です。土壁を袋に入れてありました。
IMG_20150531_075551.jpg

IMG_20150531_075657.jpg

4日目です。重機で家の半分以上が解体されていました。
IMG_20150602_072038.jpg

IMG_20150602_072055.jpg

5日目です。家が完全に全部バラバラになっていました。
IMG_20150602_185954.jpg

6日目です。バラバラになった材木が綺麗に回収されていました。
IMG_20150604_182529.jpg

IMG_20150604_182604.jpg

7日目で完全に家が壊されて更地になりました。
IMG_20150606_172722.jpg

思い出が詰まった家が壊されるのは残念ですが、維持するにも費用がかかります。古いものは壊して行くしか無いですね。
  1. 2015年06月09日 05:05 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 でディスプレイ・コントローラを作る4(PIPELINEディレクティブのrewindオプション)

Vivado HLS 2014.4 でディスプレイ・コントローラを作る3(実機でテスト)”の続き

前回で終わりにするつもりだったのだが、ツィッターで、waka3_m さんにPIPELINEディレクティブのrewind オプションの事を教えて頂いたので、やってみることにした。waka3_m さんありがとうございました。

さて、PIPELINEディレクティブのrewind オプションは、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2014.3) 2014 年 10 月 1 日”の165ページからの”パイプ ラ イ ン されたループの巻き戻しによるパフ ォーマンス改善”に書かれている。
それによると、PIPELINEディレクティブのrewind オプションを付けると、ループは継続して実行されるそうだ。

それでは、display_cont.cpp を次のように変更して、高位合成してみよう。display_cont() はもう要らなそうなので、display_cont_sub() だけにした。

// display_cont.cpp
// 2015/06/03 by marsee
//
// 画面を4分割した第1象限は赤、第2象限は緑、第3象限は青、第4象限は白を表示する
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>

// SVGA 解像度
#define H_ACTIVE_VIDEO    800
#define H_FRONT_PORCH    40
#define H_SYNC_PULSE    128
#define H_BACK_PORCH    88
#define H_SUM            (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH)

#define V_ACTIVE_VIDEO    600
#define V_FRONT_PORCH    1
#define V_SYNC_PULSE    4
#define V_BACK_PORCH    23
#define V_SUM            (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH)

void display_cont_sub(ap_uint<8> *red, ap_uint<8> *green, ap_uint<8> *blue, ap_uint<1> *display_enable, ap_uint<1> *hsyncx, ap_uint<1> *vsyncx){
#pragma HLS INTERFACE ap_none register port=red
#pragma HLS INTERFACE ap_none register port=green
#pragma HLS INTERFACE ap_none register port=blue
#pragma HLS INTERFACE ap_none register port=display_enable
#pragma HLS INTERFACE ap_none register port=hsyncx
#pragma HLS INTERFACE ap_none register port=vsyncx
#pragma HLS INTERFACE ap_ctrl_none port=return

    ap_uint<16> h_count, v_count;

    for (v_count=0; v_count<V_SUM; v_count++){
        for (h_count=0; h_count<H_SUM; h_count++){
#pragma HLS PIPELINE rewind
            if (h_count > (H_ACTIVE_VIDEO +H_FRONT_PORCH) && h_count < (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE))
                *hsyncx = 0;
            else
                *hsyncx = 1;

            if (v_count > (V_ACTIVE_VIDEO + V_FRONT_PORCH) && v_count < (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE))
                *vsyncx = 0;
            else
                *vsyncx = 1;

            if (h_count < H_ACTIVE_VIDEO && v_count < V_ACTIVE_VIDEO)
                *display_enable = 1;
            else
                *display_enable = 0;

            if (v_count < V_ACTIVE_VIDEO/2){
                if (h_count < H_ACTIVE_VIDEO/2){
                    *red=0xff; *green=0; *blue=0;
                } else if (h_count < H_ACTIVE_VIDEO){
                    *red=0; *green=0xff; *blue=0;
                } else {
                    *red=0; *green=0; *blue=0;
                }
            } else if (v_count < V_ACTIVE_VIDEO){
                if (h_count < H_ACTIVE_VIDEO/2){
                    *red=0; *green=0; *blue=0xff;
                } else if (h_count < H_ACTIVE_VIDEO){
                    *red=0xff; *green=0xff; *blue=0xff;
                } else {
                    *red=0; *green=0; *blue=0;
                }
            } else {
                *red=0; *green=0; *blue=0;
            }
        }
    }
}


高位合成結果を示す。
HLS_Disp_Cont_22_150608.png
HLS_Disp_Cont_23_150608.png

Latency が min = 663168, max = 663169 で、Interval が 663168 になった。この値は、水平ピクセル x 垂直ラインの総ピクセル数は、(800+40+128+88)*(600+1+4+23) = 663168 ピクセルと一致している。

次に、 display_cont_sim プロジェクトでシミュレーションした結果を下に示す。
HLS_Disp_Cont_24_150608.png

C++ ソースコードで、 for() の2重ループが終了して、初期化処理をして、また for() の2重ループに入るまでに時間を計測してみた。
HLS_Disp_Cont_25_150608.png

26.4 us で、初期化処理の時間が 0 になっていた。やった~。これで HDL で書いたのと同じになった。

ZドライブのHLS_disp_cont_151 プロジェクト(Vivado 2015.1)の display_cont_top IP の display_cont_sub.v を書き換えて、再IP化した。
HLS_Disp_Cont_26_150608.png

これで、論理合成、インプリメント、ビットストリームの生成を行った。
HLS_Disp_Cont_27_150608.png

ZYBOをコンフィギュレーションして、ディスプレイに表示してみたところ、左上に赤、右上に緑、左下に青、右下に白が表示された。
前回のVGA出力はメイン・ディスプレイではきちんと写ったが、サブ・ディスプレイでは、一番上のラスタの右端が少し歪んでいたのだが、今回の表示は完璧だ。良かった。。。
HLS_Disp_Cont_28_150608.jpg
  1. 2015年06月08日 04:46 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 でディスプレイ・コントローラを作る3(実機でテスト)

Vivado HLS 2014.4 でディスプレイ・コントローラを作る2(RTLシミュレーション)”の続き。

前回、RTLシミュレーションをして、display_cont_sub も1フレーム終了しても、次のフレームを実行することが分かった。今回は、実際にZYBOでVGAポートに画像を表示させた。

まずは、ZYBOのVGAポートは red が 5 ビット長、green が 6 ビット長、blue が 5 ビット長なので、それに合わせるために、 disp_cont_top.v を作製した。disp_cont_top.v を下に示す。

//
// disp_cont_top.v
// 15/06/07 by marsee
//

`default_nettype none

module disp_cont_top (
    input    wire            clk,
    input    wire            reset,
    output    wire    [4:0]    vga_red,
    output    wire    [5:0]    vga_green,
    output    wire    [4:0]    vga_blue,
    output    wire            vga_hsync,
    output    wire            vga_vsync
);
    wire    [7:0]    red;
    wire    [7:0]    green;
    wire    [7:0]    blue;

    display_cont_sub display_cont_i (
        .ap_clk(clk),
        .ap_rst(reset),
        .red_V(red),
        .green_V(green),
        .blue_V(blue),
        .display_enable_V(),
        .hsyncx_V(vga_hsync),
        .vsyncx_V(vga_vsync)
    );

    assign vga_red = red[7:3];
    assign vga_green = green[7:2];
    assign vga_blue = blue[7:3];
endmodule

`default_nettype wire


Zドライブの IP_test フォルダの下に、disp_cont_top フォルダを作って、disp_cont_top をIP化した。
HLS_Disp_Cont_18_150607.png

次に、ZドライブにHLS_disp_cont_151 プロジェクト(Vivado 2015.1)を作って、IP化した disp_cont_top を使ってブロックデザインを作った。今回はZYBOのPLのみで処理を行う。PSは使用しない。
ZYBOは125MHzのクロックがPL部に供給されているので、それを使用した。125MHzのクロックは Clocking Wizard に入って、40MHz のピクセル・クロックに変換される。それを disp_cont_top に供給する。Clocking Wizard のreset はBTN0 に接続されている。
util_vector_logic_0 は clk_wiz_0 のlocked をdisp_cont_top のreset と使用するために極性を反転している。
HLS_Disp_Cont_19_150607.png

Digilent社の制約ファイルのリファレンス ZYBO_Master.xdc を一部引用して、制約ファイル HLS_disp_cont.xdc を作製した。HLS_disp_cont.xdc を下に示す。

set_property PACKAGE_PIN L16 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports clk]

set_property IOSTANDARD LVCMOS33 [get_ports {vga_blue[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_blue[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_blue[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_blue[0]}]
set_property PACKAGE_PIN J18 [get_ports {vga_blue[3]}]
set_property PACKAGE_PIN K19 [get_ports {vga_blue[2]}]
set_property PACKAGE_PIN M20 [get_ports {vga_blue[1]}]
set_property PACKAGE_PIN P20 [get_ports {vga_blue[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_blue[4]}]
set_property PACKAGE_PIN G19 [get_ports {vga_blue[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_green[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_green[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_green[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_green[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_green[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_green[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_red[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_red[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_red[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_red[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_red[0]}]
set_property PACKAGE_PIN F20 [get_ports {vga_green[5]}]
set_property PACKAGE_PIN H20 [get_ports {vga_green[4]}]
set_property PACKAGE_PIN J19 [get_ports {vga_green[3]}]
set_property PACKAGE_PIN L19 [get_ports {vga_green[2]}]
set_property PACKAGE_PIN N20 [get_ports {vga_green[1]}]
set_property PACKAGE_PIN H18 [get_ports {vga_green[0]}]
set_property PACKAGE_PIN F19 [get_ports {vga_red[4]}]
set_property PACKAGE_PIN G20 [get_ports {vga_red[3]}]
set_property PACKAGE_PIN J20 [get_ports {vga_red[2]}]
set_property PACKAGE_PIN L20 [get_ports {vga_red[1]}]
set_property PACKAGE_PIN M19 [get_ports {vga_red[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports vga_hsync]
set_property IOSTANDARD LVCMOS33 [get_ports vga_vsync]
set_property PACKAGE_PIN P19 [get_ports vga_hsync]
set_property PACKAGE_PIN R19 [get_ports vga_vsync]

# btn[0]
set_property PACKAGE_PIN R18 [get_ports reset]
set_property IOSTANDARD LVCMOS33 [get_ports reset]


論理合成、インプリメント、ビットストリームの生成を行って、成功した。
HLS_Disp_Cont_20_150607.png

Hardware Manager で ZYBO のPL部をコンフィギュレーションした。
HLS_Disp_Cont_21_150607.png

ディスプレイに予定通りの表示が出た。
HLS_Disp_Cont_22_150607.jpg

これで、Vivado HLS 2014.4 でディスプレイ・コントローラを作製することが出来た。
しかし、ディスプレイ・コントローラはHDLで書いたほうがずっと楽だと思う。
  1. 2015年06月07日 05:55 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:2

Vivado HLS 2014.4 でディスプレイ・コントローラを作る2(RTLシミュレーション)

Vivado HLS 2014.4 でディスプレイ・コントローラを作る1(高位合成、C/RTLコシミュレーション)”の続き。

前回は、ディスプレイ・コントローラをC++ で書いて高位合成し、きちんとハードウェアになることを検証した。
今回、return ポードの HLS INTERFACE を ap_ctrl_none にした状態で、つまり、ap_start, ap_done, ap_idle, ap_ready を取り払った状態で display_cont_sub() の高位合成された Verilog HDL ソースコードが自動的にループして画像の信号を出し続けるのか?に興味がある。そこで、Verilog HDLファイルをテストベンチを作製して、RTLシミュレーションすることで解明する。

まずは、display_cont_sub() の return ポードの HLS INTERFACE を ap_ctrl_none にして高位合成を行った。

//#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_ctrl_none port=return


レポートを貼っておく。リソースが増えているのはなぜだろう?
HLS_Disp_Cont_7_150606.png
HLS_Disp_Cont_8_150606.png

新しいVivado のプロジェクトを作製し、C:\Users\Masaaki\Documents\Vivado_HLS\ZYBO\display_cont\solution1\syn\verilog フォルダの display_cont_sub.v をAdd し、テストベンチを書いてシミュレーションを行う。

まずは、Vivado 2015.1で display_cont_sim プロジェクトを作製した。

display_cont_sub.v を Add Source した。

テストベンチ display_cont_tb.v を作製した。

現在のプロジェクトの状態を示す。
HLS_Disp_Cont_9_150606.png

これで、Behavioral Simulation の準備が整った。

Flow Navigator から Simulation -> Run Simulation をクリックし、 Run Behavioral Simulation を選択した。

下に 34 ms までシミュレーションした時のシミュレーション波形を示す。
HLS_Disp_Cont_10_150606.png

大体、VGAの波形が出ている。1回目の画像フレームが終了した後もスムーズに2回目の画像フレームを出力している。

問題は、C++ ソースコードで、 for() の2重ループが終了して、初期化処理をして、また for() の2重ループに入るまでに時間が気になるので、検証した。
下にその部分のシミュレーション波形を示す。
HLS_Disp_Cont_11_150606.png

水平同期信号の周期は、通常は 26.4 us なのだが、ここでは、26.475 us となっている。つまり3クロック余計にかかっている。

通常の部分の水平同期信号の周期を見ると、26.4 us だった。
HLS_Disp_Cont_12_150606.png


次に、Vivado HLS 2014.4 で高位合成のTop Function を display_cont に変更して、高位合成を行った。
高位合成結果を下に示す。
HLS_Disp_Cont_13_150606.png
HLS_Disp_Cont_14_150606.png

C:\Users\Masaaki\Documents\Vivado_HLS\ZYBO\display_cont\solution1\syn\verilog フォルダの display_cont.v をVivado 2015.1のプロジェクトにコピーして、プロジェクトにAdd した。

テストベンチのインスタンス・モジュールを display_cont に書き換えた。
HLS_Disp_Cont_15_150606.png

Flow Navigator から Simulation -> Run Simulation をクリックし、 Run Behavioral Simulation を選択した。

下に 34 ms までシミュレーションした時のシミュレーション波形を示す。
HLS_Disp_Cont_16_150606.png

C++ ソースコードで、 for() の2重ループが終了して、while(1){ } に戻って、また for() の2重ループに入る所の水平同期信号の周期を測定したところ、26.45 us で2クロックだけ余計にかかっている。
HLS_Disp_Cont_17_150606.png

display_cont_sub.v との差は1 クロックとなった。

最後にテストベンチの display_cont_tb.v を貼っておく。

`default_nettype none
`timescale 100ps / 1ps

// display_cont_tb.v
// by marsee
// 2015/06/05
//

module display_cont_tb;

    wire    ap_clk;
    wire    ap_rst;
    wire    [7:0]   red_V;
    wire    [7:0]   green_V;
    wire    [7:0]   blue_V;
    wire    display_enable_V;
    wire    hsyncx_V;
    wire    vsyncx_V;

    //display_cont_sub uut (
    display_cont uut (
        .ap_clk(ap_clk),
        .ap_rst(ap_rst),
        .red_V(red_V),
        .green_V(green_V),
        .blue_V(blue_V),
        .display_enable_V(display_enable_V),
        .hsyncx_V(hsyncx_V),
        .vsyncx_V(vsyncx_V)
    );

    // clk_gen のインスタンス(ap_clk)
    clk_gen #(
        .CLK_PERIOD(250),    // 25 nsec, 40 MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(ap_clk)
    );

    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ap_rst),
        .init_done()
    );
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out,
    output    reg        init_done
);
    begin
        initial begin
            reset_out = RESET_STATE;
            init_done = 1'b0;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
            init_done = 1'b1;
        end
    end
endmodule

`default_nettype wire

  1. 2015年06月06日 05:32 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 でディスプレイ・コントローラを作る1(高位合成、C/RTLコシミュレーション)

まだ、AXI4-Stream 版ラプラシアンフィルタIP の実装の途中なのだが、Vivdo HLS 2014.4 でディスプレイ・コントローラを作り始めてしまったので、こっちを先に書くことにする。

まずは、C++ のソースコードを示す。ちょっと理由があって display_cont() とdisplay_cont_sub() の2つの関数を書いた。
display_cont() はこう書くんじゃないかな?という自分の予想で、1フレームの水平、垂直カウンタのカウントを while(1){ } で囲うことによって無限ループにしている。しかしこれではC シミュレーションも C/RTL コシミュレーションもできないし、レイテンシやインターバルの値も?になってしまう。
もしかすると、display_cont_sub() でも、”#pragma HLS INTERFACE ap_ctrl_none port=return”だと、制御用のポートが全てなくなって、最初の初期化過程は踏むかもしれないが、ループするのでは?という疑惑もある。これは後で、display_cont() とdisplay_cont_sub() の高位合成で生成されたVerilog HDL を自分でテストベンチを書いてシミュレーションしてみたい。なお、現在はC/RTL コシミュレーションのために ap_ctrl_hs に指定してある。

// display_cont.cpp
// 2015/06/03 by marsee
//
// 画面を4分割した第1象限は赤、第2象限は緑、第3象限は青、第4象限は白を表示する
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>

// SVGA 解像度
#define H_ACTIVE_VIDEO    800
#define H_FRONT_PORCH    40
#define H_SYNC_PULSE    128
#define H_BACK_PORCH    88
#define H_SUM            (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH)

#define V_ACTIVE_VIDEO    600
#define V_FRONT_PORCH    1
#define V_SYNC_PULSE    4
#define V_BACK_PORCH    23
#define V_SUM            (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH)

void display_cont(ap_uint<8> *red, ap_uint<8> *green, ap_uint<8> *blue, ap_uint<1> *display_enable, ap_uint<1> *hsyncx, ap_uint<1> *vsyncx){
#pragma HLS INTERFACE ap_none register port=red
#pragma HLS INTERFACE ap_none register port=green
#pragma HLS INTERFACE ap_none register port=blue
#pragma HLS INTERFACE ap_none register port=display_enable
#pragma HLS INTERFACE ap_none register port=hsyncx
#pragma HLS INTERFACE ap_none register port=vsyncx
#pragma HLS INTERFACE ap_ctrl_none port=return

    ap_uint<16> h_count, v_count;

    while(1){
        for (v_count=0; v_count<V_SUM; v_count++){
            for (h_count=0; h_count<H_SUM; h_count++){
#pragma HLS PIPELINE
#pragma HLS LATENCY min=1 max=1
                if (h_count > (H_ACTIVE_VIDEO +H_FRONT_PORCH) && h_count < (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE))
                    *hsyncx = 0;
                else
                    *hsyncx = 1;

                if (v_count > (V_ACTIVE_VIDEO + V_FRONT_PORCH) && v_count < (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE))
                    *vsyncx = 0;
                else
                    *vsyncx = 1;

                if (h_count < H_ACTIVE_VIDEO && v_count < V_ACTIVE_VIDEO)
                    *display_enable = 1;
                else
                    *display_enable = 0;

                if (v_count < V_ACTIVE_VIDEO/2){
                    if (h_count < H_ACTIVE_VIDEO/2){
                        *red=0xff; *green=0; *blue=0;
                    } else if (h_count < H_ACTIVE_VIDEO){
                        *red=0; *green=0xff; *blue=0;
                    } else {
                        *red=0; *green=0; *blue=0;
                    }
                } else if (v_count < V_ACTIVE_VIDEO){
                    if (h_count < H_ACTIVE_VIDEO/2){
                        *red=0; *green=0; *blue=0xff;
                    } else if (h_count < H_ACTIVE_VIDEO){
                        *red=0xff; *green=0xff; *blue=0xff;
                    } else {
                        *red=0; *green=0; *blue=0;
                    }
                } else {
                    *red=0; *green=0; *blue=0;
                }
            }
        }
    }

}

void display_cont_sub(ap_uint<8> *red, ap_uint<8> *green, ap_uint<8> *blue, ap_uint<1> *display_enable, ap_uint<1> *hsyncx, ap_uint<1> *vsyncx){
#pragma HLS INTERFACE ap_none register port=red
#pragma HLS INTERFACE ap_none register port=green
#pragma HLS INTERFACE ap_none register port=blue
#pragma HLS INTERFACE ap_none register port=display_enable
#pragma HLS INTERFACE ap_none register port=hsyncx
#pragma HLS INTERFACE ap_none register port=vsyncx
#pragma HLS INTERFACE ap_ctrl_hs port=return

    ap_uint<16> h_count, v_count;

    for (v_count=0; v_count<V_SUM; v_count++){
        for (h_count=0; h_count<H_SUM; h_count++){
#pragma HLS PIPELINE
#pragma HLS LATENCY min=1 max=1
            if (h_count > (H_ACTIVE_VIDEO +H_FRONT_PORCH) && h_count < (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE))
                *hsyncx = 0;
            else
                *hsyncx = 1;

            if (v_count > (V_ACTIVE_VIDEO + V_FRONT_PORCH) && v_count < (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE))
                *vsyncx = 0;
            else
                *vsyncx = 1;

            if (h_count < H_ACTIVE_VIDEO && v_count < V_ACTIVE_VIDEO)
                *display_enable = 1;
            else
                *display_enable = 0;

            if (v_count < V_ACTIVE_VIDEO/2){
                if (h_count < H_ACTIVE_VIDEO/2){
                    *red=0xff; *green=0; *blue=0;
                } else if (h_count < H_ACTIVE_VIDEO){
                    *red=0; *green=0xff; *blue=0;
                } else {
                    *red=0; *green=0; *blue=0;
                }
            } else if (v_count < V_ACTIVE_VIDEO){
                if (h_count < H_ACTIVE_VIDEO/2){
                    *red=0; *green=0; *blue=0xff;
                } else if (h_count < H_ACTIVE_VIDEO){
                    *red=0xff; *green=0xff; *blue=0xff;
                } else {
                    *red=0; *green=0; *blue=0;
                }
            } else {
                *red=0; *green=0; *blue=0;
            }
        }
    }
}


C コードを見れば何を表示するものかすぐわかると思うが、画面を4つに分けて、左上には赤を、右上には緑、左下には青、右下には白の四角を表示する回路だ。
いろいろとディレクティブを試行錯誤したのだが、行の for() 文に PIPELINE ディレクティブを付ければ良いようだ。なお、LATENCY = 1 にすると、行のループに必要なクロックが1クロック減る効果があった。
この display_cont_sub() を Top Function に指定して、高位合成を行った。結果を下に示す。なお、画面の解像度は800 x 600 ピクセルなので、標準的なピクセルクロックの 40 MHz = 25 ns に設定した。
HLS_Disp_Cont_1_150605.png

HLS_Disp_Cont_2_150605.png

Analyse 結果を示す。PIPELINEディレクティブを指定し、行の for() ループに信号の決定を集めたので、1クロックで動作が完了している。
HLS_Disp_Cont_3_150605.png

C/RTL コシミュレーションを行った。その際に、Dump Trace を all に設定して、C/RTL コシミュレーション後の波形を見られるようにした。
HLS_Disp_Cont_4_150605.png

C/RTL コシミュレーションの結果を示す。
HLS_Disp_Cont_5_150605.png

水平ピクセル x 垂直ラインの総ピクセル数は、(800+40+128+88)*(600+1+4+23) = 663168 ピクセルだ。
C/RTL コシミュレーションの結果のレイテンシは 663170 で2クロック違うだけだ。

Vivado 2015.1 を立ちあげてC/RTL コシミュレーションを観察した。
その際に使用したTCLコマンドを下に示す。

cd C:/Users/Masaaki/Documents/Vivado_HLS/ZYBO/display_cont/solution1/sim/verilog
current_fileset
open_wave_database display_cont_sub.wdb
open_wave_config display_cont_sub.wcfg


C/RTL コシミュレーションの波形を下に示す。
HLS_Disp_Cont_6_150605.png

前半部分を拡大した。
HLS_Disp_Cont_7_150605.png

水平同期信号の1週期は 26.4 us だった。それをピクセルクロックの 25 ns で割ると、26400 ns / 25 ns = 1056 クロックとなった。これは水平ラインの総ピクセル数 800+40+128+88 = 1056 と合っている。

最後にテストベンチの display_cont_tb.cpp を示す。

//
// display_cont_tb.cpp
// 2015/06/03 by marsee
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>

void display_cont_sub(ap_uint<8> *red, ap_uint<8> *green, ap_uint<8> *blue, ap_uint<1> *display_enable, ap_uint<1> *hsyncx, ap_uint<1> *vsyncx);
int main(){
    ap_uint<8> redb, *red;
    ap_uint<8> greenb, *green;
    ap_uint<8> blueb, *blue;
    ap_uint<1> deb, *display_enable;
    ap_uint<1> hb, *hsyncx;
    ap_uint<1> vb, *vsyncx;

    red = &redb;
    green = &greenb;
    blue = &blueb;
    display_enable = &deb;
    hsyncx = &hb;
    vsyncx = &vb;

    display_cont_sub(red, green, blue, display_enable, hsyncx, vsyncx);

    return 0;
}

  1. 2015年06月05日 05:41 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム4(ブロックデザイン2)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム3(ブロックデザイン1)”の続き。

前回はブロックデザインの途中までだったので、今回はブロックデザインを完成させる。

CMOSカメラのコントロール用I2Cを制御するためのAXI_IIC を追加し、bitmap_disp_cntrler_axi_master_0 のポートを外部ポートして設定した。一応のブロックデザインの完成形を下に示す。
V_ZYBO_CAMD_151_32_150603.png

このブロックデザインを見ても分かるようにIP が増えすぎている。IP を階層化しようと思う。
mt9d111_inf_axis_0, axis_switch_1, lap_filter_axis_0, axis_switch_0, axi_vdma_0, axi_iic_0 を選択して、右クリックし、右クリックメニューから Create Hierarchy... を選択した。
V_ZYBO_CAMD_151_33_150603.png

Create Hierarchy ダイアログで Cell name を camera_interface に指定した。
V_ZYBO_CAMD_151_34_150603.png

すると、camera_interface モジュールができて、ブロックデザインがスッキリした。
V_ZYBO_CAMD_151_35_150603.png

camera_interface モジュールをダブルクリックで開いてみた。
V_ZYBO_CAMD_151_36_150603.png

いい感じに階層化できた。

Address Editor を見ると、bitmap_disp_cntrler_axi_master_0 の M_AXI と camera_interface/axi_vdma_0 の Data_S2MM のアドレスがマップされていなかったので、右クリックメニューから Auto Assign Address を選択して、アドレスをマップした。
V_ZYBO_CAMD_151_37_150603.png

ブロックデザインの Validate Design ボタンをクリックして、ブロックデザインを確かめたところ、問題がなかった。
V_ZYBO_CAMD_151_38_150603.png

ブロックデザインと Address Editor を Vivado 2015.1 にドッキングした。
V_ZYBO_CAMD_151_39_150603.png
  1. 2015年06月03日 04:27 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム3(ブロックデザイン1)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム2(Vivado 2015.1でプロジェクトを作製)”の続き。

前回は、必要なIPをIP Catalog に登録した。今回は、V_ZYBO_CAMDS というブロックデザインを作っていく。

左端のウインドウ、Flow Navigator -> IP Integrator -> Create Block Design を選択する。

Create Block DesignダイアログのDesign name に V_ZYBO_CAMDS と入力した。
V_ZYBO_CAMD_151_12_150601.png

V_ZYBO_CAMDS ブロックデザインが生成された。
V_ZYBO_CAMD_151_13_150601.png

add IP ボタンをクリックして、Add IP のダイアログを表示させた。
ZYNQ7 Processing System を選択してダブルクリックした。
V_ZYBO_CAMD_151_14_150601.png

processing_system7_0 ができた。
Run Block Automation をクリックした。
V_ZYBO_CAMD_151_15_150601.png

Run Block Automation ダイアログが表示された。
FIXED_IO と DDR の入出力ポートを作るというダイアログだ。
V_ZYBO_CAMD_151_16_150601.png

processing_system7_0 にFIXED_IO と DDR の入出力ポートが生成された。
V_ZYBO_CAMD_151_17_150601.png

順番が逆になってしまったが、ZYBOの構成が書いてある ZYBO_zynq_def.xml をインポートする。

processing_system7_0 をダブルクリックした。

Re-customize IP ダイアログが表示された。

Import XPS Settings をクリックし、 ZYBO_zynq_def.xml を選択した。
V_ZYBO_CAMD_151_18_150601.png

ZYBOの設定が反映された。
V_ZYBO_CAMD_151_19_150601.png

左端のPage Navigator -> Clock Configuration を選択して、FCLK_CLK1 を IO_PLL の 25 MHz に設定した。FCLK_CLK2を IO_PLL の 36 MHz に設定した。
V_ZYBO_CAMD_151_20_150601.png

ZYBOの設定と自分で設定した設定が、processing_system7_0 に反映された。
V_ZYBO_CAMD_151_21_150601.png

ここで、ビットマップ・ディスプレイ・コントローラーIPをIP Catalog に入れるのを忘れていたので、Vivado HLS のラプラシアンフィルタIP をテストしていたプロジェクトから BMDispCaL フォルダを現在のプロジェクト・フォルダにコピーした。
V_ZYBO_CAMD_151_22_150601.png

Project Setting ダイアログでIP の Repository Manager タブに bitmap_disp_cntrler_axi_master_v1_0 が追加された。
V_ZYBO_CAMD_151_23_150601.png

自作IP と AXI VDMA IP をAdd IP した。
V_ZYBO_CAMD_151_24_150601.png

AXI VDMA をダブルクリックして設定する。Read は使用しないので、Enable Read Channel のチェックを外した。Write のみになった。
V_ZYBO_CAMD_151_25_150601.png

Run Block Automation をクリックして、AXI Lite Save をすべて接続する。
V_ZYBO_CAMD_151_26_150601.png

AXI Lite Save が接続された。
V_ZYBO_CAMD_151_27_150601.png

Run Block Automation でAXI Master を接続しようとしたが、正しく認識してくれなかったので、processing_system7_0 でAXI HPポートを設定する。

processing_system7_0 をダブルクリックして、Re-customize IP ダイアログを表示させた。

Page Navigator から S AXI HP0 interface と S AXI_ HP1 interface にチェックを入れた。
V_ZYBO_CAMD_151_28_150601.png

processing_system7_0 に S_AXI_HP0 と S_AXI_HP2 が現れた。
V_ZYBO_CAMD_151_29_150601.png

ここから、AXI Interconnect を2つ Add IP して S_AXI_HP0 と S_AXI_HP2 に接続した。

自作の AXIS_Switcher を接続しようとしたが、2入力1出力しか使っていないので、1入力2出力を作るのを忘れてしまった。

IP Catalog を見てみると、AXI4-Stream Switch IP があって、これを使用すれば良さそうだった。

Slave Interface と Master Interface の数を変更できるので、1つは 2 と 1 に設定し、もう1つは1 と 2 に設定した。

Use control register routing を Yes にすると、設定用のAXI Lite Slave が生成された。
V_ZYBO_CAMD_151_30_150602.png

現在のブロックデザインを下に示す。
V_ZYBO_CAMD_151_31_150602.png
  1. 2015年06月02日 04:31 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0