FC2カウンター FPGAの部屋 AXI4 Master アクセスのラプラシアン・フィルタ IP9(できた。完成)

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

FPGAの部屋

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

AXI4 Master アクセスのラプラシアン・フィルタ IP9(できた。完成)

AXI4 Master アクセスのラプラシアン・フィルタ IP8(インプリメント2、実機テスト)”の続き。

前回、バグが見つかったので、修正した。

・論理合成、インプリメント、ビットストリームの生成をProject Navigator で行い、ハードウェアをSDKにエクスポートして、SDKを立ちあげた。

・SDKでBOOT.binを作製して、SDカードに書き込んだ。

・SDカードをZedBoardに入れて、電源ON

・SDKで、lap_filter_axim の Run Configuration を作製して起動した。

うまく表示されました。完成。。。
axi4m_lap_filter_36_131202.jpg

トータルの実行時間は 23.6msec 程度だった。(起動しているソフトウェアは、lap_filter_axim.elf)
最適化したソフトウェア laplacian_filter.elf の実行時間は約398msec だったので、398 / 23.6 ≒ 16.9倍、高速になった。
axi4m_lap_filter_37_131202.png

フレームレートの16.7msec には足りないが、23.6msec は、ソフトウェア実行時間、全てのトータルの時間だ。

次に、ハードウェア処理のラプラシアン・フィルタ部分のみの経過時間を計測した。
axi4m_lap_filter_38_131202.png

約 10.1msec となった。これだと、フレームレートよりも速くなり、十分に仕様を満足する。これで、C言語で生成できる形式でのラプラシアン・フィルタのハードウェアのオフロードは終了とする。この様なハードウェアをVivado HLSでCソフトウェアで簡単に出力できるととっても良いと思う。今回のは作るのに時間が掛かってしまったが、HLSで合成できればとっても良いと思う。

最後に、lap_filter_axim.c のCソースを貼っておく。

// lap_filter_axim.c
// AXI4 Master のVivado HLS出力ハードウェア・バージョン
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/10/15

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <ctype.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/kernel.h>
#include "lap_fil_axim_uty.h"

#define LAP_FILTER_AXIM_HW_ADDRESS    0x4a000000    // 自作ラプラシアン・フィルタのAXI4 Masterハードウェアのアドレス

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

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

#define BUFSIZE    1024

#define MEASURE_COUNT    5000

int conv_rgb2y(int rgb);
int chkhex(char *str);
volatile unsigned *setup_io(off_t mapped_addr, unsigned int *buf_addr);

int main()
{
    FILE *fd;
    unsigned int bitmap_dc_reg_addr;
    unsigned int read_num;
    volatile unsigned *bm_disp_cnt_reg;
    unsigned int fb_addr, next_frame_addr;
    int return_val;
    struct timeval start_time, end_time;
    unsigned int rmmap_cnt=0, wmmap_cnt=0;
    unsigned int lap_fil_hw, *lap_fil_hw_addr;
    char buf[BUFSIZE], *token;
    unsigned int val;
    unsigned int bitmap_buf;

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

    // fb_start_addr.txt の内容をパイプに入れる
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // fb_start_addr.txt を開く
    fd = popen("cat /Apps/fb_start_addr.txt""r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            sscanf(buf, "%x\n", &fb_addr);
        }
    }
    pclose(fd);

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    next_frame_addr = ((fb_addr + (ALL_PIXEL_VALUE*4)) & (~(int)(PAGE_SIZE-1))) + PAGE_SIZE;

    // Vivado HLS で作製したラプラシアン・フィルタIPのアドレスを取得
    lap_fil_hw_addr = setup_io((off_t)LAP_FILTER_AXIM_HW_ADDRESS, &lap_fil_hw);

    lap_fil_initialize(lap_fil_hw_addr);    // ラプラシアン・フィルタIPの初期化とap_start
    lap_fil_env_set(lap_fil_hw_addr, fb_addr, next_frame_addr);    // ラプラシアン・フィルタ環境セット

    gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録
    // ラプラシアン・フィルタAXI4 Master IPスタート
    return_val = laplacian_fil_hw(lap_fil_hw_addr);
    gettimeofday(&end_time, NULL);

    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %d.%6.6d sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %d.%6.6d sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    printf("return Value = %d\n", return_val);
    
    munmap((unsigned int *)lap_fil_hw_addr, BLOCK_SIZE);
    free((unsigned int *)lap_fil_hw);

    // bitmap-disp-cntrler-axi-master のアドレスを取得
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // ls /sys/devices/axi.0 の内容をパイプに入れる
    fd = popen("ls /sys/devices/axi.0""r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            token = buf;
            if ((token=strtok(token, ".\n")) != NULL){
                do {
                    if (chkhex(token)){ // 16進数
                        sscanf(token, "%x", &val);
                    } else {
                        if (strcmp(token, "bitmap-disp-cntrler-axi-master") == 0)
                            bitmap_dc_reg_addr = val;
                    }
                }while((token=strtok(NULL, ".\n")) != NULL);
            }
        }
    }
    pclose(fd);

    // ラプラシアンフィルタの掛かった画像のスタートアドレスを bitmap-disp-cntrler-axi-master にセット
    bm_disp_cnt_reg = setup_io((off_t)bitmap_dc_reg_addr, &bitmap_buf);
    *bm_disp_cnt_reg = next_frame_addr;

    munmap((unsigned int *)bm_disp_cnt_reg, BLOCK_SIZE);
    free((unsigned int *)bitmap_buf);

    /*gettimeofday(&end_time, NULL);    if (end_time.tv_usec < start_time.tv_usec) {        printf("total time = %d.%6.6d sec\n", end_time.tv_sec - start_time.tv_sec - 1, 1000000 + end_time.tv_usec - start_time.tv_usec);    }    else {        printf("total time = %d.%6.6d 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);
}

//
// Set up a memory regions to access GPIO
//
volatile unsigned *setup_io(off_t mapped_addr, unsigned int *buf_addr)
// void setup_io()
{
    int  mem_fd;
    char *gpio_mem, *gpio_map;

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      printf("mapped_addr = %x\n", mapped_addr);
      exit (-1);
   }

   /* mmap GPIO */

   // Allocate MAP block
   if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }
    *buf_addr = gpio_mem;    // mallocしたアドレスをコピー

   // Make sure pointer is on 4K boundary
   if ((unsigned long)gpio_mem % PAGE_SIZE)
     gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);

   // Now map it
   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      mapped_addr
   );

   if ((long)gpio_map < 0) {
      printf("mmap error %d\n", (int)gpio_map);
      printf("mapped_addr = %x\n", mapped_addr);
      exit (-1);
   }

   close(mem_fd); // /dev/mem のクローズ

   // Always use volatile pointer!
   // gpio = (volatile unsigned *)gpio_map;
   return((volatile unsigned *)gpio_map);

