FC2カウンター FPGAの部屋 2017年02月

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

FPGAの部屋

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

PYNQボードのFASTX コーナー検出にラプラシアンフィルタIPとアンシャープ・マスクキング・フィルタIPを追加4(SDK)

PYNQボードのFASTX コーナー検出にラプラシアンフィルタIPとアンシャープ・マスクキング・フィルタIPを追加3(Vivado)”の続き。

前回は、Vivado 2016.4 のブロックデザイン上で、FASTXコーナー検出IP にラプラシアンフィルタIP とアンシャープ・マスクキング・フィルタIP を追加した。今回はアプリケーションソフトを作成して、動作させてみよう。

前回、Vivado 2016.4 でビットストリームの生成までを行ったので、ハードウェアをエクスポートして、SDK を立ち上げた。
その時に、エラーが出てしまったので、PYNQ_FASTX_164.sdk フォルダを一旦捨てて、もう一度、ハードウェアをエクスポートして、SDK を立ち上げた。そうすると、PYNQ_FASTX_164.sdk フォルダがもう一度、作られる。

これで、SDK で File メニューからNew -> Application Project を選択して、disp_lap_mask_on_serial プロジェクトを作成した。
カメラ出力、FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタを行うことができる disp_lap_mask_on_serial.c を作成した。
pynq_de_other_filters_21_170226.png

disp_lap_mask_on_serial.elf を起動すると最初に、カメラ出力、FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタを選択する表示が出てくる。それで機能を選ぶと、例えばFASTX コーナー検出を選べば threshold を入力できるようになる。その様子を下の図に示す。
pynq_de_other_filters_22_170226.png

サブディスプレイにHDMI 端子が無いため、HDMI-VGA変換器を購入して画面をキャプチャした。”TechRise金メッキ高速1080P HDMI オス to VGAメスビデオ変換アダプタケーブル オーディオとマイクロUSBケーブル付き”を購入したのだが、PYNQのHDMIの電源でSVGAの画面がVGA端子のディスプレイできちんとできた。699円は安かった。

さて、最初のカメラ画像を貼っておく。
pynq_de_other_filters_23_170227.jpg

threshold = 5 のFASTX コーナー検出の画像を貼っておく。
pynq_de_other_filters_24_170227.jpg

ラプラシアンフィルタの画像を貼っておく。
pynq_de_other_filters_25_170227.jpg

アンシャープ・マスクキング・フィルタ k = 3.75 の画像を貼っておく。これは、見た限りではカメラ画像と区別がつかない。
pynq_de_other_filters_26_170227.jpg

最後に、アプリケーションソフト disp_lap_mask_on_serial.c のソースコードを貼っておく。

// disp_lap_mask_on_serial.c
// 2017/02/25 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 <stdlib.h>
#include "xfastx_corner_det.h"
#include "XUnsharp_mask_axis.h"
#include "XLap_filter_axis.h"
#include "xparameters.h"
#include "xaxivdma.h"
#include "xil_io.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 FASTX_THRESHOLD        20

#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(){
    XFastx_corner_det fastx_filter;
    XFastx_corner_det_Config *fastx_filterPtr;
    XLap_filter_axis lap_filter;
    XLap_filter_axis_Config *lap_filterPtr;
    XUnsharp_mask_axis usm_filter;
    XUnsharp_mask_axis_Config *usm_filterPtr;
    XAxiVdma_Config *XAxiVdma0_Config;
    XAxiVdma XAxiVdma0;
    int XAxiVdma0_Status;
    float k;
    int k_fixed;
    int i, threshold;

    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;
    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);
    }

    // Look Up the device configuration
    fastx_filterPtr = XFastx_corner_det_LookupConfig(0);
    if (!fastx_filterPtr){
        fprintf(stderr, "XFastx_corner_det configuration failed.\n");
        return(-1);
    }
    lap_filterPtr = XLap_filter_axis_LookupConfig(0);
    if (!lap_filterPtr){
        fprintf(stderr, "XLap_filter_axis configuration failed.\n");
        return(-1);
    }
    usm_filterPtr = XUnsharp_mask_axis_LookupConfig(0);
    if (!usm_filterPtr){
        fprintf(stderr, "XUnsharp_mask_axis configuration failed.\n");
        return(-1);
    }

    // Initialize the Device
    int fastx_status = XFastx_corner_det_CfgInitialize(&fastx_filter, fastx_filterPtr);
    if (fastx_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XFastx_corner_det\n");
        return(-1);
    }
    int lap_status = XLap_filter_axis_CfgInitialize(&lap_filter, lap_filterPtr);
    if (lap_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XLap_filter_axis\n");
        return(-1);
    }
    int usm_status = XUnsharp_mask_axis_CfgInitialize(&usm_filter, usm_filterPtr);
    if (usm_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XUnsharp_mask_axis\n");
        return(-1);
    }

    // Unsharp_masking filter k_fixed set
    XFastx_corner_det_Set_cols(&fastx_filter, (u32)800);
    XFastx_corner_det_Set_rows(&fastx_filter, (u32)600);
    XFastx_corner_det_Set_threshold(&fastx_filter, (u32)FASTX_THRESHOLD);
    XUnsharp_mask_axis_Set_k_fixed_V(&usm_filter, (u32)10); // k = 2.5

    // 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), 0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x48), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x4C), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // 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), 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

    while(1){
        printf("\n\r0:normal camera, 1:fastx, 2:laplacian filter, 3:unsharp_masking filter, 999:end");
        printf("\n\rPlease input = ");
        scanf("%d", &i);
        if (i == 999)
            break;
        switch(i){
        case 0 : // normal camera
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x48), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x4C), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x0);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers
            while(1){
                printf("\n\rEnter 999 to return to the function selection mode\n");
                scanf("%d", &i);
                if (i == 999)
                    break;
            }
            break;
        case 1 : // fastx
            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+0x48), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x4C), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers
            XFastx_corner_det_Start(&fastx_filter);
            XFastx_corner_det_EnableAutoRestart(&fastx_filter);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x1);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers
            while(1){
                printf("\n\rEnter 999 to return; threshold = ");
                scanf("%d", &threshold);
                if (threshold == 999)
                    break;
                
                XFastx_corner_det_Set_threshold(&fastx_filter, (u32)threshold);
            }
            XFastx_corner_det_DisableAutoRestart(&fastx_filter);
            break;
        case 2 : // laplacian filter
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x48), 0);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x4C), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers
            XLap_filter_axis_Start(&lap_filter);
            XLap_filter_axis_EnableAutoRestart(&lap_filter);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x2);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers
            while(1){
                printf("\n\rEnter 999 to return to the function selection mode\n");
                scanf("%d", &i);
                if (i == 999)
                    break;
            }
            XLap_filter_axis_DisableAutoRestart(&lap_filter);
            break;
        case 3 :
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x48), 0x80000000); // disable
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x4C), 0);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers
            XUnsharp_mask_axis_Start(&usm_filter);
            XUnsharp_mask_axis_EnableAutoRestart(&usm_filter);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x3);
            Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers
            while(1){
                printf("\n\rEnter 999 to return; k (0.25 ~ 3.75) = ");
                scanf("%f", &k);
                if (k == 999)
                    break;

                if (k < 0)
                    k_fixed = 0;
                else if (k > 4)
                    k_fixed = (int)(3.75*4);
                else
                    k_fixed = (int)(k*4);

                XUnsharp_mask_axis_Set_k_fixed_V(&usm_filter, (u32)k_fixed);
            }
            XUnsharp_mask_axis_DisableAutoRestart(&usm_filter);
            break;
        }
    }
        
    return(0);
}

  1. 2017年02月26日 04:46 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

PYNQボードのFASTX コーナー検出にラプラシアンフィルタIPとアンシャープ・マスクキング・フィルタIPを追加3(Vivado)

”PYNQボードのFASTX コーナー検出にラプラシアンフィルタIPとアンシャープ・マスクキング・フィルタIPを追加2(Vivado HLS2)”の続き。

前回は、PYNQボード用の AXI4-Stream版のラプラシアンフィルタIP を作成した。今回は、AXI4-Stream版アンシャープ・マスクキング・フィルタIP と AXI4-Stream版のラプラシアンフィルタIP をPYNQボードのFASTX コーナー検出のVivado 2016.4 プロジェクトに追加する。

PYNQボードのFASTX コーナー検出のVivado プロジェクト PYNQ_FASTX_164 を示す。
pynq_de_other_filters_16_170224.png

pynq_fastx ブロックデザインを示す。
pynq_de_other_filters_17_170224.png

camera_interface モジュールを表示した。
pynq_de_other_filters_18_170224.png

AXI4-Stream版アンシャープ・マスクキング・フィルタIP と AXI4-Stream版のラプラシアンフィルタIP を camera_interface モジュールに追加した。

Address Editor のアドレスを示す。AXI4-Stream版アンシャープ・マスクキング・フィルタIP と AXI4-Stream版のラプラシアンフィルタIP がアドレス・マップされていた。
pynq_de_other_filters_19_170224.png

論理合成、インプリメント、ビットストリームの生成を行った。レポートを示す。
pynq_de_other_filters_20_170224.png
  1. 2017年02月24日 05:15 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

MT9D111のフレームレートを 15 fps から 30 fps にした

CMOSカメラモジュールのMT9D111 のCLKINに 36 MHz を入れると、PIXCLK に 18 MHz が出てきていた。
カメラでは8ビットの 2 バイトで1つのピクセルを表すので、つまり、ピクセルクロックとしては 18 MHz の半分の 9 MHz となり、約 15 fps ということになる。(SVGA でのピクセルクロックは 40 MHz)アプティナのカメラの出力フォーマットはVGA信号よりも情報が詰まっているので、これで計算が合うのだ。

このフレームレートを 15 fps から 30 fps にしたいと常々思っていた。MT9D111 のマニュアルにも「30 fps in preview mode (800 x 600)」 と書いてあるし、やってみた。

まずは、preview mode のデフォルトでは、PLL がディスエーブルされていると思うので、PLL を活かしてクロックを倍にしたいなと思った。しかし、カメラの設定用レジスタは多すぎて、やってみるのが怖い。
それでも mbed の mt9d111.cpp mt9d111.h は参考になりそうだった。

とりあえず、カメラのレジスタ設定は変えずにフレームレートを倍にするには、カメラのCLKIN に入れるクロックの周波数を倍にすれば良いだろうというということで、今まで 36 MHz を入れていたが、72 MHz にしてみることにした。MT9D111 のマニュアルでもPLL をバイパスしているときの最高の入力周波数は 80 MHz ということなので、問題ないはずだ。

PYNQ のFASTX コーナー検出のプロジェクトでやってみた。
pynq_fastx ブロックデザインで processing_system7_0 をダブルクリックして設定する。
mt9d111_15_30fps_1_170223.png

開いたダイアログで、Clock Configuration をクリックして、FCLK_CLK2 の周波数設定を 36 MHz から 72 MHz に変更した。
mt9d111_15_30fps_2_170223.png

制約ファイルでは、カメラの出力クロック pclk の period を 55.56 ns から 27.88 ns に変更した。
mt9d111_15_30fps_3_170223.png

これで、論理合成、インプリメント、ビットストリームの生成を行い、SDK でPYNQボードにダウンロードしてFASTX コーナー検出をやってみたが動作した。フレームレートが速くなっている気がする(気のせいじゃないよね?)
うまく行ったので、30 fps で使うことにする。

なお、ZYBO でもコードで延長したMT9D111 でもうまく動いている。72 MHz のクロックを出してもちゃんと受けられているみたいだ。
  1. 2017年02月23日 04:43 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0

Zybot による障害物回避

Zybot による障害物回避が曲がりなりにもできた。

今のところ、超音波距離センサで距離を測って一定の距離以下だったら右旋回か左旋回をするようにしている。ただし、元の方向には戻れない。戻るすべがない状態だ。これについては電子コンパスを付けようと思っている。光誘導でもよいが。。。

右旋回をするか?左旋回をするか?は今のところターゲットの障害物がどの位置にあるかを画像で検知している。つまり青いゴミ箱が画像の右側にあれば左旋回でよける。左側にあれば右旋回でよける。青いゴミ箱の検知にはRGB2HSV IP を使って画像を HSV に変換して、H をある一定幅で検知している。それがどっちにあるかでよける方向を決めている。今のところ、青いゴミ箱しか障害物回避できないが、H が同じ幅である一定数ある場合は、それを物体として認知して、その物体の無い方向によけることもできると思う。

