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

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

FPGAの部屋

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

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板7(基板完成、発注)

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板6(基板作成途中)”の続き。

前回は、階層回路図の3番目の図面の+3.3V と +1.2V がトップで接続しているかに見えたが、接続していなかった。トップの階層回路図でもう一度配線を消して、引き直したら、ネットがつながった。そういう訳で3回くらい自動配線をやり直している。また、特に +1.2V のラインがIC の電源ピンとパスコンのピンの配線が離れてしまっていた。そこで、そこだけは自分で修正した。今回は、図面をまた修正してFixし、Fusion PCB に発注した。

もう一度自動配線して、電源ラインを見直して、これで大丈夫かな?ということになったので、最後にもう一度、コネクタの配置と取り付け穴などの寸法を見直すことにした。すると、高速コネクタが 0.35 mm ずれていたので、修正した。
よってももう一度、自動配線して電源ラインを見直して完成した。
これで発注できるということで、Fusion PCB に発注した。
基板の大きさは、85 mm X 72.5 mm で、2層の 1.2 mm 厚、青で発注したが、中国では10月はじめはお休みなので、いつ出来上がるかわかんないな。。。

基板の表面を示す。
Ultra96V2_HL_exp_board_28_190930.png

裏面を示す。
Ultra96V2_HL_exp_board_29_190930.png

シルク画面を示す。
残念ながら、左上のタクトスイッチのPOR、Power はFusion PCB に注文する際にはシルクを忘れてしまった。
Ultra96V2_HL_exp_board_30_190930.png
  1. 2019年09月30日 04:31 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板6(基板作成途中)

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板5(とりあえず基板配線ができた)”の続き。

前回は、部品の位置を決定して、FreeRouter で自動配線してもらって、KiCad にインポートしたのだが、階層回路図の3番目の図面の+3.3V と +1.2V がトップで接続しているかに見えたが、接続していなかった。トップの階層回路図でもう一度配線を消して、引き直したら、ネットがつながった。そういう訳で3回くらい自動配線をやり直している。また、特に +1.2V のラインがIC の電源ピンとパスコンのピンの配線が離れてしまっていた。そこで、そこだけは自分で修正した。

電源ラインの配線を示す。
+3.3V から。
Ultra96V2_HL_exp_board_23_190929.png

+1.2V
Ultra96V2_HL_exp_board_24_190929.png

+1.8V
Ultra96V2_HL_exp_board_25_190929.png

現在はシルクを修正中だ。下の図は、裏面のシルクを修正しているところだ。
Ultra96V2_HL_exp_board_26_190929.png
  1. 2019年09月29日 05:07 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板5(とりあえず基板配線ができた)

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板4(基板作成開始)”の続き。

前回は、パーツにフットプリントをアサインして、基板の取り付け穴とコネクタの位置を決定した。今回は、部品の位置を決定して、FreeRouter で自動配線してもらって、KiCad にインポートした。

まずは、FreeRouter の画面を示す。KiCad からフットプリントや配線情報をエクスポートして、FreeRouter に読み込み、自動配線した結果だ。
Ultra96V2_HL_exp_board_22_190927.png

FreeRouter の自動配線した結果をエクスポートして、KiCad でインポートした。
Ultra96V2_HL_exp_board_21_190927.png

DRC もOK だが、特に 3 つの電源ラインがどうつながっているか?を確認する必要がある。
  1. 2019年09月27日 05:21 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS でAXI4 Lite のレジスタのHDL コードを生成しよう

レジスタを生成できるツールがあるようだが、Vivado HLS でも同様に AXI4 Lite インターフェースのレジスタを C ソースコードから生成することができる。これを使って、 AXI4 Lite インターフェースのレジスタを Verilog HDL と VHDL のソースコードとして簡単に生成することができるので紹介する。

紹介するVivado HLS のプロジェクトは”Ultra96のDisplayPortを使用するためのIPを作成する2(pattern_gen_axis IP)”の pattern_gen_axis IP とする。

pattern_gen_axis IP の C ソースコードの一部分を抜き出す。