// setup_io

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


(追加)
ランダム遅延なしのシミュレーション波形を下に示す。大体、10msec で終了していて、実機テストの時の値と合う。つまり、ほとんどAXIバスでのWaitは無いということだと思う。
axi4m_lap_filter_39_131202.png

ChipScope の波形を見ても、Write, Read共に、下のようにほとんどWaitがない。
axi4m_lap_filter_40_131202.png

axi4m_lap_filter_41_131202.png

やはりReadの方が、DDR3 SDRAMからRead するレイテンシがあるので、遅くなるようだ。

(2013/12/03:追記)
変更した、lap_fil_axim_uty.c と lap_fil_axim_uty.h を貼っておきます。
lap_fil_axim_uty.c です。

/* * lap_fil_axim_uty.c * *  Created on: 2013/10/15 *      Author: Masaaki */

#include "xlap_filter_axim_hw.h"

#define AP_START_BIT_POS        1    // ap_start のビット位置 bit0
#define AP_DONE_BIT_POS            2    // ap_done のビット位置 bit1
#define AP_AUTO_RESTART_BIT_POS    0x80    // auto_restart のビット位置 bit7
#define VLD_BIT_POS                1

void lap_fil_initialize(unsigned int *lap_fil_hw_addr)
{
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_GIE) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_IER) = 1;    // ap_done=1
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_CTRL) = 0;
}

// ラプラシアン・フィルタ環境セット
int lap_fil_env_set(unsigned int *lap_fil_hw_addr, unsigned int cam_fb, unsigned int lap_fb)
{
    int ap_status, ap_done;
    int ap_return;
    int cam_fb_read, lap_fb_read;
    int cam_addr_vld, lap_addr_vld;

    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_DATA) = cam_fb;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_DATA) = lap_fb;
    cam_fb_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_DATA);
    lap_fb_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_DATA);

     // vld enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_CTRL) = VLD_BIT_POS;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_CTRL) = VLD_BIT_POS;
}

// ラプラシアン・フィルタ・スタート
int laplacian_fil_hw(unsigned int *lap_fil_hw_addr)
{
    int ap_status, ap_done;
    int ap_return;

    // ap_start enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL) = AP_START_BIT_POS;

    // wait ap_done
    do{
        ap_status = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL);
        ap_done = ap_status & AP_DONE_BIT_POS;
    }while(!ap_done);
    
    // ap_return read
    ap_return = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_RETURN);
    //printf("ap_return = %d\n", ap_return);
    return(ap_return);
}


lap_fil_axim_uty.h です。


* lap_fil_axim_uty.h
*
* Created on: 2013/10/15
* Author: Masaaki
*/

#ifndef LAP_FIL_AXIM_UTY_H_
#define LAP_FIL_AXIM_UTY_H_

void lap_fil_initialize(unsigned int *lap_fil_hw_addr);
int laplacian_fil_hw(unsigned int *lap_fil_hw_addr);
void lap_fil_env_set(unsigned int *lap_fil_hw_addr, unsigned int *cam_fb, unsigned int *lap_fb);

#endif /* LAP_FIL_AXIM_UTY_H_ */

  1. 2013年12月02日 18:15 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog19.fc2.com/tb.php/2661-aad2a1f4
この記事にトラックバックする(FC2ブログユーザー)