実際の動画をお見せしようと思う。まずは、Zybot による障害物回避の右旋回だ。動画の最初にZybotがごみ箱に向かって、ごみ箱の真正面よりも少し右側にずれていることに注目してほしい。この様な時には右旋回する。


次に、Zybot による障害物回避の左旋回。動画の最初にZybotがごみ箱に向かって、ごみ箱の真正面よりも少し左側にずれていることに注目してほしい。この様な時には左旋回する。


最後に障害物回避のアプリケーションソフトの obstacle_avoid.cpp を貼っておく。

// obstacle_avoid.cpp : Obstacle avoidance
// 2017/02/13 by marsee
//

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

#include "xpwm.h"
#include "xmotor_monitor.h"

#define DIR_LEFT_NORMAL        1
#define DIR_LEFT_REVERSE    0
#define DIR_RIGHT_NORMAL    0q
#define DIR_RIGHT_REVERSE    1

#define PIXEL_NUM_OF_BYTES    4
#define SVGA_HORIZONTAL_PIXELS  800
#define SVGA_VERTICAL_LINES     600
#define SVGA_ALL_DISP_ADDRESS   (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)

#define CHECK_LINE_NUMBER    290

#define OBSTACLE_H_HIGH_LIMIT    200
#define OBSTACLE_H_LOW_LIMIT    160

#define SEARCH_OBSTACLE_THRESHOLD    15

#define OBSTACLE_AVOID_DISTANCE            60.0    // cm
#define OBSTACLE_AVOID_NEAR_DISTANCE    30.0    // cm

//#define DEBUG
//#define MOTOR_OFF

// Start RGB2HSV 
//
void rgb2hsv_on(){
    int fd2, fd3, fd15;
    volatile unsigned *axis_switch_0, *axis_switch_1;
    volatile unsigned *rgb2hsv_0;

   // axis_switch_0 (UIO2)
    fd2 = open("/dev/uio2", O_RDWR); // axis_switch_0 interface AXI4 Lite Slave
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axis_switch_0) open error\n");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!axis_switch_0){
        fprintf(stderr, "axis_switch_0 mmap error\n");
        exit(-1);
    }
    
    // axis_switch_1 (UIO3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_1 interface AXI4 Lite Slave
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_1) open error\n");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!axis_switch_1){
        fprintf(stderr, "axis_switch_1 mmap error\n");
        exit(-1);
    }
    
    // rgb2hsv_0 (UIO15)
    fd15 = open("/dev/uio15", O_RDWR); // rgb2hsv_0 interface AXI4 Lite Slave
    if (fd15 < 1){
        fprintf(stderr, "/dev/uio15 (rgb2hsv_0) open error\n");
        exit(-1);
    }
    rgb2hsv_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd15, 0);
    if (!rgb2hsv_0){
        fprintf(stderr, "rgb2hsv_0 mmap error\n");
        exit(-1);
    }
      
    // axis_switch_1, 1to2 ,Select M01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x80000000// 0x40 = 0x80000000; disable
    axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000; disable
    axis_switch_1[18] = 0x80000000// 0x48 = 0x80000000, disable
    axis_switch_1[19] = 0x0// 0x4C = 0
    axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
    // rgb2hsv IP AXIS Start
    rgb2hsv_0[0] = 0x01// Start bit set
    rgb2hsv_0[0] = 0x80// Auto Restart bit set
    
    // axis_switch_0, 2to1, Select S01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x3// 0x40 = 0x3;
    axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers

    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    munmap((void *)rgb2hsv_0, 0x10000);
    
    close(fd2);
    close(fd3);
    close(fd15);   
}    

// Motor
//
void motor_settings(XPwm *motorLp, XPwm *motorRp){
    XPwm_DisableAutoRestart(motorLp);
    while(!XPwm_IsIdle(motorLp)) ;
    XPwm_Start(motorLp);
    XPwm_EnableAutoRestart(motorLp);
    
     XPwm_DisableAutoRestart(motorRp);
    while(!XPwm_IsIdle(motorRp)) ;
    XPwm_Start(motorRp);
    XPwm_EnableAutoRestart(motorRp);
}

void Stopped_Zybot(XPwm &motorLp, XPwm &motorRp){
    XPwm_Set_sw_late_V(&motorLp, 0);
    XPwm_Set_sw_late_V(&motorRp, 0);
}

void motor_initialize(XPwm &motorL, XPwm &motorR, XMotor_monitor &mmL, XMotor_monitor &mmR){
    XPwm *motorLp, *motorRp;
    XMotor_monitor *mmLp, *mmRp;
    
    motorLp = &motorL;
    motorRp = &motorR;
    mmLp = &mmL;
    mmRp = &mmR;
    
    // Initialization of motor
    if (XPwm_Initialize(motorLp, "pwm_0") != XST_SUCCESS){
        fprintf(stderr,"pwm_0 (Left) open error\n");
        exit(-1);
    }
    if (XPwm_Initialize(motorRp, "pwm_1") != XST_SUCCESS){
        fprintf(stderr,"pwm_1 (Right) open error\n");
        exit(-1);
    }
    
    
    // Initialization of motor monitor
    if (XMotor_monitor_Initialize(mmLp, "motor_monitor_0") != XST_SUCCESS){
        fprintf(stderr,"motor_monitor_0 (Left) open error\n");
        exit(-1);
    }
    if (XMotor_monitor_Initialize(mmRp, "motor_monitor_1") != XST_SUCCESS){
        fprintf(stderr,"motor_monitor_1 (Right) open error\n");
        exit(-1);
    }

    // The Motors is rotated in the forward direction.
    XPwm_Set_sw_late_V(motorLp, 0);
    XPwm_Set_dir_V(motorLp, 1);

    XPwm_Set_sw_late_V(motorRp, 0);
     XPwm_Set_dir_V(motorRp, 0);

    motor_settings(motorLp, motorRp);
}

void SetMotorLR(XPwm &motorL, XPwm &motorR, int left_rate, int right_rate){
    static int PowerR=0;
    static int PowerL=0;
    
#ifndef MOTOR_OFF
    if (PowerR == 0 && PowerL == 0 && left_rate != 0 && right_rate != 0){
        XPwm_Set_sw_late_V(&motorL, 25);
        XPwm_Set_sw_late_V(&motorR, 25);
        usleep(500000);
    }
    XPwm_Set_sw_late_V(&motorL, left_rate);
    XPwm_Set_sw_late_V(&motorR, right_rate);
#endif
    PowerL = left_rate;
    PowerR = right_rate;
}

// Search Obstacle
// return 0 -- Success, return 1 -- I can not find a marker.
// 
int SearchObstacle(volatile unsigned int *addr, int &obstacle_size, int &obstacle_start){
    enum STATE {IDLE, FIND_OBSTACLE};
    enum STATE cs = IDLE;
    int h = 0;

    obstacle_size = 0;
    for (int i=0; i<SVGA_HORIZONTAL_PIXELS; i++){
        h = (addr[i]/65536) & 0x1ff; // H select
#ifdef DEBUG
        if (i==SVGA_HORIZONTAL_PIXELS/2)
            printf("H = %d\n", h);
#endif

        switch (cs){
            case IDLE :
                if (obstacle_size<SEARCH_OBSTACLE_THRESHOLD){
                    if (h<OBSTACLE_H_HIGH_LIMIT && h>OBSTACLE_H_LOW_LIMIT){
                        obstacle_size++;
                    } else {
                        obstacle_size = 0;
                    }
                } else {
                    cs = FIND_OBSTACLE;
                }
                break;
            case FIND_OBSTACLE :
                if (h<OBSTACLE_H_HIGH_LIMIT && h>OBSTACLE_H_LOW_LIMIT){ // obstacle
                    if (obstacle_size == SEARCH_OBSTACLE_THRESHOLD){
                        if (i>=SEARCH_OBSTACLE_THRESHOLD)
                            obstacle_start = i-SEARCH_OBSTACLE_THRESHOLD;
                        else // error
                            obstacle_start = 0;
                    }
                    obstacle_size++;
                } else {
                    return(0); // end
                }
                break;
        }
    }
        
    return(1); // error
}

// ultrasonic sensor detect
float us_sensor_det(volatile unsigned int *ultrasonic_sensor_inf_0){
    ultrasonic_sensor_inf_0[0] = 0x1// ap_start = 1
    
    while((ultrasonic_sensor_inf_0[0] & 0x2) == 0// wait ap_done
        usleep(500); // 5 us
        
    if(int ret_val = (int)ultrasonic_sensor_inf_0[4]) // 0x10 : Data signal of ap_return
        printf("Error: %d\n", ret_val);
    
    float measure = (float)ultrasonic_sensor_inf_0[6] * 0.000342 / 2.0// 0x18 : Data signal of count_val
#ifdef DEBUG
    printf("Distance is %.2f cm\n", measure);
#endif
    return(measure);
}

// debug_print()
void debug_print(const char s[]){
#ifdef DEBUG
    printf("%s\n", s);
    //usleep(500000);
#endif
}


int main(){
    int fd1;
    XPwm motorL, motorR;
    XMotor_monitor mmL, mmR;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    int c;
    int obstacle_size, obstacle_start, obstacle_center;
    struct termios save_settings;
    struct termios settings;
    volatile unsigned int *ultrasonic_sensor_inf_0;
    int fd16;
    int distance;

    // Reffered to http://d.hatena.ne.jp/mFumi/20101002/1286003738
    tcgetattr(0,&save_settings);
    settings = save_settings;

    settings.c_lflag &= ~(ECHO|ICANON);
    settings.c_cc[VTIME] = 0;
    settings.c_cc[VMIN] = 1;
    tcsetattr(0,TCSANOW,&settings);
    fcntl(0,F_SETFL,O_NONBLOCK);

    // Start RGB2HSV
    rgb2hsv_on();
    
    // Motor Initialize
    motor_initialize(motorL, motorR, mmL, mmR);

    // udmabuf0
    int fdf = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The cache is disabled. 
    if (fdf == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    volatile unsigned int *frame_buffer = (volatile unsigned *)mmap(NULL, 3*SVGA_ALL_DISP_ADDRESS, PROT_READ|PROT_WRITE, MAP_SHARED, fdf, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }

    // phys_addr of udmabuf0
    int fdp = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fdp == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open error\n");
        exit(-1);
    }
    read(fdp, attr, 1024);
    sscanf((const char *)attr, "%lx", &phys_addr);  
    close(fdp);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);

    // ultrasonic_sensor_inf_0 (UIO 16)
    fd16 = open("/dev/uio16", O_RDWR); // ultrasonic_sensor_inf_0 interface AXI4 Lite Slave
    if (fd16 < 1){
        fprintf(stderr, "/dev/uio16 (ultrasonic_sensor_inf_0) open error\n");
        exit(-1);
    }
    ultrasonic_sensor_inf_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd16, 0);
    if (!ultrasonic_sensor_inf_0){
        fprintf(stderr, "ultrasonic_sensor_inf_0 mmap error\n");
        exit(-1);
    }


    // main loop
    printf("Obstacle avoidance.\n");
    printf("if pushed 'q' key then exit.\n");
    SetMotorLR(motorL, motorR, 2525); // Slow Straight

    while(1){
        if ((c = getchar()) != EOF){
            if (c == 'q'){
                SetMotorLR(motorL, motorR, 00); // Stop
                break;
            }
        }

        if ((distance=us_sensor_det(ultrasonic_sensor_inf_0)) <= OBSTACLE_AVOID_DISTANCE){
            int result = SearchObstacle(&frame_buffer[SVGA_HORIZONTAL_PIXELS*CHECK_LINE_NUMBER], obstacle_size, obstacle_start);
            obstacle_center = obstacle_start + obstacle_size/2;
#ifdef DEBUG
            printf("obstacle_size = %d, obstacle_start = %d, obstacle_center = %d\n", obstacle_size, obstacle_start, obstacle_center);
#endif
            if (distance <= OBSTACLE_AVOID_NEAR_DISTANCE){
                if (obstacle_center <= SVGA_HORIZONTAL_PIXELS/2){
                    debug_print("Strong right turn\n");
                    SetMotorLR(motorL, motorR, 4525); // Strong right turn                
                } else {
                    debug_print("Strong left turn\n");
                    SetMotorLR(motorL, motorR, 2045); // Strong left turn                
                }
            } else {
                if (obstacle_center <= SVGA_HORIZONTAL_PIXELS/2){
                    debug_print("Right turn\n");
                    SetMotorLR(motorL, motorR, 4020); // Right turn                
                } else {
                    debug_print("Left turn\n");
                    SetMotorLR(motorL, motorR, 1540); // left turn                
                }
            }
        } else {
            SetMotorLR(motorL, motorR, 2525); // Slow Straight
        }
    }
    // Reffered to http://d.hatena.ne.jp/mFumi/20101002/1286003738
    tcsetattr(0,TCSANOW,&save_settings);
    return(0);
}

  1. 2017年02月22日 04:23 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

