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

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

FPGAの部屋

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

Vivado 2015.4による組み合わせ回路の場合の消費電力解析

組み合わせ回路をVivado 2015.4でインプリメントすると消費電力解析でとんでもない値が出てしまう。それを解消する方法を探っていく。

まずは、”Vivado hls勉強会1(基礎編)”で使用しているVivado HLS 2015.4 の掛け算IPの multi_apuint プロジェクトをVivado 2015.4 の multi_ex1 プロジェクトにインポートして実機で確認するのだが、インプリメントした時にPower が 6.6W も消費する計算になって赤くなってしまう。これを解消したいと思った。実際は、スライドスイッチで入力を入れて、掛け算出力をLEDに表示するので、消費電力は多くなるわけがないはずだ。

2016/02/28 追記: 、”Vivado hls勉強会1(基礎編)”を書き換えてしまったので、以前の multi_bd_wrapper.xdc を貼っておきます)

# multi_bd_wrapper.xdc
# 2015/07/02 by marsee
#

##Switches
##IO_L19N_T3_VREF_35 sw0
set_property PACKAGE_PIN G15 [get_ports {In0_1[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {In0_1[0]}]

##IO_L24P_T3_34 sw1
set_property PACKAGE_PIN P15 [get_ports {In0_1[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {In0_1[1]}]

##IO_L4N_T0_34 sw2
set_property PACKAGE_PIN W13 [get_ports {In0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {In0[0]}]

##IO_L9P_T1_DQS_34 sw3
set_property PACKAGE_PIN T16 [get_ports {In0[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {In0[1]}]

##LEDs
##IO_L23P_T3_35 Dout[0]
set_property PACKAGE_PIN M14 [get_ports {Dout[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Dout[0]}]

##IO_L23N_T3_35 Dout[1]
set_property PACKAGE_PIN M15 [get_ports {Dout[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Dout[1]}]

##IO_0_35 Dout[2]
set_property PACKAGE_PIN G14 [get_ports {Dout[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Dout[2]}]

##IO_L3N_T0_DQS_AD1N_35 Dout[3]
set_property PACKAGE_PIN D18 [get_ports {Dout[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Dout[3]}]


まずは、multi_apuint (ここでは、multi_apuint2 になっている)のVivado HLS 2015.4 プロジェクトを見ていこう。
Power_Analysis_1_160228.png

multi_apuint.cpp を示す。

// multi_apuint.cpp

#include <ap_int.h>

void multi_apuint(ap_uint<8> multi_in0, ap_uint<8> multi_in1,
        ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE ap_none port=multi_out
#pragma HLS INTERFACE ap_none port=multi_in1
#pragma HLS INTERFACE ap_none port=multi_in0
    *multi_out = multi_in0 * multi_in1;
}


HDLへ合成した時の multi_apuint.v を示す。

// ==============================================================
// RTL generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2015.4
// Copyright (C) 2015 Xilinx Inc. All rights reserved.
// 
// ===========================================================

`timescale 1 ns / 1 ps 

(* CORE_GENERATION_INFO="multi_apuint,hls_ip_2015_4,{HLS_INPUT_TYPE=cxx,HLS_INPUT_FLOAT=0,HLS_INPUT_FIXED=1,HLS_INPUT_PART=xc7z010clg400-1,HLS_INPUT_CLOCK=10.000000,HLS_INPUT_ARCH=others,HLS_SYN_CLOCK=6.380000,HLS_SYN_LAT=0,HLS_SYN_TPT=none,HLS_SYN_MEM=0,HLS_SYN_DSP=1,HLS_SYN_FF=0,HLS_SYN_LUT=0}" *)

module multi_apuint (
        multi_in0_V,
        multi_in1_V,
        multi_out_V
);

parameter    ap_true = 1'b1;
parameter    ap_const_logic_1 = 1'b1;
parameter    ap_const_logic_0 = 1'b0;

input  [7:0] multi_in0_V;
input  [7:0] multi_in1_V;
output  [15:0] multi_out_V;

wire   [7:0] r_V_fu_53_p0;
wire   [7:0] r_V_fu_53_p1;
wire   [15:0] r_V_fu_53_p00;
wire   [15:0] r_V_fu_53_p10;


assign multi_out_V = (r_V_fu_53_p0 * r_V_fu_53_p1);

assign r_V_fu_53_p0 = r_V_fu_53_p00;

assign r_V_fu_53_p00 = multi_in1_V;

assign r_V_fu_53_p1 = r_V_fu_53_p10;

assign r_V_fu_53_p10 = multi_in0_V;

endmodule //multi_apuint


これを掛け算IPとして追加した Vivado 2015.4 の multi_ex1 プロジェクトを示す。
Power_Analysis_2_160228.png

インプリメントした時のProject Summary を示す。
Power_Analysis_3_160228.png

Power が 6.616W になっていてむちゃくちゃな値になっている。

Implementation -> Open Implemented Design -> Report Power をクリックして消費電力解析を行った。
Power_Analysis_4_160228.png

その結果、Dout の消費電力が凄いことになっている。また、Signal Rate が大変高くなっている。人間が手で入力するので、限りなく静的だと思うのだが。。。([Signal Rate] (信号レート)参照

参考URL
AR# 62437 2014.3 Vivado 消費電力 - 「set_switching_activity -signal_rate」はグリッチ消費電力解析に影響するか
Vivado Design Suite ユーザー ガイド 消費電力解析および最適化 UG907 (v2015.3) 2015 年 9 月 30 日


それを解消するために制約ファイル(XDC)に制約を追加することにした。
制約は、set_switching_activity で、これについての情報は、”Vivado Design Suite Tcl コマンド リファレンス ガイド UG835 (v2015.4) 2015 年 11 月 18 日”の第3章 : Tcl コマンド リスト (アルファベット順 )、1,320ページの”set_switching_activity”に詳しく書いてある。
検討して以下の制約を追加することにした。

set_switching_activity -signal_rate 1 -static_probability .99 [get_ports]


すべてのポートの signal_rate を 1 に、スタティック確率値を 0.99 にした。
これで、もう一度インプリメントしてみると、消費電力解析は正常と思える値になった。
Power_Analysis_5_160228.png

もう一度、Implementation -> Open Implemented Design -> Report Power をクリックして消費電力解析を行った。
Power_Analysis_6_160228.png

問題が無くなった。
最初からこういう結果を出してくれれば問題なかったと思うのだが。。。クロックが無いと -signal_rate のデフォルト値は 0.0 と書いてあるのだが、このデフォルト値が効かなくなって、最大値になるような感じだね?なぜだろうか?バグなのかな?
  1. 2016年02月28日 07:24 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:2

左目カメラ画像、右目カメラ画像のBMPファイルをVGAサイズ+グレースケールに変換

左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した”で作った左目カメラ画像と右目カメラ画像のSVGAサイズのBMPファイルをVGAサイズに変更して、グレースケールにも変更する。

変換するには、Linux のコマンドの convert を使用して、シェルスクリプトのコマンドを作製する。

主に参考にさせていただいたWebページは、”bashで始めるシェルスクリプト基礎の基礎”でした。

convert_calibf という名前でシェルスクリプトを作成した。

chmod +x convert_calibf で実行権限を与えた。
stereo_calib_29_160226.png

./convert_calibf
で実行したが、convert コマンドが無いので、imagemagick と graphicsmagick-imagemagick-compat を apt-get install でインストールしろと言われた。
stereo_calib_30_160226.png

最初に
sudo apt-get install imagemagick
を実行した。
stereo_calib_31_160226.png

次に
sudo apt-get install graphicsmagick-imagemagick-compat
を実行した。
stereo_calib_32_160226.png

convert -gemonetory 640x480 -depth 8 -type GrayScale left_0.bmp left0.jpg

コマンドを実行したところ、left0.jpg ができた。
stereo_calib_33_160226.png

left0.jpg ができていて、白黒になっているのが分かる。
stereo_calib_34_160226.png

left_0.bmp、left0.jpg を並べてみた。画像が800 x 600 から 640 x 480 に縮小されている。
stereo_calib_35_160226.jpg

left_?.bmp を left?.bmp に名前を変更して、
./convert_calibf
を起動したところ、left?.jpg、right?.jpg ができた。
stereo_calib_36_160226.png

stereo_calib_37_160226.png

最後に convert_calib を貼っておく。

#!/bin/sh

for file in *.bmp
do
    convert -geometry 640x480 -depth 8 -type GrayScale $file ${file%bmp}jpg
done

  1. 2016年02月27日 03:58 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した

OpenCV 2.4.10 の stereo_match.cpp をやってみた”と”OpenCV 2.4.10 の stereo_match.cpp をやってみた2”でやってみたOpenCVのステレオカメラのキャリブレーションを自分の環境でもやってみたくなって、左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した。

2016/02/26:修正 左目カメラ画像の名前が left_0.bmp になってしまって、”_”が余計だったので、left0.bmp になるようにアプリケーションを修正しました。)

左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションは、RL_capture_bmp.cpp という名前にした。
引数で左目カメラ画像と右目カメラ画像の名前を指定できる。デフォルトではそれぞれ、left, right という名前で、その後に番号とBMPファイルの拡張子が付く。(left00.bmp, right00.bmp left01.bmp, right01.bmp)
2016/03/22: -n オプションを追加)

// -l : left bmp file name
// -r : right bmp file name
// -n : Start File Number
// -h : help


stereo_calib_24_160225.png

g++ でコンパイルを行った。
g++ RL_capture_bmp.cpp -o RL_capture_bmp

./RL_capture_bmp で起動した。
コマンドは2つで w で左目カメラ画像と右目カメラ画像をBMPファイルに保存する。q で Exit する。
stereo_calib_25_160225.png

left00.bmp, right00.bmp left01.bmp, right01.bmp left02.bmp, right02.bmp ができているのが分かる。
stereo_calib_26_160225.png

left00.bmp を表示してみた。
stereo_calib_27_160225.jpg

right00.bmp を表示してみた。
stereo_calib_28_160225.jpg

やはり、細かいドットが出ているがステレオカメラのキャリブレーションは大丈夫だろうか?ダメならば、ここをデバックする必要があるかも知れない?

最後に、RL_capture_bmp.cpp を貼っておく。

//
// RL_capture_bmp.cpp
// 2016/02/24
// 2016/03/22 : -n : Start File Number
//
// This software converts the left and right of the camera image to BMP file.
// -l : left bmp file name
// -r : right bmp file name
// -n : Start File Number
// -h : help
//

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

#include "bmpheader.h"

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

#define XGA_HORIZONTAL_PIXELS   1024
#define XGA_VERTICAL_LINES       768
#define XGA_ALL_DISP_ADDRESS    (XGA_HORIZONTAL_PIXELS * XGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define XGA_3_PICTURES          (XGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES) 

#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 SVGA_3_PICTURES         (SVGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES)

int WriteBMPfile(FILE *fbmp, volatile unsigned int *frame_buffer, BMP24FORMAT **bmp_data);

int main(int argc, char *argv[]){
    int opt;
    int c, help_flag=0;
    char left_bmp_fn[256] = "left";
    char right_bmp_fn[256] = "right";
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    volatile unsigned int *Leye_addr, *Reye_addr;
    int i, j;
    int file_no = 0;
    FILE *fbmp;
    BMP24FORMAT **bmp_data; // 24 bits Date of BMP files (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES)

    while ((opt=getopt(argc, argv, "l:r:n:h")) != -1){
        switch (opt){
            case 'l':
                strcpy(left_bmp_fn, optarg);
                break;
            case 'r':
                strcpy(right_bmp_fn, optarg);
                break;
            case 'n':
                file_no = atoi(optarg);
                break;
            case 'h':
                help_flag = 1;
                break;
        }
    }
   
    if (help_flag == 1){ // help
        printf("Usage : RL_capture_bmp [-l <left bmp file name>] [-r <right bmp file name>] [-n <Start File Number>] [-h]\n");
    }

    // udmabuf0
    int fdf = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fdf == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    volatile unsigned *frame_buffer = (volatile unsigned *)mmap(NULL, SVGA_3_PICTURES+XGA_3_PICTURES+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);

    // allocated the memory for bmp file
    if ((bmp_data=(BMP24FORMAT **)malloc(sizeof(BMP24FORMAT *)*SVGA_VERTICAL_LINES)) == NULL){
        fprintf(stderr, "Can not allocate memory of the first dimension of SVGA_VERTICAL_LINES of bmp_data\n");
        exit(1);
    }
    for (i=0; i<SVGA_VERTICAL_LINES; i++){
        if ((bmp_data[i]=(BMP24FORMAT *)malloc(sizeof(BMP24FORMAT) * SVGA_HORIZONTAL_PIXELS)) == NULL){
            fprintf(stderr, "Can not allocate %d th memory of the first dimension of bmp_data\n", i);
            exit(1);
        }
    }
   
    // assigned the left and right eys's frame buffer
    Leye_addr = frame_buffer; // The Left Camera Image
    Reye_addr = (volatile unsigned int *)((unsigned)frame_buffer+SVGA_3_PICTURES+0x8); // The Right Camera Image

    char lbmp_file[256];
    char rbmp_file[256];

    // w - writed the left and right eye's bmp files.  q - exit.
    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case 'w' : // w - writed the left and right eye's bmp files.
                // writed the left and right eys's frame buffer
                sprintf(lbmp_file, "left%d.bmp", file_no);
                if ((fbmp=fopen(lbmp_file, "wb")) == NULL){
                    fprintf(stderr, "Cannot open %s in binary mode\n", lbmp_file);
                    exit(1);
                }
                WriteBMPfile(fbmp, Leye_addr, bmp_data);
                fclose(fbmp);

                sprintf(rbmp_file, "right%d.bmp", file_no);
                if ((fbmp=fopen(rbmp_file, "wb")) == NULL){
                    fprintf(stderr, "Cannot open %s in binary mode\n", rbmp_file);
                    exit(1);
                }
                WriteBMPfile(fbmp, Reye_addr, bmp_data);
                fclose(fbmp);
                
                printf("file No. = %d\n", file_no);

                file_no++;
                break;
        }
        c = getc(stdin);
    }

    for(i=0; i<SVGA_VERTICAL_LINES; i++){
        free(bmp_data[i]);
    }
    free(bmp_data);
    munmap((void *)frame_buffer, (SVGA_3_PICTURES+XGA_3_PICTURES+SVGA_ALL_DISP_ADDRESS));
    close(fdf);
    
    return(0);
}

int WriteBMPfile(FILE *fbmp, volatile unsigned *frame_buffer, BMP24FORMAT **bmp_data){
    BITMAPFILEHEADER bmpfh; // file header for a bmp file
    BITMAPINFOHEADER bmpih; // INFO header for BMP file

    // Copy the camera color data of the bmp_data (data of BMP when its starts from lower left)
    for (int i=0; i<SVGA_VERTICAL_LINES; i++){
        for (int j=0; j<SVGA_HORIZONTAL_PIXELS; j++){
            bmp_data[(SVGA_VERTICAL_LINES-1)-i][j].red = (frame_buffer[i*SVGA_HORIZONTAL_PIXELS+j]>>16)&0xff;
            bmp_data[(SVGA_VERTICAL_LINES-1)-i][j].green = (frame_buffer[i*SVGA_HORIZONTAL_PIXELS+j]>>8)&0xff;
            bmp_data[(SVGA_VERTICAL_LINES-1)-i][j].blue = (frame_buffer[i*SVGA_HORIZONTAL_PIXELS+j])&0xff;
        }
    }

    // Assign a value to the file header of the BMP file
    bmpfh.bfType = 0x4d42;
    bmpfh.bfSize = SVGA_HORIZONTAL_PIXELS*SVGA_VERTICAL_LINES*3+54;
    bmpfh.bfReserved1 = 0;
    bmpfh.bfReserved2 = 0;
    bmpfh.bfOffBits = 0x36;

    // Assign a value to the INFO header of the BMP file
    bmpih.biSize = 0x28;
    bmpih.biWidth = SVGA_HORIZONTAL_PIXELS;
    bmpih.biHeight = SVGA_VERTICAL_LINES;
    bmpih.biPlanes = 0x1;
    bmpih.biBitCount = 24;
    bmpih.biCompression = 0;
    bmpih.biSizeImage = 0;
    bmpih.biXPixPerMeter = 3779;
    bmpih.biYPixPerMeter = 3779;
    bmpih.biClrUsed = 0;
    bmpih.biClrImporant = 0;

    // Writing of BMP file header
    fwrite(&bmpfh.bfType, sizeof(char), 2, fbmp);
    fwrite(&bmpfh.bfSize, sizeof(long), 1, fbmp);
    fwrite(&bmpfh.bfReserved1, sizeof(short), 1, fbmp);
    fwrite(&bmpfh.bfReserved2, sizeof(short), 1, fbmp);
    fwrite(&bmpfh.bfOffBits, sizeof(long), 1, fbmp);

    // Writing of BMP INFO header
    fwrite(&bmpih, sizeof(BITMAPINFOHEADER), 1, fbmp);

    // Writing of lbmp_data anr rbmp_data
    for (int i=0; i<SVGA_VERTICAL_LINES; i++) {
        for (int j=0; j<SVGA_HORIZONTAL_PIXELS; j++) {
            fputc((int)bmp_data[i][j].blue, fbmp);
            fputc((int)bmp_data[i][j].green, fbmp);
            fputc((int)bmp_data[i][j].red, fbmp);
        }
    }
}

  1. 2016年02月25日 05:22 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

OpenCV 2.4.10 の stereo_match.cpp をやってみた2

OpenCV 2.4.10 の stereo_match.cpp をやってみた”の続き。

stereo_match.cpp をやってみて、面白かったので、もう少しやってみた。

./stereo_match -i intrinsics.yml -e extrinsics.yml left02.jpg right02.jpg
stereo_calib_21_160223.png

stereo_calib_22_160223.jpg

./stereo_match -i intrinsics.yml -e extrinsics.yml left03.jpg right03.jpg
stereo_calib_23_160223.png

stereo_calib_24_160223.jpg

OpenCV 2.4.10 の stereo_match.cpp をやってみた”の時の -p オプションだが、ソースコードには、”point_cloud file”と書かれている。これは、”Point Cloud Library(PCL)という、フリーでオープンソースの3Dポイントクラウド(点群)データ処理をまとめたライブラリ”じゃないか?と思っている。(”【シリーズ「PCLを触ってみよう!」第一回】3D点群処理ライブラリ「Point Cloud Library」の概要”から引用)

参考になるWebページを貼っておく。
【シリーズ「PCLを触ってみよう!」第一回】3D点群処理ライブラリ「Point Cloud Library」の概要
【シリーズ】「PCLを触ってみよう!」 第2回改訂版:Point Cloud Library1.x(1.3以降)のインストール(Windows編)
【シリーズ】「PCLを触ってみよう!」 第3回:Point Cloudデータの読み込みと可視化
ここのリンクにある”The PCD (Point Cloud Data) file format”のファイルフォーマットは -p オプションで出力したファイルにそっくりだ。

Google で”【シリーズ】「PCLを触ってみよう!」”で検索した結果

ということは、-p オプションで出力したファイルがあれば、The CloudViewer で、たぶんdisparity(視差)ウインドウが見られるということ?かな?
  1. 2016年02月25日 04:35 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

OpenCV 2.4.10 の stereo_match.cpp をやってみた

OpenCV 2.4.10 の stereo_calib.cpp をやってみた”の続き。

前回は、stereo_calib.cpp を使って、左目カメラ画像と右目カメラ画像のキャリブレーションを行い、2つのキャリブレーション・ファイルを出力した。今回は、stereo_match.cpp を使用して、2つのキャリブレーション・ファイルを入力して、左目カメラ画像と右目カメラ画像を補正してみた。

この辺りの情報は日本語、英語を問わずに少ない気がするので、苦労している。

~/OpenCV/opencv-2.4.10/samples/cpp を見ると、stereo_match.cpp があった。
stereo_calib_12_160220.png

これを、この前作成した ~/OpenCV/work/stereo_calib ディレクトリにコピー&ペーストした。
stereo_calib_13_160220.png

stereo_match.cpp ファイルの中身はこんな感じだ。
stereo_calib_14_160220.png

g++_opencv stereo_match.cpp
コマンドでコンパイルした。
stereo_calib_15_160220.png

./stereo_match -i intrinsics.yml -e extrinsics.yml left01.jpg right01.jpg
コマンドを実行してみた。
stereo_calib_16_160222.png

すると、left, right, disparity ウインドウが開いた。下の left01.jpg と right01.jpg 画像と比べると、チェスボードの縁がまっすぐになったのが分かる。画像も補正の結果、縁が糸巻きの形になっているようだ。これだけカメラ画像が歪んでいたのだろう?
(disparity(視差)ウインドウは、近くを白、遠くを黒として距離を可視化した情報を示すそうだ。”機械の目が見たセカイ -コンピュータビジョンがつくるミライ”参照)
stereo_calib_17_160222.jpg

ネットで見たので、-p cloud.asc オプションを付け加えてみた。
./stereo_match -i intrinsics.yml -e extrinsics.yml left01.jpg right01.jpg -p cloud.asc
stereo_calib_18_160222.png

cloud.asc ファイルができていた。gedit で開くと、何かパラメータのようだった。
stereo_calib_19_160222.png

cloud.asc ファイルのファイルサイズは凄く大きい。5.3 MB 位だ。
stereo_calib_20_160222.png
  1. 2016年02月22日 05:06 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

MPSoC勉強会で”MPSoCのPLの性能について”という発表をしてきた(サポート用のブログ)

昨日、MPSoC勉強会で、”MPSoCのPLの性能について”という発表をして来ました。MPSoC勉強会はいろいろと有意義なことが聞けて良かったです。中でもACPバスのコヒーレンシの制限についてはびっくりしました。良かったです。

このブログで、、”MPSoCのPLの性能について”を詳しく書いておこうと思います。

まずは、Zynq-7000 のC言語で書いた AXI4 Stream 版ラプラシアンフィルタを用意しました。
lap_filter_axis.h を貼っておきます。

// lap_filter_axis.h
// 2015/05/01

#define HORIZONTAL_PIXEL_WIDTH 800
#define VERTICAL_PIXEL_WIDTH 600

//#define HORIZONTAL_PIXEL_WIDTH 50
//#define VERTICAL_PIXEL_WIDTH 10

#define ALL_PIXEL_VALUE (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


lap_filter_axis.cppを貼っておきます。

//
// lap_filter_axis.cpp
// 2015/05/01
// 2015/06/25 : 修正、ラプラシアンフィルタの値が青だけ担っていたので、RGBに拡張した
//

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

#include "lap_filter_axis.h"

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

int lap_filter_axis(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> lap;

    unsigned 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

    int lap_fil_val;

    do {    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            for (int k=0; k<3; k++){
                for (int m=0; m<2; m++){
#pragma HLS UNROLL
                    pix_mat[k][m] = pix_mat[k][m+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            int y_val = conv_rgb2y(pix.data);
            pix_mat[2][2] = y_val;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = y_val;

            lap_fil_val = laplacian_fil(    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]);
            lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる

            if (x<2 || y<2// 最初の2行とその他の行の最初の2列は無効データなので0とする
                lap.data = 0;

            if (x==0 && y==0// 最初のデータでは、TUSERをアサートする
                lap.user = 1;
            else
                lap.user = 0;

            if (x == (HORIZONTAL_PIXEL_WIDTH-1))    // 行の最後で TLAST をアサートする
                lap.last = 1;
            else
                lap.last = 0;

            outs << lap;    // AXI4-Stream へ出力
        }
    }

    return 1;
}

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

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

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

    return(y);
}

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

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


Zynq-7010 (xc7z010clg400-1) の ZYBO 用のプロジェクトです。これで、AXI4 Stream 版のラプラシアンフィルタのC++ソースコードを HDLへ合成した結果はEstimated で 6.88 ns でした。
MPSoC_test1_24_160221.png

リソース使用量です。
MPSoC_test1_25_160221.png

メインのループのパイプライン段数は13 段です。
MPSoC_test1_26_160221.png

次にMPSoC (xczu9eg-ffvb1156-1-i-EVAL) でプロジェクトを作って、Zynq-7010 と全く同じ AXI4 Stream 版のラプラシアンフィルタのC++ソースコードを HDLへ合成した結果はEstimated で 4.53 ns でした。
MPSoC_test1_27_160221.png

リソース使用量です。MPSoC の方がパイプライン段数が1段減っているので、FFとLUT の使用量が少なかったです。
MPSoC_test1_28_160221.png

メインのループのパイプライン段数は12 段です。
MPSoC_test1_29_160221.png

Zynq-7000 が 6.88 ns で、145 MHz 動作、MPSoC が 4.53 ns で、221 MHz 動作なので、221/145 ≒ 1.52 倍 MPSoC が高速ということが言えると思います。


さて、AXI4 Stream 版ラプラシアンフィルタを IP にして、Vivado でプロジェクトを作って、実際の動作周波数を見てみましょう。
最初にZynq-7000 です。Zynq-7010 (xc7z010clg400-1) の ZYBO 用のプロジェクトを作って、IP Integrator のブロックデザインを作成しました。
MPSoC_test1_30_160221.png

ブロックデザインを拡大します。
MPSoC_test1_31_160221.png

インプリメントのSummary を示します。
MPSoC_test1_32_160221.png

6.8 ns のタイミング成約を満足できました。147 MHz で動作できるという結果ですが、スラックを考えると 149 MHz で動作できることになります。

次に、次にMPSoC (xczu9eg-ffvb1156-1-i-EVAL) でVivado 2015.4 のプロジェクトを作って、IP Integrator のブロックデザインを作成しました。
MPSoC_test1_33_160221.png

ブロックデザインを拡大します。
MPSoC_test1_34_160221.png

インプリメントのSummary を示します。
MPSoC_test1_35_160221.png

LUT 使用量が Zynq-7000 よりもMPSoC の方が2倍以上多いですね。FF の使用量も 1.5 倍位になっています。Vivado HLSでの推計上はZynq-7000 の方がリソース使用量が少なかったので、これは周辺のIP の規模が違うのかもしれない?です。

タイミングは 3 ns の成約に対して、-0.029 ns 足りなかったです。よって、約 330 MHz で動作すると思います。
よって、Vivado での性能比は 330 MHz /149 MHz = 2.21 倍でした。

Vivado HLS で推定された性能比は、1.52 倍だったので、Vivado HLS の推計が少し甘いのかも知れない?ですね。
  1. 2016年02月21日 05:52 |
  2. MPSoC
  3. | トラックバック:0
  4. | コメント:0

OpenCV 2.4.10 の stereo_calib.cpp をやってみた

ステレオカメラによる距離測定テスト14(ステレオカメラ・キャリブレーション方法の参考になるWebサイト)”でやると言っていた opencv-2.4.10/samples/cpp の stereo_calib.cpp をやってみることにした。これは、ステレオカメラのキャリブレーション用ソフトウェアだ。

日本語で参考にしたページは”OpenCVでステレオ画像処理(その3)”だった。

~/OpenCV/opencv-2.4.10/samples/cpp から left??.jpg, right??.jpg, stereo_calib.cpp, stereo_calib.xml をコピーして
stereo_calib_1_160220.png
(ZYBO上で起動している nautilus を使っています。自分のパソコンのWindows7 でXサーバーを立ち上げていて、ZYBOのGUI を持ってきて表示しています。便利ですよ。。。やり方は、Vivado and zybo linux勉強会資料3 に書きました)

~/OpenCV/work/stereo_calib ディレクトリを作製してペーストした。
stereo_calib_2_160220.png

stereo_calib_3_160220.png

g++_opencv コマンドで、stereo_calib.cpp をコンパイルした。
g++_opencv stereo_calib.cpp
stereo_calib ができた。
stereo_calib_4_160220.png

stereo_calib.xml を開いてみた。設定ファイルのようだ。
stereo_calib_5_160220.png

./stereo_calib を起動した。

..........................13 pairs have been successfully detected.
Running stereo calibration ...
done with RMS error=0.482794
average reprojection err = 0.485068

が表示されたあと、左目カメラ画像と右目カメラ画像が表示された。
stereo_calib_6_160220.png

stereo_calib_7_160220.png

左目カメラ画像と右目カメラ画像ウインドウ上で何かキーを押すと次の画像に移動する。
stereo_calib_8_160220.png

14個の左目カメラ画像と右目カメラ画像を表示した所でソフトウェアが終了した。
extrinsics.yml と intrinsics.yml ができていた。
stereo_calib_9_160220.png

stereo_calib_10_160220.png

stereo_calib_11_160220.png

OpenCV 2.4.10 の stereo_match.cpp をやってみた”に続く。
  1. 2016年02月20日 04:54 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト14(ステレオカメラ・キャリブレーション方法の参考になるWebサイト)

ステレオカメラによる距離測定テスト13(チェッカーボードの撮影結果)”の続きです。

前回は、チェッカーボード(チェスボード)をカメラで撮影して、歪を確認した。今回は、ステレオカメラやカメラのキャリブレーション方法についての参考になるWebページを書いておこうと思います。

ステレオカメラの歪み補正&平行化
ここからのリンクで、OpenCV のカメラキャリブレーションがあるので、これもやってみようと思っています。
但し、これを行うためには、画像をファイルに変換する必要があるので、画像保存ソフトウェアを作る必要があります。

OpenCV Stereo Camera Calibration/Image Rectification
Stereo Camera Calibration by Detecting Chessboard Corners (Project SAHE)のビデオへのリンクがありました。
これ見てて気がついたのですが、OpenCVのC++ のサンプルに、stereo_calib.cpp がありました。
StereoCamTest_83_160219.png

最初にこれを見てみて、やってみようと思っています。但し、これをやるにも画像を画像ファイルにするソフトウェアが必要ですね。

Camera calibration With OpenCV
Theory も載っているので、勉強になりますね。最も、”詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識”の”第11章 カメラモデルとキャリブレーション”にカメラ単体のキャリブレーション方法とステレオカメラのキャリブレーション方法のやり方が詳しく載ってますね。これを読むのがお勧めです。

OpenCVでステレオ画像処理
PointGreyのGigEカメラ BlackFly2台でステレオカメラにしているようです。

OpenCVでステレオ画像処理(その2)
ステレオ対応点探索の前に、左右の画像を平行化を行っています。チェスボード画像やサンプルコードがあります。

OpenCVでステレオ画像処理(その3)
キャリブレーションボードを使ったいわゆる普通のステレオキャリブレーションをOpenCVでやる方法が書いてあります。今から stereo_calib.cpp をやってみるのに、とってもためになりそうです。

カメラキャリブレーションと3次元再構成
カメラのキャリブレーション関連の関数の説明があります。ステレオカメラのキャリブレーション関連の関数の説明もあります。
  1. 2016年02月19日 05:20 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト13(チェッカーボードの撮影結果)

ステレオカメラによる距離測定テスト12(焦点距離の計算)”の続き。

前回の結果はおかしいというご指摘を頂いたので、すでにチェッカーボードを自作してあったので、これでカメラの歪をみようと思う。
なお、カメラの性能やステレオカメラのキャリブレーション方法のことはよく知らないので、どの本が良いか、コメント欄にアドバイスしていただくととっても嬉しい。よろしくお願いします。
今持っている本は、”詳解 OpenCV――コンピュータビジョンライブラリを使った画像処理・認識”と”デジタル画像処理”だ。

さて、チェッカーボードを左カメラで撮影するのだが、右手でチェッカーボードを持って、左手でスマフォのカメラで撮影できない。
確か、ビットマップ・ディスプレイ・コントローラに静止画機能を付けていたと思って、HDLソースコードを見たが付いていない。もしかしてカメラ・インターフェース?と思ってみると、こっちに付いていた。

// mt9d111_axi_lite_slave.v 
// mt9d111_inf_axi_master のAXI Lite Slave モジュール。Frame Buffer のスタートアドレス・レジスタを持つ。
//
// 2014/11/08 : one_shot_reg を実装。
// オフセット0番地: フレーム・バッファの先頭アドレス(fb_start_address)
// オフセット4番地: 0 ビット目が 0 の時動画、0 ビット目に 1 の時に、ワンショットで取得した1フレームのカメラ画像を表示(one_shot_reg)
//            1 ビット目に 1 を Write した時に、ワンショットで1フレームの画像をフレーム・バッファに保存


つまり、オフセット0x4 に 0 を書くと連続画像キャプチャ、1 を書くと画像が止まる。
これを、StereoCam_Alt_Disp.c に追加した。
s キーを押すと、画像連続キャプチャ、一時停止を繰り返す。
StereoCamTest_81_160217.png

このアプリケーションで撮影したチェッカーボードを下に示す。
StereoCamTest_82_160217.jpg

樽型歪が出ているのが分かる。

修正した StereoCam_Alt_Disp.c を貼っておく。

//
// StereoCam_Alt_Disp.c
// 2016/02/01 by marsee 
//
// 2016/02/17 : 's' commannd : Camera Start and Stop Command, but left camera only.
//

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

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

#define XGA_HORIZONTAL_PIXELS    1024
#define XGA_VERTICAL_LINES        768
#define XGA_ALL_DISP_ADDRESS    (XGA_HORIZONTAL_PIXELS*XGA_VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

#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)

void cam_i2c_init(volatile unsigned *caminf_axi_iic) {
    caminf_axi_iic[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    caminf_axi_iic[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 *caminf_axi_iic, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    caminf_axi_iic[66] = 0x100 | (device_addr & 0xfe);    // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    caminf_axi_iic[66] = write_addr;
    caminf_axi_iic[66] = (write_data >> 8)|0xff;            // first data
    caminf_axi_iic[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main()
{
    int fd0, fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd9, fd10;
    volatile unsigned *bmdc_axi_lites0;
    volatile unsigned *caminf_axi_vdma_0, *dviin_axi_vdma_0;
    volatile unsigned *caminf_axis_switch_0, *caminf_axis_switch_1;
    volatile unsigned *caminf_mt9d111_inf_axis_0;
    volatile unsigned *caminf_axi_iic;
    volatile unsigned *caminf_lap_filter_axis_0;
    volatile unsigned *frame_buffer;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    char c;
    int laps_cntrl;

    // Bitmap Display Controller 0 AXI4 Lite Slave (UIO6)
    fd6 = open("/dev/uio6", O_RDWR); // bitmap_display_controller 0 axi4 lite
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (bitmap_disp_cntrler_axi_master_0) open errorn");
        exit(-1);
    }
    bmdc_axi_lites0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!bmdc_axi_lites0){
        fprintf(stderr, "bmdc_axi_lites0 mmap errorn");
        exit(-1);
    }
    
    // caminf_axi_vdma_0 (UIO1)
    fd1 = open("/dev/uio1", O_RDWR); // caminf_axi_vdma_0 interface AXI4 Lite Slave
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (caminf_axi_vdma_0) open errorn");
        exit(-1);
    }
    caminf_axi_vdma_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (!caminf_axi_vdma_0){
        fprintf(stderr, "caminf_axi_vdma_0 mmap errorn");
        exit(-1);
    }
    
    // dviin_axi_vdma_0 (UIO7)
    fd7 = open("/dev/uio7", O_RDWR); // dviin_axi_vdma_0 interface AXI4 Lite Slave
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (dviin_axi_vdma_0) open errorn");
        exit(-1);
    }
    dviin_axi_vdma_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!dviin_axi_vdma_0){
        fprintf(stderr, "dviin_axi_vdma_0 mmap errorn");
        exit(-1);
    }
    
    // mt9d111 i2c AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // mt9d111 i2c AXI4 Lite Slave
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (caminf_axi_iic) open errorn");
        exit(-1);
    }
    caminf_axi_iic = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!caminf_axi_iic){
        fprintf(stderr, "caminf_axi_iic mmap errorn");
        exit(-1);
    }

    // mt9d111 inf axis AXI4 Lite Slave (UIO5)
    fd5 = open("/dev/uio5", O_RDWR); // mt9d111 inf axis AXI4 Lite Slave
    if (fd5 < 1){
        fprintf(stderr, "/dev/uio5 (caminf_mt9d111_inf_axis_0) open errorn");
        exit(-1);
    }
    caminf_mt9d111_inf_axis_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd5, 0);
    if (!caminf_mt9d111_inf_axis_0){
        fprintf(stderr, "caminf_mt9d111_inf_axis_0 mmap errorn");
        exit(-1);
    }

    // caminf_axis_switch_0 (UIO2)
    fd2 = open("/dev/uio2", O_RDWR); // caminf_axis_switch_0 interface AXI4 Lite Slave
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (caminf_axis_switch_0) open errorn");
        exit(-1);
    }
    caminf_axis_switch_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!caminf_axis_switch_0){
        fprintf(stderr, "caminf_axis_switch_0 mmap errorn");
        exit(-1);
    }
    
    // caminf_axis_switch_1 (UIO3)
    fd3 = open("/dev/uio3", O_RDWR); // caminf_axis_switch_1 interface AXI4 Lite Slave
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (caminf_axis_switch_1) open errorn");
        exit(-1);
    }
    caminf_axis_switch_1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!caminf_axis_switch_1){
        fprintf(stderr, "caminf_axis_switch_1 mmap errorn");
        exit(-1);
    }
    
    // caminf_lap_filter_axis_0 (UIO4)
    fd4 = open("/dev/uio4", O_RDWR); // caminf_lap_filter_axis_0 interface AXI4 Lite Slave
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (caminf_lap_filter_axis_0) open errorn");
        exit(-1);
    }
    caminf_lap_filter_axis_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!caminf_lap_filter_axis_0){
        fprintf(stderr, "caminf_lap_filter_axis_0 mmap errorn");
        exit(-1);
    }
    
    // udmabuf0
    fd9 = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd9 == -1){
        fprintf(stderr, "/dev/udmabuf0 open errorn");
        exit(-1);
    }
    frame_buffer = (volatile unsigned *)mmap(NULL, (XGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES)+(SVGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES), PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap errorn");
        exit(-1);
    }

    // caminf_axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    caminf_axis_switch_1[16] = 0x0// 0x40 = 0
    caminf_axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000, disable
    caminf_axis_switch_1[0] = 0x2// Comit registers
    
    // caminf_axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    caminf_axis_switch_0[16] = 0x0// 0x40 = 0;
    caminf_axis_switch_0[0] = 0x2// Comit registers
    
    // phys_addr of udmabuf0
    fd10 = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fd10 == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open errorn");
        exit(-1);
    }
    read(fd10, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd10);
    printf("phys_addr = %xn", (unsigned)phys_addr);
    
    // AXI VDMA Initialization sequence (caminf_axi_vdma_0)
    caminf_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((caminf_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    caminf_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((caminf_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    caminf_axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    caminf_axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    caminf_axi_vdma_0[41] = SVGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    caminf_axi_vdma_0[42] = SVGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    caminf_axi_vdma_0[43] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 1
    caminf_axi_vdma_0[44] = (unsigned)phys_addr+SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 2
    caminf_axi_vdma_0[45] = (unsigned)phys_addr+2*SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 3
    caminf_axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((caminf_axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    caminf_axi_vdma_0[40] = SVGA_VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // AXI VDMA Initialization sequence (dviin_axi_vdma_0)
    dviin_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((dviin_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    dviin_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((dviin_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    dviin_axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    dviin_axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    dviin_axi_vdma_0[41] = XGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    dviin_axi_vdma_0[42] = XGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    dviin_axi_vdma_0[43] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 1
    dviin_axi_vdma_0[44] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+XGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 2
    dviin_axi_vdma_0[45] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+2*XGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 3
    dviin_axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((dviin_axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    dviin_axi_vdma_0[40] = XGA_VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(caminf_axi_iic);
    
    cam_i2c_write(caminf_axi_iic, 0xba, 0xf00x1);        // Changed regster map to IFP page 1
    cam_i2c_write(caminf_axi_iic, 0xba, 0x970x20);    // RGB Mode, RGB565

    caminf_mt9d111_inf_axis_0[1] = 0;
    
    // Camera Base Address Setting
    caminf_mt9d111_inf_axis_0[0] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS;; // Camera Interface start (Address is dummy)

    // bitmap display controller settings
    bmdc_axi_lites0[0] = (unsigned)phys_addr; // Bitmap Display Controller 0 start, My Camera Image
    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case '1' :
                bmdc_axi_lites0[0] = (unsigned)phys_addr; // Bitmap Display Controller 0 start, My Camera Image
                break;
            case '2' :
                bmdc_axi_lites0[0] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+0x8// Another one ZYBO Camera Image
                break;
            case 's' :
                if (caminf_mt9d111_inf_axis_0[1] == 0)
                    caminf_mt9d111_inf_axis_0[1] = 1// left camera is stopped.
                else
                    caminf_mt9d111_inf_axis_0[1] = 0// left camere is started. 
                break;
            case 'l' : // laplacian filter
                // caminf_axis_switch_1, 1to2 ,Select M01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_1[16] = 0x80000000// 0x40 = 0x80000000; disable
                caminf_axis_switch_1[17] = 0// 0x44 = 0;
                caminf_axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
                // laplacian filter AXIS Start
                laps_cntrl = caminf_lap_filter_axis_0[0] & 0x80// Auto Restart bit
                caminf_lap_filter_axis_0[0] = laps_cntrl | 0x01// Start bit set
                caminf_lap_filter_axis_0[0] = 0x80// Auto Restart bit set
    
                // caminf_axis_switch_0, 2to1, Select S01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_0[16] = 0x1// 0x40 = 0x1;
                caminf_axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers
                break;
            case 'c' : // camera image
                // caminf_axis_switch_1, 1to2 ,Select M01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_1[16] = 0// 0x44 = 0;
                caminf_axis_switch_1[17] = 0x80000000// 0x40 = 0x80000000; disable
                caminf_axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
                // laplacian filter AXIS Start
                caminf_lap_filter_axis_0[0] = 0x00// Auto Restart Disable
    
                // caminf_axis_switch_0, 2to1, Select S01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_0[16] = 0x0// 0x40 = 0x0;
                caminf_axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers
                break;
        }
        c = getc(stdin);
    }

    munmap((void *)bmdc_axi_lites0, 0x10000);
    munmap((void *)caminf_axi_vdma_0, 0x10000);
    munmap((void *)dviin_axi_vdma_0, 0x10000);
    munmap((void *)caminf_axi_iic, 0x10000);
    munmap((void *)caminf_mt9d111_inf_axis_0, 0x10000);
    munmap((void *)caminf_axis_switch_0, 0x10000);
    munmap((void *)caminf_axis_switch_1, 0x10000);
    munmap((void *)caminf_lap_filter_axis_0, 0x10000);
    munmap((void *)frame_buffer, (XGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES)+(SVGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES));
    
    close(fd0);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd5);
    close(fd6);
    close(fd7);
    close(fd9);
    close(fd10);
    
    return(0);
}

  1. 2016年02月17日 06:40 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト12(焦点距離の計算)

ステレオカメラによる距離測定テスト11(距離と左右視差の測定)”の続き。

前回は距離と左右カメラの視差のピクセル数を測定した。今回はその測定データからカメラの焦点距離を計算した。

(2016/02/17:追記) 光学系も素人なので、勉強しながらやっていきたいと思いますので、この辺りはトライ・アンド・エラーしています。そして厳しいご指摘のコメントをお待ちしていおります。できれば詳しくやり方をコメント欄に書いて頂くか?参考にできるURLを教えて頂けると助かります。

ステレオカメラによる交通環境認識と歩行者の検出”の12ページの”視差と物体までの距離の関係”の図を下に引用する。
StereoCamTest_79_160215.png

私が測定した距離は焦点距離 + Z になっているので、焦点距離を計算で求めてみよう。測定した距離をLとすると、
F + Z = L

F + BF/D = (FD + FB) / D = F(D + B) / D = L

F = LD / (D + B)

この式に従って、各測定した距離の焦点距離を計算してみた。
StereoCamTest_80_160215.png

距離が短い方はだいぶ誤差が出ている気がする。カメラの軸が曲がっているのだろうか?焦点距離は 540 mm 位なのだろうか?
2.2 m よりも長い距離の焦点距離がどうなるか?が気になる。

次は、自動的に視差を求めるアプリケーションを作ってみたいと思う。
  1. 2016年02月16日 04:40 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:7

ステレオカメラによる距離測定テスト11(距離と左右視差の測定)

ステレオカメラによる距離測定テスト10(左右カメラの視差の測定)”の続き。

左右画像を重ね合わせるアプリケーションが出来たので、カメラから物体までの距離と左右の視差による左右カメラ画像のピクセル位置のズレのデータを取ってみた。

そのデータを下に示す。
距離 (m)左右視差
(pixels)
上下視差
(Lines)
0.5304-11
0.6249-14
0.7213-14
0.8183-14
0.9161-14
1.0149-14
1.2119-20
1.499-20
1.590-20
1.681-20
1.869-20
2.061-20
2.257-20

なお、上下視差は測った時期によって、ばらついているかもしれない。
左右カメラ間の距離は 160 mm だった。
  1. 2016年02月15日 05:09 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト10(左右カメラの視差の測定)

ステレオカメラによる距離測定テスト9(アプリケーションの作製2)”の続き。

左右カメラの視差の測定を行うアプリケーションを作っていたのだが、取りあえず完成した。左目カメラ画像に右目カメラ画像の一部を表示するアプリケーションだ。右目カメラ画像の一部を表示するエリアは、左目カメラ画像の輝度を 1/2 にして、そこに輝度を 1/2 にした右目カメラ画像を足し合わせる。デフォルトでは、右目カメラ画像の縦方向真ん中で、横方向左端の 160 ピクセル X 120 行を切り取って、左目カメラ画像に表示する。
アプリケーションのファイル名はRL_alpha_blend.c だ。オプションは5つあって、

c : 右目カメラ画像の横方向の幅を指定する(初期値: 160 ピクセル)
r : 右目カメラ画像の縦方向の幅を指定する(初期値: 120 行)
d : 右目カメラ画像の横方向の切り取り位置(初期値: 0 ピクセル)
s : 右目カメラ画像の縦方向の切り取り位置(初期値: 0 ピクセル)

となっている。

右目カメラ画像を出力するために、ZYBO_0_2 で ./udmabuf_insmod, ./cam_disp_vdma を起動して、HDMI 端子に右目カメラ画像を出力した。

左目カメラ画像を出すためにには、StereoCamTest で ./udmabuf_insmod, ./StereoCam_Alt_Disp を実行した。
左目カメラ画像を示す。
StereoCamTest_74_160214.jpg

左目カメラ画像用ZYBO に転送されてきた右目カメラ画像を示す。
StereoCamTest_75_160214.jpg

右目カメラ画像用、左目カメラ画像用ZYBO の様子を示す。
StereoCamTest_73_160214.jpg

次に、StereoCamTest の左目カメラ画像用ZYBO で、./RL_alpha_blend でアプリケーションを立ち上げた。
StereoCamTest_70_160214.png

左目カメラ画像に右目カメラ画像の一部が表示されているのが分かる。ピンクの枠で囲った部分が右目カメラ画像の一部を傘な合わせた部分だ。
StereoCamTest_76_160214.jpg

右目カメラ画像の一部を RL_alpha_blead アプリケーション上でコマンドを入力して位置を合わせる。コマンドを紹介する。

j : 左へ 1 ピクセル移動
k : 右へ 1 ピクセル移動
h : 左へ 10 ピクセル移動
l (Lの小文字): 右へ 10 ピクセル移動
g : 左へ 30 ピクセル移動
; : 右へ 30 ピクセル移動
i : 上へ 1 ピクセル移動
u : 上へ 10 ピクセル移動
m : 下へ 1 ピクセル移動
n : 下へ 10 ピクセル移動


右目カメラ画像を移動させる途中の画面を示す。
StereoCamTest_71_160214.png

StereoCamTest_77_160214.jpg

最初に比べて、右目カメラ画像の一部が移動しているのがわかると思う。

右目カメラ画像の一部を左目カメラ画像と一致された状態を示す。
StereoCamTest_72_160214.png

StereoCamTest_78_160214.jpg

右目カメラ画像の一部を 43 ピクセル移動させると左目カメラ画像に一致した。なお、ZYBO からの一致させたオブジェクトまでの距離は 2.3 m だった。

最後に、RL_alpha_blend.c を貼っておく。

//
// RL_alpha_blend.c  by marsee
// 2016/02/06
//
// 800 pixels x 600 lines x 4 bytes x 3 pictures + 1024 pixels x 768 lines x 4 bytes x 3 pictures +
//   800 pixels x 600 lines x 4 bytes(alpha blend area for Right camera and Left camera)  = 17117184
//
// When you press the j key, right camera image is moved one pixel to the left.
// When you press the k key, right camera image is moved one pixel to the right.
// When you press the h key, right camera image is moved ten pixels to the left.
// When you press the l key, right camera image is moved ten pixels to the right.
// When you press the i key, right camera image is moved one pixel to the up.
// When you press the m key, right camera image is moved one pixel to the down.
//
// Usage : RL_alpha_blend [-c 80] [-r 60] [-h]
// -c and -r indicates the area to be cut out of the left edge of the right-eye camera image.
//

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

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

#define XGA_HORIZONTAL_PIXELS   1024
#define XGA_VERTICAL_LINES       768
#define XGA_ALL_DISP_ADDRESS    (XGA_HORIZONTAL_PIXELS * XGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define XGA_3_PICTURES          (XGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES) 

#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 SVGA_3_PICTURES         (SVGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES)

#define DEFAULT_RECTANGLE_PIXELS    160
#define DEFAULT_RECTANGLE_LINES     120

int half_bright(int pixel);
int alpha_blend(int pixel1, int pixel2);

int main(int argc, char *argv[]){
    int column_width = DEFAULT_RECTANGLE_PIXELS;
    int row_width = DEFAULT_RECTANGLE_LINES;
    int opt, c;
    int help_flag = 0;
    int fdf, fdp, fd6;
    volatile unsigned *frame_buffer;
    volatile unsigned *bmdc_axi_lites0;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    volatile unsigned *Leye_addr, *alpha_addr;
    int i, j;
    volatile unsigned *Reye_start_addr, *Reye_stride;
    volatile unsigned *abdisp_start_addr, *abdisp_addr;
    volatile unsigned *s, *t;
    unsigned int   buf_size;
    int offset_row=0, offset_column=0;
    int reye_img_offset_row=0, reye_img_offset_column=0;
    
    while ((opt=getopt(argc, argv, "c:r:d:s:h")) != -1){
        switch (opt){
            case 'c':
                column_width = atoi(optarg);
                break;
            case 'r':
                row_width = atoi(optarg);
                break;
            case 'd':
                reye_img_offset_column = atoi(optarg);
                break;
            case 's':
                reye_img_offset_row = atoi(optarg);
                break;
            case 'h':
                help_flag = 1;
                break;
        }
    }

    printf("column_width = %d, row_width = %dn", column_width, row_width);
    printf("reye_img_offset_column = %d, reye_img_offset_row = %dn", reye_img_offset_column, reye_img_offset_row);
    
    if (help_flag == 1){ // help
        printf("Usage : RL_alpha_blend [-c 160] [-r 120] [-d 0] [-s 0] [-h]n");
        printf("-c: column_width, -r: row_widthn");
        printf("-d: column of right eys's camere image offset, -s: row of right eys's camere image offsetn");
        printf("-c and -r indicates the area to be cut out of the left edge of the right-eye camera image.n");
        printf("n");
        printf("Key bindn");
        printf("j: left 1 pixelnk: right 1 pixelnh: left 10 pixelsnl: right 10 pixelsn");
        printf("g: left 30 pixelsn';': right 30 pixelsn");
        printf("i: up 1 pixelnu: up 10 pixelsnm: down 1 pixelnn: down 10 pixelsn");
        return(0);
    }

    // Bitmap Display Controller 0 AXI4 Lite Slave (UIO6)
    fd6 = open("/dev/uio6", O_RDWR); // bitmap_display_controller 0 axi4 lite
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (bitmap_disp_cntrler_axi_master_0) open errorn");
        exit(-1);
    }
    bmdc_axi_lites0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!bmdc_axi_lites0){
        fprintf(stderr, "bmdc_axi_lites0 mmap errorn");
        exit(-1);
    }

    // udmabuf0
    fdf = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fdf == -1){
        fprintf(stderr, "/dev/udmabuf0 open errorn");
        exit(-1);
    }
    frame_buffer = (volatile unsigned *)mmap(NULL, SVGA_3_PICTURES+XGA_3_PICTURES+SVGA_ALL_DISP_ADDRESS, PROT_READ|PROT_WRITE, MAP_SHARED, fdf, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap errorn");
        exit(-1);
    }

    // phys_addr of udmabuf0
    fdp = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fdp == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open errorn");
        exit(-1);
    }
    read(fdp, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fdp);
    printf("phys_addr = %xn", (int)phys_addr);

    // buf_size of udmabuf0
    fdp = open("/sys/devices/virtual/udmabuf/udmabuf0/size", O_RDONLY);
    if (fdp == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/size open errorn");
        exit(-1);
    }
    read(fdp, attr, 1024);
    sscanf(attr, "%d", &buf_size);  
    close(fdp);
    printf("buf_size = %dn", (int)buf_size);
    
    // Display the left-eye camera image
    Leye_addr = frame_buffer;
    alpha_addr = (volatile unsigned *)((unsigned)frame_buffer+SVGA_3_PICTURES+XGA_3_PICTURES);

    s = alpha_addr;
    t = Leye_addr;
    for (i=0; i<SVGA_VERTICAL_LINES; i++){
        for (j=0; j<SVGA_HORIZONTAL_PIXELS; j++){
            *s =  *t;
            s++;
            t++;
        }
    }
    bmdc_axi_lites0[0] = (unsigned)phys_addr+SVGA_3_PICTURES+XGA_3_PICTURES;
    
    // Display the right eye camera image, taken in a rectangular
    Reye_start_addr = (volatile unsigned *)((unsigned)frame_buffer + SVGA_3_PICTURES + (SVGA_HORIZONTAL_PIXELS * (SVGA_VERTICAL_LINES - row_width)/2 * PIXEL_NUM_OF_BYTES)+0x8+reye_img_offset_column+(SVGA_HORIZONTAL_PIXELS * reye_img_offset_row * PIXEL_NUM_OF_BYTES)); // left edge + offset
    Reye_stride = (volatile unsigned *)((SVGA_HORIZONTAL_PIXELS - column_width)*PIXEL_NUM_OF_BYTES);

    abdisp_start_addr = (volatile unsigned *)((unsigned)frame_buffer + SVGA_3_PICTURES + XGA_3_PICTURES+ (SVGA_HORIZONTAL_PIXELS * (SVGA_VERTICAL_LINES - row_width)/2 * PIXEL_NUM_OF_BYTES));

    abdisp_addr = abdisp_start_addr;
    s = abdisp_addr;
    t = Reye_start_addr;
    
    for (i=0; i<row_width; i++){
        for (j=0; j<column_width; j++){
            *s = alpha_blend(*s, *t);
            s++;
            t++;
        }
        s = (volatile unsigned *)((unsigned)s+ (unsigned)Reye_stride);
        t = (volatile unsigned *)((unsigned)t + (unsigned)Reye_stride);
    }
    
    printf("offset_row = %d pixels, offset_column = %d linesn", offset_row, offset_column);
    
    // j, k, h, l, i, m , q key
    while(c != 'q'){
        c = getc(stdin);
        switch ((char)c) {
            case 'j' :  // left 1 pixel
                abdisp_addr--;
                offset_row--;
                break;
            case 'k' :  // right 1 pixel
                abdisp_addr++;
                offset_row++;
                break;
            case 'h' :  // left 10 pixels
                abdisp_addr -= 10;
                offset_row -= 10;
                break;
            case 'l' :  // right 10 pixels
                abdisp_addr += 10;
                offset_row += 10;
                break;
            case 'g' : // left 30 pixels
                abdisp_addr -= 30;
                offset_row -= 30;
                break;          
            case ';' :  // right 30 pixels
                abdisp_addr += 30;
                offset_row += 30;
                break;
            case 'i' :  // up 1 pixel
                abdisp_addr -= SVGA_HORIZONTAL_PIXELS;
                offset_column--;
                break;
            case 'u' :  // up 10 pixel
                abdisp_addr -= (SVGA_HORIZONTAL_PIXELS*10);
                offset_column -= 10;
                break;
            case 'm' :  // down 1 pixel
                abdisp_addr += SVGA_HORIZONTAL_PIXELS;
                offset_column++;
                break;
            case 'n' :  // down 10 pixel
                abdisp_addr += (SVGA_HORIZONTAL_PIXELS*10);
                offset_column += 10;
                break;
        }

        // Display the left-eye camera image
        s = alpha_addr;
        t = Leye_addr;
        for (i=0; i<SVGA_VERTICAL_LINES; i++){
            for (j=0; j<SVGA_HORIZONTAL_PIXELS; j++){
                *s =  *t;
                s++;
                t++;
            }
        }

        // Display the right eye camera image, taken in a rectangular
        s = abdisp_addr;
        t = Reye_start_addr;
        for (i=0; i<row_width; i++){
            for (j=0; j<column_width; j++){
                *s = alpha_blend(*s, *t);
                s++;
                t++;
            }
            s = (volatile unsigned *)((unsigned)s+ (unsigned)Reye_stride);
            t = (volatile unsigned *)((unsigned)t + (unsigned)Reye_stride);
        }
        if (c != 'n')
            printf("offset_row = %d pixels, offset_column = %d linesn", offset_row, offset_column);
    }
    munmap((void *)bmdc_axi_lites0, 0x10000);
    munmap((void *)frame_buffer, (SVGA_3_PICTURES+XGA_3_PICTURES+SVGA_ALL_DISP_ADDRESS));
    close(fd6);
    close(fdf);
    
    return(0);
}

int half_bright(int pixel){
    int hpixel = 0;

    hpixel = (pixel & 0xff)/2;
    hpixel |= (pixel & 0xff00)/2;
    hpixel |= (pixel & 0xff0000)/2;

    return(hpixel);
}

int alpha_blend(int pixel1, int pixel2){
    int r, g, b;

    b = (pixel1 & 0xff)/2 + (pixel2 & 0xff)/2;
    g = ((pixel1 & 0xff00)/2 + (pixel2 & 0xff00)/2) & 0xff00;
    r = ((pixel1 & 0xff0000)/2 + (pixel2 & 0xff0000)/2) & 0xff0000;
    return(b+g+r);
}

  1. 2016年02月14日 06:06 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

ZYBO をUbuntu 14.04 LTS にアップグレードした場合のエコーバック

ZYBO のRoot File System には、Linaro のUbuntu 12.10 Root FS を入れている。(Vivado and zybo linux勉強会資料3)
Ubuntu 12.10 をUbuntu 14.04 LTS にアップグレードしているのだが、アップグレードした当初は問題ないのだが、
apt-get update
apt-get upgrade
した後でTera Term などのシリアルポート経由のターミナルで、自分でタイプしたコマンドがターミナル上で表示されないという不具合があった。その解決方法を @YuuichiAkagawa さんに教えて頂いたので、書いておく。 @YuuichiAkagawa さん、ありがとうございました。

それは、「CTRL+D」キーを押して、再度、自動ログインすることだ。再度、自動ログインするとエコーバックされるようになった。
Ubuntu1404_echoback_1_160213.png
  1. 2016年02月13日 03:42 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

ZYBO 上にインストールしたEclipse でHello World

ZYBO の Ubuntu 14.04 LTS に GDB のGUI フロントエンドをインストール2(Eclipse をインストール)”で ZYBO に Eclipse をインストールしたので、Hello World ができるかどうか?試してみた。

ターミナルから eclipse & でEclipse を立ち上げた。

Workspace Launcher が開いた。Workspace を指定した。
Eclipse_1_160211.png

Eclipse が起動した。
Eclipse_2_160211.png

File メニューから New -> Projcet を選択した。
Eclipse_3_160211.png

New Project ダイアログが表示された。C/C++ -> C Project を選択して、Next > ボタンをクリックした。
Eclipse_4_160211.png

C Project ダイアログで、Project type で Hello World ANSI C Project を選択して、Toolchains から Linux GCC を選択した。Next > ボタンをクリックした。
Eclipse_5_160211.png

Basic Settings はそのまま Next > ボタンをクリックした。
Eclipse_6_160211.png

Select Configuration で、Finish ボタンをクリックした。
Eclipse_7_160211.png

Open Associated Perspective ダイアログが表示された。Yes ボタンをクリックした。
Eclipse_8_160211.png

見慣れたウインドウが表示された。
Eclipse_9_160211.png

Welcome 画面を閉じた。

そう言えば g++ をインストールするのを忘れていたので、
sudo apt-get install g++
でインストールした。
Eclipse_10_160211.png
Eclipse_11_160211.png

プロジェクトをビルドしよう。
Project メニューから Build Project を選択した。
Eclipse_12_160211.png

ビルドが成功して、Hello_World バイナリが生成された。
Eclipse_13_160211.png

Hello_World バイナリを実行してみよう。
Hello_World バイナリを右クリックし、右クリックメニューから Run As -> Local C/C++ Application を選択する。
Eclipse_14_160211.png

下のConsole に「!!! Hello World !!!」が表示された。
Eclipse_15_160211.png

Debug パースペクティブも試してみたが、問題ない。
Eclipse_16_160211.png

Eclipse で行こうと思ったのだが、少しやっていると、下に示すようにkswapd0 の負荷が重くなって、ターミナルの応答も重くなってしまう。。。やはり負荷が重いのかもしれない。メモリが 1GB あれば良いのだが。。。
Eclipse_17_160211.png

ということで、結局、CUI 上で gdb を使うことにした。だいぶ回り道してしまったね。。。
  1. 2016年02月12日 05:28 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

ZYBO の Ubuntu 14.04 LTS に GDB のGUI フロントエンドをインストール2(Eclipse をインストール)

”ZYBO の Ubuntu 14.04 LTS に GDB のGUI フロントエンドをインストール1(DDD を試す)”の続き。

前回はZYBO のUbuntu 14.04 LTS にGDB のフロントエンドとして、DDD をインストールしたのだが、原因不明の症状が出て上手く動作しなかった。今回は、少し遅いとは思うのだがEclipse をインストールしてみる。

Eclipse のインストールはapt-get で行った。
sudo apt-get install eclipse
かなり、インストールするパッケージが多い。
gdb_frontend_5_160210.png

時間がかかったがインストール終了した。
gdb_frontend_6_160210.png

df コマンドでの/dev/root の Use% はEclipse インストール前の 55% から 62% になった。
gdb_frontend_7_160210.png

eclipse とコマンドを入力して、Eclipse を起動した。
gdb_frontend_8_160210.png

Eclipse のWorkspace Launcher が立ち上がった。デフォルト状態でOK ボタンをクリックした。
gdb_frontend_9_160210.png

Eclipse が起動した。
gdb_frontend_11_160210.png

Project を作ってみよう。File メニューからNew -> Project を選択した。
gdb_frontend_12_160210.png

New Project ダイアログが開いた。CVS とJava くらいしか項目が無く、C/C++ の選択肢が無かった。
gdb_frontend_13_160210.png

いろいろとネットをググってみると、eclipse-cdt のインストールが必要なようだ。
apt-get でインストールを行った。
sudo apt-get install eclipse-cdt
gdb_frontend_14_160210.png
gdb_frontend_15_160210.png
今回は、/dev/root の Use% は 1 % の増加だった。
gdb_frontend_15_160210.png

Eclipse を起動すると、C/C++ Development がインストールされていた。
gdb_frontend_16_160210.png

File メニューからNew -> Project を選択して、New Project ダイアログが開くと、C/C++ プロジェクトがあるのが見えた。
StereoCamTest_17_160209.png

Eclipse インストール成功なのだと思う。
  1. 2016年02月11日 05:03 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

ZYBO の Ubuntu 14.04 LTS に GDB のGUI フロントエンドをインストール1(DDD を試す)

ステレオカメラによる距離測定テスト9(アプリケーションの作製2)”で自作アプリケーションが Segmentation fault になってしまったので、デバックを行うのだが、いつものprintf デバックはけっこう大変なので、GDB を使うことにしたが、どうせ使うならば GUI のフロントエンドを試してみたいということで、やってみた。
なお、ZYBO 上で動作している Ubuntu 14.04 LTS でネイティブにデバックする。

最初に、DDD (Data Display Debugger) を試してみることにした。
調べると、apt-get でインストールできるようだ。
sudo apt-get install ddd
gdb_frontend_1_160210.png
gdb_frontend_2_160210.png

RL_alpha_blend.c を -g オプション付きでコンパイルを行う。
gcc RL_alpha_blend.c -g -o RL_alpha_blend

DDD を起動する。
ddd ./RL_alpha_blend &
gdb_frontend_3_160210.png

DDD が起動した。(Tera Term から Xming を使って、Windows7 のパソコンにX を表示している。つまりZYBO 上で動作しているDD DD を見ている)
gdb_frontend_4_160210.png

DDD は起動したのだが、Searching "vsllib/ddd.vsl..." が表示されて、 RL_alpha_blend.c のソースコードが表示されるはずが表示されない。いろいろとやってみたのだが、上手く動作しなかった。残念だ。次はEclipse をインストールしてみることにする。
  1. 2016年02月11日 04:35 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト9(アプリケーションの作製2)

”ステレオカメラによる距離測定テスト8(アプリケーションの作製)”の続き。

前回は、StereoCam_Alt_Disp を作製して、自分の左目カメラ画像とHDMI ポート経由で送られてきた右目カメラ画像を選択して表示することができるようになった。
今回は、左目カメラ画像に真ん中のある領域を切り出した右目カメラ画像を重ねて表示してみることにした。
まずは、左目カメラ画像をSVGA画面にすべて表示する。次に、真ん中のある領域を切り出した右目カメラ画像を左端の上下方向は真ん中に表示する。
その表示する時に、表示する位置の左目カメラ画像のピクセルを1/2 にしたものと、切り出した右目カメラ画像の当該ピクセルに 1/2 を掛けたものを足し算して、表示しようと思う。切り出した右目カメラ画像はキー操作で動くようにする。キーバインドを下に示す。

j キーで 1 ピクセル左へ移動
k キーで 1 ピクセル右へ移動
h キーで 10 ピクセル左へ移動
l (Lの小文字)キーで 10 ピクセル右へ移動
i キーで 1 ピクセル上へ移動
m キーで 1 ピクセル下に移動


ファイル名はRL_alpha_blend.c とした。
gcc RL_alpha_blend.c -o RL_alpha_blend
でコンパイルを行った。
./RL_alpha_blend
でアプリケーションを起動したが、Segmentation fault だった。デバックする必要があるが、GDBだとコマンド打つのが面倒なので、GDBのフロントエンドをインストールすることにした。
StereoCamTest_66_160209.png
  1. 2016年02月09日 05:29 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

守谷ハーフマラソン

今日は、守谷ハーフマラソンに行ってきました。

うちの奥さんが5km に出場しました。私も5km に出場予定だったのですが、右足が鵞足炎ということで、まだ痛いので、棄権しましたが、私も一緒に守谷ハーフマラソン会場まで行って応援してきました。

今日はとっても良い天気でコンディションは良かったですね。コースは2回折り返しがあるコースで、最後が登りなので、最後が苦しいですね。これは嫌なパターンです。それに、折り返しがたくさんあると、あまりよい気持ちはしませんね。運動ベクトルを180度方向転換する必要があるので、エネルギーを使います。

うちの奥さんは苦しそうに走ってました。応援した時には、20分位だったので、残り距離から23分くらいかな?と思っていたら、23分1秒だったそうです。5km、40歳以上クラスで1位でした。本人はタイムが不本意だったようです。牛久シティマラソンでは、22分16秒だったので、22分台だったらと悔やんでいました。

とにかく、1位なので表彰がありました。
moriya_half_1_160208.jpg

おめでとう。タイムは不満タラタラだろうけど、一番早かったので、素直に喜べば良いんじゃないの?
  1. 2016年02月07日 20:47 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:1

映画「オデッセイ」を見てきました

映画「オデッセイ」を見てきました。
面白かったです。宇宙船での遊泳シーンは全く不自然さが無く、どうやって撮影したんでしょうか?見て良かったですね。
火星でひとりぼっちになっちゃったら、本当に心細いですね。気持ちの強い人だと思いました。
  1. 2016年02月06日 21:36 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

MPSoCのVivado プロジェクトを作ってみよう2(MPSoCのPSの設定を確認した)

”MPSoCのVivado プロジェクトを作ってみよう1(MPSoC のプロジェクトを作製した)”の続き。

前回はMPSoC のプロジェクトを作製し、MPSoC のProcessing System をAdd IP した。今回は、MPSoC のProcessing System を開いて設定できる項目を見ていこう。

最初に、PS UltraScale+ Block Design から見ていこう。各ユニットをクリックするとその設定項目を表示することができる。
MPSoC_test1_13_160201.png

次に、I/O Configuration を見ていこう。Memory Interface とI/O Peripherals が見える。
MPSoC_test1_14_160205.png

I/O Configuration の続き、High Speed のI/O の設定もある。
MPSoC_test1_15_160205.png

Clock Configuration を見ていこう。
PL Fabric Clocks がPL でPS から供給されて使えるクロックだ。やはり4本のようだ。初期設定が400MHzとは、期待できそうだ。
MPSoC_test1_16_160205.png

Clock Configuration の続き。SERDES IO Clocks などが見える。
MPSoC_test1_17_160205.png

DDR Configurations を見ていこう。DRAM デバイスのビット幅は8, 16, 32ビット幅から選択できる。
MPSoC_test1_18_160205.png

MPSoC_test1_19_160205.png

最後に PS-PL Configuration だ。PS - PL間のインターフェース用のAXI バスの設定も行うことができる。
MPSoC_test1_20_160205.png
MPSoC_test1_21_160205.png
MPSoC_test1_22_160205.png

128ビット幅固定のS_AXI_ACP などは、活かすかどうか?の設定しか無いが、AXI_HP0_FPD などは、1 に変更すると、128/64/32 ビット幅を選択する事ができる。
MPSoC_test1_23_160205.png
  1. 2016年02月05日 05:11 |
  2. MPSoC
  3. | トラックバック:0
  4. | コメント:0

MPSoCのVivado プロジェクトを作ってみよう1(MPSoC のプロジェクトを作製した)

MPSoCのお勉強1(概要)”、”MPSoCのお勉強2 (PSとPLのインターフェース)”を踏まえて、MPSoC のVivado プロジェクトを作って、MPSoC の中身がどうなっているかを探っていって、その後、適当に回路を組んで、どのくらいの動作周波数で動作するのか?を探っていきたいと思う。

使用するのは、Vivado 2015.4 とする。それではプロジェクトを作ってみよう。

Vivado 2015.4 を立ち上げて、Create New Project アイコンをクリックした。

New Project ダイアログが開いた。
MPSoC_test1_1_160201.png

Project name をMPSoC_test1 とした。
MPSoC_test1_2_160201.png

Project Type はRTL Project とした。
MPSoC_test1_3_160201.png

Add Source はそのままで、Next > ボタンをクリックした。
MPSoC_test1_4_160201.png

Add Existiing IP (optional) もそのままで、Next > ボタンをクリックした。
MPSoC_test1_5_160201.png

Add Constraints (optional) もそのままで、Next > ボタンをクリックした。
MPSoC_test1_6_160201.png

Default Part はどうしようか?迷ったが、適当に選択した。ピン数は多い方を選択した。スピードグレードはZYBO と比較しようとして、-1 を選択した。結局、選択したMPSoC は、xczu9eg-ffvb1156-1-e-EVAL だった。
MPSoC_test1_7_160201.png

Nww Project Summary が表示された。Finish ボタンをクリックした。
MPSoC_test1_8_160201.png

MPSoc_test1 プロジェクトが生成された。
MPSoC_test1_9_160201.png

IP Integrator を起動して、ブロックデザインを生成しよう。

IP Integrator から Create Block Design をクリックした。

Create Block Design ダイアログが開いた。
Design name を MPSoC_test1 に変更した。
MPSoC_test1_10_160201.png

add IP ボタンをクリックして、ZYNQ UltraScale+ MPSoC を選択した。
MPSoC_test1_11_160201.png

MPSoC のPS 部分がインスタンスされた。
MPSoC_test1_12_160201.png

ZYNQ UltraScale+ MPSoC をダブルクリックすると、設定ダイアログが開き、MPSoC のPS のブロック図が表示された。
MPSoC_test1_13_160201.png
  1. 2016年02月04日 04:49 |
  2. MPSoC
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト8(アプリケーションの作製)

ステレオカメラによる距離測定テスト7(BOOT.bin, devicetree.dtb)”の続き。

前回は、BOOT.bin とdevicetree.dtb を作製して、ZYBO にMicroSDカードを挿入してUbuntu 14.04 LTS を立ち上げた。
今回は、自分のカメラ画像とそのラプラシアンフィルタ処理画像、それにZYBO_0_2 からHDMI ポート経由で転送されてきたカメラ画像を表示するアプリケーションを作製する。

最初に、udmabuf デバイス・ドライバをロードするスクリプト udmabuf_insmod を作製した。

sudo insmod udmabuf.ko udmabuf0=15197184
sudo chmod 666 /dev/udmabuf0


15197184 の意味は、SVGA画面(800 x 600)を 3 画面とXGA画面(1024 x 768)を 3 画面の1ピクセル = 4 バイト分のフレームバッファということである。

自分のカメラ画像とそのラプラシアンフィルタ処理画像、それにZYBO_0_2 からHDMI ポート経由で転送されてきたカメラ画像を表示するアプリケーションのStereoCam_Alt_Disp.c を作製し、gcc でコンパイルした。

右目用、左目用のZYBO をTera Term でコネクトした。

右目用、左目用のZYBOの写真を示す。
StereoCamTest_49_160128.jpg

右目用のZYBO_0_2 は cam_disp_vdma アプリケーションを起動した。(cam_disp_vdma.c については、”デバイスドライバ udmabuf を使用する2”を参照のこと)

左目用のZYBO は
.udmabuf_insmod
を起動してから、
./StereoCam_Alt_Disp
を起動した。
StereoCamTest_61_160203.png
上図の右上が右目用ZYBO_0_2 のシリアル・インターフェースに接続したTera Term、右下が右目用ZYBO_0_2 にSSH で接続したTera Term、左上が左目用ZYBO のシリアル・インターフェースに接続したTera Term、左下が左目用ZYBO にSSH で接続したTera Term を示す。

アプリケーションのStereoCam_Alt_Disp は起動後、自分のカメラ画像を表示するが、その後はキー待ちをしていて

1 とリターンキーを押すと自分のカメラ画像を表示する。
2 とリターンキーを押すと右目用ZYBO からHDMI ポート経由で転送されてきた画像を表示する。
l (Lの小文字)とリターンキーを押すと、自分のカメラ画像にラプラシアンフィルタ処理を施す。
c とリターンキーを押すと、カメラ画像に戻る。
q とリターンキーで、アプリケーションを終了する。

という処理を行う。

左目カメラ画像を示す。
StereoCamTest_62_160203.jpg

右目カメラ画像を示す。
StereoCamTest_63_160203.jpg

左目ラプラシアンフィルタ処理画像を示す。
StereoCamTest_64_160203.jpg

右目ラプラシアンフィルタ処理画像を示す。
StereoCamTest_65_160203.jpg

暗くなっているので、エッジが見にくくなっている。こういう場合は、以前作成したアンシャープマスキング・フィルタIP を使って、エッジを強調すれば良いのじゃないかな?と思っている。

最後に、StereoCam_Alt_Disp.c を貼っておく。

//
// StereoCam_Alt_Disp.c
// 2016/02/01 by marsee 
//

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

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

#define XGA_HORIZONTAL_PIXELS    1024
#define XGA_VERTICAL_LINES        768
#define XGA_ALL_DISP_ADDRESS    (XGA_HORIZONTAL_PIXELS*XGA_VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

#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)

void cam_i2c_init(volatile unsigned *caminf_axi_iic) {
    caminf_axi_iic[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    caminf_axi_iic[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 *caminf_axi_iic, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    caminf_axi_iic[66] = 0x100 | (device_addr & 0xfe);    // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    caminf_axi_iic[66] = write_addr;
    caminf_axi_iic[66] = (write_data >> 8)|0xff;            // first data
    caminf_axi_iic[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main()
{
    int fd0, fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd9, fd10;
    volatile unsigned *bmdc_axi_lites0;
    volatile unsigned *caminf_axi_vdma_0, *dviin_axi_vdma_0;
    volatile unsigned *caminf_axis_switch_0, *caminf_axis_switch_1;
    volatile unsigned *caminf_mt9d111_inf_axis_0;
    volatile unsigned *caminf_axi_iic;
    volatile unsigned *caminf_lap_filter_axis_0;
    volatile unsigned *frame_buffer;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    char c;
    int laps_cntrl;

    // Bitmap Display Controller 0 AXI4 Lite Slave (UIO6)
    fd6 = open("/dev/uio6", O_RDWR); // bitmap_display_controller 0 axi4 lite
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (bitmap_disp_cntrler_axi_master_0) open error\n");
        exit(-1);
    }
    bmdc_axi_lites0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!bmdc_axi_lites0){
        fprintf(stderr, "bmdc_axi_lites0 mmap error\n");
        exit(-1);
    }
    
    // caminf_axi_vdma_0 (UIO1)
    fd1 = open("/dev/uio1", O_RDWR); // caminf_axi_vdma_0 interface AXI4 Lite Slave
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (caminf_axi_vdma_0) open error\n");
        exit(-1);
    }
    caminf_axi_vdma_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (!caminf_axi_vdma_0){
        fprintf(stderr, "caminf_axi_vdma_0 mmap error\n");
        exit(-1);
    }
    
    // dviin_axi_vdma_0 (UIO7)
    fd7 = open("/dev/uio7", O_RDWR); // dviin_axi_vdma_0 interface AXI4 Lite Slave
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (dviin_axi_vdma_0) open error\n");
        exit(-1);
    }
    dviin_axi_vdma_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!dviin_axi_vdma_0){
        fprintf(stderr, "dviin_axi_vdma_0 mmap error\n");
        exit(-1);
    }
    
    // mt9d111 i2c AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // mt9d111 i2c AXI4 Lite Slave
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (caminf_axi_iic) open error\n");
        exit(-1);
    }
    caminf_axi_iic = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!caminf_axi_iic){
        fprintf(stderr, "caminf_axi_iic mmap error\n");
        exit(-1);
    }

    // mt9d111 inf axis AXI4 Lite Slave (UIO5)
    fd5 = open("/dev/uio5", O_RDWR); // mt9d111 inf axis AXI4 Lite Slave
    if (fd5 < 1){
        fprintf(stderr, "/dev/uio5 (caminf_mt9d111_inf_axis_0) open error\n");
        exit(-1);
    }
    caminf_mt9d111_inf_axis_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd5, 0);
    if (!caminf_mt9d111_inf_axis_0){
        fprintf(stderr, "caminf_mt9d111_inf_axis_0 mmap error\n");
        exit(-1);
    }

    // caminf_axis_switch_0 (UIO2)
    fd2 = open("/dev/uio2", O_RDWR); // caminf_axis_switch_0 interface AXI4 Lite Slave
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (caminf_axis_switch_0) open error\n");
        exit(-1);
    }
    caminf_axis_switch_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!caminf_axis_switch_0){
        fprintf(stderr, "caminf_axis_switch_0 mmap error\n");
        exit(-1);
    }
    
    // caminf_axis_switch_1 (UIO3)
    fd3 = open("/dev/uio3", O_RDWR); // caminf_axis_switch_1 interface AXI4 Lite Slave
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (caminf_axis_switch_1) open error\n");
        exit(-1);
    }
    caminf_axis_switch_1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!caminf_axis_switch_1){
        fprintf(stderr, "caminf_axis_switch_1 mmap error\n");
        exit(-1);
    }
    
    // caminf_lap_filter_axis_0 (UIO4)
    fd4 = open("/dev/uio4", O_RDWR); // caminf_lap_filter_axis_0 interface AXI4 Lite Slave
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (caminf_lap_filter_axis_0) open error\n");
        exit(-1);
    }
    caminf_lap_filter_axis_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!caminf_lap_filter_axis_0){
        fprintf(stderr, "caminf_lap_filter_axis_0 mmap error\n");
        exit(-1);
    }
    
    // udmabuf0
    fd9 = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd9 == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned *)mmap(NULL, (XGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES)+(SVGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES), PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }

    // caminf_axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    caminf_axis_switch_1[16] = 0x0// 0x40 = 0
    caminf_axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000, disable
    caminf_axis_switch_1[0] = 0x2// Comit registers
    
    // caminf_axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    caminf_axis_switch_0[16] = 0x0// 0x40 = 0;
    caminf_axis_switch_0[0] = 0x2// Comit registers
    
    // phys_addr of udmabuf0
    fd10 = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fd10 == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open error\n");
        exit(-1);
    }
    read(fd10, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd10);
    printf("phys_addr = %x\n", (unsigned)phys_addr);
    
    // AXI VDMA Initialization sequence (caminf_axi_vdma_0)
    caminf_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((caminf_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    caminf_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((caminf_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    caminf_axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    caminf_axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    caminf_axi_vdma_0[41] = SVGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    caminf_axi_vdma_0[42] = SVGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    caminf_axi_vdma_0[43] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 1
    caminf_axi_vdma_0[44] = (unsigned)phys_addr+SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 2
    caminf_axi_vdma_0[45] = (unsigned)phys_addr+2*SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 3
    caminf_axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((caminf_axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    caminf_axi_vdma_0[40] = SVGA_VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // AXI VDMA Initialization sequence (dviin_axi_vdma_0)
    dviin_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((dviin_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    dviin_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((dviin_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    dviin_axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    dviin_axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    dviin_axi_vdma_0[41] = XGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    dviin_axi_vdma_0[42] = XGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    dviin_axi_vdma_0[43] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 1
    dviin_axi_vdma_0[44] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+XGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 2
    dviin_axi_vdma_0[45] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+2*XGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 3
    dviin_axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((dviin_axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    dviin_axi_vdma_0[40] = XGA_VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(caminf_axi_iic);
    
    cam_i2c_write(caminf_axi_iic, 0xba, 0xf00x1);        // Changed regster map to IFP page 1
    cam_i2c_write(caminf_axi_iic, 0xba, 0x970x20);    // RGB Mode, RGB565

    caminf_mt9d111_inf_axis_0[1] = 0;
    
    // Camera Base Address Setting
    caminf_mt9d111_inf_axis_0[0] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS;; // Camera Interface start (Address is dummy)

    // bitmap display controller settings
    bmdc_axi_lites0[0] = (unsigned)phys_addr; // Bitmap Display Controller 0 start, My Camera Image
    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case '1' :
                bmdc_axi_lites0[0] = (unsigned)phys_addr; // Bitmap Display Controller 0 start, My Camera Image
                break;
            case '2' :
                bmdc_axi_lites0[0] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+0xc; // Another one ZYBO Camera Image
                break;
            case 'l' : // laplacian filter
                // caminf_axis_switch_1, 1to2 ,Select M01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_1[16] = 0x80000000// 0x40 = 0x80000000; disable
                caminf_axis_switch_1[17] = 0// 0x44 = 0;
                caminf_axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
                // laplacian filter AXIS Start
                laps_cntrl = caminf_lap_filter_axis_0[0] & 0x80// Auto Restart bit
                caminf_lap_filter_axis_0[0] = laps_cntrl | 0x01// Start bit set
                caminf_lap_filter_axis_0[0] = 0x80// Auto Restart bit set
    
                // caminf_axis_switch_0, 2to1, Select S01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_0[16] = 0x1// 0x40 = 0x1;
                caminf_axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers
                break;
            case 'c' : // camera image
                // caminf_axis_switch_1, 1to2 ,Select M01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_1[16] = 0// 0x44 = 0;
                caminf_axis_switch_1[17] = 0x80000000// 0x40 = 0x80000000; disable
                caminf_axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
                // laplacian filter AXIS Start
                caminf_lap_filter_axis_0[0] = 0x00// Auto Restart Disable
    
                // caminf_axis_switch_0, 2to1, Select S01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_0[16] = 0x0// 0x40 = 0x0;
                caminf_axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers
                break;
        }
        c = getc(stdin);
    }

    munmap((void *)bmdc_axi_lites0, 0x10000);
    munmap((void *)caminf_axi_vdma_0, 0x10000);
    munmap((void *)dviin_axi_vdma_0, 0x10000);
    munmap((void *)caminf_axi_iic, 0x10000);
    munmap((void *)caminf_mt9d111_inf_axis_0, 0x10000);
    munmap((void *)caminf_axis_switch_0, 0x10000);
    munmap((void *)caminf_axis_switch_1, 0x10000);
    munmap((void *)caminf_lap_filter_axis_0, 0x10000);
    munmap((void *)frame_buffer, (XGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES)+(SVGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES));
    
    close(fd0);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd5);
    close(fd6);
    close(fd7);
    close(fd9);
    close(fd10);
    
    return(0);
}

  1. 2016年02月03日 04:50 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

MPSoCのお勉強2 (PSとPLのインターフェース)

MPSoCのお勉強1(概要)”の続き。

今回はPSとPLのインターフェースについて見ていこう。

今回も、”Zynq UltraScale+ MPSoC Technical Reference Manual UG1085 (v1.0) November 24, 2015”を参考にさせて頂いて、引用させていただくことにする。

まずは、PSとPLのインターフェースの図を”Zynq UltraScale+ MPSoC Technical Reference Manual UG1085 (v1.0) November 24, 2015”の19ページの”Figure 2-1: Programmable Logic Block Diagram”を引用させていただくことにする。
MPSoC_2_160202.png

もう1つ表を引用する。”Zynq UltraScale+ MPSoC Technical Reference Manual UG1085 (v1.0) November 24, 2015”の26ページの”Table 2-8: AXI Interfaces”を引用させていただくことにする。
MPSoC_3_160202.png

HPポートはZynq と同じ4ポートだが、128/64/32 ビット幅で、128 ビット幅が追加されている。これで少なくともZynq の2倍の帯域になった。Figure 1-1 を見ると、FPD (Full Power Domain ?) にもアクセスできそうだ。40ビット・アドレッシング、オプションで、 support I/O coherency to the APU L2 and L1 caches. (注:もしかするとI/O coherency はHPCだけかも?)

PL_LPD と LPD_PL が追加されて、LPD (Low Power Domain ?) にPL からアクセスすることができそうだ。Low Latency アクセス。Cortex-R5からPLへはここを通ると思う。

HPMがPS がマスターのハイ・パフォーマンスAXI インターフェースのようだ。

ACE が追加になって、36ビットアドレッシング、128ビット幅、PLとPS のAPU メモリシステム間で、full 2-way coherency。

HPCは、SMMUに直接接続されている。これは128ビット幅のみか。(追記:Vivado 2015.4 のIPI でPS を見ると、AXI_HPCは 32/64/128 ビット幅に設定できるようです) 40ビット・アドレッシング、オプションで、 support I/O coherency to the APU L2 and L1 caches.

HPM がZynq のGP ポートに相当するようだ。Cortex-A53 からPLへのアクセス。

ACP も健在で、128ビット幅になった。

プロセッサの種類がCortex-A53 とCortex-R5 と2つになったので、面倒になっているようだ。
  1. 2016年02月02日 05:05 |
  2. MPSoC
  3. | トラックバック:0
  4. | コメント:0

MPSoCのお勉強1(概要)

ZynqMP勉強会 に参加を申し込んだので、この際なので、Zynq UltraScale+ MPSoC (以下MPSoC とする)に付いて勉強していきたいと思った。

MPSoC のマニュアルといえば、”Zynq UltraScale+ MPSoC Technical Reference Manual UG1085 (v1.0) November 24, 2015”がある。これを参考にして勉強していくことにする。

まずは、”Zynq UltraScale+ MPSoC Technical Reference Manual UG1085 (v1.0) November 24, 2015”の13ページ、”Figure 1-1: Zynq UltraScale+ MPSoC Top-Level Block Diagram”を引用させて頂く。
MPSoC_1_160201.png

・CPUとGPU
  ・Cortex-A53 application processing unit (APU) - ARM v8アーキテクチャで64ビットプロセッサ x4個
  ・Cortex-R5 real-time processing unit (RPU) - ARM v7アーキテクチャで32ビットプロセッサ x2個
  ・Mali-400 graphics processing unit (GPU) - 64KBのL2 キャッシュを持ったGPU

・MPSoCの4つのhigh-speed serial I/O (HSSIO) インターフェース
  ・PCI Express version 2.1
  ・SATA 3.1 (なんとSATA がある。これは嬉しい) 1.5G, 3G, 6G
  ・Display Port (4K-2K まで)
  ・USB 3.0
  ・1 Gb/s SGMII

・MIOのピンは78個、EMIOで288個。
・Figure 1-1 を見るとBattery Power, Low Power, Full Power に分かれている。
・DDRメモリ・コントローラ: DDR3, DDR3L, DDR4, LPDDR4 対応。ECCサポート。
・GPU: ジオメトリ・プロセッサ1つ、ピクセル・プロセッサ2つ、Open GL ES 1.1 and 2.0サポート、OpenVG 1.1
・OCM は 256KB でZynq と同じか。。。
・TCM (Tightly-coupled memory) というのが付いているのか?64KB、4バンク
  1. 2016年02月01日 05:29 |
  2. MPSoC
  3. | トラックバック:0
  4. | コメント:0