FC2カウンター FPGAの部屋 CMOSイメージセンサ
FC2ブログ

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

FPGAの部屋

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

秋月電子カメラモジュールOV5642を使う10(アプリを用意して実機テスト)

秋月電子カメラモジュールOV5642を使う9(デバイスツリー・オーバレイ用ファイルの整備)”の続き。

前回は、Ultra96 のDebian 上でソフトウェアを起動するために、デバイスツリー・オーバレイ関連のファイルを整備した。今回は、アプリケーション・ソフトを作成して、OV5642 を動作させてみよう。

最初に、cam_cap_ov5642.cpp アプリケーションソフトを作成した。
OV5642_37_181216.png

これを、
g++_opencv cam_cap_ov5642.cpp
でコンパイルして、cam_cap_ov5642 を作成した。
./cam_cap_ov5642 で起動した。
OV5642_43_181216.jpg

ところが、cam_cap_ov5642 を起動しても、XCLK は約 24 MHz の波形が出力されるが、PCLK には何も出力されていなかった。
そこで、OV5642 カメラを設定するI2C の波形を見た。
OV5642_38_181216.jpg

カメラからのACK も 0 で帰ってきているので問題なさそうだ。
OV5642_39_181216.jpg

ところが、送っているデータの値を確認してみると送る予定のデータと異なることが分かった。
cam_cap_ov5642.cpp を確認してみるとバグがあった。バグを修正後のI2C の波形を示す。
OV5642_40_181216.jpg

これでI2C は正常になった。
PCLK を見てみると、約 58.6 MHz が出ていた。
OV5642_41_181216.jpg

OV5642 のレジスタの設定を
cam_i2c_write(axi_iic, device_addr, 0x3011, 0x14);
から
cam_i2c_write(axi_iic, device_addr, 0x3011, 0x08);
に変更すると、PCLK は約 24 MHz となった。これで行くことにする。
OV5642_42_181216.jpg

HREF とVSYNC を見てみたが、出力されていなかった。下図で上がHREF 、したがVSYNC だ。
OV5642_44_181216.jpg

HREF とVSYNC が出力されていないので、私の設定のどこかが悪いのではないだろうか?
インターフェース2014年11月号の設定に戻してみて、HREF とVSYNC が出力されるのか?を見てみよう。
  1. 2018年12月16日 06:15 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0

秋月電子カメラモジュールOV5642を使う8(OV5642用Vivadoプロジェクト)

秋月電子カメラモジュールOV5642を使う7(OV5642の設定レジスタの設定値2)”の続き。

前回は、OV5642を使用するための設定レジスタの設定値を決定して、Python でC コードに変換した。今回は、Ultra96 用の cam_test_182 のVivado プロジェクトを少し改変して、OV5642用のVivado プロジェクトを作ろう。

まずは、HDL/Ultra96 ディレクトリの cam_test_182 ディレクトリ(Vivado 2018.2 のプロジェクト)をコピーして、ペーストし、名前を cam_test_ov5642_182 に変更した。
OV5642_25_181212.png

cam_test ブロック・デザインを起動して、Zynq UltraScale+ MPSoC をダブルクリックしてダイアログを表示させた。
Page Navigator からClock Configuration を選択し、Output Clocks のPL Fabric Clocks のPL1 を 24 MHz に設定した。
OV5642_21_181211.png

そして、もう一度、Generate BitStream をクリックして、論理合成、インプリメンテーション、ビットストリームを行った。
OV5642_22_181211.png

結果を示す。
OV5642_26_181212.png

問題なさそうだ。
次に、Export Hardware をビットストリーム付で行った。

~/HDL/cam_test_ov5642_182/cam_test_182.sdk/cam_test_wrapper_hw_platform_1 ディレクトリの cam_test_wrapper.bit を上のディレクトリにコピーした。
cam_test_wrapper.bif は”Ultra96用PMOD拡張ボードでカメラ入力11(Debian上でカメラ画像を画像ファイルに1)”で作ってあるので、コマンドを入力して、cam_test_ov5642.bin を作成する。
bootgen -image cam_test_wrapper.bif -arch zynqmp -w -o cam_test_ov5642.bin
OV5642_23_181212.png

cam_test_ov5642.bin が作成された。
OV5642_24_181212.png
  1. 2018年12月12日 05:28 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0