PYNQボードのFASTX コーナー検出にラプラシアンフィルタIPとアンシャープ・マスクキング・フィルタIPを追加2(Vivado HLS2)

PYNQボードのFASTX コーナー検出にラプラシアンフィルタIPとアンシャープ・マスクキング・フィルタIPを追加1(Vivado HLS)”の続き。

前回は、アンシャープ・マスクキング・フィルタIP を生成した。今回はラプラシアンフィルタIP を生成する。

PYNQ のZynq 7020 (xc7z020clg400-1)用のVivado HLS 2016.4 のプロジェクトpynq_lap_filter_axis を生成した。
AXI4-Stream版のラプラシアンフィルタIPを持ってきて、Source とTest Bench に入れた。
PIPELINE指示子の下のUNROLL指示子は効果が無いというか、PIPELINE指示子で展開されているので、削除した。
Vivado HLS 2016.4 からINTERFACE指示子のAXI4-Stream (axis) では、「registor both」オプションが必須になった。

#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE axis register both port=outs

よって、そのように修正した。
pynq_de_other_filters_6_170221.png

そして、カメラ画像のラプラシアンフィルタ処理結果をリアルタイムで表示するので、私のラプラシアンフィルタは2次微分結果の半分しか表示していない。そこで、ラプラシアンフィルタ演算後に丸め処理をするところで、マイナスの時に 0 にするのではなく、絶対値を取ることにする。これで、マイナス方向の2次微分成分も表示することができるはずだ。
pynq_de_other_filters_7_170221.png

これで C シミュレーションを行った。
pynq_de_other_filters_8_170221.png

ラプラシアンフィルタ処理後の画像を示す。
pynq_de_other_filters_9_170221.png

やはり、マイナスの2次微分成分が入っているので、線がぼやける感じがする。

ラプラシアンフィルタ演算後に丸め処理をするところで、マイナスの時に 0 にしたほうが見た目がよさそうだ。やはり、マイナスの2次微分成分は取り除くことにする。
pynq_de_other_filters_10_170221.png

Top Function に lap_filter_axis を指定して、C コードの合成を行った。
pynq_de_other_filters_11_170221.png

Estimated が 11.37 ns になってしまっている。Latency は 480009 クロックで良好の様だ。

これではまずいので、Solutionメニューから Solution Settings... を選択して、Solution Settings ダイアログを表示し、Clock Period を 10 から 9 (ns) に変更した。
pynq_de_other_filters_12_170221.png

これでもう一度、C コードの合成を行った。
pynq_de_other_filters_13_170221.png

Estimated が 9.40 ns となって、10 ns の動作には問題が無くなった。Latency は 480010 クロックで、1 クロックだけ増えていた。LUT やFF も少しだけ増えていた。

これで、IP としてパッケージを行った。
Vivado RTL Synthesis にチェックを入れた。
pynq_de_other_filters_14_170221.png

Timing met 出来て、IP が生成された。
pynq_de_other_filters_15_170222.png
  1. 2017年02月21日 05:26 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

PYNQボードのFASTX コーナー検出にラプラシアンフィルタIPとアンシャープ・マスクキング・フィルタIPを追加1(Vivado HLS)

PYNQボードには、”PYNQボードでカメラ画像をリアルタイムにFASTX コーナー検出1”、”PYNQボードでカメラ画像をリアルタイムにFASTX コーナー検出2”で、PYNQボードに付けたカメラの画像にFASTX コーナー検出をリアルタイムで行うことができた。
でも、そろそろPYNQ祭りに向けて、もう少しPYNQボードに芸をさせたい。そこで、いつものラプラシアンフィルタIPと最近、assert() の例としてやってみたアンシャープ・マスクキング・フィルタを追加することにした。

まずは、アンシャープ・マスクキング・フィルタIP をPYNQボード用に生成する。

PYNQ のZynq 7020 (xc7z020clg400-1)用のVivado HLS 2016.4 のプロジェクトpynq_unsharp_mask_fixedp を生成した。

unsharp_mask_fixedp のファイルをコピー&ペーストして、Explorer のSource とTest Bench にAdd File した。
assert() 付きの任意精度固定小数点型の実装だ。

アンシャープ・マスクキング・フィルタIP を 800 x 600 解像度に変更した。
pynq_de_other_filters_1_170220.png

Top Function を指定して、一旦、合成した。合成結果を示す。
pynq_de_other_filters_2_170220.png

やはり、Timing のEstimated は、10.08 ns でわずかながらTarget を越している。
Latency は 480036 クロックだった。そうピクセル数が 480000 ピクセルのことを思えば、大体 1 クロックで 1 ピクセルを処理できている。

Timing のEstimated を改善するために、Solution Settings のSynthesis Settings のClock Period を 10 ns から 9 ns に変更した。
これでもう一度、C コードの合成を行った。でもやはり、Timing のEstimated は、10.08 ns で変化が無かった。

Solution Settings のSynthesis Settings のClock Period を 8 ns に設定したところ、Timing のEstimated が 8.25 ns に収まった。
合成結果を示す。
pynq_de_other_filters_3_170220.png

Latency は 480037 クロックで、Timing のEstimated が 10 ns の時よりも 1 クロック増えた。
パーセンテージには表れないほどだが、FF と LUT もリソース使用量が増えている。
これで、10 ns のクロックで動作するだろう。

Export RTL を行った。その際にVivado RTL Synthesis にチェックを入れて、論理合成テストを行った。
pynq_de_other_filters_4_170220.png

論理合成テストも問題ないようだ。

C:\Users\Masaaki\Documents\VIvado_HLS\PYNQ\pynq_unsharp_mask_fixedp\solution1\impl\ip に xilinx_com_hls_unsharp_mask_axis_1_0.zip ができた。
pynq_de_other_filters_5_170220.png
  1. 2017年02月20日 05:14 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.4 でUNROLL指示子を試してみた

Vivado HLS 2016.4 でUNROLL指示子を試してみた。

UNROLL指示子については、Vivado HLS のユーザーズガイド、”高位合成 UG902 (v2016.4) 2016 年 11 月 30 日”の 133 ページの”最適なループ展開によるパイ プ ラ イ ンの改善”を参考にした。

まずは、134 ページの”図 1‐60: ループの展開”がUNROLL指示子の説明の図となっている。その図を引用する。引用した図にわかりやすいように軸を付け加えさせて頂いた。
unroll_test_1_170218.png

ラプラシアンフィルタの実装による UNROLL 指示子の効果について調べてみよう。まずは、すべての要素を足し算するだけの簡単な 3 x 3 のフィルタを AXI4-Stream で実装した。その unroll_test.cpp を示す。

// unroll_test.cpp
// 2017/02/17 by marsee
//

#include <hls_stream.h>

#define HORIZONTAL_PIXEL_WIDTH 5
#define VERTICAL_PIXEL_WIDTH 5

