FPGAの部屋

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

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。ご利用ください。 http://marsee101.web.fc2.com/index.html

2016年02月

組み合わせ回路を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 プロジェクトを見ていこう。
e331e9aa.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 プロジェクトを示す。
41d5d19f.png


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


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

Implementation -> Open Implemented Design -> Report Power をクリックして消費電力解析を行った。
1295cef1.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 にした。
これで、もう一度インプリメントしてみると、消費電力解析は正常と思える値になった。
605f6653.png


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


問題が無くなった。
最初からこういう結果を出してくれれば問題なかったと思うのだが。。。クロックが無いと -signal_rate のデフォルト値は 0.0 と書いてあるのだが、このデフォルト値が効かなくなって、最大値になるような感じだね?なぜだろうか?バグなのかな?

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

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

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

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

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


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


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


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


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

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


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


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


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


3ca62856.png


最後に convert_calib を貼っておく。

#!/bin/sh

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


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)

// -l : left bmp file name
// -r : right bmp file name
// -h : help


2a54e5ed.png


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

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


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


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


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


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

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

//
// RL_capture_bmp.cpp
// 2016/02/24
//
// This software converts the left and right of the camera image to BMP file.
// -l : left bmp file name
// -r : right bmp file name
// -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;
    FILE *fbmp;
    BMP24FORMAT **bmp_data; // 24 bits Date of BMP files (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES)

    while ((c=getopt(argc, argv, "l:r:h")) != -1){
        switch (opt){
            case 'l':
                strcpy(left_bmp_fn, optarg);
                break;
            case 'r':
                strcpy(right_bmp_fn, 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>] [-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+0xc); // The Right Camera Image

    int file_no = 0;
    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);

                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^1].blue, fbmp);
            fputc((int)bmp_data[i][j^1].green, fbmp);
            fputc((int)bmp_data[i][j^1].red, fbmp);
        }
    }
}


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

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

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


17440719.jpg


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


b579eb6a.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(視差)ウインドウが見られるということ?かな?

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 があった。
70cf1275.png


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


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


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


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


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


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


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


cloud.asc ファイルのファイルサイズは凄く大きい。5.3 MB 位だ。
c99ac7e2.png

↑このページのトップヘ