秋月電子カメラモジュールOV5642を使う7(OV5642の設定レジスタの設定値2)

秋月電子カメラモジュールOV5642を使う6(OV5642の設定レジスタの設定値)”の続き。

前回は、OV5642を使用するための設定レジスタの設定値を半分程度決定した。今回は、OV5642を使用するための設定レジスタの設定値を決定して、Python でC コードに変換してみよう。

OV5642を使用するための設定レジスタの設定値を示す。全部で 322 個になった。これは、インターフェース誌の2014年の11月号のダウンロードデータにOV5642の初期設定レジスタ一覧を示す「表2.pdf」があるので、それの設定値を頂いて、設定値が異なるところを書き換えて、足りない設定値を追加した。変更点は、インターフェースのカメラ画像はVGAだが、私の設定値ではSVGA の 800 x 600 ピクセルであることだ。
OV5642_18_181210.png

現在はLibreOffice Calc で作ってあるので、それを CSV ファイルに変換した。
OV5642_19_181210.png

Python で CSV ファイルを読んで、C コードに変換した。
Python のプログラムを作る際に参考にしたのは、”PythonでCSVの読み書き”だ。
作成したPython コードを貼っておく。

import csv

with open('OV5642_SVGA_regset.csv', 'r') as f:
    reader = csv.reader(f)
    header = next(reader)  # ヘッダーを読み飛ばしたい時
    
    for row in reader:
        print("cam_i2c_write(axi_iic, 0x78, {0}, {1});".format(row[1], row[2]),)


Python だと少ない行数で書けるので、とっても良い。
Jupyter Notebook での実行結果を示す。
OV5642_20_181210.png

これで、OV5642のレジスタ設定用のプログラムも完成した。
  1. 2018年12月11日 04:31 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0

秋月電子カメラモジュールOV5642を使う6(OV5642の設定レジスタの設定値)

秋月電子カメラモジュールOV5642を使う5(OV5642インターフェースIP)”の続き。

OV5642のインターフェースIP を作ろうとし作ったのだが、設定レジスタの設定により、作成済みのMT9D111 のインターフェースIP と違いが無くなったためMT9D111 のインターフェースIP を使用することにした。今回は、OV5642を使用するための設定レジスタの設定値を決定する。ただしまだ半分くらいだ。

基本的に、OV5642の設定レジスタの設定値はインターフェース誌の2014年11月号の”ウェアラブルの可能性を探る 徹底研究!指先サイズ スーパーカメラ”に基づいているが、インターフェース誌では、640 x 480 ピクセルのVGA のところを 800 x 600 の SVGA でしかも、MT9D111 と同じフォーマットで出力波形を出すという目標があるので、設定レジスタの設定値を1つ1つ確認している。

インターフェース誌の2014年の11月号のダウンロードデータにOV5642の初期設定レジスタ一覧を示す「表2.pdf」があるので、それの設定値を頂いて、設定値が異なるところを書き換えている。
なお、説明は、OV5642 データシートのものをコピペしてある。
一部を引用する。
OV5642_17_181210.png

まだ、半分だが、早急に表を完成させて、その表のデータから設定するC コードを出力するプログラムをPython で作りたい。
  1. 2018年12月10日 05:12 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0

秋月電子カメラモジュールOV5642を使う5(OV5642インターフェースIP)

秋月電子カメラモジュールOV5642を使う4(AXI-Stream Data FIFO のシミュレーション)”の続き。

前回は、、AXI-Stream Data FIFO が使い物になるかどうか?ということで、HDL シミュレーションを行ったところ、うまく行きそうにないということがわかった。(AXI4-Stream の動作としては問題ない)今回は、OV5642のインターフェースIP を作っていこう。

MT9D111 のインターフェースIP を最近作成してある。
Ultra96用PMOD拡張ボードでカメラ入力1(mt9d111_inf_axis IP の作成1)
Ultra96用PMOD拡張ボードでカメラ入力2(mt9d111_inf_axis IP の作成2)

これを改造してOV5642 のインターフェースIP にすることにした。
mt9d111_axis_mpsoc プロジェクトのファイルをコピーして、新規作成した ov5642_axis_mpsoc プロジェクトに追加した。
一応、IP まで作った。
OV5642_15_181206.png