int unroll_test(hls::stream<int>& ins, hls::stream<int>& outs){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE axis register both port=ins
    int pix;
    int usm;

    int line_buf[2][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

    int pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete

    Loop_y : for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        Loop_x : for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
//#pragma HLS PIPELINE II=1
            ins >> pix;

            Loop_i : for (int i=0; i<3; i++){
//#pragma HLS UNROLL factor=3
                Loop_j : for (int j=0; j<2; j++){
//#pragma HLS UNROLL factor=2
                    pix_mat[i][j] = pix_mat[i][j+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            pix_mat[2][2] = pix;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = pix;
        }
        usm = pix_mat[0][0] + pix_mat[0][1] + pix_mat[0][2]
            + pix_mat[1][0] + pix_mat[1][1] + pix_mat[1][2]
            + pix_mat[2][0] + pix_mat[2][1] + pix_mat[2][2];

        outs << usm;
    }
    return(0);
}


Vivado HLS 2016.4 の unroll_test プロジェクトを示す。
unroll_test_2_170218.png

合成結果を示す(図A)。
unroll_test_3_170218.png

Latency が 416 クロックかかっている。内訳はLoop に示されている。Loop_y, Loop_x, Loop_i, Loop_j が表示されていて、UNROLL されいていないのがわかる。

次に、Loop_j の「#pragma HLS UNROLL factor=2」を生かした。
どうも、Vivado HLS 2016.4 では、factor を書かないとまずいようだ。ディレクティブ・エディタで修正できない。不正だと言われてしまう。
unroll_test_6_170218.png

合成結果を示す(図B)。
unroll_test_7_170218.png

UNROLL指示子を全く入れていない時(図A)に比べてLatency が 416 クロックのところ、191 クロックに縮小した。Loop を見ると Loop_j が展開されて無くなっている。

次に、Loop_i の「#pragma HLS UNROLL factor=3」を生かした。
unroll_test_8_170218.png

合成結果を示す(図C)。
unroll_test_9_170218.png

Loop_j も無くなって、Latency が図B の 191 クロックから 66 クロックに縮小した。Loop を見ると、Loop_j も無くなっている。

Loop_x にPIPELINE 指示子の「#pragma HLS PIPELINE II=1」を入れた。
unroll_test_10_170218.png

合成結果を示す(図D)。
unroll_test_11_170218.png

Latency が 29 クロックに縮小した。Loop を見ても Loop_y_Loop_x になっている。

最後に、Loop_j と Loop_i の UNROLL 指示子をコメントアウトした。
unroll_test_12_170218.png

合成結果を示す(図E)
unroll_test_13_170218.png

図D と同じになった。

つまり、この状況では、PIPELINE 指示子を入れれば下の for ループが展開されるようだ。つまり、PIPELINE 指示子の下にはUNROLL指示子は要らないということになる。
  1. 2017年02月18日 15:20 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.4 における assert() 文の効果5(AXI4-Stream版での比較)

Vivado HLS 2016.4 における assert() 文の効果4(自分定義した固定小数点)”の続き。

前回は、アンシャープマスキング・フィルタ単体では、任意精度固定小数点型の方が自分で定義した固定小数点型に比べてリソース使用量が少なくなったという結果だったが、AXI4-Stream インターフェースを被せた状態ではどうなるか?をやってみた。

まずは、変数の変域を指定するために、pow() を使用して、定義値からの生成を行うようにコードを変更した。
それを利用してassert() を書いた。
まずは、任意精度固定小数点型を試してみた。ピンクの枠で示した assert() を追加した。
assert_35_170216.png

C コードの合成結果を示す。
assert_36_170216.png

Analysis 結果を示す。
assert_37_170216.png

37ステートだった。

Analysis 結果で、unsharp_masking() の使用リソースとステート見てみよう。
右上のModule Hierarchy で unsharp_masking をクリックした。
assert_38_170216.png

unsharp_masking() の使用リソースは、FF が 1313 個、LUT が 1331 個だそうだ。ステートは 31 ステートだった。

次に、assert() 文をコメントアウトしてみた。
合成結果を示す。
assert_39_170216.png

LUT が 2244 個から、2262 個に増えていた。FF も 1995 個から 2021 個に増えていた。Latency は同じだった。

Analysis 結果を示す。
assert_40_170216.png

ステートは 38 ステートで変更はない。

Analysis 結果で、unsharp_masking() の使用リソースとステートを見た。
assert_41_170216.png

31ステートで変化はない。FF が 1331 個、LUT が 1357 個だった。assert() が入っているときは、FF が 1313 個、LUT が 1331 個なので、明らかに assert() が無いほうが増えている。

次に、自分で定義した固定小数点型をやってやってみよう。このようにassert() が入っている。
assert_42_170216.png

合成結果を示す。
assert_43_170216.png

FF が 1986 個 、LUT が 1895 個だった。任意精度固定小数点型では、FF が 2244 個、LUT が 1995 なので、明らかに自分で定義した固定小数点型の方がリソース使用量が少ない。単体でテストしたのとは逆の結果になった。
Latency も 3104 クロックに対して、任意精度固定小数点型では 3108 クロックとなっている。

Analysis 結果を示す。
assert_44_170216.png

ステートも 33 ステートだった。こちらも任意精度固定小数点型では、37 ステートだったので、自分で定義した固定小数点型のほうがステートが少ない。

Analysis 結果で、unsharp_masking() の使用リソースとステートを見た。
assert_45_170216.png

FF は 1083 個、LUT は 1245 個だった。任意精度固定小数点型ではFF が 1313 個、LUT が 1331 個だった。

次に、自分で定義した固定小数点型で、assert() をすべてコメントアウトした。
assert_46_170217.png

合成結果を示す。
assert_47_170217.png

FF 、LUT 共に、assert() を入れてあるときと変化が無かった。assert() は効いていない。

AXI4-Stream インターフェースを被せた状態でアンシャープマスキング・フィルタの任意精度固定小数点型と自分で定義した固定小数点型はどっちが使用量が少ないか比べてみたが、自分で定義した固定小数点型の方が少ないという結果になった。これは、アンシャープマスキング・フィルタ単体とは逆の結果だ。

前回は、自分で定義した固定小数点型の方がリソース使用量が少ないという結果になって、自分で定義した固定小数点型を使おうという話になったが、assert() でリソース使用量を減らすことができるし、演算の間違いも少なくなるので、積極的に任意精度固定小数点型を使っていこうと思う。
  1. 2017年02月17日 04:28 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.4 における assert() 文の効果4(自分で定義した固定小数点)

Vivado HLS 2016.4 における assert() 文の効果3”の続き。

これまでは、任意精度固定小数点型で assert() 文で変数の変域を指定するリソース使用量の低減効果を見てきたが、今回は、任意精度固定小数点型を使用しない自分で定義した固定小数点演算との比較をしてみた。

最初にC ソースコードを示す。これは、int 型を使用して、PRECISION の 6 ビット左シフトを行って小数桁を確保している。最後には 6 ビット右シフトして整数に戻す。

// unsharp_mask_axis.cpp
// 2015/09/24 by marsee
// assertテスト用

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

#include "unsharp_mask_axis.h"

// アンシャープマスキング・フィルタ
// x0y0 x1y0 x2y0 -k   -j  -k
// x0y1 x1y1 x2y1 -k  9+8k -k x 1/9
// x0y2 x1y2 x2y2 -k   -k  -k
//
// k : 鮮鋭化の強さ(固定小数点) , k != 0
// num_adec_k : Kの小数点の位置
// 2015/09/27 : 演算の小数部は num_adec_k*2 ビットとする。
//

#define PRECISION    6    // 小数点以下の桁数、精度(1以上)

int unsharp_masking(int pix_mat[3][3], int k, int num_adec_k)
{
    int y;
    int xy[3][3];
    int result=0;
    int z;

    int x1y1 = (9<<(PRECISION+num_adec_k))/k + (8<<PRECISION);

    for (int i=0; i<=16; i += 8){
        for (int j=0; j<3; j++){
            for (int k=0; k<3; k++){
                xy[j][k] = (pix_mat[j][k] >> i) & 0xff; // RGBのいずれかを抽出
            }
        }

        y = -(xy[0][0]<<PRECISION) -(xy[0][1]<<PRECISION) -(xy[0][2]<<PRECISION)
            -(xy[1][0]<<PRECISION) +x1y1*xy[1][1]         -(xy[1][2]<<PRECISION)
            -(xy[2][0]<<PRECISION) -(xy[2][1]<<PRECISION) -(xy[2][2]<<PRECISION);

        y = ((k * y)/9) >> num_adec_k; // k は num_adc_k だけ左シフトされているので戻す

        z = y + (1<<(PRECISION-1)); // 四捨五入 +0.5
        z = z >> PRECISION; // 小数点以下切り捨て

        if (z<0// 飽和演算
            z = 0;
        else if (z>255)
            z = 255;

        result += z<<i; // i=0 : blue, i=8 : green, i=16 : red
    }

    return(result);
}


プロジェクトを示す。
assert_26_170215.png

C コードの合成結果を示す。
assert_27_170215.png

任意精度固定小数点型で実装したときの assert() 無しに比べて、int 型を使用してシフトで小数を表した時のほうがリソース使用量が多い。それにLatency も長い。

Analysis 結果を示す。
assert_28_170215.png

こちらも、58 ステートで任意精度固定小数点型の38 ステートよりも多い。

次に assert.h をインクルードして、適当な assert() を入れてみた。
assert_29_170215.png

C コードlの合成を行った。
assert_30_170215.png

ものすごくリソース使用量が減った。やはり、assert() が効いているようだ。

Analysis 結果を示す。
assert_31_170215.png

4 ステートになってしまった。

最後に、まともに変域を考えて指定した。
assert_32_170215.png

C コードlの合成を行った。
assert_33_170215.png

元の合成結果と比べて、1353 個が 1291 個に減った。

Analysis 結果を示す。
assert_34_170215.png

58ステートになった。元と変化が無かった。

結果として、自分で定義した固定小数点演算をするよりも任意精度固定小数点型を使ったほうが、リソース使用量が少なかった。更に assert() を使うとリソース使用量が削減できるので、固定小数点を使うときは任意精度固定小数点型を使ったほうが良いと思う。
  1. 2017年02月15日 07:05 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.4 における assert() 文の効果3

Vivado HLS 2016.4 における assert() 文の効果2”の続き。

前回も assert() 文を使用して、演算に使用する変数の変域を指定することにより、任意精度固定小数点型の飽和演算は必要ないとVivado HLS に認識してもらい、しかもビット長も制限されることでリソース使用量の低減を図ってきた。しかし、ループカウンタのカウント数の制限によってリソース使用量が低減はされたが、任意精度固定小数点型の通常の演算では assert() 文が効いていなかった。

次に、assert() 文の変域の書き方は C や C++ の if() 文の中の様に C や C++ の結合規則によって記述するということを hiyuh さんに教えてもらった。それで書き直しても、任意精度固定小数点型の通常の演算では assert() 文が効いていなかった。

もう一度、hiyuh さんに、assert() 文を入れる規則を教えてもらった。それを引用する。

assertの挿入箇所が適切ではありません.assertに与えられた条件式はその挿入箇所で評価されますので,被演算対象に対しては値が確定した演算前,代入対象については値が確定した代入後,可及的速やかに適用する必要があります.


ということで、下のソースコードで確かめてみたが、やはりリソース使用量には変化が無かった。

// ap_fixed_test.c
// 2017/02/12 by marsee

#include <assert.h>
#include <ap_fixed.h>

typedef ap_fixed<16,8, AP_RND, AP_SAT> ap_xfix_def;
//typedef ap_ufixed<16,8, AP_RND, AP_SAT> ap_xfix_def;

int ap_fixed_test(ap_xfix_def in0, ap_xfix_def in1, int limit,
        ap_xfix_def *out){

    ap_xfix_def temp;

    assert(in0 > (ap_xfix_def)(-1.0) && in0 < (ap_xfix_def)1.0);
    temp = in0;
    assert(temp > (ap_xfix_def)(-10.0) && temp < (ap_xfix_def)10.0);

    //assert(limit <= 10);
    for (int i=0; i<limit; i++){
#pragma HLS LOOP_TRIPCOUNT min=1 max=10 avg=2
        assert(temp > (ap_xfix_def)(-10.0) && temp < (ap_xfix_def)10.0);
        temp = temp * (ap_xfix_def)2.0;
        assert(temp > (ap_xfix_def)(-10.0) && temp < (ap_xfix_def)10.0);
    }

    assert(temp > (ap_xfix_def)(-10.0) && temp < (ap_xfix_def)10.0);
    assert(in1 > (ap_xfix_def)(-2.0) && in1 < (ap_xfix_def)2.0);
    *out = temp * in1;

    return(0);
}



次に、アンシャープ・マスキング・フィルタでもう一度、検証してみよう。ソースコードを示す。

// unsharp_mask_axis.cpp
// 2015/09/24 by marsee
// ap_fixedバージョン 2015/10/04
//

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <ap_int.h>
#include <ap_fixed.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <iostream>

#include "unsharp_mask_axis.h"

using namespace std;

// アンシャープマスキング・フィルタ
// x0y0 x1y0 x2y0 -k   -j  -k
// x0y1 x1y1 x2y1 -k  9+8k -k x 1/9
// x0y2 x1y2 x2y2 -k   -k  -k
//
// k : 鮮鋭化の強さ(固定小数点) , k != 0
// num_adec_k : Kの小数点の位置
//

int unsharp_masking(int pix_mat[3][3], k_fixed_td k_fixed)
{
    ap_ufixed<88, AP_RND, AP_SAT> xy[3][3];
    int result=0;
    ap_ufixed<88, AP_RND, AP_SAT> z;
    x1y1_fixed_td x1y1;
    y_fixed_td y;
    /*ap_ufixed<8, 8> xy[3][3];    int result=0;    ap_ufixed<8, 8, AP_RND, AP_SAT> z;    x1y1_fixed_td x1y1;    y_fixed_td y;*/

    x1y1 = (x1y1_fixed_td)9/(x1y1_fixed_td)k_fixed + (x1y1_fixed_td)8// ビット長は 9+8 が2^5=32 より小さく、k_fixedで割るので、NUM_ADC_Kが増える可能性がある

    for (int i=0; i<=16; i += 8){
        for (int j=0; j<3; j++){
            for (int k=0; k<3; k++){
                xy[j][k] = (pix_mat[j][k] >> i) & 0xff; // RGBのいずれかを抽出
            }
        }

        y = -xy[0][0]    -xy[0][1]        -xy[0][2]
            -xy[1][0]    +x1y1*xy[1][1]    -xy[1][2]
            -xy[2][0]    -xy[2][1]        -xy[2][2];
        assert(y > (y_fixed_td)(-10) && y < (y_fixed_td)(10));

        y = (k_fixed * y)/(y_fixed_td)9;
        assert(y > (y_fixed_td)(-10) && y < (y_fixed_td)(10));

        y = y+(y_fixed_td)0.5// 四捨五入
        assert(y > (y_fixed_td)(-10) && y < (y_fixed_td)(10));
        if (y < 0)
            z = 0;
        else if (y > 255)
            z = 255;
        else
            z = y;

        result += z.to_int()<<i; // i=0 : blue, i=8 : green, i=16 : red
    }
    return(result);
}


ヘッダファイルを示す。

// unsharp_mask_axis.h
// 2015/09/26 by marsee

#ifndef __UNSHARP_MASK_AXIS_H_
#define __UNSHARP_MASK_AXIS_H_

//#define HORIZONTAL_PIXEL_WIDTH    1280
//#define VERTICAL_PIXEL_WIDTH    720

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define PRECISION    6    // 小数点以下の桁数、精度(0 以上の数を指定する)
#define K_BITLEN    4    // k のビット長
#define NUM_ADC_K    2    // k の小数点の位置

typedef ap_ufixed<K_BITLEN, K_BITLEN-NUM_ADC_K> k_fixed_td;
typedef ap_fixed<6+PRECISION+NUM_ADC_K, (6+PRECISION+NUM_ADC_K)-PRECISION> x1y1_fixed_td;
typedef ap_fixed<6+PRECISION+NUM_ADC_K+8+3, (6+PRECISION+NUM_ADC_K+8+3)-PRECISION> y_fixed_td;

#define K 2.5 // 鮮鋭化の強さ

#endif


最初に assert() をコメントアウトして合成を行った。(なお、assert() 文の変域は正しくない。結果を見るためにわざと厳しい値にしてある)
assert_17_170214.png

合成後の結果を示す。
assert_18_170214.png

Analysis 結果を示す。
assert_19_170214.png

37 ステートだった。

次に assert() のコメントを外して合成を行った。
assert_20_170214.png

合成後の結果を示す。
assert_21_170214.png

Latency が 146 クロックから 131 クロックに 15 クロック減った。DSP48E, FF, LUT もすべて減っている。

Analysis 結果を示す。
assert_22_170214.png

32ステートだった。assert() 文を入れないときは、37ステートだったので、5ステート減っている。

これで、assert() 文で変数の変域を指定することにより、リソース使用量を減らせることが分かった。

最後に実際に使用予定の変数の変域を入れてみよう。
assert_23_170214.png

合成後の結果を示す。
assert_24_170214.png

やはり、assert() 文が無いのに比べてリソース使用量が減っている。

Analysis 結果を示す。
assert_25_170214.png

36ステートで、assert() 文が無いのに比べて 1 ステート減少した。
  1. 2017年02月14日 05:14 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.4 における assert() 文の効果2

Vivado HLS 2016.4 における assert() 文の効果”の続き。

前回は、assert() はfor ループのカウント用変数の変域を与えられるが、通常の変数の変域を指定できるわけでは無いという結論になったが、hiyuh さんに再度教えてもらって、もう一度やってみた。

前回の引数の変域の指定方法が間違っていた。hiyuh さんに教えてもらった C /C++ の結合則に従うということだった。それに、値をキャストするのを忘れていた。更に ap_ufixed もやってみようということでソースコードを書き換えた。
新しい ap_fixed_test.cpp を示す。
assert_7_170213.png

ap_fixed_test.cpp のソースコードを示す。

// ap_fixed_test.c
// 2017/02/12 by marsee

#include <assert.h>
#include <ap_fixed.h>

typedef ap_fixed<16,8, AP_RND, AP_SAT> ap_xfix_def;
//typedef ap_ufixed<16,8, AP_RND, AP_SAT> ap_xfix_def;

int ap_fixed_test(ap_xfix_def in0, ap_xfix_def in1, int limit,
        ap_xfix_def *out){

    ap_xfix_def temp;

    //assert(in0 > (ap_xfix_def)(-1.0) && in0 < (ap_xfix_def)1.0);
    //assert(in1 > (ap_xfix_def)(-2.0) && in1 < (ap_xfix_def)2.0);
    //assert(*out > (ap_xfix_def)(-50.0) && *out < (ap_xfix_def)50.0);
    //assert(in0 < (ap_xfix_def)1.0);
    //assert(in1 < (ap_xfix_def)2.0);
    //assert(*out < (ap_xfix_def)50.0);

    temp = in0;
    //assert(limit <= 10);
    for (int i=0; i<limit; i++)
#pragma HLS LOOP_TRIPCOUNT min=1 max=10 avg=2
        temp = temp * (ap_xfix_def)2.0;

    *out = temp * in1;

    return(0);
}


最初にもう一度、このソースコードのまま合成を行った。
assert_8_170213.png

次に、最初の 3 行の assert() 文を活かしてみよう。これは、符号あり小数の場合だ。
assert_9_170213.png

これを合成した。
assert_10_170213.png

レポートを見ると、assert() 文を活かして無い時と変わらない。

次に、ap_ufixed にして、符号なし小数の場合をやってみた。assert() 文はすべてコメントアウトしてある。
assert_11_170213.png

これを合成した。
assert_12_170213.png

符号あり少数の場合よりも、レイテンシもリソース使用量も少ない。符号なし小数で良いのであれば、大分お得ということができると思う。

次に、下の 3 つの assert() 文を活かした。
assert_13_170213.png

合成を行った。
assert_14_170213.png

assert() 文を入れたときと、入れないときでは変化が無かった。

最後に変数の temp に asset() 文を試しに入れてみた。
assert_15_170213.png

合成を行った。
assert_16_170213.png

temp 変数に assert() 文で変域を指定すると合成されなくなるようだ。
  1. 2017年02月13日 04:54 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.4 における assert() 文の効果

Vivado HLS 2016.4 で変域を指定することができる assert() 文の効果を確かめてみた。

まずは、assert() は、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.4) 2016 年 11 月 30 日”の 344 ページの”アサート”に書かれている。最初の1文を引用する。

C の assert マクロは、 範囲情報をアサートするのに使用される場合、 合成でサポートされます。 たとえば、 変数 とループ境界の上限を指定でき ます。


そこで、任意精度固定小数点型を使う際の問題点の変数の変域が飽和演算が必要ない状態で、飽和演算回路を入れないようにできるかを探ってみた。
任意精度型固定小数点型を使用したアンシャープマスキング・フィルタの例については下のリンクを参照のこと。
Vivado HLS によるアンシャープマスクキング・フィルタの作製7(C++ の任意精度固定小数点型)
Vivado HLS によるアンシャープマスクキング・フィルタの作製8(C++ の任意精度固定小数点型2)
Vivado HLS によるアンシャープマスクキング・フィルタの作製9(C++ の任意精度固定小数点型3)

ap_fixed_test というVivado HLS 2016.4 のプロジェクトを生成した。
assert_1_170212.png

ap_fixed_test.cpp を貼っておく。

// ap_fixed_test.c
// 2017/02/12 by marsee

#include <assert.h>
#include <ap_fixed.h>

int ap_fixed_test(ap_fixed<16,8, AP_RND, AP_SAT> in0, ap_fixed<16,8, AP_RND, AP_SAT> in1, int limit,
        ap_fixed<16,8, AP_RND, AP_SAT> *out){

    ap_fixed<16,8, AP_RND, AP_SAT> temp;

    /*assert(-1 <= in0 <= 1);    assert(-2 <= in1 <= 2);    assert(-10 <= temp <= 10);    assert(-100 <= *out <= 100); */

    temp = in0;
    //assert(limit <= 10);
    for (int i=0; i<limit; i++)
#pragma HLS LOOP_TRIPCOUNT min=1 max=10 avg=2
        temp = temp * (ap_fixed<16,8, AP_RND, AP_SAT>)2.0;

    *out = temp * in1;

    return(0);
}


まずはC コードの合成を行った。結果を示す。
assert_2_170212.png

Latency のmin は 4、max は 13 だった。これは、LOOP_TRIPCOUNT に設定した値が元になっている。LOOP_TRIPCOUNTのカウントにその他のレイテンシを加えたものが Latency として示されている。だが、LOOP_TRIPCOUNT は合成には影響しない。
そこで、合成に変数の変域を反映させるのが assert() だ。
まずは、引数と変数の変域を指定する assert() のコメントを外した。
assert_3_170212.png

これで合成してみよう。
assert_4_170212.png

前回と同じだ。この assert() は効かないようだ。

次に、今コメントを外した部分をコメントにして、LOOP_TRIPCOUNT の max と同じ値を assert() で指定した文のコメントを外す。
assert_5_170212.png

合成結果を示す。
assert_6_170212.png

FF は 137 個が 110 個に、LUT が 243 個が 189 個に減った。

この結果から、assert() はfor ループのカウント用変数の変域を与えられるが、通常の変数の変域を指定できるわけでは無い様だ。

Vivado HLS 2016.4 における assert() 文の効果2”に続く。
  1. 2017年02月12日 07:16 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

バイキュービック法のFPGAへの適用

演算処理をFPGA へ適用するにはどうすれば良いかを検討している。私も先は長くないと思うので、少し公開してみようと思う。そのうちにVivaodo HLS で実装してみよう。

今回はバイキュービック(BiCubic)法をFPGA に適用できるように固定小数点に直して演算する方法だ。
具体的には、バイキュービック法を用いて画像を横2倍、縦2倍にするときの演算である。

参考したのは、「画像の拡大「Bicubic法」」と「分かる!画像補間 ~基本から応用まで~」だ。

このうちの「分かる!画像補間 ~基本から応用まで~」からバイキュービック(BiCubic)法の部分をそのまま引用させて頂いて、最初の式を式(1)、次の式を式(2)とした。
BiCubic_1_170210.png

中間値Mnの式を式3に示す。中間値 Mn は、ピクセル値 xmk に x の重み wxm と y の重み wyk を掛けて合計し、それを xmk に x の重み wxm と y の重み wyk を掛けた値の合計で割ることで求められる。
BiCubic_2_170210.png

今、図2に示す画像のピクセル4x4点を使用して、x11に3点(M0, M1, M2)を追加する。
BiCubic_3_170210.png

M0の値の導出
M0はy方向がyと一致しているので、y=0の時の値のみとなる。y=1の時はy軸方向の重みwy1が0となる。(y=0の時はwy0 = 1)
・M0からx11、x21へのx方向の重み
式(1)から
w(0.5) = (a+2)(0.5)3 – (a+3)(0.5)2 + 1 = 0.125(a+2) – (0.25)(a+3) + 1
= 0.125a + 0.25 – 0.25a + 1 = 0.5 – 0.125a
  ・a = -0.5の場合 w(0.5) = 0.5625
  ・a = -1の場合 w(0.5) = 0.625

・M0からx01、x31へのx方向の重み
式(1)から
w(1.5) = a(1.5)3 – 5a(1.5)2 + 8a(1.5) – 4a = 3.375a – 11.25a + 12a -4a
= 0.125a
  ・a = -0.5の場合 w(1.5) = -0.0625
  ・a = -1の場合 w(1.5) = -0.125

M0の値
よって、a = -0.5の場合(但しwy0 = 1)式3から
M0 = (0.5625x11 + 0.5625x21 - 0.0625x01 - 0.0625x31)/(0.5625×2 - 0.0625×2)
M0 = 0.5625x11 + 0.5625x21 - 0.0625x01 - 0.0625x31
a = -1の場合(但しwy0 = 1)式3から
M0 = (0.625x11 + 0.625x21 - 0.125x01 - 0.125x31)/(0.625×2 - 0.125x01×2)
M0 = 0.625x11 + 0.625x21 - 0.125x01 - 0.125x31

M1の値の導出
M1はx方向がxと一致しているのでx=0の時の値のみとなる。x=1の時はx軸方向の重みwx1が0となる。(x=0の時はwx0 = 1)
・M1からx11、x21へのy方向の重み
式(1)から
w(0.5) = (a+2)(0.5)3 – (a+3)(0.5)2 + 1 = 0.125(a+2) – (0.25)(a+3) + 1
= 0.125a + 0.25 – 0.25a + 1 = 0.5 – 0.125a
  ・a = -0.5の場合 w(0.5) = 0.5625
  ・a = -1の場合 w(0.5) = 0.625
  ・M1からx10、x13へのy方向の重み
式(1)から
w(1.5) = a(1.5)3 – 5a(1.5)2 + 8a(1.5) – 4a = 3.375a – 11.25a + 12a -4a
= 0.125a
  ・a = -0.5の場合 w(1.5) = -0.0625
  ・a = -1の場合 w(1.5) = -0.125

M1の値
よって、a = -0.5の場合(但しwy0 = 1)式3から
M1 = (0.5625x11 + 0.5625x21 - 0.0625x10 - 0.0625x13)/(0.5625×2 - 0.0625×2)
M1 = 0.5625x11 + 0.5625x21 - 0.0625x10 - 0.0625x13
a = -1の場合(但しwy0 = 1)式3から
M1 = (0.625x11 + 0.625x21 - 0.125x10 - 0.125x13)/(0.625×2 - 0.125x10×2)
M1 = 0.625x11 + 0.625x21 - 0.125x10 - 0.125x13

M2の値の導出

・M2からx11, x12, x21, x22へのx方向y方向の重み
式(1)から
w(0.5) = (a+2)(0.5)3 – (a+3)(0.5)2 + 1 = 0.125(a+2) – (0.25)(a+3) + 1
= 0.125a + 0.25 – 0.25a + 1 = 0.5 – 0.125a
  ・a = -0.5の場合 w(0.5) = 0.5625
  ・a = -1の場合 w(0.5) = 0.625
・M2からx00, x10, x20, x30, x01, x31, x02, x32, x03, x13, x23, x33へのx方向y方向の重み
式(1)から
w(1.5) = a(1.5)3 – 5a(1.5)2 + 8a(1.5) – 4a = 3.375a – 11.25a + 12a -4a
= 0.125a
  ・a = -0.5の場合 w(1.5) = -0.0625
  ・a = -1の場合 w(1.5) = -0.125

M2の値
a = -0.5の場合、式3から
M2 = {(0.5625)2x11 + (0.5625)2x21 + (0.5625)2x12 + (0.5625)2x22
+ (-0.0625)2x00 + (0.5625)(-0.0625)x10 + (0.5625)(-0.0625)x20 +(-0.0625)2x30
+ (-0.0625)(0.5625)x01 + (-0.0625)(0.5625)x31
+ (-0.0625)(0.5625)x02 + (-0.0625)(0.5625)x32
+ (-0.0625)2x03 + (0.5625)(-0.0625)x13 + (0.5625)(-0.0625)x23 +(-0.0625)2x33} /
{(0.5625)2 + (0.5625)2 + (0.5625)2 + (0.5625)2
+ (-0.0625)2 + (0.5625)(-0.0625) + (0.5625)(-0.0625) +(-0.0625)2
+ (-0.0625)(0.5625) + (-0.0625)(0.5625)
+ (-0.0625)(0.5625) + (-0.0625)(0.5625)
+ (-0.0625)2 + (0.5625)(-0.0625) + (0.5625)(-0.0625) +(-0.0625)2}
= {0.3164x11 + 0.3164x21 + 0.3164x12 + 0.3164x22
+ 0.0039x00 + (-0.0352)x10 + (-0.0352)x20 +0.0039x30
+ (-0.0352)x01 + (-0.0352)x31 + (-0.0352)x02 + (-0.0352)x32
+ 0.0039x03 + (-0.0352)x13 + (-0.0352)x23 +0.0039x33} /
{0.3164 + 0.3164 + 0.3164 + 0.3164
+ 0.0039 + (-0.0352) + (-0.0352) +0.0039
+ (-0.0352) + (-0.0352) + (-0.0352) + (-0.0352)
+ 0.0039 + (-0.0352) + (-0.0352) +0.0039}
M2 = 0.3164x11 + 0.3164x21 + 0.3164x12 + 0.3164x22
+ 0.0039x00 + (-0.0352)x10 + (-0.0352)x20 +0.0039x30
+ (-0.0352)x01 + (-0.0352)x31 + (-0.0352)x02 + (-0.0352)x32
+ 0.0039x03 + (-0.0352)x13 + (-0.0352)x23 +0.0039x33

a = -1.0の場合、式3から
M2 = {(0.625)2x11 + (0.625)2x21 + (0.625)2x12 + (0.625)2x22
+ (-0.125)2x00 + (0.625)(-0.125)x10 + (0.625)(-0.125)x20 +(-0.125)2x30
+ (-0.125)(0.625)x01 + (-0.125)(0.625)x31
+ (-0.125)(0.625)x02 + (-0.125)(0.625)x32
+ (-0.125)2x03 + (0.625)(-0.125)x13 + (0.625)(-0.125)x23 +(-0.125)2x33} /
{(0.625)2 + (0.625)2 + (0.625)2 + (0.625)2
+ (-0.125)2 + (0.625)(-0.125) + (0.625)(-0.125) +(-0.125)2
+ (-0.125)(0.625) + (-0.125)(0.625)
+ (-0.125)(0.625) + (-0.125)(0.625)
+ (-0.125)2 + (0.625)(-0.125) + (0.625)(-0.125) +(-0.125)2}
= {0.3906x11 + 0.3906x21 + 0.3906x12 + 0.3906x22
+ 0.0156x00 + (-0.0781)x10 + (-0.0781)x20 +0.0156x30
+ (-0.0781)x01 + (-0.0781)x31 + (-0.0781)x02 + (-0.0781)x32
+ 0.0156x03 + (-0.0781)x13 + (-0.0781)x23 +0.0156x33} /
{0.3906 + 0.3906 + 0.3906 + 0.3906
+ 0.0156 + (-0.0781) + (-0.0781) +0.0156
+ (-0.0781) + (-0.0781) + (-0.0781) + (-0.0781)
+ 0.0156 + (-0.0781) + (-0.0781) +0.0156}
M2 = 0.3906x11 + 0.3906x21 + 0.3906x12 + 0.3906x22
+ 0.0156x00 + (-0.0781)x10 + (-0.0781)x20 +0.0156x30
+ (-0.0781)x01 + (-0.0781)x31 + (-0.0781)x02 + (-0.0781)x32
+ 0.0156x03 + (-0.0781)x13 + (-0.0781)x23 +0.0156x33


式は小数点数なので、FPGAに実装するに重みを整数とする必要がある。整数にするために重みを256倍して、演算してから8ビット右シフト(1/256)する必要がある。
256倍した時のバイキュービック法のM0, M1, M2の式を示す。
 ・a = -0.5の場合
M0 = 144x11 + 144x21 - 16x01 - 16x31
M1 = 144x11 + 144x21 - 16x10 - 16x13
M2 = 81x11 + 81x21 + 81x12 + 81x22 + x00 + (-9)x10 + (-9)x20 +x30
+ (-9)x01 + (-9)x31 + (-9)x02 + (-9)x32 + x03 + (-9)x13 + (-9)x23 +x33

 ・a = -1.0の場合
M0 = 160x11 + 160x21 - 32x01 - 32x31
M1 = 160x11 + 160x21 - 32x10 - 32x13
M2 = 81x11 + 81x21 + 81x12 + 81x22 + x00 + (-9)x10 + (-9)x20 +x30
+ (-9)x01 + (-9)x31 + (-9)x02 + (-9)x32 + x03 + (-9)x13 + (-9)x23 +x33
  1. 2017年02月10日 05:14 |
  2. 演算処理のFPGAへの適用方法
  3. | トラックバック:0
  4. | コメント:0

PYNQボードでカメラ画像をリアルタイムにFASTX コーナー検出2

PYNQボードでカメラ画像をリアルタイムにFASTX コーナー検出1”の続き。

前回、ブロックデザインが完成したので、今回は、ビットストリームまで生成して、SDKを立ち上げてアプリケーションソフトを作り、PYNQボードでカメラ画像をリアルタイムにFASTX コーナー検出してみよう。

まずは、前回からの続きでHDL のラッパーファイル(pynq_fastx_wrapper)を生成した。
pynq_de_fastx_18_170207.png

次に制約を書いた。
pynq_de_fastx_19_170207.png

set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data[0]}]