int pattern_gen_axis(hls::stream<ap_axis<32,1,1,1> >& outs,
        int v_size, int h_size,
        ap_uint<1> &init_done, ap_uint<1> &init_done_out
){
#pragma HLS INTERFACE ap_none register port=init_done_out
#pragma HLS INTERFACE s_axilite port=init_done
#pragma HLS INTERFACE s_axilite port=v_size
#pragma HLS INTERFACE s_axilite port=h_size
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE s_axilite port=return


上のソースコードを見ると、ブロックレベルのインターフェースは s_axilite で、init_done と v_size, h_size が s_axilite つまりAXI4 Lite インターフェースとなっている。
これを合成すると、solution1/syn の verilog ディレクトリと vhdl ディレクトリにHDL ファイルが生成される。
vivado_hls_reg_1_190926.png

verilog を見てみると、

pattern_gen_axis.v
pattern_gen_axis_AXILiteS_s_axi.v


があるのが分かる。pattern_gen_axis.v がトップのVerilog HDL ファイルだが、そのAXI4 Lite インターフェース部分は、pattern_gen_axis_AXILiteS_s_axi.v にまとめられている。
pattern_gen_axis_AXILiteS_s_axi.v を見ると、とても読みやすいのが分かるだろうか?
pattern_gen_axis_AXILiteS_s_axi.v を示す。

// ==============================================================
// File generated on Tue Jan 22 04:34:10 JST 2019
// Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC v2018.3 (64-bit)
// SW Build 2405991 on Thu Dec  6 23:36:41 MST 2018
// IP Build 2404404 on Fri Dec  7 01:43:56 MST 2018
// Copyright 1986-2018 Xilinx, Inc. All Rights Reserved.
// ==============================================================
`timescale 1ns/1ps
module pattern_gen_axis_AXILiteS_s_axi
#(parameter
    C_S_AXI_ADDR_WIDTH = 6,
    C_S_AXI_DATA_WIDTH = 32
)(
    // axi4 lite slave signals
    input  wire                          ACLK,
    input  wire                          ARESET,
    input  wire                          ACLK_EN,
    input  wire [C_S_AXI_ADDR_WIDTH-1:0] AWADDR,
    input  wire                          AWVALID,
    output wire                          AWREADY,
    input  wire [C_S_AXI_DATA_WIDTH-1:0] WDATA,
    input  wire [C_S_AXI_DATA_WIDTH/8-1:0] WSTRB,
    input  wire                          WVALID,
    output wire                          WREADY,
    output wire [1:0]                    BRESP,
    output wire                          BVALID,
    input  wire                          BREADY,
    input  wire [C_S_AXI_ADDR_WIDTH-1:0] ARADDR,
    input  wire                          ARVALID,
    output wire                          ARREADY,
    output wire [C_S_AXI_DATA_WIDTH-1:0] RDATA,
    output wire [1:0]                    RRESP,
    output wire                          RVALID,
    input  wire                          RREADY,
    // user signals
    output wire [10:0]                   v_size_V,
    output wire [10:0]                   h_size_V,
    output wire [0:0]                    init_done_V
);
//------------------------Address Info-------------------
// 0x00 : reserved
// 0x04 : reserved
// 0x08 : reserved
// 0x0c : reserved
// 0x10 : Data signal of v_size_V
//        bit 10~0 - v_size_V[10:0] (Read/Write)
//        others   - reserved
// 0x14 : reserved
// 0x18 : Data signal of h_size_V
//        bit 10~0 - h_size_V[10:0] (Read/Write)
//        others   - reserved
// 0x1c : reserved
// 0x20 : Data signal of init_done_V
//        bit 0  - init_done_V[0] (Read/Write)
//        others - reserved
// 0x24 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

//------------------------Parameter----------------------
localparam
    ADDR_V_SIZE_V_DATA_0    = 6'h10,
    ADDR_V_SIZE_V_CTRL      = 6'h14,
    ADDR_H_SIZE_V_DATA_0    = 6'h18,
    ADDR_H_SIZE_V_CTRL      = 6'h1c,
    ADDR_INIT_DONE_V_DATA_0 = 6'h20,
    ADDR_INIT_DONE_V_CTRL   = 6'h24,
    WRIDLE                  = 2'd0,
    WRDATA                  = 2'd1,
    WRRESP                  = 2'd2,
    WRRESET                 = 2'd3,
    RDIDLE                  = 2'd0,
    RDDATA                  = 2'd1,
    RDRESET                 = 2'd2,
    ADDR_BITS         = 6;

//------------------------Local signal-------------------
    reg  [1:0]                    wstate = WRRESET;
    reg  [1:0]                    wnext;
    reg  [ADDR_BITS-1:0]          waddr;
    wire [31:0]                   wmask;
    wire                          aw_hs;
    wire                          w_hs;
    reg  [1:0]                    rstate = RDRESET;
    reg  [1:0]                    rnext;
    reg  [31:0]                   rdata;
    wire                          ar_hs;
    wire [ADDR_BITS-1:0]          raddr;
    // internal registers
    reg  [10:0]                   int_v_size_V = 'b0;
    reg  [10:0]                   int_h_size_V = 'b0;
    reg  [0:0]                    int_init_done_V = 'b0;

//------------------------Instantiation------------------

//------------------------AXI write fsm------------------
assign AWREADY = (wstate == WRIDLE);
assign WREADY  = (wstate == WRDATA);
assign BRESP   = 2'b00;  // OKAY
assign BVALID  = (wstate == WRRESP);
assign wmask   = { {8{WSTRB[3]}}, {8{WSTRB[2]}}, {8{WSTRB[1]}}, {8{WSTRB[0]}} };
assign aw_hs   = AWVALID & AWREADY;
assign w_hs    = WVALID & WREADY;

// wstate
always @(posedge ACLK) begin
    if (ARESET)
        wstate <= WRRESET;
    else if (ACLK_EN)
        wstate <= wnext;
end

// wnext
always @(*) begin
    case (wstate)
        WRIDLE:
            if (AWVALID)
                wnext = WRDATA;
            else
                wnext = WRIDLE;
        WRDATA:
            if (WVALID)
                wnext = WRRESP;
            else
                wnext = WRDATA;
        WRRESP:
            if (BREADY)
                wnext = WRIDLE;
            else
                wnext = WRRESP;
        default:
            wnext = WRIDLE;
    endcase
end

// waddr
always @(posedge ACLK) begin
    if (ACLK_EN) begin
        if (aw_hs)
            waddr <= AWADDR[ADDR_BITS-1:0];
    end
end

//------------------------AXI read fsm-------------------
assign ARREADY = (rstate == RDIDLE);
assign RDATA   = rdata;
assign RRESP   = 2'b00;  // OKAY
assign RVALID  = (rstate == RDDATA);
assign ar_hs   = ARVALID & ARREADY;
assign raddr   = ARADDR[ADDR_BITS-1:0];

// rstate
always @(posedge ACLK) begin
    if (ARESET)
        rstate <= RDRESET;
    else if (ACLK_EN)
        rstate <= rnext;
end

// rnext
always @(*) begin
    case (rstate)
        RDIDLE:
            if (ARVALID)
                rnext = RDDATA;
            else
                rnext = RDIDLE;
        RDDATA:
            if (RREADY & RVALID)
                rnext = RDIDLE;
            else
                rnext = RDDATA;
        default:
            rnext = RDIDLE;
    endcase
end

// rdata
always @(posedge ACLK) begin
    if (ACLK_EN) begin
        if (ar_hs) begin
            rdata <= 1'b0;
            case (raddr)
                ADDR_V_SIZE_V_DATA_0: begin
                    rdata <= int_v_size_V[10:0];
                end
                ADDR_H_SIZE_V_DATA_0: begin
                    rdata <= int_h_size_V[10:0];
                end
                ADDR_INIT_DONE_V_DATA_0: begin
                    rdata <= int_init_done_V[0:0];
                end
            endcase
        end
    end
end


//------------------------Register logic-----------------
assign v_size_V    = int_v_size_V;
assign h_size_V    = int_h_size_V;
assign init_done_V = int_init_done_V;
// int_v_size_V[10:0]
always @(posedge ACLK) begin
    if (ARESET)
        int_v_size_V[10:0] <= 0;
    else if (ACLK_EN) begin
        if (w_hs && waddr == ADDR_V_SIZE_V_DATA_0)
            int_v_size_V[10:0] <= (WDATA[31:0] & wmask) | (int_v_size_V[10:0] & ~wmask);
    end
end

// int_h_size_V[10:0]
always @(posedge ACLK) begin
    if (ARESET)
        int_h_size_V[10:0] <= 0;
    else if (ACLK_EN) begin
        if (w_hs && waddr == ADDR_H_SIZE_V_DATA_0)
            int_h_size_V[10:0] <= (WDATA[31:0] & wmask) | (int_h_size_V[10:0] & ~wmask);
    end
end

// int_init_done_V[0:0]
always @(posedge ACLK) begin
    if (ARESET)
        int_init_done_V[0:0] <= 0;
    else if (ACLK_EN) begin
        if (w_hs && waddr == ADDR_INIT_DONE_V_DATA_0)
            int_init_done_V[0:0] <= (WDATA[31:0] & wmask) | (int_init_done_V[0:0] & ~wmask);
    end
end


//------------------------Memory logic-------------------

endmodule


どうですか?読みやすいですよね?
という具合に、AXI4 Lite のレジスタ記述が欲しい場合は、レジスタにしたい名称をトップの関数の引数にして、
#pragma HLS INTERFACE s_axilite port=<レジスタにしたい名称>
ディレクティブを与えて、適当な演算を書いて合成すれば、XXXX_AXILiteS_s_axi.v や XXXX_AXILiteS_s_axi.vhd ができるので、それを抜き出して使ってAXI4 Lite のレジスタ記述のあるHDL 回路を作成しましょう。

ただし、Vivado HLS で作れる回路だったら、そのまま C で記述して作ったほうが簡単で確実なのは言うまでもない。これを使用するのはカメラのインターフェース回路にレジスタを付けるというような場合のみとなるだろう。
そして、使用する場合はライセンセンスを守って使いましょう
  1. 2019年09月26日 06:27 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板4(基板作成開始)

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板4(回路図修正、フットプリント作成)”の続き。

前回は、電源ICのフットプリントを作るのが難しいので、1.2V の電源モジュールを MOUSER で売っているTRACO Power TSR 1-0512SM に変更した。更に 3 個のフットプリントを作成した。今回は、進捗は少ないが、パーツにフットプリントをアサインして、基板の取り付け穴とコネクタの位置を決定した。

回路図が完成し、フットプリントを作成したので、パーツにフットプリントを割り当てた。
Ultra96V2_HL_exp_board_18_190922.png

Ultra96V2_HL_exp_board_19_190922.png

すべてのパーツにフットプリントを割り当てることができた。
Eeschema でネットリストを出力した。

Pcbnew を立ち上げてEeschema で出力したネットリストを読み込んだ。
パーツが読み込まれてきた。
取り付け穴とコネクタの位置を決定した。
Ultra96V2_HL_exp_board_20_190922.png

低速コネクタPMOD 拡張ボードと低速コネクタからの基板の距離は一緒だ。低速コネクタPMOD 拡張ボードのPMOD は 2 つ並んでいるが、その面に 4 個 PMOD コネクタを並べる予定だ。
後 2 個は右の辺と左の辺に 1 個ずつ並べる予定だ。
基板のサイズは、73 mm X 85 mm の予定だ。
  1. 2019年09月24日 05:16 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

FPGAの部屋のまとめサイトの更新(2019年9月23日)

FPGAの部屋のまとめサイトを更新しました。 2019 年 9 月 23 日までの記事を更新しました。
  1. 2019年09月23日 06:52 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板4(回路図修正、フットプリント作成)

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板3(回路図完成)”の続き。

前回は、「Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板の回路図が完成した」と書いたが、やはり、電源ICのフットプリントを作るのが難しいので、1.2V の電源モジュールを MOUSER で売っているTRACO Power TSR 1-0512SM に変更した。更に 3 個のフットプリントを作成した。

変更した回路図を示す。1.2V の電源モジュールを TRACO Power TSR 1-0512SM に変更した。
Ultra96V2_HL_exp_board_14_190922.png

最初に、作成した集合抵抗チップネットワーク抵抗器 CND2A10Y 1kΩ のフットプリントを示す。
秋月電子には、1 KΩの集合抵抗しかないが、MOUSER には、同じフットプリントの集合抵抗が売っているので、それを使おう。3.3 kΩがこれだ
Ultra96V2_HL_exp_board_17_190922.png

1.2V の電源モジュールの TRACO Power TSR 1-0512SMのフットプリントを作成した。
Ultra96V2_HL_exp_board_16_190922.png

最後に、高速コネクタに勘合するコネクタ TE5179031-2 のフットプリントを作成した。
Ultra96V2_HL_exp_board_15_190922.png

これでフットプリントができたので、基板を作成する準備が整った。
  1. 2019年09月22日 05:53 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

「雪ミルク」でかき氷を食べてきました

今日は、奥さんと奥さんのお母さんと笠間市の雪ミルクでかき氷を食べてきました。

雪ミルクの LINEブログです。

行ってみると笠間の普通の民家でしたが、かき氷屋になっていて、果物も沢山売っていました。

頼んだかき氷は 3 つです。

メロンのかき氷。半分のメロンの上にかき氷が盛り付けてあります。かき氷もふわふわで美味しいです。
yuki_milk_1_190922.jpg

いちじくのかき氷。これも美味しかったですよ。。。
yuki_milk_2_190922.jpg

栗金時大納言。これは味が薄くて食べた気がしませんでした。
yuki_milk_3_190922.jpg

いずれも氷がふわふわでしたよ。天然氷を使っているそうです。値段は全部 800 円でした。

メロンのかき氷は本当にメロン半分使ってました。800 円は安いかも? これが一番オトクな感じですね。
yuki_milk_4_190922.jpg

食べかけですみません。

果物も安いようです。
家ではシャインマスカット 1000 円を買ってきました。
  1. 2019年09月21日 20:58 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2019.1 でLOOG_MERGE 指示子を使う

Adom Taylor さんの”MicroZed Chronicles: HLS Working with Loops”を見て、LOOP_MERGE 指示子を発見した。Loop を 1 つにしてくれるということで、もしかしたら、AXI4-Stream で 2 つの入力ストリームがある時の 2 つの USER 待ちループを 1 個にしてくれるんじゃないだろうか?ということでやってみた。

今回、対象にするのは、
2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1
2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力2

2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1”の s_squares_axis.cpp ソースコードを見てもらうと分かると思うが、 x と y の 2 つの入力を一緒にしたコードを書いている。それを x だけ、y だけの USER 信号待ちループにして LOOP_MERGE 指示子を与えて、x と y の 2 つの入力を一緒にしたコードと同じ結果になるのではないか?ということでやってみよう。使用するのは、Vivado HLS 2019.1 とする。

s_squares_axis.cpp を示す。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=result
#pragma HLS INTERFACE axis register both port=y
#pragma HLS INTERFACE axis register both port=x
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    xt.user = 0; yt.user = 0;
    Loop_xw : while(xt.user == 0){
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        x >> xt;
    }

    Loop_yw : while(yt.user == 0){
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        y >> yt;
    }

    Loop_main : for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        if(i != 0){
            x >> xt; y >> yt;
        }
        rlt.data = xt.data*xt.data + yt.data*yt.data;
        if(i == 0)
            rlt.user = 1;
        else
            rlt.user = 0;
        if(i==9)
            rlt.last = 1;
        else
            rlt.last = 0;
        result << rlt;
    }
    return(0);
}


x と y のUSER 待ちループを分離して、まずは、LOOP_MERGE 指示子を入れないで性能を見てみよう。

C シミュレーション結果を示す。
s_squares_axis_8_190920.png

問題ない。

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

Latency が 18 クロックで、”2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1”の 16 クロックよりも 2 クロック長い。

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

C/RTL 協調シミュレーションの波形を示す。
s_squares_axis_11_190920.png

やはり、x と y の入力は排他的になっているようだ。

次に、Loop_xw と Loop_yw に LOOP_MERGE 指示子を入れた。s_squares_axis.cpp を示す。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=result
#pragma HLS INTERFACE axis register both port=y
#pragma HLS INTERFACE axis register both port=x
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    xt.user = 0; yt.user = 0;
    Loop_xw : while(xt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        x >> xt;
    }

    Loop_yw : while(yt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        y >> yt;
    }

    Loop_main : for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        if(i != 0){
            x >> xt; y >> yt;
        }
        rlt.data = xt.data*xt.data + yt.data*yt.data;
        if(i == 0)
            rlt.user = 1;
        else
            rlt.user = 0;
        if(i==9)
            rlt.last = 1;
        else
            rlt.last = 0;
        result << rlt;
    }
    return(0);
}


これで、solution2 を作成してやってみた。

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

LOOP_MERGE 指示子を入れないときと同じ結果になった。

C/RTL 協調シミュレーションの結果を示す。
s_squares_axis_13_190920.png

C/RTL 協調シミュレーションの波形を示す。
s_squares_axis_14_190920.png

これも LOOP_MERGE 指示子を入れないときと同じ結果だった。

次に、試しに Loop_main にも LOOP_MERGE 指示子を入れてみた。s_squares_axis.cpp を示す。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=result
#pragma HLS INTERFACE axis register both port=y
#pragma HLS INTERFACE axis register both port=x
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    xt.user = 0; yt.user = 0;
    Loop_xw : while(xt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        x >> xt;
    }

    Loop_yw : while(yt.user == 0){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        y >> yt;
    }

    Loop_main : for(int i=0; i<10; i++){
#pragma HLS LOOP_MERGE
#pragma HLS PIPELINE II=1
        if(i != 0){
            x >> xt; y >> yt;
        }
        rlt.data = xt.data*xt.data + yt.data*yt.data;
        if(i == 0)
            rlt.user = 1;
        else
            rlt.user = 0;
        if(i==9)
            rlt.last = 1;
        else
            rlt.last = 0;
        result << rlt;
    }
    return(0);
}


このコードでのC コードの合成結果、C/RTL 協調シミュレーション結果も同じだった。

LOOP_MERGE 指示子は入出力を伴うループのマージは行わないのかも知れない?
計算のループだけマージされるのかも知れない?
  1. 2019年09月20日 05:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板3(回路図完成)

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板2(回路図作成途中)”の続き。

前回は、Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板の回路図を作成している途中だった。今回は、Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板の回路図が完成した。

まずは、階層回路図のトップを示す。
Ultra96V2_HL_exp_board_9_190918.png

低速コネクタ部分の回路図を示す。
Ultra96V2_HL_exp_board_10_190918.png

高速コネクタの 1 枚目の回路図を示す。
Ultra96V2_HL_exp_board_11_190918.png

高速コネクタの 2 枚目の回路図を示す。
Ultra96V2_HL_exp_board_12_190918.png

実は、高速コネクタのPL に接続されてる端子のUSB2D3 がUSB 3.1 コントローラーに接続されていてPL のピンに接続していなかった。そこで、P6 は 4 個のピンだけを接続してある。残念。。。

BOM を示す。
Ultra96V2_HL_exp_board_13_190918.png
  1. 2019年09月18日 05:32 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板2(回路図作成途中)

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板1”の続き。

前回は、”Ultra96-V2 の低速コネクタに接続する PMOD 拡張基板を作ってきたが、PMOD が 2 個だと、CMOS カメラを接続するので、精一杯で、ロボットを動かすPWM を出力するPMOD が確保できなかった。よって、高速コネクタのピンも使用して、最大PMOD 6 個出力できるようにしようと思う。”ということで、Ultra96V2 のコネクタの仕様や使用する部品について検討した。今回は、KiCad で回路図を作成している。新たな部品のシンボルを作成して、階層回路図を使用して書いている。

KiCad 階層回路図を書くのは初めてなので、”KiCad 5.0 / 5.1 入門実習テキスト『KiCad Basics for 5.x』 #マッハ新書”を見ながら階層回路図を書き方を学習しながら回路図を書いている。

まずは、作成したシンボルを示す。
秋月電子のチップネットワーク抵抗器 CND2A10Y 1kΩの回路図シンボルをR_Network08 を修正して作成した。
Ultra96V2_HL_exp_board_7_190917.png

次に、秋月電子のムラタ表面実装DC-DCコンバーター OKL-T/3-W5N-C の回路図シンボルを作成した。これは同じピン番号が 2 つあるピンがあるので、それは大きな番号に振り直した。
Ultra96V2_HL_exp_board_8_190917.png

これを使用してPMOD 2個分ずつに回路図を分割した。
Ultra96V2_HL_exp_board_4_190917.png

low_speed_con, high_speed_con0, high_speed_con1 の 3 つの階層回路図に分割した。

low_speed_con を示す。これはだいたい完成している。
Ultra96V2_HL_exp_board_3_190917.png

high_speed_con0 を示す。これは高速コネクタは書けているが、高速コネクタの片側のピンをhigh_speed_con1 に回す必要がある。その部分はまだだ。
Ultra96V2_HL_exp_board_5_190917.png

high_speed_con0 を示す。これは、PMOD 回路をコピーしただけで、まだ高速コネクタからの信号線をつないでない。高速コネクタからの信号線は階層ラベルにする予定だ。
Ultra96V2_HL_exp_board_6_190917.png

全体の完成度は 50 % と言ったところか? 引き続き回路図の完成に向けて努力しよう。
  1. 2019年09月17日 05:11 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

「記憶にございません!」(映画)見てきました

今日は午後から奥さんと「記憶にございません!」(映画)見てきました。中居貴一がいい味出してました。笑っちゃいました。とっても良かったです。ストレスが解消されましたよ。。。
  1. 2019年09月16日 21:48 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

同じ Vivado HLS IP を複数個インスタンスした場合、Vivado 2018.3 でハードウェアをエクスポートしてSDKを立ち上げたときにその他のVivado HLS のIPのドライバがSDKのハードウェア・プラットフォームに入らない

Vivado 2018.3 でハードウェアをエクスポートしてSDKを立ち上げたときに、同じ Vivado HLS IP を複数個インスタンスした場合、その他のVivado HLS のIPのドライバが入らないという問題があったので、ブログに書いておく。多分バグじゃないのかな?

Vivado 2018.3 の sums_ex4 プロジェクトを示す。このプロジェクトはZybo Z7-10 用のプロジェクトだ。
SDK_bug_1_190914.png

sums_bd ブロックデザインを示す。
SDK_bug_2_190914.png

ブロックデザインを見ると DMA2axis8 IP が 2 つあるのが分かる。これらは、DDRメモリからDMA Read を行って、s_squares_axis IP にAIX4-Stream でデータを供給し、平方数の和を取って、axis2DMA IP にAIX4-Stream でデータを送る。axis2DMA IP はAXI4-Stream で送られてきたデータをDMA Write するというような回路になっている。
s_squares_axis IP のソースコードは”2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1”だ。
DMA2axis8.cpp を貼っておく。

// DMA2axis8.cpp (2019/09/10 by marsee)

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

int DMA2axis8(volatile ap_int<8> *in, hls::stream<ap_axis<8,1,1,1> >& outs){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=10 port=in offset=slave
    ap_axis<8,1,1,1> out_val;

    for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        out_val.data = in[i];
        if(i == 0)
            out_val.user = 1;
        else
            out_val.user = 0;
        if(i==9)
            out_val.last = 1;
        else
            out_val.last = 0;
        outs << out_val;
    }
    return(0);
}


axis2DMA.cpp を貼っておく。

// axis2DMA.cpp (2018/05/18 by marsee)

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

int axis2DMA(hls::stream<ap_axis<32,1,1,1> >& ins, volatile ap_int<32> *out){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE m_axi depth=10 port=out offset=slave
#pragma HLS INTERFACE axis register both port=ins
    ap_axis<32,1,1,1> in_val;

    for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        ins >> in_val;
        out[i] = in_val.data;
    }
    return(0);
}


論理合成、インプリメンテーション、ビットストリームの生成を行った。レポートを示す。
SDK_bug_3_190914.png
SDK_bug_4_190914.png

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

axis2DMA, DMA2axis8, s_squares_axis が入っているのは見えるが、ハードウェア・プラットフォームの drivers には、DMA2axis8 しか入っていないのが分かる。これだと他のVivado HLS IP のドライバ関数が使用できなかった。

次に Vivado 2018.3 で sums_ex4_temp プロジェクトを作成した。
SDK_bug_6_190914.png

sums_bd ブロックデザインを示す。
SDK_bug_7_190914.png

DMA2axis8 を 1 個のみインスタンスして、他は Constant で代用した。
これで論理合成、インプリメンテーション、ビットストリームの生成を行った。レポートを示す。
SDK_bug_8_190914.png
SDK_bug_9_190914.png

これでハードウェアをエクスポートしてSDKを立ち上げたところ、ハードウェア・プラットフォームには、3 個のVivado HLS で作成したIP のドライバが入っている。
SDK_bug_10_190914.png

やはり、Vivado 2018.3 のブロックデザインでVivado HLS で作成したIP を複数個入れているとSDK のハードウェア・プラットフォーム上に他のVivado HLS IP のドライバがロードされないようだ。バグじゃないだろうか?

なお、Vivado HLS IP のドライバがハードウェア・プラットフォームに入っていないことの回避方法としては、”SDKにVivado HLSで作製したIPのドライバをインポートする”がある。
この方法で、local Repositories か Global Repositories にIP のフォルダを追加しておけばよい。今回は、Global Repositories にIP のフォルダを追加した。これでVivado HLS で作成した IP のドライバを使えるようになる。
  1. 2019年09月15日 04:50 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2 の高速、低速コネクタ用PMOD 拡張基板1

Ultra96-V2 の低速コネクタに接続する PMOD 拡張基板を作ってきたが、PMOD が 2 個だと、CMOS カメラを接続するので、精一杯で、ロボットを動かすPWM を出力するPMOD が確保できなかった。よって、高速コネクタのピンも使用して、最大PMOD 6 個出力できるようにしようと思う。

当初は、低速コネクタのPMOD 拡張基板と高速コネクタの拡張基板を分けようと思っていたのだが、Ultra96-V2 の回路図を見ると高速コネクタに電源の出力がない。回路図を一部を引用する。
Ultra96V2_HL_exp_board_1_190913.png

低速コネクタの方には、+5V と+VCC_PSAUX (1.8V) が出力されているので、高速コネクタと一緒に低速コネクタ用のヘッダを基板に搭載する必要がある。回路図の一部を引用する。
Ultra96V2_HL_exp_board_2_190913.png

やはり +5.0V を電源に使用する必要がある。+5.0V は 6A ということなので、電流容量に不安はないはず。
高速コネクタのFPGA 出力ピンの電圧はほとんど 1.2V ということが分かった。制約ファイル Ultra96_constraints_180318.xdc の一部を引用する。

# ----------------------------------------------------------------------------
# High-speed expansion connector
# ---------------------------------------------------------------------------- 
# Bank 65
set_property PACKAGE_PIN P1   [get_ports {CSI0_C_N                }];  # "P1.CSI0_C_N"
set_property PACKAGE_PIN N2   [get_ports {CSI0_C_P                }];  # "N2.CSI0_C_P"
set_property PACKAGE_PIN N4   [get_ports {CSI0_D0_N               }];  # "N4.CSI0_D0_N"
set_property PACKAGE_PIN N5   [get_ports {CSI0_D0_P               }];  # "N5.CSI0_D0_P"
set_property PACKAGE_PIN M1   [get_ports {CSI0_D1_N               }];  # "M1.CSI0_D1_N"
set_property PACKAGE_PIN M2   [get_ports {CSI0_D1_P               }];  # "M2.CSI0_D1_P"
set_property PACKAGE_PIN M4   [get_ports {CSI0_D2_N               }];  # "M4.CSI0_D2_N"
set_property PACKAGE_PIN M5   [get_ports {CSI0_D2_P               }];  # "M5.CSI0_D2_P"
set_property PACKAGE_PIN L1   [get_ports {CSI0_D3_N               }];  # "L1.CSI0_D3_N"
set_property PACKAGE_PIN L2   [get_ports {CSI0_D3_P               }];  # "L2.CSI0_D3_P"
set_property PACKAGE_PIN T2   [get_ports {CSI1_C_N                }];  # "T2.CSI1_C_N"
set_property PACKAGE_PIN T3   [get_ports {CSI1_C_P                }];  # "T3.CSI1_C_P"
set_property PACKAGE_PIN R3   [get_ports {CSI1_D0_N               }];  # "R3.CSI1_D0_N"
set_property PACKAGE_PIN P3   [get_ports {CSI1_D0_P               }];  # "P3.CSI1_D0_P"
set_property PACKAGE_PIN U1   [get_ports {CSI1_D1_N               }];  # "U1.CSI1_D1_N"
set_property PACKAGE_PIN U2   [get_ports {CSI1_D1_P               }];  # "U2.CSI1_D1_P"
set_property PACKAGE_PIN H5   [get_ports {DSI_CLK_N               }];  # "H5.DSI_CLK_N"
set_property PACKAGE_PIN J5   [get_ports {DSI_CLK_P               }];  # "J5.DSI_CLK_P"
set_property PACKAGE_PIN F1   [get_ports {DSI_D0_N                }];  # "F1.DSI_D0_N"
set_property PACKAGE_PIN G1   [get_ports {DSI_D0_P                }];  # "G1.DSI_D0_P"
set_property PACKAGE_PIN E3   [get_ports {DSI_D1_N                }];  # "E3.DSI_D1_N"
set_property PACKAGE_PIN E4   [get_ports {DSI_D1_P                }];  # "E4.DSI_D1_P"
set_property PACKAGE_PIN D1   [get_ports {DSI_D2_N                }];  # "D1.DSI_D2_N"
set_property PACKAGE_PIN E1   [get_ports {DSI_D2_P                }];  # "E1.DSI_D2_P"
set_property PACKAGE_PIN C3   [get_ports {DSI_D3_N                }];  # "C3.DSI_D3_N"
set_property PACKAGE_PIN D3   [get_ports {DSI_D3_P                }];  # "D3.DSI_D3_P"
set_property PACKAGE_PIN C2   [get_ports {HSIC_DATA               }];  # "C2.HSIC_DATA"
# Bank 66
set_property PACKAGE_PIN A2   [get_ports {HSIC_STR                }];  # "A2.HSIC_STR"
# Bank 26
set_property PACKAGE_PIN E8   [get_ports {CSI0_MCLK               }];  # "E8.CSI0_MCLK"
set_property PACKAGE_PIN D8   [get_ports {CSI1_MCLK               }];  # "D8.CSI1_MCLK"


制約ファイルに書いてあったが、Bank 26 は 1.8V 、Bank 65 は 1.2V だそうだ。

# IO standard for Bank 26 Vcco supply is fixed at 1.8V
# IO standard for Bank 65 Vcco supply is fixed at 1.2V


となると 1.2V 電源を用意する必要があるが、1.8V だと秋月電子のシリーズ・レギュレータに対応品があるが、1.2V だとない。
そこで、可変電圧レギュレータを使うことにしようと思う。
ムラタ表面実装DC-DCコンバーター OKL-T/3-W5N-C
◆入力電圧:DC2.4V~5.5V(3V/5V兼用)
◆出力電圧:0.6V~3.63V(可変)
◆出力電流:最大3A
なので、良さそうだ。
1.2V 出力のときの抵抗は 2.0 kΩだそうだ。
表面実装品の抵抗は秋月電子では5% 精度なので、これだけはアキシャルでよいか。
小型 金属皮膜抵抗1/4W2kΩ(100本入)
「従来の1/6Wと同サイズの小型1/4W、±1%の金属皮膜抵抗です。」ということなので、小さくて良さそうだ。

(2019/09/17:追記)
1.2V の電源モジュールとして、MOUSER で売っているTRACO Power TSR 1-0512SM も良さそう。
表面実装品、入力電圧: 3 V to 5.5 V、出力電圧 - チャンネル1: 1.2 V 、出力電流 - チャンネル1: 1 A
  1. 2019年09月13日 05:33 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力2

2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1”の続き。

普通の C ソースコードで、2つのAXI4 Stream 入力からデータを受け取る方法を見つけたので、ソースコードを貼って、C シミュレーションを行った。今回は、C コードの合成、C/RTL 協調シミュレーション、Export RTL を行う。なお、Vivado HLS 2019.1 を使用している。

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

Loop2 のLatency が 11 クロックと長めだが、1 クロックで 1 出力できるようだ。
リソース使用量はBRAM_18K は 0 個、DSP48E は 1 個、FF が 282 個、LUT が 676 個だった。

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

Latency は 44 クロックだった。

C/RTL 協調シミュレーション波形を示す。全体波形から。
s_squares_axis_5_190912.png

データ転送している辺りを拡大してみよう。
s_squares_axis_6_190912.png

入力 x と y は途中で、TREADY が 0 になってうけらない部分はあるが、その後は順調に 1 入力を 1 クロックでこなしている。
出力は、順調に 1 出力を 1 クロックでこなせている。

Export RTL を行った。結果を示す。
s_squares_axis_7_190912.png

LUT が 141 個、FF が 189 個、DSP が 2 個、使用されている。
CP achieved post-implementation は、 6.859 ns で問題無さそうだ。
  1. 2019年09月12日 04:39 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Ultra96のPMOD拡張ボード計画(高速、低速コネクタをPMODへ変換)の部品調査結果の覚書2

Ultra96のPMOD拡張ボード計画(高速、低速コネクタをPMODへ変換)の部品調査結果の覚書”の続き。

前回は、Ultra96のコネクタや拡張ボードを作るためのコネクタについて考察した。
石原ひでみさんの”FPGAの内容の薄い本2”を見て、改めてPMOD X 6 個のボードを作りたくなったので、もう一度コネクタを考えてみよう。

Hi speedコネクタ 5177983-2
勘合する相手方コネクタ たぶんコネクタ勘合時の高さ 8mmの5179031-2
MIPI Adapter Mezzanineカードを入れると少し斜めになっている(低速コネクタより高速コネクタの高さが高い)。だが、そうでないとUSBコネクタの金属部分に基板が当たってしまうためだろう?
Mouser https://www.mouser.jp/ProductDetail/TE-Connectivity-AMP/5179031-2?qs=sGAEpiMZZMvffgRu4KC1R8rBJR2XJ4CGVDXQfomnbnc%3D
データシート https://www.mouser.jp/datasheet/2/418/NG_CD_5179031_A4-1251986.pdf

Ultra96 と MIPI Adapter Mezzanine が勘合した時の高さは 7.9 mm 。低速コネクタの勘合高は 7.3 mm で 0.5 mm 斜めになっている。低速コネクタ高さは基板から 6.5 mm だった。

コネクタ高さ 12.5 mm と 9.5 mm の低速コネクタ用ヘッダを買ったのだが、標準ヘッダとの高さは 3 mm刻みだった。

石原ひでみさんの”FPGAの内容の薄い本2”で知った”96Boards Consumer Edition Low Cost Hardware Platform Specification Version 1.0, January 2015”にExpansion Board Connectors が載っている。
TE 5179030-2: High speed”は、高さ 6.6 mm のコネクタだったので、”5179031-2”よりも背が低いPULGコネクタとなっている。

もう1つHi Speed 用のPULG コネクタは、”FCI 61083-063400LF”で、これは、6.7 mm の高さのPULG コネクタになっている。データシートを見ると、4.7 mm, 5.7 mm, 6.7 mm, 7.7 mm の 4 種類の高さのコネクタがあるようだ。

MIPI Adapter Mezzanineカードでは、7.7 mm の高さのコネクタが使用されている。
値段は、”5179031-2”が 1 個で 454 円、”61083-064400LF”が 615 円なので、”5179031-2”が良さそうだ。
もっと背の高いPULG コネクタは無さそうなので、斜めになってしまうな。。。
またまたIDAさんからHi Speed コネクタの KiCad モデルについて教えてもらった。


低速コネクタ用のヘッダは、表面実装品よりも背が多少低いが秋月電子の”2mmピッチピンヘッダ 2×40 (80P)”にしよう。
理由は、表面実装品だと位置がいい加減になってしまって、コネクタに入らないことがあるかも知れないからだ。スルーホールだったら、Ultra96に入れといて、はんだ付けすればバッチリ。ちょっと怖いけど。。。

PMOD コネクタ 2X6P が欲しいのだが、MOUSERで検索できない。分かる方型番教えてください。
IDAさんに教えてもらいました。ありがとうございました。
SSW-106-02-T-D-RA
10 個で 126 円なので、秋月電子の 2.5 倍の価格か。。。でも割らなくて良いので、この方が見栄えも良いな。。。

中国の部品屋さんをIDAさんに教えてもらいました。ありがとうございます。
Changjiang Connectors A2541HWR-2x6P

レベル変換ICのプルアップ抵抗
PMOD 1 個のみ 1608 の 1 kΩを 8 個実装して、後は集合抵抗を実装する。

集合抵抗
チップネットワーク抵抗器 CND2A10Y 1kΩ (5個入)
http://akizukidenshi.com/catalog/g/gP-10717/

上の秋月電子の集合抵抗のパッケージ互換品(データシートのフットプリント例も参考になる)
CAT25-102JALF
https://www.mouser.jp/ProductDetail/Bourns/CAT25-102JALF?qs=sGAEpiMZZMvrmc6UYKmaNYMTSqkpbtN5ShD9l70ohz4%3D
  1. 2019年09月11日 04:34 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

2つのAXI4 Stream 入力データを演算してAXI4 Stream 出力1

以前、”2つのHLSストリームを同時に入力して演算してHLSストリーム出力2”でDATAFLOW 指示子を使って、2 つのHLS Stream 入力を同時に受け取ることができた。
しかし、その方法は大げさというかソースコードが分かりにくくなるため、普通の C ソースコードで、2つのAXI4 Stream 入力からデータを受け取る方法を見つけたので、書いておく。

s_squares_axis.cpp のコンセプトは、user == 1 だったら、AXI4 Stream 入力を止めて、2 つのAXI4 Steam 入力が共に user == 1 になることを待つということだ。
それでは、s_squares_axis.cpp を貼っておく。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=result
#pragma HLS INTERFACE axis register both port=y
#pragma HLS INTERFACE axis register both port=x
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    xt.user = 0; yt.user = 0;
    Loop1 : while(!(xt.user==1 && yt.user==1)){
#pragma HLS PIPELINE II=1
    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        if(xt.user == 0)
            x >> xt;
        if(yt.user == 0)
            y >> yt;
    }

    Loop2 : for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        if(i != 0){
            x >> xt; y >> yt;
        }
        rlt.data = xt.data*xt.data + yt.data*yt.data;
        if(i == 0)
            rlt.user = 1;
        else
            rlt.user = 0;
        if(i==9)
            rlt.last = 1;
        else
            rlt.last = 0;
        result << rlt;
    }
    return(0);
}


なお、いつもの do{ } while( ); 文を使用すると、PIPELINE 指示子の II を 1 にしても、ループを 1 回回るのに 2 クロックかかってしまう。 do{ } while( ); 文を使用すると、ストリーム用のパイプから入力した user 信号をそのクロックで処理する必要があるから、1 クロックで処理することができなくなってしまうのだろう? while() 文を使用すると、現在のクロックでストリーム用のパイプから入力した user 信号は次のクロックで評価することができるので、ループを 1 回回るのに 1 クロックで済むのだと思う。
このように、ソフトウェアとしてではなく、ハードウェアとして、ソースコードを書く必要がある。

次に、テストベンチの s_squares_axis_tb.cpp を示す。

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

int s_squares_axis(hls::stream<ap_axis<8,1,1,1> >& x,
    hls::stream<ap_axis<8,1,1,1> >& y, hls::stream<ap_axis<32,1,1,1> >& result);

int main(){
    using namespace std;
    hls::stream<ap_axis<8,1,1,1> > x;
    hls::stream<ap_axis<8,1,1,1> > y;
    hls::stream<ap_axis<32,1,1,1> > result;
    ap_axis<8,1,1,1> xt;
    ap_axis<8,1,1,1> yt;
    ap_axis<32,1,1,1> rlt;

    for(int i=0; i<5; i++){ // dummy data
        xt.user = 0; yt.user = 0;
        xt.data = i; yt.data = i;
        x << xt;
        if(i>2)
            y << yt;
    }
    for(int i=0; i<10; i++){
        xt.data = i; yt.data = i+1;
        if(i == 0){
            xt.user = 1; yt.user = 1;
        }else{
            xt.user = 0; yt.user = 0;
        }
        if(i == 9){
            xt.last = 1; yt.last = 1;
        }else{
            xt.last = 0; yt.last = 0;
        }
        x << xt; y << yt;
    }
    s_squares_axis(x, y, result);

    cout << endl;
    cout << "result" << endl;
    for(int i=0; i<10; i++){
        result >> rlt;
        cout << "i = " << i << " result = " << rlt.data << " user = "
                << rlt.user << " last = " << rlt.last << endl;
    }
    cout << endl;
    return(0);
}


AXI4 Stream の x パイプでは、5 つのダミーデータを入れてあるが、y パイプでは、2 つになっている。これで、本体のデータを正常に受けることができれば成功だ。

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

C シミュレーションを行った。問題ないようだ。
s_squares_axis_2_190910.png
  1. 2019年09月10日 05:08 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

台風15号で暴風雨です

今日は、台風15号が来ていて暴風雨となっています。雨風強くて不安な早朝です。
今日は技術的なブログはお休みで、強い直撃台風15号のことを日記として書いておこうと思います。
台風15号は今、横須賀市付近ですが、これから茨城県に来るようです。
taifu15_1_190909.jpg

ファクサイは、960 hPa の強い台風で最大瞬間風速は 60 m/s です。怖いですね。。。アンテナが飛ばされないかどうか?不安です。
taifu15_2_190909.png

雨雲レーダーが真っ赤というのは、経験が無いやばい状態ですね。特にこれから台風がやってくるというのが怖いです。
taifu15_3_190909.jpg

なんとか被害が無いことを祈ります。。。

(結果)
千葉と違って、風は強かったんですが、ものすごい暴風とまでは行かなかった感じです。被害なくてよかったですが、千葉は停電もしているようでかわいそうです。
職場ではメインで使用しているパソコンが壊れました。サブのVivado 用パソコンをメインに仕立てていました。メースソフトをインストール、設定してさてメールを出そうという時にネットワーク・スイッチが壊れました。orz
気圧が低いとコンデンサが膨れやすくて、壊れやすいということがあるのでしょうか?厄日でした。。。
  1. 2019年09月09日 04:48 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

演算に任意精度固定小数点データ型を使用したUltra96用ガボールフィルタ2

演算に任意精度固定小数点データ型を使用したUltra96用ガボールフィルタ1”の続き。

前回は、ガボールフィルタの演算を任意精度固定小数点データ型で実装したソースコードを示した。今回は、C シミュレーション、C コードの合成を行った。

C シミュレーション結果を示す。
Gabor_filter_lh_2_27_190907.png

Gabor_filter_lh_2_28_190907.png

なんと 60123 個のエラーが出ている。
しかし、ガボールフィルタの結果を浮動小数点演算の結果と見比べてみても良く分からない。

まずは、ガボールフィルタの左車線検出用の画像を示す。左が任意精度固定小数点データ型でハードウェア化するコードで、右が浮動小数点演算したガボールフィルタ画像だ。
Gabor_filter_lh_2_29_190907.jpg

次に、ガボールフィルタの右白線検出用の画像を示す。同様の配置になっている。
Gabor_filter_lh_2_30_190907.jpg

大丈夫そうだ。

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

Estimated は 4.288 ns で良さそうだ。
Detail の Loop をみても 1 クロック 1 出力出ているのが確認できる。
リソース使用量は 9 x 9 のフィルタということでかなり多くなっている。DSP48E も 49 個使用している。
合成されたVerilog HDL ファイルも 18 個で多くなっている。
Gabor_filter_lh_2_33_190908.png

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

960124 クロックだった。ガボールフィルタの画像は 800 ピクセル X 600 行 X 2 個 = 960000 なので、ほとんど 1 クロック 1 ピクセルを出力できている。

C/RTL 協調シミュレーションの波形を示す。
Gabor_filter_lh_2_34_190908.png

outs と ins のTVALID , TREADY がほとんど 1 でスループットが大きいのが分かる。

Export RTL の結果を示す。
Gabor_filter_lh_2_35_190908.png

合成時よりもLUT や FF の使用量がだいぶ減っているのが分かる。CP achieved post-implementation は 4.196 ns で問題無さそうだ。
  1. 2019年09月08日 05:11 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

演算に任意精度固定小数点データ型を使用したUltra96用ガボールフィルタ1

ZYBO で使用したガボールフィルタをUltra96 に持ってきたが、C/RTL 協調シミュレーションできちんと出力が出ていたのに、Vivado でIP として使うと白一色になってしまった。(ソースコードは、”Ultra96用ガボールフィルタIP の作成1”参照)
どうしようか?と思ったが、試しに演算を任意精度固定小数点データ型でやってみたところ、Vivado に持っていっても正常動作するようになったので、紹介する。

Vivado HLS 2018.3 で作ってあって、プロジェクト名は、Gabor_filter_lh_2_fp だ。
Gabor_filter_lh_2_26_190906.png

ヘッダファイルの Gabor_filter_lh_2_fp.h を貼っておく。

// Gabor_filter_lh_2_fp.h
// 2016/07/24
// 2016/07/25 : 右白線検出用のGabor Filterの重みを追加
// 2016/07/27 : 右白線検出用配列と左白線検出用配列を統合
// 2016/08/29 : 1回目左白線検出、2回目右白線検出のモードを追加
// 2019/08/22 : 更新
// 2019/09/03 : 任意精度固定小数点データ型使用
//

#ifndef __Gabor_filter_lh_H__
#define __Gabor_filter_lh_H__

#include <ap_int.h>

#define ARRAY_SIZE  9

#define RIGHT_WEIGHT    1
#define LEFT_WEIGHT     0
#define L_R_WEIGHT      2

const ap_fixed<32,24,AP_TRN,AP_WRAP> gabor_weight[2][ARRAY_SIZE][ARRAY_SIZE] = { // 左白線検出用+右白線検出用
    {
        {0,-0.01171875,-0.0390625,-0.0625,-0.02734375,0.02734375,0.0390625,0.01953125,0.00390625},
        {-0.01171875,-0.05859375,-0.10546875,-0.04296875,0.125,0.1953125,0.11328125,0.01953125,-0.0078125},
        {-0.04296875,-0.09375,-0.01953125,0.28515625,0.52734375,0.37109375,0.0625,-0.06640625,-0.0390625},
        {-0.04296875,0.015625,0.33203125,0.73046875,0.625,0.0546875,-0.28125,-0.203125,-0.0546875},
        {0.015625,0.19921875,0.52734375,0.53515625,-0.0703125,-0.62109375,-0.53125,-0.17578125,-0.0078125},
        {0.0625,0.1953125,0.23046875,-0.15234375,-0.69921875,-0.72265625,-0.28515625,0.01171875,0.05078125},
        {0.0390625,0.046875,-0.09765625,-0.40625,-0.51171875,-0.234375,0.05859375,0.10546875,0.04296875},
        {0.00390625,-0.02734375,-0.12109375,-0.1875,-0.09375,0.0703125,0.11328125,0.0546875,0.01171875},
        {-0.00390625,-0.01953125,-0.03515625,-0.015625,0.0390625,0.0625,0.0390625,0.0078125,-0.00390625}
    },
    {
        {0.00390625,0.01953125,0.02734375,0.00390625,-0.046875,-0.06640625,-0.0390625,-0.01171875},
        {0.00390625,0.04296875,0.12890625,0.17578125,0.08203125,-0.0625,-0.10546875,-0.05859375},
        {-0.03125,-0.01953125,0.13671875,0.41796875,0.51171875,0.26953125,-0.0078125,-0.08203125},
        {-0.06640625,-0.18359375,-0.19921875,0.15625,0.66015625,0.73046875,0.36328125,0.05078125},
        {-0.03125,-0.2109375,-0.5234375,-0.57421875,-0.0703125,0.48046875,0.5078125,0.2265625},
        {0.03515625,-0.0234375,-0.3203125,-0.72265625,-0.73046875,-0.25390625,0.140625,0.171875},
        {0.04296875,0.09375,0.046875,-0.21484375,-0.48828125,-0.4375,-0.16796875,0.00390625},
        {0.01171875,0.0546875,0.1171875,0.08984375,-0.05078125,-0.16015625,-0.12890625,-0.046875},
        {0,0.0078125,0.03515625,0.06640625,0.0546875,0.00390625,-0.0234375,-0.01953125}
    }
};

const float gabor_fweight[2][ARRAY_SIZE][ARRAY_SIZE] = { // 左白線検出用+右白線検出用(float)
    {
        {0.001282,-0.009914,-0.04062,-0.060586,-0.027574,0.026072,0.038427,0.018191,0.003056},
        {-0.012155,-0.057448,-0.104645,-0.042953,0.123263,0.197238,0.114451,0.020448,-0.007239},
        {-0.042252,-0.093065,-0.018911,0.285374,0.525746,0.372687,0.060734,-0.064748,-0.040465},
        {-0.042261,0.015673,0.332798,0.728763,0.625046,0.053591,-0.283076,-0.203293,-0.05608},
        {0.017342,0.198305,0.52554,0.535526,-0.069756,-0.622839,-0.531089,-0.177366,-0.006367},
        {0.060866,0.19708,0.231032,-0.154219,-0.699885,-0.721808,-0.286707,0.013004,0.049249},
        {0.038379,0.04877,-0.098477,-0.404993,-0.510165,-0.233566,0.057894,0.104366,0.041887},
        {0.0047,-0.0278,-0.121277,-0.187262,-0.093276,0.070512,0.113857,0.055799,0.009976},
        {-0.003798,-0.01885,-0.035607,-0.01709,0.037692,0.064268,0.038606,0.007536,-0.002133}
    },
    {
        {0.005562,0.018882,0.028293,0.004499,-0.044995,-0.064838,-0.039469,-0.009822,0.000815},
        {0.002294,0.04108,0.127023,0.175094,0.083025,-0.063755,-0.106402,-0.057798,-0.01406},
        {-0.031269,-0.021096,0.135641,0.417286,0.512467,0.269946,-0.008354,-0.082091,-0.041357},
        {-0.066348,-0.184919,-0.197802,0.15614,0.65976,0.728616,0.361674,0.052074,-0.027152},
        {-0.031146,-0.211178,-0.523777,-0.573856,-0.069756,0.480311,0.506451,0.225223,0.041031},
        {0.035552,-0.023892,-0.320104,-0.723563,-0.728735,-0.253689,0.1391,0.170625,0.067723},
        {0.04216,0.094939,0.047511,-0.216623,-0.488075,-0.437898,-0.168739,0.003336,0.027009},
        {0.012112,0.056596,0.115239,0.090332,-0.05076,-0.158403,-0.127847,-0.046375,-0.004918},
        {-0.00168,0.007437,0.036985,0.067021,0.053689,0.004977,-0.02365,-0.018248,-0.005928}
    }
};

#endif


ソースコードのGabor_filter_lh_2_fp.cpp を貼っておく。

// Gabor_fiter_lh_2.cpp
// 2016/07/23 by marsee
// 2016/07/25 : 右白線検出用のGabor Filterを追加して、右左の白線を指定するRorL 引数を追加
// 2016/07/27 : 右白線検出用配列と左白線検出用配列を統合
// 2016/08/29 : 1回目左白線検出、2回目右白線検出のモードを追加
// 2019/08/22 : 引数に int row, int col を追加
// 2019/09/03 : 任意精度固定小数点データ型を演算に使用することにした
//

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

#include "Gabor_filter_lh_2_fp.h"

uint8_t conv_rgb2y(ap_int<32> rgb);

int Gabor_filter_lh_2(hls::stream<ap_axis<32,1,1,1> >& ins,
        hls::stream<ap_axis<32,1,1,1> >& outs, int row, int col, ap_uint<2> RorL){
#pragma HLS INTERFACE s_axilite port=RorL
#pragma HLS INTERFACE s_axilite port=col
#pragma HLS INTERFACE s_axilite port=row
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE s_axilite port=return

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

    hls::LineBuffer<ARRAY_SIZE-1, 1920, uint8_t> linebuf;
    hls::Window<ARRAY_SIZE, ARRAY_SIZE, uint8_t> mbuf;

    int i, j, x, y;
    ap_fixed<32,24,AP_TRN,AP_WRAP> gray_pix, val;
    ap_int<32> val32;

    for (int k=0; k<2; k++){
        do {
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        // user が 1になった時にフレームがスタートする
            ins >> pix;
        } while(pix.user == 0);

        for (y=0; y<row; y++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=1080 avg=600
            for (x=0; x<col; x++){
#pragma HLS LOOP_TRIPCOUNT min=64 max=1920 avg=800          
#pragma HLS PIPELINE II=1
                if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                    ins >> pix;    // AXI4-Stream からの入力

                mbuf.shift_left();    // mbuf の列を1ビット左シフト
                for(i=ARRAY_SIZE-2; i>=0; --i){
                    mbuf.insert(linebuf(i,x), i+1, ARRAY_SIZE-1);
                }
                gray_pix = conv_rgb2y(pix.data);
                mbuf.insert(gray_pix, 0, ARRAY_SIZE-1);

                // LineBuffer の更新
                linebuf.shift_down(x);
                linebuf.insert_bottom(gray_pix, x);

                // 使用する配列を決定する
                int ano;
                switch (RorL){
                case LEFT_WEIGHT :
                    ano = LEFT_WEIGHT;
                    break;
                case RIGHT_WEIGHT :
                    ano = RIGHT_WEIGHT;
                    break;
                case L_R_WEIGHT :
                    if (k == 0)
                        ano = LEFT_WEIGHT;
                    else
                        ano = RIGHT_WEIGHT;
                    break;
                default :
                    ano = LEFT_WEIGHT;
                    break;
                }

                // Gabor filter の演算
                for (j=0, val=0; j<ARRAY_SIZE-1; j++){
                    for (i=0; i<ARRAY_SIZE-1; i++){
                        val += gabor_weight[ano][j][i] * ap_fixed<32,24,AP_TRN_ZERO,AP_SAT>(mbuf(ARRAY_SIZE-1-j,i));
                    }
                }
                val += ap_fixed<32,24,AP_TRN_ZERO,AP_SAT>(0.5); // 四捨五入
                if (val<0)
                    //val = -val; // 絶対値
                    val = 0; // マイナスの値を0に丸める
                else if (val>255)
                    val = 255;
                val32 = ap_int<32>(val);
                // Gabor filter・データの書き込み
                gabor.data = (val32<<16)+(val32<<8)+val32;
                // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので0とする
                if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1))
                    gabor.data = 0;

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

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

                outs << gabor;    // AXI4-Stream へ出力
             }
         }
    }
     return(0);
}

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


テストベンチのGabor_filter_lh_2_fp_tb.cpp を貼っておく。

// Gabor_filter_lh_tb.cpp
// 2016/07/24 by marsee
// 2016/07/25 : 右白線検出用のGabor Filterを追加して、右左の白線を指定するRorL 引数を追加
// 2019/08/22 : 引数に int row, int col を追加
// 2019/09/03 : 任意精度固定小数点データ型を演算に使用することにした
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <math.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>

#include "Gabor_filter_lh_2_fp.h"
#include "bmp_header.h"

int Gabor_filter_lh_2(hls::stream<ap_axis<32,1,1,1> >& ins,
    hls::stream<ap_axis<32,1,1,1> >& outs, int row, int col, ap_uint<2> RorL);

int conv_rgb2y_soft(int rgb);
int Gabor_filter_lh_2_soft(hls::stream<ap_axis<32,1,1,1> >& ins, 
    hls::stream<ap_axis<32,1,1,1> >& outs, int row, int col, ap_uint<2> RorL);

#define CLOCK_PERIOD 10
#define RIGHT_OR_LEFT   L_R_WEIGHT
#define BMP_FILE_NAME   "road_1.bmp"

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    hls::stream<ap_axis<32,1,1,1> > ins_soft;
    hls::stream<ap_axis<32,1,1,1> > outs;
    hls::stream<ap_axis<32,1,1,1> > outs_soft;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;
    ap_axis<32,1,1,1> vals_soft;

    int m_seq = 1; // M系列の値
    int i, k;
    int xor_shift;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw, *fbmpwf;
    int *rd_bmp, *hw_gabor, *sw_gabor;
    int blue, green, red;
    ap_uint<2> r_l;
    char fhname[100];
    char fsname[100];

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_gabor =(int *)malloc(2 * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_gabor memory\n");
        exit(1);
    }
    if ((sw_gabor =(int *)malloc(2 * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_gabor memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    // ins に入力データを用意する
    for(int i=0; i<5; i++){    // dummy data
        pix.user = 0;
        pix.data = i;
        ins << pix;
    }

    // 2画面分を入力する
    for(int k=0; k<2; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(i=0; i < bmpihr.biWidth; i++){
                pix.data = (ap_int<32>)rd_bmp[(j*bmpihr.biWidth)+i];

                if (j==0 && i==0)    // 最初のデータの時に TUSER を 1 にする
                    pix.user = 1;
                else
                    pix.user = 0;

                if (i == bmpihr.biWidth-1) // 行の最後でTLASTをアサートする
                    pix.last = 1;
                else
                    pix.last = 0;

                ins << pix;
                ins_soft << pix;
            }
        }
    }

    r_l = (ap_uint<2>)RIGHT_OR_LEFT;
    Gabor_filter_lh_2(ins, outs, bmpihr.biHeight, bmpihr.biWidth, r_l);
    Gabor_filter_lh_2_soft(ins_soft, outs_soft, bmpihr.biHeight, bmpihr.biWidth, r_l);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    cout << endl;
    cout << "outs" << endl;
    int errcnt = 0;
    for(k=0; k<2; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(i=0; i < bmpihr.biWidth; i++){
                outs >> vals;
                outs_soft >> vals_soft;
                ap_int<32> val = vals.data;
                ap_int<32> val_soft = vals_soft.data;

                hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+(j*bmpihr.biWidth)+i] = (int)val;
                sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+(j*bmpihr.biWidth)+i] = (int)val_soft;

                //printf("k=%d, j=%d, i=%d\n",k,j,i);
                if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > 4){ // 2乗誤差が4よりも大きい
                    printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %08x, SW = %08x\n", i, j, (int)val, (int)val_soft);
                    //return(1);
                    errcnt++;
                }
                //if (vals.last)
                    //cout << "AXI-Stream is end" << endl;
            }
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << "Error Count = " << errcnt << endl;
    cout << endl;

    // ハードウェアのラプラシアンフィルタの結果を temp_gabor0.bmp, temp_gabor1.bmp へ出力する
    for(k=0; k<2; k++){
        if(k == 0){
            if ((fbmpw=fopen("temp_gabor0.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor0.bmp by binary write mode\n");
                exit(1);
            }
        } else {
            if ((fbmpw=fopen("temp_gabor1.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor1.bmp by binary write mode\n");
                exit(1);
            }
        }

        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);
        // RGB データの書き込み、逆順にする
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }

    // ソフトウェアのラプラシアンフィルタの結果を temp_gabor_float0.bmp, temp_gabor_float1.bmp へ出力する
    for(k=0; k<2; k++){
        if (k == 0){
            if ((fbmpwf=fopen("temp_gabor_float0.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor_float0.bmp by binary write mode\n");
                exit(1);
            }
        } else {
            if ((fbmpwf=fopen("temp_gabor_float1.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor_float1.bmp by binary write mode\n");
                exit(1);
            }
        }

        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpwf);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpwf);
        // RGB データの書き込み、逆順にする
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpwf);
                fputc(green, fbmpwf);
                fputc(red, fbmpwf);
            }
        }
        fclose(fbmpwf);
    }

    free(rd_bmp);
    free(hw_gabor);

    return 0;
}

int Gabor_filter_lh_2_soft(hls::stream<ap_axis<32,1,1,1> >& ins, 
    hls::stream<ap_axis<32,1,1,1> >& outs, int row, int col, ap_uint<2> RorL){
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> gabor;

    hls::LineBuffer<ARRAY_SIZE-1, 1920, int> linebuf;
    hls::Window<ARRAY_SIZE, ARRAY_SIZE, int> mbuf;

    int gray_pix, val, i, j, x, y;
    float valf;

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

    for (int k=0; k<2; k++){
        for (y=0; y<row; y++){
            for (x=0; x<col; x++){
                if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                    ins >> pix;    // AXI4-Stream からの入力

                mbuf.shift_left();    // mbuf の列を1ビット左シフト
                for(i=ARRAY_SIZE-2; i>=0; --i){
                    mbuf.insert(linebuf(i,x), i+1, ARRAY_SIZE-1);
                }
                gray_pix = conv_rgb2y_soft(pix.data);
                mbuf.insert(gray_pix, 0, ARRAY_SIZE-1);

                // LineBuffer の更新
                linebuf.shift_down(x);
                linebuf.insert_bottom(gray_pix, x);

                // 使用する配列を決定する
                int ano;
                switch (RorL){
                case LEFT_WEIGHT :
                    ano = LEFT_WEIGHT;
                    break;
                case RIGHT_WEIGHT :
                    ano = RIGHT_WEIGHT;
                    break;
                case L_R_WEIGHT :
                    if (k == 0)
                        ano = LEFT_WEIGHT;
                    else
                        ano = RIGHT_WEIGHT;
                    break;
                default :
                    ano = LEFT_WEIGHT;
                    break;
                }

                // Gabor filter の演算
                for (j=0, valf=0; j<ARRAY_SIZE-1; j++){
                    for (i=0; i<ARRAY_SIZE-1; i++){
                        valf += gabor_fweight[ano][j][i] * (float)mbuf(ARRAY_SIZE-1-j,i);
                    }
                }

                val = (int)valf;
                if (val<0)
                    //val = -val; // 絶対値
                    val = 0; // マイナスの値を0に丸める
                else if (val>255)
                    val = 255;

                // Gabor filter・データの書き込み
                gabor.data = (val<<16)+(val<<8)+val;
                // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので0とする
                if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1))
                    gabor.data = 0;

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

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

                outs << gabor;    // AXI4-Stream へ出力
             }
         }
    }
     return(0);
}

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


  1. 2019年09月06日 04:55 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96-V2のPMOD拡張ボードに秋月電子OV5642を付けたデモ

8月26日の”Ultra96用ガボールフィルタIP の作成1”からの 11 日間で、RECONF研展示用のデモを作っていた。会場の電源の関係で、デモは無しで、PMOD拡張ボードの展示だけになるかも知れない?

Ultra96-V1 とUltra96-V2 兼用の低速コネクタに接続するためのPMOD 拡張ボードを作ったので、それに秋月電子の OV5642 を接続して、カメラ画像を表示することができた。それだけでは、面白くないので、数秒ごとにカメラ画像 ー ラプラシアン・フィルタ画像 ー ガボールフィルタ左白線検出用画像 ー ガボールフィルタ右白線検出用画像を繰り返し表示するデモを作ることにした。そして、8月26日の”Ultra96用ガボールフィルタIP の作成1”からの 10 日間で、RECONF研展示用のデモを作った。ガボールフィルタの実装が曲者だったが、何とか形になった。ただし、ラプラシアンフィルタの演算で、より細いエッジを出すためにマイナスの値は 0 にまるめてある。

実際の展示では、道の写真(家の前の道だが)をカメラで撮影して、カメラ画像とラプラシアンフィルタ画像とガボールフィルタ左白線検出用画像、ガボールフィルタ右白線検出用画像の画像の違いを楽しんでほしいと思っている。ラプラシアンフィルタでは、綺麗にエッジが出ているが、ゴミというか縦横斜めに感度があるため、細かいエッジが沢山ある。ガボールフィルタでは直線のしかも特定の角度で感度があるように値を設定してあるので、左白線検出するパラメータと右白線検出するパラメータの2種類の画像がある。それぞれ特定の角度の線を検出するので、ラプラシアンフィルタに比べてゴミが少ない。

最初に家の前の道のカメラ画像を示す。
RECONF_DEMO_1_190906.jpg

それのラプラシアンフィルタ画像を示す。
RECONF_DEMO_2_190906.jpg

ガボールフィルタ左白線検出用画像を示す。
RECONF_DEMO_3_190906.jpg

ガボールフィルタ右白線検出用画像を示す。
RECONF_DEMO_4_190906.jpg

撮影の様子を示す。
RECONF_DEMO_5_190906.jpg
  1. 2019年09月06日 04:37 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSのバージョンによる性能差(ガボールフィルタを使用)

Vivado HLS 2017.4、Vivado HLS 2018.3、Vivado HLS 2019.1 の性能差をガボールフィルタを使って比較してみよう。
ガボールフィルタの実装では結構差が出ている。Vviado HLS は各バージョンで同じプロジェクトを使えていたが、最近はプロバティが変わっていてエラーが出ることが多くなった。この3バージョンは同じプロジェクトで使いまわすことができなかった。各バージョンで独立のプロジェクトを必要とした。
なお、使用しているソースコードは、”Ultra96用ガボールフィルタIP の作成1”のものを使用している。

まずは、Vivado HLS 2017.4 から合成結果を示す。
Gabor_filter_lh_2_21_190905.png

Timing の Estimated は 5.84 ns と制約をオーバーしている。
Latency の min は 6152 クロック、max は 4147208 クロックだった。
リソース使用量はBRAM_18K が 24 個、DSP48E が 98 個、FF が6736 個、LUT が 8032 個だった。

Export RTL の結果をみてみよう。
Gabor_filter_lh_2_22_190905.png

CP achieved post-synthesis が 9.790 ns で、CP achieved post-implementation が 9.958 ns とターゲットの倍近くになってしまっている。これでは使い物にならないと言える。

次に、Vivado HLS 2018.3 の結果をみてみよう。
合成結果を示す。
Gabor_filter_lh_2_23_190905.png

Timing Estimated が 4.360 ns でターゲットをクリアできた。その代わりLatency は伸びている。
リソース使用量は 2017.4 よりも少なくなっている。

Export RTL の結果をみてみよう。
Gabor_filter_lh_2_24_190905.png

CP achieved post-synthesis が 3.388 ns で、CP achieved post-implementation が 4.347 ns とターゲットをクリアできた。

最後に、現在の最新バージョンの Vivado HLS 2019.1 の結果をみてみよう。
合成結果を示す。
Gabor_filter_lh_2_19_190905.png

2018.3 と余り変化はないようだ。

Export RTL の結果をみてみよう。
Gabor_filter_lh_2_20_190905.png

CP achieved post-synthesis が 3.388 ns で、CP achieved post-implementation が 4.643 ns とターゲットをクリアできたが 2018.3 よりも、CP achieved post-implementation が長くなっている。

最後に、全バージョンのVivado HLS での性能比較表を示す。
Gabor_filter_lh_2_25_190905.png

2018.3 と 2019.1 はほとんど一緒だが、2017.4 の性能はあまり良くないようだ。特に、Vivado での CP achieved post-implementation が 9.958 ns はターゲットの 2 倍近くになっている。なるべく新しいバージョンのVivado HLS と Vivado を使ったほうが良さそうだ。
  1. 2019年09月05日 06:33 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

ガボールフィルタとラプラシアンフィルタを使ってUltra96-V2でカメラ画像をフィルタする3(完成)

ガボールフィルタとラプラシアンフィルタを使ってUltra96-V2でカメラ画像をフィルタする2(デバック中)”の続き。

前回は、ガボールフィルタの出力が真っ白になってしまって、デバックしていた。今回は、やっとガボールフィルタが動作するようになった。どうやったかというと、
Ultra96用ガボールフィルタIP の作成1
Ultra96用ガボールフィルタIP の作成2
Ultra96用ガボールフィルタIP の作成3
では、ガボールフィルタの演算は、重みを256 倍して int 型で演算して、最後に 256 で割っていた。これだと、Vivado に持っていくと真っ白になってしまうようである。Vivado HLS ではC/RTL 協調シミュレーションでもガボールフィルタ出力画像がそれらしく出ていたのだが? これは、ツールのバグじゃないのだろうか?

さて、その演算に任意精度固定小数点データ型を使用すると、Vivado に持っていてもガボールフィルタ画像がうまく出力できるようになった。任意精度固定小数点データ型演算のガボールフィルタは別のVivado HLS プロジェクトにしてある。Gabor_filter_lh_2_fp だ。
cam_dp_V2_filter_12_190904.png

ガボールフィルタの左白線検出用画像を示す。
cam_dp_V2_filter_14_190904.jpg

ガボールフィルタの右白線検出用画像を示す。
cam_dp_V2_filter_15_190904.jpg

ガボールフィルタ画像が出たので、元の cam_dp_V2_filter_183 のGabor_filter_lh_2 のIP と演算に任意精度固定小数点データ型を使用したIP を入れ替えて、ビットストリームを生成すると、こちらもガボールフィルタ画像が出てきた。
Ultra96-V2 のDebian で cam_dp_ov_5642_demo.cpp を修正してデモモードでカメラ画像、ラプラシアンフィルタ画像、ガボールフィルタ左白線検出用画像、ガボールフィルタ右白線検出用画像が回って表示されるようにした。
cam_dp_V2_filter_13_190904.png

なお、このデモは、家の前の道で白線が各フィルタでどう表示されるか?というコンセプトで、道の画像を写して、2019年9月19日(木)-20日(金)の北九州国際会議場で開催されるRECONF研のAVNETさんのブースで展示される予定なので、行かれる方はご覧ください。

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

// cam_dp_ov5642_demo.cpp (for Ultra96-V2)
// 2018/12/14 by marsee
//
// This software converts the left and right of the camera image to BMP file.
// -b : bmp file name
// -n : Start File Number
// -h : help
//
// 2018/12/20 : completed.
// I am using the SVGA driver register setting of https://github.com/virajkanwade/rk3188_android_kernel/blob/master/drivers/media/video/ov5642.c
// 2018/12/22 : fixed
// 2018/12/30 : ov5642_inf_axis[0] fixed
// 2019/02/06 : for DisplayPort
// 2019/08/18 : Changed to /dev/uio4 ~ uio9 for Ultra96-V2. by marsee
// 2019/08/24 : Added lap_filter, Gabor_filter IP

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

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

#define PIXEL_NUM_OF_BYTES    4
#define NUMBER_OF_WRITE_FRAMES  3

#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 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 HD_HORIZONTAL_PIXELS  1920
#define HD_VERTICAL_LINES     1080
#define HD_ALL_DISP_ADDRESS   (HD_HORIZONTAL_PIXELS * HD_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define HD_3_PICTURES         (HD_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES)

#define LAP_FILTER          0
#define GABOR_FILTER        1

#define GABOR_LEFT_WEIGHT   0
#define GABOR_RIGHT_WEIGHT  1
#define GABOR_LR_WEIGHT     2

int WriteBMPfile(char *bmp_file, volatile unsigned int *frame_buffer, int active_frame, int img_height, int img_width);

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

int cam_reg_set(volatile unsigned *axi_iic, unsigned int device_addr);

int main(int argc, char *argv[]){
    int opt;
    int c, help_flag=0;
    char bmp_fn[256] = "bmp_file";
    char  attr[1024];
    unsigned long  phys_addr;
    int file_no = -1;
    int fd4, fd5, fd6, fd7, fd8, fd9, fd10, fd11, fd12, fd13, fd20, fd21;
    volatile unsigned int *ov5642_inf_axis, *axi_iic, *disp_dmar_axis, *vflip_dma_write;
    volatile unsigned int *axi_gpio_0, *axi_gpio_1, *axis_switch_0, *axis_switch_1;
    volatile unsigned int *lap_filter_axis_0, *Gabor_filter_hl_2_0;
    volatile unsigned int *frame_buffer;
    int active_frame;
    int resolution;
    int all_disp_addr;
    int demo_mode = 0;
    int img_width, img_height;
    int interval_time;
        
    resolution = 1; // XGA
    interval_time = 5; // default
    while ((opt=getopt(argc, argv, "hdb:n:r:t:")) != -1){
        switch (opt){
            case 'b':
                strcpy(bmp_fn, optarg);
                break;
            case 'n':
                file_no = atoi(optarg);
                printf("file_no = %d\n", file_no+1);
                break;
            case 'r':
                resolution = atoi(optarg);
                break;
            case 'h':
                help_flag = 1;
                break;
            case 'd':
                demo_mode = 1;
                break;
            case 't':
                interval_time = atoi(optarg);
                break;
        }
    }
    if(resolution == 0){
        printf("SVGA\n");
    } else if(resolution == 1){
        printf("XGA\n");
    } else {
        printf("HD\n");
    }
    fflush(stdout);
    
    if (help_flag == 1){ // help
        printf("Usage : cam_dp_ov5642_demo [-b <bmp file name>] [-n <Start File Number>] [-h]\n");
        printf("         [-r [0|1|2]](0:SVGA, 1:XGA, 2:HD)\n");
        printf("         -d (demo mode)\n");
        printf("         -t <demo interval time> (demo mode)\n");
        exit(0);
    }
       
    // all_disp_addr
    switch(resolution){
        case 0 :
            img_width = SVGA_HORIZONTAL_PIXELS;
            img_height = SVGA_VERTICAL_LINES;
            all_disp_addr = SVGA_ALL_DISP_ADDRESS;
            break;
        case 1 :
            img_width = XGA_HORIZONTAL_PIXELS;
            img_height = XGA_VERTICAL_LINES;
            all_disp_addr = XGA_ALL_DISP_ADDRESS;
            break;
        default : // 2
            img_width = HD_HORIZONTAL_PIXELS;
            img_height = HD_VERTICAL_LINES;
            all_disp_addr = HD_ALL_DISP_ADDRESS;
            break;
    }
    
    // ov5642_inf_axis-uio IP
    fd4 = open("/dev/uio4", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (ov5642_inf_axis) open error\n");
        exit(-1);
    }
    ov5642_inf_axis = (volatile unsigned *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!ov5642_inf_axis){
        fprintf(stderr, "ov5642_inf_axis mmap error\n");
        exit(-1);
    }
    
    // axi_iic-uio IP
    fd5 = open("/dev/uio5", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd5 < 1){
        fprintf(stderr, "/dev/uio5 (axi_iic) open error\n");
        exit(-1);
    }
    axi_iic = (volatile unsigned int *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd5, 0);
    if (!axi_iic){
        fprintf(stderr, "axi_iic mmap error\n");
        exit(-1);
    }

    // disp_dmar_axis-uio IP
    fd6 = open("/dev/uio6", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (disp_dmar_axis) open error\n");
        exit(-1);
    }
    disp_dmar_axis = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!disp_dmar_axis){
        fprintf(stderr, "disp_dmar_axis mmap error\n");
        exit(-1);
    }

    // vflip_dma_write-uio IP
    fd7 = open("/dev/uio7", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (vflip_dma_write) open error\n");
        exit(-1);
    }
    vflip_dma_write = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!vflip_dma_write){
        fprintf(stderr, "vflip_dma_write mmap error\n");
        exit(-1);
    }

    // axi_gpio_0-uio IP (init_done output)
    fd8 = open("/dev/uio8", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd8 < 1){
        fprintf(stderr, "/dev/uio8 (axi_gpio_0) open error\n");
        exit(-1);
    }
    axi_gpio_0 = (volatile unsigned int *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd8, 0);
    if (!axi_gpio_0){
        fprintf(stderr, "axi_gpio_0 mmap error\n");
        exit(-1);
    }

    // axi_gpio_1-uio IP (active_frame input)
    fd9 = open("/dev/uio9", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd9 < 1){
        fprintf(stderr, "/dev/uio9 (axi_gpio_1) open error\n");
        exit(-1);
    }
    axi_gpio_1 = (volatile unsigned int *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (!axi_gpio_1){
        fprintf(stderr, "axi_gpio_1 mmap error\n");
        exit(-1);
    }
    
    // axis_switch_0-uio IP
    fd10 = open("/dev/uio10", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd10 < 1){
        fprintf(stderr, "/dev/uio10 (axis_switch_0-uio) open error\n");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned int *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd10, 0);
    if (!axis_switch_0){
        fprintf(stderr, "axis_switch_0 mmap error\n");
        exit(-1);
    }
    
    // axis_switch_1-uio IP
    fd11 = open("/dev/uio11", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd11 < 1){
        fprintf(stderr, "/dev/uio11 (axis_switch_1-uio) open error\n");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned int *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd11, 0);
    if (!axis_switch_1){
        fprintf(stderr, "axis_switch_1 mmap error\n");
        exit(-1);
    }
    
    // lap_filter_axis_0-uio IP
    fd12 = open("/dev/uio12", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd12 < 1){
        fprintf(stderr, "/dev/uio12 (lap_filter_axis_0-uio) open error\n");
        exit(-1);
    }
    lap_filter_axis_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd12, 0);
    if (!lap_filter_axis_0){
        fprintf(stderr, "lap_filter_axis_0 mmap error\n");
        exit(-1);
    }
    
    // Gabor_filter_hl_2_0-uio IP
    fd13 = open("/dev/uio13", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd13 < 1){
        fprintf(stderr, "/dev/uio13 (Gabor_filter_hl_2_0-uio) open error\n");
        exit(-1);
    }
    Gabor_filter_hl_2_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd13, 0);
    if (!Gabor_filter_hl_2_0){
        fprintf(stderr, "Gabor_filter_hl_2_0 mmap error\n");
        exit(-1);
    }
    
    // udmabuf4
    fd20 = open("/dev/udmabuf4", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd20 == -1){
        fprintf(stderr, "/dev/udmabuf4 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, all_disp_addr*NUMBER_OF_WRITE_FRAMES, PROT_READ|PROT_WRITE, MAP_SHARED, fd20, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer4 mmap error\n");
        exit(-1);
    }
    
    // phys_addr of udmabuf4
    fd21 = open("/sys/class/udmabuf/udmabuf4/phys_addr", O_RDONLY);
    if (fd21 == -1){
        fprintf(stderr, "/sys/class/udmabuf/udmabuf4/phys_addr open error\n");
        exit(-1);
    }
    read(fd21, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd21);
    printf("phys_addr = %x\n", (int)phys_addr);
    
    // axis_switch_0 settings
    axis_switch_0[16] = 0x0; // 0x40 = 0x00; enable
    axis_switch_0[17] = 0x80000000; // 0x44 = 0x80000000; disable
    axis_switch_0[18] = 0x80000000; // 0x48 = 0x80000000; disable
    axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
    // axis_switch_1 settings
    axis_switch_1[16] = 0x0; // 0x40 = 0x0;
    axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
    
    // lap_filter_axis_0 settings
    lap_filter_axis_0[6] = img_height; // row
    lap_filter_axis_0[8] = img_width; // col
    lap_filter_axis_0[0] = 0x01; // ap_start
    lap_filter_axis_0[0] = 0x80; // auto_restart
    
    // Gabor_filter_lh_2_0
    Gabor_filter_hl_2_0[6] = img_height; // row
    Gabor_filter_hl_2_0[8] = img_width; // col
    Gabor_filter_hl_2_0[10] = GABOR_LEFT_WEIGHT; // RorL_V
    Gabor_filter_hl_2_0[0] = 0x01; // ap_start
    Gabor_filter_hl_2_0[0] = 0x80; // auto_restart
    
    // vflip_dma_write start
    vflip_dma_write[6] = phys_addr; // fb0
    vflip_dma_write[8] = phys_addr+all_disp_addr; // fb1
    vflip_dma_write[10] = phys_addr+2*all_disp_addr; // fb2
    vflip_dma_write[12] = resolution;
    vflip_dma_write[0] = 0x1; // start
    vflip_dma_write[0] = 0x80; // EnableAutoRestart
    
    // CMOS Camera initialize, ov5642
    cam_i2c_init(axi_iic);
    
    cam_reg_set(axi_iic, 0x78); // OV5642 register set

    ov5642_inf_axis[0] = phys_addr; // ov5642 AXI4-Stream Start
    ov5642_inf_axis[1] = 0;
 
     // disp_dmar_axis start
    disp_dmar_axis[4] = phys_addr; // fb0
    disp_dmar_axis[6] = phys_addr+all_disp_addr; // fb1
    disp_dmar_axis[8] = phys_addr+2*all_disp_addr; // fb2
    disp_dmar_axis[10] = resolution;
    axi_gpio_0[0] = 1; // disp_dmar_axis start(init_done = 1)
    //disp_dmar_axis[0] = 0x01; // ap_start
    //disp_dmar_axis[0] = 0x80; // auto_restart
    
    char bmp_file[256];

    // All 0 set
    int all_disp_frame_index = all_disp_addr/PIXEL_NUM_OF_BYTES*NUMBER_OF_WRITE_FRAMES;
    for (int i=0; i<all_disp_frame_index; i++){
        frame_buffer[i] = 0x0;
    }
    
    if (demo_mode == 1){
        while(1){
            // normal camera
            // axis_switch_0 settings
            axis_switch_0[16] = 0x0; // 0x40 = 0x00; enable
            axis_switch_0[17] = 0x80000000; // 0x44 = 0x80000000; disable
            axis_switch_0[18] = 0x80000000; // 0x48 = 0x80000000; disable
            axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
            // axis_switch_1 settings
            axis_switch_1[16] = 0x0; // 0x40 = 0x0;
            axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
            printf("camera\n");
            sleep(interval_time);
            
            // laplacian filter
            // axis_switch_0 settings
            axis_switch_0[16] = 0x80000000; // 0x40 = 0x80000000; disable
            axis_switch_0[17] = 0x80000000; // 0x80000000; disable
            axis_switch_0[18] = 0x0; // 0x48 = 0x00; enable
            axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
            // axis_switch_1 settings
            axis_switch_1[16] = 0x2; // 0x40 = 0x2;
            axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
            printf("laplacian filter\n");
            sleep(interval_time);
            
            // gabor filter(Left Line)
            // axis_switch_0 settings
            // axis_switch_0 settings
            axis_switch_0[16] = 0x80000000; // 0x40 = 0x80000000; disable
            axis_switch_0[17] = 0x0; // 0x44 = 0x00; enable
            axis_switch_0[18] = 0x80000000; // 0x48 = 0x80000000; disable
            axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
            // axis_switch_1 settings
            axis_switch_1[16] = 0x1; // 0x40 = 0x1;
            axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
            Gabor_filter_hl_2_0[10] = GABOR_LEFT_WEIGHT; // RorL_V
            printf("Gabor filter (Left Line)\n");
            sleep(interval_time);
            
            // gabor filter(Right Line)
            Gabor_filter_hl_2_0[10] = GABOR_RIGHT_WEIGHT; // RorL_V
            printf("Gabor filter (Right Line)\n");
            sleep(interval_time);
        }
    }
            
    // 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 a bmp files.
                // writed the frame buffer
                file_no++;
                sprintf(bmp_file, "%s%d.bmp", bmp_fn, file_no);
                active_frame = (int)(axi_gpio_1[0] & 0x3); // Data signal of active_frame_V
                WriteBMPfile(bmp_file, frame_buffer, active_frame, img_height, img_width);
                
                printf("file No. = %d\n", file_no);

                break;
            case 'e' : // e - writed a same bmp files.
                // writed the frame buffer
                if (file_no == -1)
                    file_no = 0;
                
                sprintf(bmp_file, "%s%d.bmp", bmp_fn, file_no);
                active_frame = (int)(axi_gpio_1[0] & 0x3); // Data signal of active_frame_V
                WriteBMPfile(bmp_file, frame_buffer, active_frame, img_height, img_width);
                
                printf("file No. = %d\n", file_no);

                break;
            case 'p': // p - laplacian filter
                // axis_switch_0 settings
                axis_switch_0[16] = 0x80000000; // 0x40 = 0x80000000; disable
                axis_switch_0[17] = 0x80000000; // 0x80000000; disable
                axis_switch_0[18] = 0x0; // 0x48 = 0x00; enable
                axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
                // axis_switch_1 settings
                axis_switch_1[16] = 0x2; // 0x40 = 0x2;
                axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
                break;
            case 'l': // l - gabor filter(Left Line)
                // axis_switch_0 settings
                axis_switch_0[16] = 0x80000000; // 0x40 = 0x80000000; disable
                axis_switch_0[17] = 0x0; // 0x44 = 0x00; enable
                axis_switch_0[18] = 0x80000000; // 0x48 = 0x80000000; disable
                axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
                // axis_switch_1 settings
                axis_switch_1[16] = 0x1; // 0x40 = 0x1;
                axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
                Gabor_filter_hl_2_0[10] = GABOR_LEFT_WEIGHT; // RorL_V
                break;
            case 'r': // r - gabor filter(Right Line)
                // axis_switch_0 settings
                axis_switch_0[16] = 0x80000000; // 0x40 = 0x80000000; disable
                axis_switch_0[17] = 0x0; // 0x44 = 0x00; enable
                axis_switch_0[18] = 0x80000000; // 0x48 = 0x80000000; disable
                axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
                // axis_switch_1 settings
                axis_switch_1[16] = 0x1; // 0x40 = 0x1;
                axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
                Gabor_filter_hl_2_0[10] = GABOR_RIGHT_WEIGHT; // RorL_V
                break;
            case 'c' : // c - camera image
                // axis_switch_0 settings
                axis_switch_0[16] = 0x0; // 0x40 = 0x00; enable
                axis_switch_0[17] = 0x80000000; // 0x44 = 0x80000000; disable
                axis_switch_0[18] = 0x80000000; // 0x48 = 0x80000000; disable
                axis_switch_0[0] = 0x2; // 0x0 = 2; Commit registers
                // axis_switch_1 settings
                axis_switch_1[16] = 0x0; // 0x40 = 0x0;
                axis_switch_1[0] = 0x2; // 0x0 = 2; Commit registers
            case 'h': // help
                printf("w - writed a bmp files.\n");
                printf("e - writed a same bmp files.\n");
                printf("p - laplacian filter\n");
                printf("l - gabor filter(Left Line)\n");
                printf("r - gabor filter(Reft Line)\n");
                printf("c - camera image\n");
                break;
        }
        c = getc(stdin);
    }
    
    munmap((void *)ov5642_inf_axis, 0x1000);
    munmap((void *)axi_iic, 0x1000);
    munmap((void *)disp_dmar_axis, 0x10000);
    munmap((void *)vflip_dma_write, 0x10000);
    munmap((void *)axi_gpio_0, 0x1000);
    munmap((void *)axi_gpio_1, 0x1000);
    munmap((void *)frame_buffer, all_disp_addr*3);
    
    close(fd4);
    close(fd5);
    close(fd6);
    close(fd7);
    close(fd8);
    close(fd9);
    close(fd10);
    close(fd11);
    close(fd12);
    close(fd13);
    close(fd20);
    
    return(0);
}

int WriteBMPfile(char *bmp_file, volatile unsigned int *frame_buffer, int active_frame, int img_height, int img_width){
    int read_frame;
    
    if (active_frame == 0)
        read_frame = 2;
    else if (active_frame == 1)
        read_frame = 0;
    else // active_frame == 2
        read_frame = 1;
        
    int offset_addr = read_frame * img_width * img_height;
    
    cv::Mat img(img_height, img_width, CV_8UC3);

    cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
    for(int y=0; y<img.rows; y++){
        for(int x=0; x<img.cols; x++){
            cv::Vec3b pixel;
            int rgb = frame_buffer[offset_addr+y*img.cols+x];
            pixel[0] = (rgb & 0xff); // blue
            pixel[1] = (rgb & 0xff00) >> 8; // green
            pixel[2] = (rgb & 0xff0000) >> 16; // red
            dst_vec3b(y,x) = pixel;
        }
    }
    
    cv::imwrite(bmp_file, img);
    
    return(0);
}

int cam_reg_set(volatile unsigned *axi_iic, unsigned int device_addr){
    cam_i2c_write(axi_iic, device_addr, 0x3103, 0x93);
    cam_i2c_write(axi_iic, device_addr, 0x3008, 0x82);
    cam_i2c_write(axi_iic, device_addr, 0x3017, 0x7f);
    cam_i2c_write(axi_iic, device_addr, 0x3018, 0xfc);
    cam_i2c_write(axi_iic, device_addr, 0x3810, 0xc2);
    cam_i2c_write(axi_iic, device_addr, 0x3615, 0xf0);
    cam_i2c_write(axi_iic, device_addr, 0x3000, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3001, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3002, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3003, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3000, 0xf8);
    cam_i2c_write(axi_iic, device_addr, 0x3001, 0x48);
    cam_i2c_write(axi_iic, device_addr, 0x3002, 0x5c);
    cam_i2c_write(axi_iic, device_addr, 0x3003, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x3004, 0x07);
    cam_i2c_write(axi_iic, device_addr, 0x3005, 0xb7);
    cam_i2c_write(axi_iic, device_addr, 0x3006, 0x43);
    cam_i2c_write(axi_iic, device_addr, 0x3007, 0x37);
    cam_i2c_write(axi_iic, device_addr, 0x3011, 0x08); // 0x08 - 15fps, 0x10 - 30fps
    cam_i2c_write(axi_iic, device_addr, 0x3010, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x460c, 0x22);
    cam_i2c_write(axi_iic, device_addr, 0x3815, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x370d, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x370c, 0xa0);
    cam_i2c_write(axi_iic, device_addr, 0x3602, 0xfc);
    cam_i2c_write(axi_iic, device_addr, 0x3612, 0xff);
    cam_i2c_write(axi_iic, device_addr, 0x3634, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x3613, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3605, 0x7c);
    cam_i2c_write(axi_iic, device_addr, 0x3621, 0x09);
    cam_i2c_write(axi_iic, device_addr, 0x3622, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3604, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x3603, 0xa7);
    cam_i2c_write(axi_iic, device_addr, 0x3603, 0x27);
    cam_i2c_write(axi_iic, device_addr, 0x4000, 0x21);
    cam_i2c_write(axi_iic, device_addr, 0x401d, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x3600, 0x54);
    cam_i2c_write(axi_iic, device_addr, 0x3605, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x3606, 0x3f);
    cam_i2c_write(axi_iic, device_addr, 0x3c01, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5000, 0x4f);
    cam_i2c_write(axi_iic, device_addr, 0x5020, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x5181, 0x79);
    cam_i2c_write(axi_iic, device_addr, 0x5182, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5185, 0x22);
    cam_i2c_write(axi_iic, device_addr, 0x5197, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x5001, 0xff);
    cam_i2c_write(axi_iic, device_addr, 0x5500, 0x0a);
    cam_i2c_write(axi_iic, device_addr, 0x5504, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5505, 0x7f);
    cam_i2c_write(axi_iic, device_addr, 0x5080, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x300e, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x4610, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x471d, 0x05);
    cam_i2c_write(axi_iic, device_addr, 0x4708, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x3710, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x3632, 0x41);
    cam_i2c_write(axi_iic, device_addr, 0x3702, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x3620, 0x37);
    cam_i2c_write(axi_iic, device_addr, 0x3631, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x3808, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x3809, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x380a, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x380b, 0xe0);
    cam_i2c_write(axi_iic, device_addr, 0x380e, 0x07);
    cam_i2c_write(axi_iic, device_addr, 0x380f, 0xd0);
    cam_i2c_write(axi_iic, device_addr, 0x501f, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5000, 0x4f);
    cam_i2c_write(axi_iic, device_addr, 0x4300, 0x61); // RGB565
    cam_i2c_write(axi_iic, device_addr, 0x3503, 0x07);
    cam_i2c_write(axi_iic, device_addr, 0x3501, 0x73);
    cam_i2c_write(axi_iic, device_addr, 0x3502, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x350b, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3503, 0x07);
    cam_i2c_write(axi_iic, device_addr, 0x3824, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x3501, 0x1e);
    cam_i2c_write(axi_iic, device_addr, 0x3502, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x350b, 0x7f);
    cam_i2c_write(axi_iic, device_addr, 0x380c, 0x0c);
    cam_i2c_write(axi_iic, device_addr, 0x380d, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x380e, 0x03);
    cam_i2c_write(axi_iic, device_addr, 0x380f, 0xe8);
    cam_i2c_write(axi_iic, device_addr, 0x3a0d, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x3a0e, 0x03);
    cam_i2c_write(axi_iic, device_addr, 0x3818, 0xc1);
    cam_i2c_write(axi_iic, device_addr, 0x3705, 0xdb);
    cam_i2c_write(axi_iic, device_addr, 0x370a, 0x81);
    cam_i2c_write(axi_iic, device_addr, 0x3801, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x3621, 0xc7);
    cam_i2c_write(axi_iic, device_addr, 0x3801, 0x50);
    cam_i2c_write(axi_iic, device_addr, 0x3803, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x3827, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x3810, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x3804, 0x05);
    cam_i2c_write(axi_iic, device_addr, 0x3805, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5682, 0x05);
    cam_i2c_write(axi_iic, device_addr, 0x5683, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3806, 0x03);
    cam_i2c_write(axi_iic, device_addr, 0x3807, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x5686, 0x03);
    cam_i2c_write(axi_iic, device_addr, 0x5687, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x3a00, 0x78);
    cam_i2c_write(axi_iic, device_addr, 0x3a1a, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x3a13, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x3a18, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3a19, 0x7c);
    cam_i2c_write(axi_iic, device_addr, 0x3a08, 0x12);
    cam_i2c_write(axi_iic, device_addr, 0x3a09, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x3a0a, 0x0f);
    cam_i2c_write(axi_iic, device_addr, 0x3a0b, 0xa0);
    cam_i2c_write(axi_iic, device_addr, 0x3004, 0xff);
    cam_i2c_write(axi_iic, device_addr, 0x350c, 0x07);
    cam_i2c_write(axi_iic, device_addr, 0x350d, 0xd0);
    cam_i2c_write(axi_iic, device_addr, 0x3500, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3501, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3502, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x350a, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x350b, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3503, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x528a, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x528b, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x528c, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x528d, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x528e, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x528f, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5290, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5292, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5293, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5294, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5295, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5296, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5297, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5298, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5299, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x529a, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529b, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x529c, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529d, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x529e, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529f, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x3a0f, 0x3c);
    cam_i2c_write(axi_iic, device_addr, 0x3a10, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x3a1b, 0x3c);
    cam_i2c_write(axi_iic, device_addr, 0x3a1e, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x3a11, 0x70);
    cam_i2c_write(axi_iic, device_addr, 0x3a1f, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x3030, 0x0b);
    cam_i2c_write(axi_iic, device_addr, 0x3a02, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3a03, 0x7d);
    cam_i2c_write(axi_iic, device_addr, 0x3a04, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3a14, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3a15, 0x7d);
    cam_i2c_write(axi_iic, device_addr, 0x3a16, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3a00, 0x78);
    cam_i2c_write(axi_iic, device_addr, 0x3a08, 0x09);
    cam_i2c_write(axi_iic, device_addr, 0x3a09, 0x60);
    cam_i2c_write(axi_iic, device_addr, 0x3a0a, 0x07);
    cam_i2c_write(axi_iic, device_addr, 0x3a0b, 0xd0);
    cam_i2c_write(axi_iic, device_addr, 0x3a0d, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x3a0e, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x5193, 0x70);
    cam_i2c_write(axi_iic, device_addr, 0x3620, 0x57);
    cam_i2c_write(axi_iic, device_addr, 0x3703, 0x98);
    cam_i2c_write(axi_iic, device_addr, 0x3704, 0x1c);
    cam_i2c_write(axi_iic, device_addr, 0x589b, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x589a, 0xc5);
    cam_i2c_write(axi_iic, device_addr, 0x528a, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x528b, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x528c, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x528d, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x528e, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x528f, 0x28);
    cam_i2c_write(axi_iic, device_addr, 0x5290, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5292, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5293, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5294, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5295, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5296, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5297, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5298, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5299, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x529a, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529b, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x529c, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529d, 0x28);
    cam_i2c_write(axi_iic, device_addr, 0x529e, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529f, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5282, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5300, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5301, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5302, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5303, 0x7c);
    cam_i2c_write(axi_iic, device_addr, 0x530c, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x530d, 0x0c);
    cam_i2c_write(axi_iic, device_addr, 0x530e, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x530f, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5310, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5311, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5308, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5309, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x5304, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5305, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5306, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5307, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5314, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5315, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5319, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5316, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5317, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5318, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5380, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x5381, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5382, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5383, 0x4e);
    cam_i2c_write(axi_iic, device_addr, 0x5384, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5385, 0x0f);
    cam_i2c_write(axi_iic, device_addr, 0x5386, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5387, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5388, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x5389, 0x15);
    cam_i2c_write(axi_iic, device_addr, 0x538a, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538b, 0x31);
    cam_i2c_write(axi_iic, device_addr, 0x538c, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538d, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538e, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538f, 0x0f);
    cam_i2c_write(axi_iic, device_addr, 0x5390, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5391, 0xab);
    cam_i2c_write(axi_iic, device_addr, 0x5392, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5393, 0xa2);
    cam_i2c_write(axi_iic, device_addr, 0x5394, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5480, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5481, 0x21);
    cam_i2c_write(axi_iic, device_addr, 0x5482, 0x36);
    cam_i2c_write(axi_iic, device_addr, 0x5483, 0x57);
    cam_i2c_write(axi_iic, device_addr, 0x5484, 0x65);
    cam_i2c_write(axi_iic, device_addr, 0x5485, 0x71);
    cam_i2c_write(axi_iic, device_addr, 0x5486, 0x7d);
    cam_i2c_write(axi_iic, device_addr, 0x5487, 0x87);
    cam_i2c_write(axi_iic, device_addr, 0x5488, 0x91);
    cam_i2c_write(axi_iic, device_addr, 0x5489, 0x9a);
    cam_i2c_write(axi_iic, device_addr, 0x548a, 0xaa);
    cam_i2c_write(axi_iic, device_addr, 0x548b, 0xb8);
    cam_i2c_write(axi_iic, device_addr, 0x548c, 0xcd);
    cam_i2c_write(axi_iic, device_addr, 0x548d, 0xdd);
    cam_i2c_write(axi_iic, device_addr, 0x548e, 0xea);
    cam_i2c_write(axi_iic, device_addr, 0x548f, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5490, 0x05);
    cam_i2c_write(axi_iic, device_addr, 0x5491, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5492, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x5493, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5494, 0x03);
    cam_i2c_write(axi_iic, device_addr, 0x5495, 0x60);
    cam_i2c_write(axi_iic, device_addr, 0x5496, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5497, 0xb8);
    cam_i2c_write(axi_iic, device_addr, 0x5498, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5499, 0x86);
    cam_i2c_write(axi_iic, device_addr, 0x549a, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x549b, 0x5b);
    cam_i2c_write(axi_iic, device_addr, 0x549c, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x549d, 0x3b);
    cam_i2c_write(axi_iic, device_addr, 0x549e, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x549f, 0x1c);
    cam_i2c_write(axi_iic, device_addr, 0x54a0, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x54a1, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x54a2, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54a3, 0xed);
    cam_i2c_write(axi_iic, device_addr, 0x54a4, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54a5, 0xc5);
    cam_i2c_write(axi_iic, device_addr, 0x54a6, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54a7, 0xa5);
    cam_i2c_write(axi_iic, device_addr, 0x54a8, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54a9, 0x6c);
    cam_i2c_write(axi_iic, device_addr, 0x54aa, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54ab, 0x41);
    cam_i2c_write(axi_iic, device_addr, 0x54ac, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54ad, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x54ae, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x54af, 0x16);
    cam_i2c_write(axi_iic, device_addr, 0x3406, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5192, 0x04); // 0x04
    cam_i2c_write(axi_iic, device_addr, 0x5191, 0xf8); // 0xf8
    cam_i2c_write(axi_iic, device_addr, 0x5193, 0x70);
    cam_i2c_write(axi_iic, device_addr, 0x5194, 0xf0);
    cam_i2c_write(axi_iic, device_addr, 0x5195, 0xf0);
    cam_i2c_write(axi_iic, device_addr, 0x518d, 0x3d);
    cam_i2c_write(axi_iic, device_addr, 0x518f, 0x54);
    cam_i2c_write(axi_iic, device_addr, 0x518e, 0x3d);
    cam_i2c_write(axi_iic, device_addr, 0x5190, 0x54);
    cam_i2c_write(axi_iic, device_addr, 0x518b, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x518c, 0xbd);
    cam_i2c_write(axi_iic, device_addr, 0x5187, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x5188, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x5189, 0x6e);
    cam_i2c_write(axi_iic, device_addr, 0x518a, 0x68);
    cam_i2c_write(axi_iic, device_addr, 0x5186, 0x1c);
    cam_i2c_write(axi_iic, device_addr, 0x5181, 0x50);
    cam_i2c_write(axi_iic, device_addr, 0x5184, 0x25);
    cam_i2c_write(axi_iic, device_addr, 0x5182, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x5183, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5184, 0x25);
    cam_i2c_write(axi_iic, device_addr, 0x5185, 0x24);
    cam_i2c_write(axi_iic, device_addr, 0x5025, 0x82);
    cam_i2c_write(axi_iic, device_addr, 0x5583, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x5584, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x5580, 0x02); // 0x02
    cam_i2c_write(axi_iic, device_addr, 0x3633, 0x07);
    cam_i2c_write(axi_iic, device_addr, 0x3702, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x3703, 0xb2);
    cam_i2c_write(axi_iic, device_addr, 0x3704, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x370b, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x370d, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x3620, 0x52);
    cam_i2c_write(axi_iic, device_addr, 0x3c00, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x5001, 0xFF);
    cam_i2c_write(axi_iic, device_addr, 0x5282, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5300, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5301, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5302, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5303, 0x7c);
    cam_i2c_write(axi_iic, device_addr, 0x530c, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x530d, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x530e, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x530f, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5310, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5311, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5308, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5309, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x5304, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5305, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5306, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5307, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5314, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5315, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5319, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5316, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5317, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5318, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5500, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5502, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5503, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x5504, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5505, 0x7f);
    cam_i2c_write(axi_iic, device_addr, 0x5025, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5300, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5301, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5302, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5303, 0x7c);
    cam_i2c_write(axi_iic, device_addr, 0x530c, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x530d, 0x0c);
    cam_i2c_write(axi_iic, device_addr, 0x530e, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x530f, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5310, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5311, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5308, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5309, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x5304, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5305, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5306, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5307, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5314, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5315, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x5319, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5316, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5317, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5318, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5380, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x5381, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5382, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5383, 0x1f);
    cam_i2c_write(axi_iic, device_addr, 0x5384, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5385, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x5386, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5387, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5388, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5389, 0xE1);
    cam_i2c_write(axi_iic, device_addr, 0x538A, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538B, 0x2B);
    cam_i2c_write(axi_iic, device_addr, 0x538C, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538D, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538E, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x538F, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5390, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5391, 0xB3);
    cam_i2c_write(axi_iic, device_addr, 0x5392, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5393, 0xA6);
    cam_i2c_write(axi_iic, device_addr, 0x5394, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5480, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x5481, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x5482, 0x2a);
    cam_i2c_write(axi_iic, device_addr, 0x5483, 0x49);
    cam_i2c_write(axi_iic, device_addr, 0x5484, 0x56);
    cam_i2c_write(axi_iic, device_addr, 0x5485, 0x62);
    cam_i2c_write(axi_iic, device_addr, 0x5486, 0x6c);
    cam_i2c_write(axi_iic, device_addr, 0x5487, 0x76);
    cam_i2c_write(axi_iic, device_addr, 0x5488, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x5489, 0x88);
    cam_i2c_write(axi_iic, device_addr, 0x548a, 0x96);
    cam_i2c_write(axi_iic, device_addr, 0x548b, 0xa2);
    cam_i2c_write(axi_iic, device_addr, 0x548c, 0xb8);
    cam_i2c_write(axi_iic, device_addr, 0x548d, 0xcc);
    cam_i2c_write(axi_iic, device_addr, 0x548e, 0xe0);
    cam_i2c_write(axi_iic, device_addr, 0x548f, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5490, 0x3);
    cam_i2c_write(axi_iic, device_addr, 0x5491, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x5492, 0x3);
    cam_i2c_write(axi_iic, device_addr, 0x5493, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5494, 0x2);
    cam_i2c_write(axi_iic, device_addr, 0x5495, 0xa0);
    cam_i2c_write(axi_iic, device_addr, 0x5496, 0x2);
    cam_i2c_write(axi_iic, device_addr, 0x5497, 0x48);
    cam_i2c_write(axi_iic, device_addr, 0x5498, 0x2);
    cam_i2c_write(axi_iic, device_addr, 0x5499, 0x26);
    cam_i2c_write(axi_iic, device_addr, 0x549a, 0x2);
    cam_i2c_write(axi_iic, device_addr, 0x549b, 0xb);
    cam_i2c_write(axi_iic, device_addr, 0x549c, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x549d, 0xee);
    cam_i2c_write(axi_iic, device_addr, 0x549e, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x549f, 0xd8);
    cam_i2c_write(axi_iic, device_addr, 0x54a0, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54a1, 0xc7);
    cam_i2c_write(axi_iic, device_addr, 0x54a2, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54a3, 0xb3);
    cam_i2c_write(axi_iic, device_addr, 0x54a4, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54a5, 0x90);
    cam_i2c_write(axi_iic, device_addr, 0x54a6, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54a7, 0x62);
    cam_i2c_write(axi_iic, device_addr, 0x54a8, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54a9, 0x27);
    cam_i2c_write(axi_iic, device_addr, 0x54aa, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54ab, 0x09);
    cam_i2c_write(axi_iic, device_addr, 0x54ac, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x54ad, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x54ae, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x54af, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x54b0, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54b1, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x54b2, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54b3, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x54b4, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x54b5, 0xf0);
    cam_i2c_write(axi_iic, device_addr, 0x54b6, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x54b7, 0xdf);
    cam_i2c_write(axi_iic, device_addr, 0x5583, 0x5d);
    cam_i2c_write(axi_iic, device_addr, 0x5584, 0x5d);
    cam_i2c_write(axi_iic, device_addr, 0x5580, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x5587, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5588, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x558a, 0x09);
    cam_i2c_write(axi_iic, device_addr, 0x5589, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5000, 0xcf);
    cam_i2c_write(axi_iic, device_addr, 0x5800, 0x48);
    cam_i2c_write(axi_iic, device_addr, 0x5801, 0x31);
    cam_i2c_write(axi_iic, device_addr, 0x5802, 0x21);
    cam_i2c_write(axi_iic, device_addr, 0x5803, 0x1b);
    cam_i2c_write(axi_iic, device_addr, 0x5804, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x5805, 0x1e);
    cam_i2c_write(axi_iic, device_addr, 0x5806, 0x29);
    cam_i2c_write(axi_iic, device_addr, 0x5807, 0x38);
    cam_i2c_write(axi_iic, device_addr, 0x5808, 0x26);
    cam_i2c_write(axi_iic, device_addr, 0x5809, 0x17);
    cam_i2c_write(axi_iic, device_addr, 0x580a, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x580b, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x580c, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x580d, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x580e, 0x13);
    cam_i2c_write(axi_iic, device_addr, 0x580f, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x5810, 0x15);
    cam_i2c_write(axi_iic, device_addr, 0x5811, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x5812, 0x8);
    cam_i2c_write(axi_iic, device_addr, 0x5813, 0x5);
    cam_i2c_write(axi_iic, device_addr, 0x5814, 0x4);
    cam_i2c_write(axi_iic, device_addr, 0x5815, 0x5);
    cam_i2c_write(axi_iic, device_addr, 0x5816, 0x9);
    cam_i2c_write(axi_iic, device_addr, 0x5817, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x5818, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x5819, 0xa);
    cam_i2c_write(axi_iic, device_addr, 0x581a, 0x4);
    cam_i2c_write(axi_iic, device_addr, 0x581b, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x581c, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x581d, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x581e, 0x6);
    cam_i2c_write(axi_iic, device_addr, 0x581f, 0x9);
    cam_i2c_write(axi_iic, device_addr, 0x5820, 0x12);
    cam_i2c_write(axi_iic, device_addr, 0x5821, 0xb);
    cam_i2c_write(axi_iic, device_addr, 0x5822, 0x4);
    cam_i2c_write(axi_iic, device_addr, 0x5823, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5824, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5825, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x5826, 0x6);
    cam_i2c_write(axi_iic, device_addr, 0x5827, 0xa);
    cam_i2c_write(axi_iic, device_addr, 0x5828, 0x17);
    cam_i2c_write(axi_iic, device_addr, 0x5829, 0xf);
    cam_i2c_write(axi_iic, device_addr, 0x582a, 0x9);
    cam_i2c_write(axi_iic, device_addr, 0x582b, 0x6);
    cam_i2c_write(axi_iic, device_addr, 0x582c, 0x5);
    cam_i2c_write(axi_iic, device_addr, 0x582d, 0x6);
    cam_i2c_write(axi_iic, device_addr, 0x582e, 0xa);
    cam_i2c_write(axi_iic, device_addr, 0x582f, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x5830, 0x28);
    cam_i2c_write(axi_iic, device_addr, 0x5831, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x5832, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x5833, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x5834, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x5835, 0xf);
    cam_i2c_write(axi_iic, device_addr, 0x5836, 0x15);
    cam_i2c_write(axi_iic, device_addr, 0x5837, 0x1d);
    cam_i2c_write(axi_iic, device_addr, 0x5838, 0x6e);
    cam_i2c_write(axi_iic, device_addr, 0x5839, 0x39);
    cam_i2c_write(axi_iic, device_addr, 0x583a, 0x27);
    cam_i2c_write(axi_iic, device_addr, 0x583b, 0x1f);
    cam_i2c_write(axi_iic, device_addr, 0x583c, 0x1e);
    cam_i2c_write(axi_iic, device_addr, 0x583d, 0x23);
    cam_i2c_write(axi_iic, device_addr, 0x583e, 0x2f);
    cam_i2c_write(axi_iic, device_addr, 0x583f, 0x41);
    cam_i2c_write(axi_iic, device_addr, 0x5840, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x5841, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x5842, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x5843, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x5844, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x5845, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x5846, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x5847, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x5848, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x5849, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x584a, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x584b, 0xa);
    cam_i2c_write(axi_iic, device_addr, 0x584c, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x584d, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x584e, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x584f, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5850, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x5851, 0xa);
    cam_i2c_write(axi_iic, device_addr, 0x5852, 0xf);
    cam_i2c_write(axi_iic, device_addr, 0x5853, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x5854, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5855, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5856, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5857, 0xa);
    cam_i2c_write(axi_iic, device_addr, 0x5858, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x5859, 0xe);
    cam_i2c_write(axi_iic, device_addr, 0x585a, 0xf);
    cam_i2c_write(axi_iic, device_addr, 0x585b, 0xf);
    cam_i2c_write(axi_iic, device_addr, 0x585c, 0xf);
    cam_i2c_write(axi_iic, device_addr, 0x585d, 0xa);
    cam_i2c_write(axi_iic, device_addr, 0x585e, 0x9);
    cam_i2c_write(axi_iic, device_addr, 0x585f, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x5860, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x5861, 0xb);
    cam_i2c_write(axi_iic, device_addr, 0x5862, 0xd);
    cam_i2c_write(axi_iic, device_addr, 0x5863, 0x7);
    cam_i2c_write(axi_iic, device_addr, 0x5864, 0x17);
    cam_i2c_write(axi_iic, device_addr, 0x5865, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5866, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x5867, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x5868, 0x16);
    cam_i2c_write(axi_iic, device_addr, 0x5869, 0x12);
    cam_i2c_write(axi_iic, device_addr, 0x586a, 0x1b);
    cam_i2c_write(axi_iic, device_addr, 0x586b, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x586c, 0x16);
    cam_i2c_write(axi_iic, device_addr, 0x586d, 0x16);
    cam_i2c_write(axi_iic, device_addr, 0x586e, 0x18);
    cam_i2c_write(axi_iic, device_addr, 0x586f, 0x1f);
    cam_i2c_write(axi_iic, device_addr, 0x5870, 0x1c);
    cam_i2c_write(axi_iic, device_addr, 0x5871, 0x16);
    cam_i2c_write(axi_iic, device_addr, 0x5872, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x5873, 0xf);
    cam_i2c_write(axi_iic, device_addr, 0x5874, 0x13);
    cam_i2c_write(axi_iic, device_addr, 0x5875, 0x1c);
    cam_i2c_write(axi_iic, device_addr, 0x5876, 0x1e);
    cam_i2c_write(axi_iic, device_addr, 0x5877, 0x17);
    cam_i2c_write(axi_iic, device_addr, 0x5878, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x5879, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x587a, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x587b, 0x1e);
    cam_i2c_write(axi_iic, device_addr, 0x587c, 0x1c);
    cam_i2c_write(axi_iic, device_addr, 0x587d, 0x1c);
    cam_i2c_write(axi_iic, device_addr, 0x587e, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x587f, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x5880, 0x1b);
    cam_i2c_write(axi_iic, device_addr, 0x5881, 0x1f);
    cam_i2c_write(axi_iic, device_addr, 0x5882, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5883, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x5884, 0x1d);
    cam_i2c_write(axi_iic, device_addr, 0x5885, 0x1e);
    cam_i2c_write(axi_iic, device_addr, 0x5886, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x5887, 0x1a);
    cam_i2c_write(axi_iic, device_addr, 0x528a, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x528b, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x528c, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x528d, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x528e, 0x40);
    cam_i2c_write(axi_iic, device_addr, 0x528f, 0x50);
    cam_i2c_write(axi_iic, device_addr, 0x5290, 0x60);
    cam_i2c_write(axi_iic, device_addr, 0x5292, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5293, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x5294, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5295, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x5296, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5297, 0x08);
    cam_i2c_write(axi_iic, device_addr, 0x5298, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5299, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x529a, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529b, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x529c, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529d, 0x28);
    cam_i2c_write(axi_iic, device_addr, 0x529e, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x529f, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x5282, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5680, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5681, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5682, 0x05);
    cam_i2c_write(axi_iic, device_addr, 0x5683, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5684, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5685, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x5686, 0x03);
    cam_i2c_write(axi_iic, device_addr, 0x5687, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x5180, 0xff);
    cam_i2c_write(axi_iic, device_addr, 0x5181, 0x52);
    cam_i2c_write(axi_iic, device_addr, 0x5182, 0x11);
    cam_i2c_write(axi_iic, device_addr, 0x5183, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5184, 0x25);
    cam_i2c_write(axi_iic, device_addr, 0x5185, 0x24);
    cam_i2c_write(axi_iic, device_addr, 0x5186, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5187, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5188, 0x14);
    cam_i2c_write(axi_iic, device_addr, 0x5189, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x518a, 0x60);
    cam_i2c_write(axi_iic, device_addr, 0x518b, 0xa2);
    cam_i2c_write(axi_iic, device_addr, 0x518c, 0x9c);
    cam_i2c_write(axi_iic, device_addr, 0x518d, 0x36);
    cam_i2c_write(axi_iic, device_addr, 0x518e, 0x34);
    cam_i2c_write(axi_iic, device_addr, 0x518f, 0x54);
    cam_i2c_write(axi_iic, device_addr, 0x5190, 0x4c);
    cam_i2c_write(axi_iic, device_addr, 0x5191, 0xf8);
    cam_i2c_write(axi_iic, device_addr, 0x5192, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x5193, 0x70);
    cam_i2c_write(axi_iic, device_addr, 0x5194, 0xf0);
    cam_i2c_write(axi_iic, device_addr, 0x5195, 0xf0);
    cam_i2c_write(axi_iic, device_addr, 0x5196, 0x03);
    cam_i2c_write(axi_iic, device_addr, 0x5197, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x5198, 0x05);
    cam_i2c_write(axi_iic, device_addr, 0x5199, 0x2f);
    cam_i2c_write(axi_iic, device_addr, 0x519a, 0x04);
    cam_i2c_write(axi_iic, device_addr, 0x519b, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x519c, 0x06);
    cam_i2c_write(axi_iic, device_addr, 0x519d, 0xa0);
    cam_i2c_write(axi_iic, device_addr, 0x519e, 0xa0);
    cam_i2c_write(axi_iic, device_addr, 0x3a0f, 0x3c);
    cam_i2c_write(axi_iic, device_addr, 0x3a10, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x3a1b, 0x3c);
    cam_i2c_write(axi_iic, device_addr, 0x3a1e, 0x30);
    cam_i2c_write(axi_iic, device_addr, 0x3a11, 0x70);
    cam_i2c_write(axi_iic, device_addr, 0x3a1f, 0x10);
    cam_i2c_write(axi_iic, device_addr, 0x3800, 0x1);
    cam_i2c_write(axi_iic, device_addr, 0x3801, 0x50);
    cam_i2c_write(axi_iic, device_addr, 0x3802, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x3803, 0x8);
    cam_i2c_write(axi_iic, device_addr, 0x3804, 0x5);
    cam_i2c_write(axi_iic, device_addr, 0x3805, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x3806, 0x3);
    cam_i2c_write(axi_iic, device_addr, 0x3807, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x3808, 0x3);
    cam_i2c_write(axi_iic, device_addr, 0x3809, 0x20);
    cam_i2c_write(axi_iic, device_addr, 0x380a, 0x2);
    cam_i2c_write(axi_iic, device_addr, 0x380b, 0x58);
    cam_i2c_write(axi_iic, device_addr, 0x380c, 0xc);
    cam_i2c_write(axi_iic, device_addr, 0x380d, 0x80);
    cam_i2c_write(axi_iic, device_addr, 0x380e, 0x3);
    cam_i2c_write(axi_iic, device_addr, 0x380f, 0xe8);
    cam_i2c_write(axi_iic, device_addr, 0x5001, 0x7f);
    cam_i2c_write(axi_iic, device_addr, 0x5680, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5681, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5682, 0x5);
    cam_i2c_write(axi_iic, device_addr, 0x5683, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5684, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5685, 0x0);
    cam_i2c_write(axi_iic, device_addr, 0x5686, 0x3);
    cam_i2c_write(axi_iic, device_addr, 0x5687, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x5687, 0xc0);
    cam_i2c_write(axi_iic, device_addr, 0x3815, 0x02);
    cam_i2c_write(axi_iic, device_addr, 0x3503, 0x00);
    cam_i2c_write(axi_iic, device_addr, 0x3818, 0x81); // No Mirror
    cam_i2c_write(axi_iic, device_addr, 0x3621, 0xa7);
    
    cam_i2c_write(axi_iic, device_addr, 0x4740, 0x21);
    
    cam_i2c_write(axi_iic, device_addr, 0x501e, 0x2a);
    cam_i2c_write(axi_iic, device_addr, 0x5002, 0x78);
    cam_i2c_write(axi_iic, device_addr, 0x501f, 0x01);
    cam_i2c_write(axi_iic, device_addr, 0x4300, 0x61);
    
    return(0);
}

  1. 2019年09月04日 05:23 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

ガボールフィルタとラプラシアンフィルタを使ってUltra96-V2でカメラ画像をフィルタする2(デバック中)

ガボールフィルタとラプラシアンフィルタを使ってUltra96-V2でカメラ画像をフィルタする1(デバック中)”の続き。

前回は、今まで作ってきたガボールフィルタとラプラシアンフィルタを使用して、カメラ画像をリアルタイムにフィルタする回路をUltra96-V2 で作った。全く動作しなかったが、デバックの結果、カメラ画像とラプラシアンフィルタ画像は写るようになったが、ガボールフィルタの画像だけが表示されなかった。今回は、ガボールフィルタだけをAXI4-Stream に接続してトラブルシュートしてみたが、やはりうまく行かなかった。

~/HDL/Ultra96/Ultra96V2/cam_dp_V2_gabor_183/ ディレクトリに、cam_dp_183 プロジェクトをコピーして、ガボールフィルタだけをAXI4-Stream に残した。
cam_dp_V2_filter_10_190903.png

cam_dp_V2_filter_11_190903.png

これでやってみたが、やはり白一色になってしまう。

Vivado HLS 2019.1 とVivado HLS 2017.4 でガボールフィルタを合成してみたが結果は一緒だった。
バグの原因が分からないな。。。C/RTL協調シミュレーションもうまく行っているのだけど、どこが悪いのだろうか?

ガボールフィルタはZYBO では動作しているのだが。。。
  1. 2019年09月03日 05:26 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

ガボールフィルタとラプラシアンを使ってUltra96-V2でカメラ画像をフィルタする1(デバック中)

今まで作ってきたガボールフィルタラプラシアンフィルタを使用して、カメラ画像をリアルタイムにフィルタする回路をUltra96-V2 で作ることにした。

~/HDL/Ultra96/Ultra96V2/cam_dp_V2_183/ ディレクトリの cam_dp_183 プロジェクトをベースに使用する。

そして、cam_dp_183 プロジェクトにラプラシアンフィルタとガボールフィルタを追加したのだが、全く画像が出なかった。それでVivado Analyzer を挿入してどこまで動作しているか確認することにした。なお、Debian 上で起動するアプリケーション・ソフトは作成済みだ。
下にVivado Analyzer を挿入した状態の、ブロックデザインを示す。
cam_dp_V2_filter_1_190830.png

cam_dp_V2_filter_2_190830.png

cam_dp_V2_filter_3_190830.png

Address Editor 画面を示す。
cam_dp_V2_filter_4_190830.png

当初、display 階層の disp_dmar_axis_0 のAXI4 Master インターフェースの ARVALID でトリガをかけていたのだが、トリガが来ることはなかった。これはどういうこと?ということでいろいろと試行錯誤していたのだが、axis_switch_1 に行くべき設定を、axis_switch_0 にしていたバグを発見した。でもこれでARVALID が出ない理由にはならないと思うのだが、いろいろとやってみたことが良かったのか?画像が出るようになった。
だが、カメラ画像とラプラシアンフィルタ画像は出るようになったのだが、ガボールフィルタの画像が出ないで、ほとんど白い画面になってしまう。

カメラ画像を示す。うちの奥さんが写ってしまっている。なお、照明が映り込むので、極端に上面から撮影しているので、写真の下の幅が小さくなっている。
cam_dp_V2_filter_7_190830.jpg

ラプラシアンフィルタ画像、これは問題ない。
cam_dp_V2_filter_8_190830.jpg

ガボールフィルタ画像、真っ白い。ほとんど黒になるはずなんだけど、どこでどうなった?
cam_dp_V2_filter_9_190830.jpg

Vivado Analyzer でみても 0x00ffffff が出力されている。
cam_dp_V2_filter_6_190830.png

slot_1 が入力のAXI4 Stream でガボールフィルタの出力が slot_4 なのだが、ずーと 00ffffff だった。なぜだ。。。
ガボールフィルタはVivado HLS上のC/RTL 協調シミュレーションでも正しい出力が出ていたのだが、どこが悪いんだろうか?

デバック中。
  1. 2019年09月02日 06:11 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0