しかし、MT9D111 のデータシートを見ると、今回のOV5642 の設定を適用すると、MT9D111 のタイミングチャートとだいたい同じになる。
MT9D111 カメラのデータシート”1/3.2-Inch System-On-A-Chip (SOC) CMOS Digital Image Sensor MT9D111”の119 ページのFigure 18: Pixel Timing Example とFigure 19: Row Timing and FRAME VALID/LINE_VALID Signals を引用する。
OV5642_16_181206.png

OV5642のPDFデータシートの106、107ページの figure 6-9 と table 6-8 の一部をもう一度、引用する。
OV5642_4_181203.png

今、OV5642 のVSYNC を反転させることにしているので、VSYNC を反転させればMT9D111 のデータ出力のフォーマットとだいたい同一となる。つまり、OV5642 用のインターフェースIP をわざわざ作成しなくても、MT9D111 のインターフェースIP が使えそうだ。ということだ。レジスタ設定用のI2C はXilinx 社のAXI IIC IP を使用しているので、それを使用するソフトウェアで設定を書けば良さそうだ。

せっかく、OV5642 用のインターフェースIP は作ったのだが、MT9D111 のインターフェースIP を使ってOV5642 を使っていくことにしよう。
  1. 2018年12月06日 04:58 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0

秋月電子カメラモジュールOV5642を使う4(AXI-Stream Data FIFO のシミュレーション)

秋月電子カメラモジュールOV5642を使う3(OV5642インターフェースIPの検討)”の続き。

前回は、OV5642の信号を取り込むOV5642インターフェースIP を検討した。その結果、AXI-Stream Data FIFO とOV5642 の画像データを受け取るVivado HLS のIP の組み合わせで行くことにした。今回は、AXI-Stream Data FIFO が使い物になるかどうか?HDL シミュレーションをしてみよう。

Vivado 2018.2 でUltra96 のボード・ファイルを使用して test1 プロジェクトを作成した。
その中でブロック・デザインを作成し、AXI-Stream Data FIFO をAdd IP した。
OV5642_12_181205.png

ブロック・デザインのVerilog HDL ラッパー・ファイルを作成した。
Verilog HDL ラッパー・ファイルのdesign_1_wrapper.v を示す。

//Copyright 1986-2018 Xilinx, Inc. All Rights Reserved.
//--------------------------------------------------------------------------------
//Tool Version: Vivado v.2018.2 (lin64) Build 2258646 Thu Jun 14 20:02:38 MDT 2018
//Date        : Tue Dec  4 21:21:23 2018
//Host        : masaaki-H110M4-M01 running 64-bit Ubuntu 18.04.1 LTS
//Command     : generate_target design_1_wrapper.bd
//Design      : design_1_wrapper
//Purpose     : IP block netlist
//--------------------------------------------------------------------------------
`timescale 1 ps / 1 ps

module design_1_wrapper
   (M_AXIS_0_tdata,
    M_AXIS_0_tready,
    M_AXIS_0_tuser,
    M_AXIS_0_tvalid,
    S_AXIS_0_tdata,
    S_AXIS_0_tready,
    S_AXIS_0_tuser,
    S_AXIS_0_tvalid,
    axis_data_count_0,
    axis_rd_data_count_0,
    axis_wr_data_count_0,
    m_axis_aclk_0,
    m_axis_aresetn_0,
    s_axis_aclk_0,
    s_axis_aresetn_0);
  output [7:0]M_AXIS_0_tdata;
  input M_AXIS_0_tready;
  output [0:0]M_AXIS_0_tuser;
  output M_AXIS_0_tvalid;
  input [7:0]S_AXIS_0_tdata;
  output S_AXIS_0_tready;
  input [0:0]S_AXIS_0_tuser;
  input S_AXIS_0_tvalid;
  output [31:0]axis_data_count_0;
  output [31:0]axis_rd_data_count_0;
  output [31:0]axis_wr_data_count_0;
  input m_axis_aclk_0;
  input m_axis_aresetn_0;
  input s_axis_aclk_0;
  input s_axis_aresetn_0;

  wire [7:0]M_AXIS_0_tdata;
  wire M_AXIS_0_tready;
  wire [0:0]M_AXIS_0_tuser;
  wire M_AXIS_0_tvalid;
  wire [7:0]S_AXIS_0_tdata;
  wire S_AXIS_0_tready;
  wire [0:0]S_AXIS_0_tuser;
  wire S_AXIS_0_tvalid;
  wire [31:0]axis_data_count_0;
  wire [31:0]axis_rd_data_count_0;
  wire [31:0]axis_wr_data_count_0;
  wire m_axis_aclk_0;
  wire m_axis_aresetn_0;
  wire s_axis_aclk_0;
  wire s_axis_aresetn_0;

  design_1 design_1_i
       (.M_AXIS_0_tdata(M_AXIS_0_tdata),
        .M_AXIS_0_tready(M_AXIS_0_tready),
        .M_AXIS_0_tuser(M_AXIS_0_tuser),
        .M_AXIS_0_tvalid(M_AXIS_0_tvalid),
        .S_AXIS_0_tdata(S_AXIS_0_tdata),
        .S_AXIS_0_tready(S_AXIS_0_tready),
        .S_AXIS_0_tuser(S_AXIS_0_tuser),
        .S_AXIS_0_tvalid(S_AXIS_0_tvalid),
        .axis_data_count_0(axis_data_count_0),
        .axis_rd_data_count_0(axis_rd_data_count_0),
        .axis_wr_data_count_0(axis_wr_data_count_0),
        .m_axis_aclk_0(m_axis_aclk_0),
        .m_axis_aresetn_0(m_axis_aresetn_0),
        .s_axis_aclk_0(s_axis_aclk_0),
        .s_axis_aresetn_0(s_axis_aresetn_0));
endmodule


次に、design_1_wrapper.v をシミュレーションするファイル axis_data_fifo_tb.v を作成した。

// axis_data_fifo_tb.v
// 2018/12/04 by marsee
//

`timescale 1ns / 1ps