set_property IOSTANDARD LVCMOS33 [get_ports mt9d111_iic_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports mt9d111_iic_sda_io]
set_property IOSTANDARD LVCMOS33 [get_ports href]
set_property IOSTANDARD LVCMOS33 [get_ports pclk]
set_property IOSTANDARD LVCMOS33 [get_ports standby]
set_property IOSTANDARD LVCMOS33 [get_ports vsync]
set_property IOSTANDARD LVCMOS33 [get_ports xck]

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets pclk_IBUF]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets pclk_IBUF_BUFG]

create_clock -period 55.560 -name pclk -waveform {0.000 27.780} [get_ports pclk]
set_input_delay -clock [get_clocks pclk] 10.800 [get_ports {{cam_data[0]} {cam_data[1]} {cam_data[2]} {cam_data[3]} {cam_data[4]} {cam_data[5]} {cam_data[6]} {cam_data[7]} href pclk vsync}]

set_property PULLUP true [get_ports mt9d111_iic_scl_io]
set_property PULLUP true [get_ports mt9d111_iic_sda_io]

set_property PACKAGE_PIN W14 [get_ports {cam_data[7]}]
set_property PACKAGE_PIN V16 [get_ports {cam_data[6]}]
set_property PACKAGE_PIN Y14 [get_ports {cam_data[5]}]
set_property PACKAGE_PIN W16 [get_ports {cam_data[4]}]
set_property PACKAGE_PIN T11 [get_ports {cam_data[3]}]
set_property PACKAGE_PIN V12 [get_ports {cam_data[2]}]
set_property PACKAGE_PIN T10 [get_ports {cam_data[1]}]
set_property PACKAGE_PIN W13 [get_ports {cam_data[0]}]
set_property IOSTANDARD TMDS_33 [get_ports TMDS_tx_0_B_p]
set_property IOSTANDARD TMDS_33 [get_ports TMDS_tx_1_R_p]
set_property IOSTANDARD TMDS_33 [get_ports TMDS_tx_2_G_p]
set_property IOSTANDARD TMDS_33 [get_ports TMDS_tx_clk_p]
set_property PACKAGE_PIN U19 [get_ports href]
set_property PACKAGE_PIN Y16 [get_ports standby]
set_property PACKAGE_PIN K17 [get_ports TMDS_tx_0_B_p]
set_property PACKAGE_PIN K19 [get_ports TMDS_tx_1_R_p]
set_property PACKAGE_PIN J18 [get_ports TMDS_tx_2_G_p]
set_property PACKAGE_PIN L16 [get_ports TMDS_tx_clk_p]
set_property PACKAGE_PIN Y19 [get_ports vsync]
set_property PACKAGE_PIN W19 [get_ports xck]
set_property PACKAGE_PIN Y18 [get_ports mt9d111_iic_scl_io]
set_property PACKAGE_PIN U18 [get_ports mt9d111_iic_sda_io]
set_property PACKAGE_PIN Y17 [get_ports pclk]

set_false_path -from [get_clocks pclk] -to [get_clocks clk_fpga_0]
set_false_path -from [get_clocks clk_fpga_0] -to [get_clocks pclk]
set_false_path -from [get_clocks [get_clocks -of_objects [get_pins pynq_fastx_i/bitmap_disp_cntrler_axi_master_0/inst/dvi_disp_i/BUFR_pixel_clk_io/O]]] -to [get_clocks clk_fpga_0]


論理合成、インプリメント、ビットストリームを生成した。と書いているが、何度か制約を修正しながらコンパイルしている。
pynq_de_fastx_20_170208.png

成功した。Clock Domain Closing のレポートを示す。
まずは、clk_fpga_0 から pclk
pynq_de_fastx_26_170209.png

次に、pclk から clk_fpga_0
pynq_de_fastx_27_170209.png

pclk_buf から clk_fpga_0
pynq_de_fastx_28_170209.png

ハードウェアをエクスポートして、SDK を立ち上げ、アプリケーションソフトを書いた。
pynq_de_fastx_21_170208.png

fastx_on_serial.elf を起動した。

threshold = 5 の時の FASTXコーナー検出の結果を示す。
pynq_de_fastx_22_170208.png

pynq_de_fastx_23_170208.jpg

threshold = 10 の時の FASTXコーナー検出の結果を示す。
pynq_de_fastx_24_170208.png

pynq_de_fastx_25_170208.jpg

うまく行った。PYNQボードでカメラ画像をリアルタイムにFASTX コーナー検出できた。。。うれしい。。。

PYNQボードの写真を示す。脇はZYBO だ。
pynq_de_fastx_29_170209.jpg

pynq_de_fastx_30_170209.jpg
  1. 2017年02月09日 04:18 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

PYNQボードでカメラ画像をリアルタイムにFASTX コーナー検出1

このところ実装しているVivado HLS のFASTX コーナー検出IP をPYNQ ボードにも実装してカメラ画像からリアルタイムにFASTX コーナー検出を行った。

まずは、Vivado HLS で fastx プロジェクトをPYNQ のZynq 7020 (xc7z020clg400-1)用に再度合成を行った。
pynq_de_fastx_1_170206.png

ブロックデザインを最初から作るのはとっても面倒なので、ZYBO のV_ZYBO_CAMDS_FASTXt_164 フォルダのプロジェクトのブロックデザインをtcl スクリプトにエクスポートした。
pynq_de_fastx_2_170206.png

そのエクスポートされたV_ZYBO_CAMDS.tcl をコピー&ペーストして、名前を pynq_fastx.tcl に変更した。
pynq_de_fastx_3_170206.png

pynq_fastx.tcl をエディタで開いて、V_ZYBO_CAMDS を pynq_fastx に入れ替えた。
pynq_de_fastx_4_170206.png

Vivado 2016.4 でPYNQ のZynq 7020 (xc7z020clg400-1)用のプロジェクト PYNQ_FASTX_164 を新規作成した。
pynq_de_fastx_5_170206.png

Digilent 社のPYNQボードのResouce Center から Zynq Presets をダウンロードした。
pynq_de_fastx_6_170206.png

このZIPファイルの中には、pynq_revC.tcl が入っている。それをセーブしておく。
pynq_de_fastx_7_170206.png

ビットマップ・ディスプレイ・コントローラIP、カメラ・インターフェースAXI4-Stream IP、FASTX コーナー検出IP をコピー&ペーストして、IP Catalog でそれらのIP を追加する。

pynq_fastx プロジェクトの Tcl Console で

cd z:/PYNQ_FASTX_164/
source pynq_fastx.tcl

コマンドを入力し、pynq_fastx ブロックデザインを生成した。
pynq_de_fastx_8_170206.png

これだとまだ、PS がZYBO のままなので、PYNQボードに変更しよう。
Processing System_7 をダブルクリックして開く。
Re-customize IP ダイアログのPresets -> Apply Configuration... を選択する。
pynq_de_fastx_9_170206.png

先ほどダウンロードした pynq_revC.tcl を選択する。
pynq_de_fastx_10_170206.png

これでPYNQボードの設定に入れ替えられたが、クロックを3個使っていたり、AXI_GPポート、AXI_HP0, HP2 ポートを使っているので、デフォルト状態だとそれらのポートからの配線が無くなってしまう。よって、この状態でそれらの設定を行う必要がある。
まずは、PS-PL Configuration をクリックする。
pynq_de_fastx_11_170206.png

S AXI_HP0 と S AXI_HP2 にチェックを入れる。
pynq_de_fastx_12_170206.png

次に、Clock Configuration をクリックする。
FCLK_CLK1 とFCLK_CLK2 にチェックを入れて、FCLK_CLK1 を 25 MHz に設定する。FCLK_CLK2 を36 MHz に設定する。
pynq_de_fastx_13_170206.png

pynq_fastx ブロックデザインを示す。
pynq_de_fastx_14_170206.png

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