module axis_data_fifo_tb;
    wire [7:0]M_AXIS_0_tdata;
    reg M_AXIS_0_tready;
    wire [0:0]M_AXIS_0_tuser;
    wire M_AXIS_0_tvalid;
    reg [7:0]S_AXIS_0_tdata;
    wire S_AXIS_0_tready;
    reg [0:0]S_AXIS_0_tuser;
    reg S_AXIS_0_tvalid;
    wire [31:0]axis_data_count_0;
    wire [31:0]axis_rd_data_count_0;
    wire [31:0]axis_wr_data_count_0;
    reg m_axis_aclk_0;
    reg m_axis_aresetn_0;
    reg s_axis_aclk_0;
    reg s_axis_aresetn_0;

    always #10 s_axis_aclk_0 = ~s_axis_aclk_0;
    always #5 m_axis_aclk_0 = ~m_axis_aclk_0;
    
    initial begin
        s_axis_aclk_0 = 1'b0;
        m_axis_aclk_0 = 1'b0;
        s_axis_aresetn_0 = 1'b0;
        m_axis_aresetn_0 = 1'b0;
        S_AXIS_0_tvalid = 1'b0;
        M_AXIS_0_tready = 1'b0;
        S_AXIS_0_tuser = 1'b1;
        
        // Wait 100 ns for global reset to finish
        #101;
        
        s_axis_aresetn_0 = 1'b1;
        m_axis_aresetn_0 = 1'b1;
        
        #100;
        S_AXIS_0_tvalid = 1'b1;
        
        #400;
        M_AXIS_0_tready = 1'b1;
        
        #400
        S_AXIS_0_tuser = 1'b0;
    
    end
    
    always @(posedge m_axis_aclk_0) begin
        if(s_axis_aresetn_0 == 1'b0) begin
            S_AXIS_0_tdata <= 8'd0;
        end else begin
            S_AXIS_0_tdata <= S_AXIS_0_tdata + 8'd1;
        end
    end
    
    design_1_wrapper design_1_wrapper_i (
        .M_AXIS_0_tdata(M_AXIS_0_tdata),
        .M_AXIS_0_tready(M_AXIS_0_tready),
        .M_AXIS_0_tuser(M_AXIS_0_tuser),
        .M_AXIS_0_tvalid(M_AXIS_0_tvalid),
        .S_AXIS_0_tdata(S_AXIS_0_tdata),
        .S_AXIS_0_tready(S_AXIS_0_tready),
        .S_AXIS_0_tuser(S_AXIS_0_tuser),
        .S_AXIS_0_tvalid(S_AXIS_0_tvalid),
        .axis_data_count_0(axis_data_count_0),
        .axis_rd_data_count_0(axis_rd_data_count_0),
        .axis_wr_data_count_0(axis_wr_data_count_0),
        .m_axis_aclk_0(m_axis_aclk_0),
        .m_axis_aresetn_0(m_axis_aresetn_0),
        .s_axis_aclk_0(s_axis_aclk_0),
        .s_axis_aresetn_0(s_axis_aresetn_0)
    );

endmodule


これで、論理シミュレーションを行った。結果を示す。
OV5642_10_181205.png

OV5642_11_181205.png

タイミングチャートで、S_AXIS_0_tvalid が 1 になったときに、S_AXIS_0_tready が 1 になるのがだいぶ遅れるようだ。これでは今回の用途では使い物にならない。。。

次に、OV5642 の画像データを受け取るVivado HLS のIP も試しに作ってみた。まだデータはおかしいがC/RTL 強調シミュレーションをしてみた。
OV5642_13_181205.png

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

cam_sig_TREADY が 1 になった後で、0 に戻っている部分があるので、やはり、Vivado HLS 単体でも使用できない。

以上の結果より、”秋月電子カメラモジュールOV5642を使う3(OV5642インターフェースIPの検討)”のスキームは使えないことがわかった。
次回以降は、mt9d111_inf_axis IP を修正して、OV5642 対応としたいとおもう。つまり、HDL を書いてOV5642 の画像データをAXI4-Stream に変換する。
  1. 2018年12月05日 04:25 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0

秋月電子カメラモジュールOV5642を使う3(OV5642インターフェースIPの検討)

秋月電子カメラモジュールOV5642を使う2(OV5642の設定の検討)”の続き。

前回は、OV5642の設定レジスタの設定値を検討した。今回は、OV5642の信号を取り込むOV5642インターフェースIP を検討する。

OV5642の信号をVivado HLS で制作したインターフェースIP で受け取りたいが、Vivado HLS で作成したAXI4-Stream インターフェースのIP は for 文が始まることで初期化レイテンシがあることがある。処理化のレイテンシがあるとフリーランしているOV5642の画像データを取りこぼしてしまう。
例えば下の図は、AXI4-Stream インターフェースのMax Pooling層のシミュレーション波形だが、C Inputs -> ins(axis) -> ins_TREADY を見てみると、最初の方で 0 になっている部分があることが分かる。この 0 になっている部分はTDATA を受けられない期間なのだが、これがあるとプロトコル関係なくデータを出力するOV5642のデータを取りこぼしてしまう。
Max_Pooling_6_180225.png

そこで、最初にFIFO を置いてみればどうだろうか?そのFIFO がレイテンシなくデータを受け取れる必要はあるのだが、そうすれば、Vivado HLS で作ったOV5642インターフェースIP を接続できる。
OV5642_8_181204.png

Xilinx 社のIP として、AXI-Stream Data FIFO があるのでこれを使用できないか?と思う。 AXI-Stream Data FIFO は入力、出力に独立なクロックを選べるので、OV5642のPCLK のデータを受けて、AXI4-Stream のクロックのデータを出力することができる。HREF が 1 のときしか画像データは出ていないので、HREF 信号はTVALID に入れることにする。つまり、データが有効なときのみAXI-Stream Data FIFO にデータを取り込むことが出来る。
しかし、それでは画像フレームの先頭がわからない?そこで、VSYNC も一緒にTVALID に入れることにする。つまり、HREF とVSYNC のOR を取るわけだ。VSYNC はTUSER にも同時に入れておくと、VSYNC が 1 にアサートされたときのデータなのか?それともHREF が 1 になったきちんとしたデータなのか?が分かると思う。
もう1つ条件があってVSYNC が 1 のときはOV5642 の出力クロックのPCLK のデータレートでデータが入ってくるので、それを捌き切れるように AXI4-Stream の方のクロックをPCLK よりも周波数を高くしておこう。
HREF とVSYNC のタイミングは”秋月電子カメラモジュールOV5642を使う2(OV5642の設定の検討)”のタイミングチャート参照。
OV5642_9_181204.png

次は、AXI-Stream Data FIFO をHDLシミュレーションしてみて、レイテンシが無いかどうか?を確かめてみよう。
  1. 2018年12月04日 05:39 |
  2. CMOSイメージセンサ
  3. | トラックバック:0
  4. | コメント:0
»