Address Editor を示す。これは、ZYBO と同じアドレスだ。
pynq_de_fastx_16_170207.png

Validate Design も通った。
pynq_de_fastx_17_170207.png
  1. 2017年02月08日 04:48 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

FPGAマガジンNo.16 のVivado HLS AXI4マスタ編のソースコード

FPGAマガジンNo.16 のVivado HLS AXI4マスタ編のソースコードをGithubの marsee101/FPGAmagagine16_Vivado_HLS_AXI4M に置いておきましたので、ご利用ください。
ソースコードの説明はFPGAマガジン No.16 をご覧ください。

具体的には、FPGAマガジン No.16 の

高位合成ツールVivado HLS 特設記事

第1章 【画像フィルタをハードウェア化!】無料のVivado HL WebPACK Editionで高位合成にチャレンジしよう(AXI4マスタ編)
アルゴリズム通りに記述したフィルタCプログラムをハードウェア化してみよう P.78

第2章 【ステップアップで高速化!】
無料のVivado HL WebPACK Editionで高位合成にチャレンジしよう(AXI4マスタ編)
ライン・バッファ/バースト転送/最適化指示子を駆使した高速化テクニック P.94

の記事中のソースコードと画像です。
  1. 2017年02月06日 21:56 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Zybotの障害物検知と回避1(超音波距離センサの性能)

Zybotの障害物検知と回避で使用するために、超音波距離センサの指向性を計ってみた。

まずは、現在のZybot の形状を貼っておく。
Zybot_1_170206.jpg

超音波距離センサはZybot のシャーシの下側に付いているので、床に置くと短い距離しか測れないので、平ワッシャをかませて、上向きにした。
Zybot_2_170206.jpg

指向性の測定方法としては、丸ごみ箱をZybot からある距離の正面に置いて距離を測定する。次に左側に位置をずらしていくと、あるところで丸ごみ箱の距離を測定できなくなる。そこまでのずらした距離を測定する。右側も同様に行う。

丸ごみ箱の寸法 下直径 22 cm、上直径 27.5 cm、高さ 34.5 cm
Zybotの寸法 幅 20 cm、長さ 34 cm、ベースの高さ 5.4 cm

Zybot と 丸ごみ箱の距離 60 cm
なお、これが、距離 60 cm の正面での超音波距離センサの測定の様子だ。
Zybot_3_170206.jpg

Zybot_4_170206.jpg

次に、距離 60 cm の左 30 cm での超音波距離センサの測定の様子だ。
Zybot_5_170206.jpg

正面でのZybot と丸ごみ箱の距離 62 cm
左 15 cm での距離 64 cm
左 30 cm での距離 70 cm
左 45 cm での距離 222 cm (ここで丸ごみ箱を検出できなくなる)

右 15 cm での距離 64 cm
右 30 cm での距離 70 cm
右 45 cm での距離 77 cm
右 50 cm での距離 222 cm (ここで丸ごみ箱を検出できなくなる)

右のほうが遠くまで検出できている。2つあるユニットの右側が受信なのか?

Zybot と 丸ごみ箱の距離 90 cm
正面でのZybot と丸ごみ箱の距離 92 cm
左 15 cm での距離 94 cm
左 30 cm での距離 98 cm
左 45 cm での距離 104 cm
左 60 cm での距離 204 cm (114 cm が混じっている)
左 64 cm での距離 204 cm (ここで丸ごみ箱を検出できなくなる)

右 15 cm での距離 94 cm
右 30 cm での距離 97 cm
右 45 cm での距離 103 cm
右 60 cm での距離 204 cm (114 cm が混じっている)
右 66 cm での距離 205 cm (ここで丸ごみ箱を検出できなくなる)

Zybot と 丸ごみ箱の距離 30 cm

ここでも測定している写真を撮った。
Zybot_6_170206.jpg

Zybot_7_170206.jpg

Zybot と 丸ごみ箱の距離 30 cm
正面でのZybot と丸ごみ箱の距離 31.7 cm
左 15 cm での距離 37 cm (222 cm も混じっている)
左 22 cm での距離 222 cm (ここで丸ごみ箱を検出できなくなる)

右 15 cm での距離 35 cm
右 30 cm での距離 203 cm (ここで丸ごみ箱を検出できなくなる)

ここで言えることは、30 cm まで接近したのでは、Zybot の距離センサでは、30 cm の距離に近づくと回避したとしてむ、回避しきれたかどうか超音波距離センサだけでは、分からないということだ。

よけきれかどうか超音波距離センサで検出するためには、回避動作を始める距離は少なくとも 60 cm くらいにしておく必要があるかもしれない。どちらの方向に回避するか?回避しきれたかどうか?は画像処理で行うようにしよう。
  1. 2017年02月06日 12:40 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

FPGAの部屋のブログのアクセス数が500万アクセスを突破しました

FPGAの部屋のブログのアクセス数が500万アクセスを突破しました。
FPGA_room_1_170205.png

思えば、2005年の5月にブログを書き始めてから、11年と8か月で500万アクセスに到達しました。これも皆さんが見て頂いたおかげだと思います。ありがとうございました。
これからも宜しくお願い致します。

おまけで、記録が残っている 2006年5月から2017年1月までの月別アクセス数のグラフです。段々と増えてきて、その後は変動しているのが分かると思います。
FPGA_room_2_170205.png
  1. 2017年02月05日 20:13 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

守谷ハーフマラソンの5kmの部に出場しました

去年は出られなかった守谷ハーフマラソンの5kmの部に出場しました。

午前7時35分頃、守谷の前川製作所(駐車場です)について、守谷市役所に行きました。守谷市役所はフロアをランナーのために開放してくれています。シートを敷いて場所を確保しました。
相変わらず盛況です。
moriya_marathon_1_170205.jpg

moriya_marathon_2_170205.jpg

9時35分に5km の部のスタートです。結構、アップダウンがあるので、きつかったですが、何とか走り切りました。結果は26分31秒でしたが、グロスです。ネットでは、たぶん 26分25秒くらいかな?この辺りが自己ベストです。5分17秒/km です。
何とか、25分切りたいですね。。。練習したいと思います。しかし、ほとんど風邪ひいて走れていなかったので、その方がタイム出るんでしょうか?
moriya_marathon_3_170205.jpg

去年の奥さんの5km の年代別優勝タイムは23分1秒なので、3分30秒の差があることになります。この差では見えませんね。。。

奥さんは、ハーフマラソンに出て、1時間49分くらいだったようです。自己ベスト更新だそうです。
  1. 2017年02月05日 19:58 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出による物体の形状検出4(FASTXコーナー検出の点だけを表示)

”FASTXコーナー検出による物体の形状検出3(FASTXプロジェクトの修正)”の続き。

前回は、FASTX コーナー検出の点だけを抽出するVivado HLS のプロジェクトを作成した。今回は、そのVivado HLS のIP をVivado のプロジェクトに入れて、カメラ画像からリアルタイムにFASTX コーナー検出の点を表示する。

まずは、V_ZYBO_CAMDS_FASTXt_164 をコピー&ペーストして、名前をV_ZYBO_CAMDS_FASTXt2_164 に変更した。
fastx_31_170204.png

V_ZYBO_CAMDS_FASTXt2_164 のfastx フォルダの内容を前回のVivado HLS のIP に置き換えた。
fastx_32_170204.png

V_ZYBO_CAMDS_FASTXt2_164 のVivado プロジェクトを起動し、ブロックデザインを開くと、Report IP Status が表示されるので、それをクリックした。
fastx_33_170204.png

下のウインドウにUP Status が表示される。Upgrade Selected ボタンをクリックする。
fastx_34_170204.png

Generate Output Products ダイアログが表示された。Generate ボタンをクリックした。
fastx_35_170204.png

Generate Output Products が成功した。
fastx_36_170204.png

IP Status のRerun をクリックしておこう。
fastx_37_170204.png

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

ビットストリームの生成が成功した。レポートを示す。
fastx_39_170204.png

ハードウェアをエクスポートして、SDK を立ち上げた。
今までのプロジェクトとプラットフォームを削除した。
fastx_40_170204.png

アプリケーション・プロジェクトを再生成した。
fash_on_serial プロジェクトを作成して、起動した。
fastx_41_170204.png

threshold = 5, 10, 20, 40, 60 の結果を示す。
fastx_42_170204.png

まずは原画像から。
fastx_43_170204.jpg

threshold = 5
fastx_44_170204.jpg

threshold = 10
fastx_45_170204.jpg

threshold = 20
fastx_46_170204.jpg

threshold = 40
fastx_47_170204.jpg

threshold = 60
fastx_48_170204.jpg
  1. 2017年02月04日 21:03 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出による物体の形状検出3(FASTXプロジェクトの修正)

FASTXコーナー検出による物体の形状検出2(Vivado HLS 2016.4 プロジェクト)”の続き。

前回は、FASTX コーナー検出のVivado HLS 2016.4 プロジェクトをやってみた。今回は、前回のFASTX コーナー検出は元の写真が入っているので、コーナー検出された結果から物体を検索することは難しいので、コーナー検出された位置のみを表示するようにしてみた。

まずは、fastx_2 プロジェクトを示す。
fastx_20_170202.png

C シミュレーションを行った。
fastx_21_170202.png

元画像
fastx_22_170203.jpg

元画像からこのようなFASTX コーナー検出の画像が生成できた。(theshold = 60)
fastx_23_170203.jpg

theshold = 20
fastx_30_170203.jpg

次にC コードの合成を行った。
fastx_24_170203.png

前回よりも今回のほうがリソース使用量が増えていた。

C/RTL協調シミュレーションを行った。
fastx_25_170203.png

500587で、前回とC/RTL協調シミュレーションの終了クロック時間は同じだった。

C/RTL協調シミュレーション波形を示す。
まずはAXI4-Stream の全体のシミュレーション波形を示す。
fastx_26_170203.png

1行分拡大した。
fastx_27_170203.png

更に、1行の終了部分と次の行の開始部分を拡大した。
fastx_28_170203.png

前回との違いは特にない様だ。

最後のIP へのエクスポートを行った
Vivado RTL Synthesis にチェックを入れてOK ボタンをクリックした。

IP へのエクスポートの結果を示す。
fastx_29_170203.png

fastx_2.cpp を貼っておく。

// fastx_2.cpp
// 2016/04/02 by marsee
// 2016/04/09 : FAST Corners Detection
// 2017/02/03 : 元の写真を入れないで、コーナー検出の点だけ表示した

#include "fastx.h"

void fastx_corner_det(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int cols, int threshold) {
#pragma HLS INTERFACE ap_stable port=threshold
#pragma HLS INTERFACE s_axilite port=threshold
#pragma HLS DATAFLOW
#pragma HLS INTERFACE ap_stable port=cols
#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis port=OUTPUT_STREAM
#pragma HLS INTERFACE axis port=INPUT_STREAM
#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows

    RGB_IMAGE img_0(rows, cols);
    RGB_IMAGE img_1(rows, cols);
    RGB_IMAGE img_1_(rows, cols);
 #pragma HLS STREAM variable=img_1_.data_stream depth=8192
    // FASTX に最大 7 ラインのレイテンシ、Dilate に最大 3 ラインのレイテンシがあるそうだ
    // 1ラインのピクセル数X10 ラインのFIFO バッファが必要 800x10 < 8192 (2の13乗)
    // http://japan.xilinx.com/support/documentation/application_notes/xapp1167.pdf
    // の 10 ページ参照

    GRAY_IMAGE img_1g(rows, cols);
    GRAY_IMAGE mask(rows, cols);
    GRAY_IMAGE dmask(rows, cols);
    GRAY_IMAGE img_2g(rows, cols);
    RGB_IMAGE img_3(rows, cols);
    RGB_PIXEL color(25500);

    hls::AXIvideo2Mat(INPUT_STREAM, img_0);
    hls::Duplicate(img_0, img_1, img_1_);
    hls::CvtColor<HLS_BGR2GRAY>(img_1, img_1g);
    hls::FASTX(img_1g, mask, threshold, true);
    hls::Dilate(mask, dmask);
    //hls::PaintMask(img_1_, dmask, img_3, color);
    hls::CvtColor<HLS_GRAY2BGR>(dmask, img_3);
    hls::Mat2AXIvideo(img_3, OUTPUT_STREAM);
}



なお、OpenCV の画像とハードウェア化関数の画像は全く違うので、テストベンチの fastx_tb.cpp ではエラーを表示するprintf() をコメントアウトしてある。
  1. 2017年02月03日 05:08 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出による物体の形状検出2(Vivado HLS 2016.4 プロジェクト)

FASTXコーナー検出による物体の形状検出1(Vivado 2016.4 プロジェクト)”の続き。

前回は、Vivado 2016.1 のカメラ画像から FASTX コーナー検出を行うプロジェクトをVivado 2016.4 に変換して動作を確認した。
今回は、後先違ってしまったが、FASTX コーナー検出を行う fastx.cpp のVivado HLS 2016.4 プロジェクトを示す。
前回やったときはC/RTL協調シミュレーションが出来なかったが、今回はできるだろうか?(前回やったVivado HLS 2016.1 のプロジェクトは、”FASTXコーナー検出IPのカメラ表示システム3(Vivado HLS 2016.1でやってみた1)”を参照のこと)

まずは、fastx プロジェクトを示す。
fastx_11_170202.png

C シミュレーションを行った。相変わらず、エラーがたくさん出ている。
fastx_11_170202.png

C コードの合成を行った。
fastx_13_170202.png

Vivado HLS 2016.1 の使用リソースと比べると、明らかにVivado HLS 2016.4 のほうが使用リソースが少ない。Vivado HLS が進化しているようだ。

C/RTL協調シミュレーションを行った。
fastx_14_170202.png

Vivado HLS 2016.1 では完了しなかったC/RTL協調シミュレーションがVivado HLS 2016.4 では完了した。
500587 クロックだった。1ピクセル1クロックとすると、480000 クロックなので、1ピクセル当たりの平均処理クロック数は、500587 / 480000 ≒ 1.04 クロックだった。

C/RTL協調シミュレーション波形を示す。
まずはAXI4-Stream の全体のシミュレーション波形を示す。
fastx_15_170202.png

1行分拡大してみた。
fastx_16_170202.png

更に、1行の終了部分と次の行の開始部分を拡大した。
fastx_17_170202.png

やはり、1行の処理が終了した後と次の行が始まる前にWait が入る。

最後のIP へのエクスポートを行った。
fastx_18_170202.png

Vivado RTL Synthesis にチェックを入れてOK ボタンをクリックした。

IP へのエクスポートの結果を示す。
fastx_19_170202.png

Timing もメットしている。

fastx.h を貼っておく。

// 2016/04/03 : GRAY_IMAGE を追加

#ifndef __FASTX_H__
#define __FASTX_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Scalar<3unsigned char> RGB_PIXEL;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;
#endif


fastx.cpp を貼っておく。

// fastx.cpp
// 2016/04/02 by marsee
// 2016/04/09 : FAST Corners Detection

#include "fastx.h"

void fastx_corner_det(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int cols, int threshold) {
#pragma HLS INTERFACE ap_stable port=threshold
#pragma HLS INTERFACE s_axilite port=threshold
#pragma HLS DATAFLOW
#pragma HLS INTERFACE ap_stable port=cols
#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis port=OUTPUT_STREAM
#pragma HLS INTERFACE axis port=INPUT_STREAM
#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows

    RGB_IMAGE img_0(rows, cols);
    RGB_IMAGE img_1(rows, cols);
    RGB_IMAGE img_1_(rows, cols);
#pragma HLS STREAM variable=img_1_.data_stream depth=8192
    // FASTX に最大 7 ラインのレイテンシ、Dilate に最大 3 ラインのレイテンシがあるそうだ
    // 1ラインのピクセル数X10 ラインのFIFO バッファが必要 800x10 < 8192 (2の13乗)
    // http://japan.xilinx.com/support/documentation/application_notes/xapp1167.pdf
    // の 10 ページ参照

    GRAY_IMAGE img_1g(rows, cols);
    GRAY_IMAGE mask(rows, cols);
    GRAY_IMAGE dmask(rows, cols);
    GRAY_IMAGE img_2g(rows, cols);
    RGB_IMAGE img_3(rows, cols);
    RGB_PIXEL color(25500);

    hls::AXIvideo2Mat(INPUT_STREAM, img_0);
    hls::Duplicate(img_0, img_1, img_1_);
    hls::CvtColor<HLS_BGR2GRAY>(img_1, img_1g);
    hls::FASTX(img_1g, mask, threshold, true);
    hls::Dilate(mask, dmask);
    hls::PaintMask(img_1_, dmask, img_3, color);
    hls::Mat2AXIvideo(img_3, OUTPUT_STREAM);
}


fastx_tb.cpp を貼っておく。

// fastx_tb.cpp
// 2016/04/02 by marsee
// OpenCV 2 の Mat を使用したバージョン
// 2016/04/09 : FAST Corners Detection

#include <iostream>
#include "hls_opencv.h"
#include "fastx.h"

using namespace cv;

#define INPUT_IMAGE     "test.jpg"
#define OUTPUT_IMAGE    "test_result.jpg"
#define OUTPUT_IMAGE_CV "test_result_cv.jpg"

#define THESHOLD_LEVEL    60

void fastx_corner_det(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int cols, int threshold);
void opencv_fastx_corner_det(Mat& src, Mat& dst, int threshold);

int main (int argc, char** argv) {
    // OpenCV で 画像を読み込む
    Mat src = imread(INPUT_IMAGE);
    AXI_STREAM src_axi, dst_axi;

    // Mat フォーマットから AXI4 Stream へ変換
    cvMat2AXIvideo(src, src_axi);

    // fastx_corner_det() 関数をコール
    fastx_corner_det(src_axi, dst_axi, src.rows, src.cols, THESHOLD_LEVEL);

    // AXI4 Stream から Mat フォーマットへ変換
    // dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
    Mat dst(src.rows, src.cols, CV_8UC3);
    AXIvideo2cvMat(dst_axi, dst);

    // Mat フォーマットからファイルに書き込み
    imwrite(OUTPUT_IMAGE, dst);

    // opencv_fastx_corner_det() をコール
    Mat dst_cv(src.rows, src.cols, CV_8UC3);
    opencv_fastx_corner_det(src, dst_cv, THESHOLD_LEVEL);
    imwrite(OUTPUT_IMAGE_CV, dst_cv);

    // dst と dst_cv が同じ画像かどうか?比較する
    for (int y=0; y<src.rows; y++){
        Vec3b* dst_ptr = dst.ptr<Vec3b>(y);
        Vec3b* dst_cv_ptr = dst_cv.ptr<Vec3b>(y);
        for (int x=0; x<src.cols; x++){
            Vec3b dst_bgr = dst_ptr[x];
            Vec3b dst_cv_bgr = dst_cv_ptr[x];

            // bgr のどれかが間違っていたらエラー
            if (dst_bgr[0] != dst_cv_bgr[0] || dst_bgr[1] != dst_cv_bgr[1] || dst_bgr[2] != dst_cv_bgr[2]){
                printf("x = %d, y = %d,  Error dst=%d,%d,%d dst_cv=%d,%d,%d\n", x, y,
                        dst_bgr[0], dst_bgr[1], dst_bgr[0], dst_cv_bgr[0], dst_cv_bgr[1], dst_cv_bgr[2]);
                //return 1;
            }
        }
    }
    printf("Test with 0 errors.\n");

    return 0;
}

void opencv_fastx_corner_det(Mat& src, Mat& dst, int threshold){
     src.copyTo(dst); // 深いコピー
     std::vector<Mat> layers;
     std::vector<KeyPoint> keypoints;
     split(src, layers);
     FAST(layers[0], keypoints, threshold, true);
     for (int i = 0; i < keypoints.size(); i++) {
         rectangle(dst,
                 Point(keypoints[i].pt.x-1, keypoints[i].pt.y-1),
                 Point(keypoints[i].pt.x+1, keypoints[i].pt.y+1),
                 Scalar(255,0), CV_FILLED);
     }
}

  1. 2017年02月02日 04:52 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出による物体の形状検出1(Vivado 2016.4 プロジェクト)

以前、”FASTX コーナー検出の改良3(threshold をソフトウェアで変更可能にする)”で、Vivado HLS のOpenCV 機能の一部であるFASTX コーナー検出を使用して、CMOS カメラで撮影した画像のコーナー検出を行った。
今回は、FASTX コーナー検出のプロジェクトを Vivado 2016.4 に変換して動作を確かめた。

まずは、Vivado 2016.1 版を動かしてみると、CMOSカメラ画像で、細い線を見るととぎれとぎれになっている。これは、ビットマップ・ディスプレイ・コントローラは64 ビットでデータを転送して、1ピクセルは 32 ビットである。よって、ビットマップ・ディスプレイ・コントローラは1転送で 2 ピクセルを転送していることになるが、そのピクセルの位置が反転している状態になっていた。それを、修正した。
具体的には、Z:\V_ZYBO_CAMDS_FASTXt_164\BMDispCaL\sources\verilog フォルダのbitmap_disp_engine.v ファイルの bitmap_afifo の din への信号の割り当てを変更した。

//.din(data_in), // Bus [63 : 0]
.din({data_in[31:0], data_in[63:32]}), // Bus [63 : 0]


Vivado 2016.1 からVivado 20164 へのアップグレードは、Vivado HLS で作った fastx_corner_det はVivado HLS 2016.4 で作り直したが、それ以外はすんなりとアップグレードできた。
プロジェクトを示す。もうビットストリームの生成済みだ。
fastx_1_170201.png

ブロックデザインを示す。上が全体で、下が camera_interface モジュールだ。
fastx_2_170201.png

fastx_3_170201.png

ハードウェアをエクスポートして、SDK を立ち上げた。
fastx_4_170201.png

SDK 2016.3 からだろうか?GDB でELF ファイルを RUN してもエラーになってしまう。
System Debugger を使ってRUN する必要があるようになった。しかし、System Debugger を使用すると、ELF ソフトウェアを2回実行できないようなのだ。今までのGDB の時は、カメラの設定用のソフトウェアを起動してから、FASTX を起動するソフトウェアを実行してきたが、System Debugger を使用すると、カメラの設定をして、FASTX を起動するソフトウェアを書く必要があるようだ。すべて1つのソフトウェアで完結するように書くしかなくなってしまった。
そのように fastx_on_serial.c を書き直して、ビルドした。
ZYBO の電源をON して、FASTX のスレッショルドを設定するためにTera Term を起動し、シリアルに接続した。
SDK からビットストリームをZYBO にダウンロードして、fastx_on_serial.elf をSystem Debugger で起動した。
Tera Term の画面を示す。
fastx_5_170201.png

Tera Term の画面に示すように、threshold は 5, 10, 20, 40, 60 でやってみた。

その画像を示す。
threshold = 5
fastx_6_170201.jpg

threshold = 10
fastx_7_170201.jpg

threshold = 20
fastx_8_170201.jpg

threshold = 40
fastx_9_170201.jpg

threshold = 60
fastx_10_170201.jpg

うまく行っている。

最後に、fastx_on_serial.c を示す。

// fastx_on_serial.c
// 2017/04/13 by marsee
//
// Refered to http://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2014_4/ug902-vivado-high-level-synthesis.pdf
//
// 2016/04/21 : threshold をシリアル経由で入力する
// 2017/01/30 : すべての処理を記述するように変更
//

#include <stdio.h>
#include <stdlib.h>
#include "xfastx_corner_det.h"
#include "xparameters.h"
#include "xaxivdma.h"
#include "xil_io.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 FASTX_THRESHOLD        20

#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(){
    XFastx_corner_det Ximage_filter;
    XFastx_corner_det_Config *Ximage_filterPtr;
    XAxiVdma_Config *XAxiVdma0_Config;
    XAxiVdma XAxiVdma0;
    int XAxiVdma0_Status;
    int threshold;

    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);
    }

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

    // Initialize the Device
    int Xlap_status = XFastx_corner_det_CfgInitialize(&Ximage_filter, Ximage_filterPtr);
    if (Xlap_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XFastx_corner_det\n");
        return(-1);
    }

    // image_filter rows, cols, threshold=20 set
    XFastx_corner_det_Set_cols(&Ximage_filter, (u32)800);
    XFastx_corner_det_Set_rows(&Ximage_filter, (u32)600);
    XFastx_corner_det_Set_threshold(&Ximage_filter, (u32)FASTX_THRESHOLD);

    // 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

    // fastx filter AXIS Start
    XFastx_corner_det_Start(&Ximage_filter);
    XFastx_corner_det_EnableAutoRestart(&Ximage_filter);

    // 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

    // 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

    // threshold set
    while(1){
        printf("\n\rthreshold = ");
        scanf("%d", &threshold);
        if (threshold == 999)
            break;
        
        XFastx_corner_det_Set_threshold(&Ximage_filter, (u32)threshold);
    }

    return(0);
}

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