FC2カウンター FPGAの部屋 AXI4-Stream IPの作製

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

FPGAの部屋

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

カメラ・インターフェース用AXI4-Stream IPの作製6(AXI4-Stream IPの設定)

前回、
カメラ・インターフェース用AXI4-Stream IPの作製4(HDLソース1)
カメラ・インターフェース用AXI4-Stream IPの作製5(HDLソース2)
で、カメラ・インターフェースAXI4-Stream IPのHDLソースを示した。
今回は、”カメラ・インターフェース用AXI4-Stream IPの作製1(仕様の検討)”に書かれているように、ar37425.zip を解凍して、ar37425/axi_stream フォルダをテンプレートとして、カメラ・インターフェース用のAXI4-Stream IP用に変更していく様子を示す。

・mt9d111_inf_axi_stream_v2_1_0.pao
まずは、 system\pcores\mt9d111_inf_axi_stream_v1_00_a\dataフォルダの mt9d111_inf_axi_stream_v2_1_0.pao ファイルから始める。ここには、IPに所属するHDファイルが書いてある。下のように変更した。

## AXI Stream Core
lib mt9d111_inf_axi_stream_v1_00_a mt9d111_inf_axi_stream.vhd vhdl
lib mt9d111_inf_axi_stream_v1_00_a pixel_fifo.v verilog
lib mt9d111_inf_axi_stream_v1_00_a mt9d111_cam_conts.v verilog


・mt9d111_inf_axi_stream_v2_1_0.mpd
次に、mt9d111_inf_axi_stream_v2_1_0.mpd ファイルを変更する。

1.”OPTION HDL = VHDL”を”OPTION HDL = MIXED”に変更(Verilog HDLも使用している)。

2.”OPTION STYLE = HDL”を”OPTION STYLE = MIX”に変更(pixel_fifo用にNGCファイルを使用する)。

3.”OPTION DESC”と”OPTION LONG_DESC”を下に示すように変更した。(修正)

OPTION DESC = mt9d111_inf_axi_stream
OPTION LONG_DESC = MT9D111 Interface of AXI4-Stream


4.”IO_INTERFACE IO_IF”を設定した。下の IO port for MT9D111 でグループ化する。

IO_INTERFACE IO_IF = mt9d111_inf


5.テンプレートにはマスタ用とスレーブ用の記述があるので、マスタ用だけの記述を残して、スレーブ用の記述は削除した。

6.C_M_AXIS_DATA_WIDTH の RANGE を”RANGE = (8,16,24,32,40,48,56,64,128,256)”に変更した。

6.5.C_M_AXIS_PROTOCOLパラメータを設定した。(2013/07/06:追記) ”AXI VDMAのシミュレーション1”を参照。

PARAMETER C_M_AXIS_PROTOCOL = XIL_AXI_STREAM_VID_DATA, DT = STRING, BUS = M_AXIS, ASSIGNMENT = CONSTANT, TYPE = NON_HDL


7.Ports を変更または追加した。変更、追加したポートを下に示す。

## Ports
PORT s2mm_aclk = "", DIR = I, SIGIS = CLK
PORT s2mm_prmry_reset = "", DIR = I, SIGIS = RST
PORT s2mm_fsync = "", DIR = O

# IO port for MT9D111
PORT init_done = "", DIR = I, IO_IF = mt9d111_inf, IO_IS = init_done
PORT pclk_from_pll = "", DIR = I, SIGIS = CLK, IO_IF = mt9d111_inf, IO_IS = pclk_from_pll
PORT pclk = "", DIR = I, SIGIS = CLK, IO_IF = mt9d111_inf, IO_IS = pclk
PORT xck = "", DIR =O, SIGIS = CLK, IO_IF = mt9d111_inf, IO_IS = xck
PORT href = "", DIR = I, IO_IF = mt9d111_inf, IO_IS = href
PORT vsync = "", DIR = I, IO_IF = mt9d111_inf, IO_IS = vsync
PORT cam_data = "", DIR = I, VEC = [7:0], IO_IF = mt9d111_inf, IO_IS = cam_data
PORT standby = "", DIR = O, IO_IF = mt9d111_inf, IO_IS = standby
PORT pfifo_overflow = "", DIR = O, IO_IF = mt9d111_inf, IO_IS = pfifo_overflow
PORT pfifo_underflow = "", DIR = O, IO_IF = mt9d111_inf, IO_IS = pfifo_underflow


・mt9d111_inf_axi_stream_v2_1_0.mui
次に、mt9d111_inf_axi_stream_v2_1_0.mui を編集する。このファイルは、Addした時のダイアログの表示を構築する。

1.C_M_AXIS_DATA_WIDTH の項目を削除した。

これで、mt9d111_inf_axi_stream_v2_1_0.mui の編集は終了した。

・mt9d111_inf_axi_stream_v2_1_0.bbd
system\pcores\mt9d111_inf_axi_stream_v1_00_a\dataフォルダに、mt9d111_inf_axi_stream_v2_1_0.bbdファイルを追加する。pixel_fifo のNGCファイルを追加するため、mt9d111_inf_axi_stream_v2_1_0.mpd ファイルを”OPTION STYLE = MIX”に変更し、mt9d111_inf_axi_stream_v2_1_0.bbdファイルを追加する。mt9d111_inf_axi_stream_v2_1_0.bbdファイルの内容を下に示す。

FILES
pixel_fifo.ngc


・netlistフォルダ
mt9d111_inf_axi_stream_v2_1_0.bbd フォルダに、pixel_fifo.ngc を使うと書いたが、実際の pixel_fifo.ngc を入れておくために system\pcores\mt9d111_inf_axi_stream_v1_00_a\netlist フォルダを作成する。
netlist フォルダの中には、pixel_fifo.ngc を入れておく。

これで、カメラ・インターフェース用のAXI4-Stream IPは、完成だ。

これを、XPSプロジェクトのProject メニューからRescan User Repositories を行なってから、mt9d111_inf_axi_stream をAdd IP すると下に示すダイアログが出る。
Cam_AXI4_Stream_IP_12_130603.png

M_AXISと mt9d111_inf に + 記号が付いているのが見えるだろうか?この信号線をクリックすると、グループを構成する信号名を一覧することができる。
Cam_AXI4_Stream_IP_13_130603.png

  1. 2013年06月03日 04:39 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:0

カメラ・インターフェース用AXI4-Stream IPの作製5(HDLソース2)

今回は、シミュレーション用のHDLファイルを下に示す。

AXI4-Streamマスタの mt9d111_inf_axi_stream.vhd からのAXI4-Stream に接続し、TREADYを返すだけの axi4s_slave_bfm を下に示す。

-----------------------------------------------------------------------------
--
-- AXI Stream Master用 AXI Stream Slave Bus Function Mode (BFM)
--
-----------------------------------------------------------------------------
-- 2012/02/25 : M_AXI_AWBURST=1 (INCR) にのみ対応、AWSIZE, ARSIZE = 000 (1byte), 001 (2bytes), 010 (4bytes) のみ対応。
-- 2012/01/15 : BVALID が1になる間隔をランダム変更できるようにした。

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;

package m_seq_bfm_pack is
    function M_SEQ16_BFM_F(mseq16in : std_logic_vector
        )return std_logic_vector;
end package m_seq_bfm_pack;
package body m_seq_bfm_pack is
    function M_SEQ16_BFM_F(mseq16in : std_logic_vector
        )return std_logic_vector is
            variable mseq16 : std_logic_vector(15 downto 0);
            variable xor_result : std_logic;
    begin
        xor_result := mseq16in(15) xor mseq16in(12) xor mseq16in(10) xor mseq16in(8) xor mseq16in(7) xor mseq16in(6) xor mseq16in(3) xor mseq16in(2);
        mseq16 := mseq16in(14 downto 0) & xor_result;
        return mseq16;
    end M_SEQ16_BFM_F;
end m_seq_bfm_pack;


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
use IEEE.math_real.all;

library work;
use work.m_seq_bfm_pack.all;

--library unisim;
--use unisim.vcomponents.all;

entity axi4s_slave_bfm is
  generic (
        -- Master AXI Stream Data Width
        C_M_AXIS_DATA_WIDTH : integer range 8 to 1024 := 24;    C_M_AXI_ID_WIDTH     : integer := 1;
        
        TREADY_RANDOM_WAIT    : integer := 1 -- m_axis_treadyにランダムなWaitを発生させる=1, Waitしない=0
   );
  port(
        -- System Signals
        ACLK    : in std_logic;
        ARESETN : in std_logic;

        -- Master Stream Ports
    --    m_axis_aresetn : out std_logic;
        m_axis_tdata   : in std_logic_vector(C_M_AXIS_DATA_WIDTH-1 downto 0);
        m_axis_tstrb   : in std_logic_vector((C_M_AXIS_DATA_WIDTH/8)-1 downto 0);
        m_axis_tvalid  : in std_logic;
        m_axis_tready  : out  std_logic;
        m_axis_tlast   : in std_logic
    );

end axi4s_slave_bfm;

architecture RTL of axi4s_slave_bfm is

signal m_seq16_wr    : std_logic_vector(15 downto 0);
signal reset_1d, reset_2d, reset : std_logic := '1';
signal tready : std_logic;

begin
    -- ARESETN をACLK で同期化
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            reset_1d <= not ARESETN;
            reset_2d <= reset_1d;
        end if;
    end process;
    reset <= reset_2d;
    
    -- m_seq_wr、16ビットのM系列を計算する
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                m_seq16_wr <= (0 => '1', others => '0');
            else
                if TREADY_RANDOM_WAIT=1 then -- ランダムなWaitを挿入する
                    m_seq16_wr <= M_SEQ16_BFM_F(m_seq16_wr);
                else -- Wait無し
                    m_seq16_wr <= (others => '0');
                end if;
            end if;
        end if;
    end process;
    
    -- tready のアサート、TREADY_RANDOM_WAIT=1のときはランダムにアサートする
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                tready <= '0';
            else
                if TREADY_RANDOM_WAIT=1 then -- tready の処理、M系列を計算して128以上だったらWaitする。
                    if m_seq16_wr(7)='0' then -- tready='1' 
                        tready <= '1';
                    else -- m_seq16_wr(7)='1' then -- tready='0'
                        tready <= '0';
                    end if;
                else -- 常時1
                    tready <= '1';
                end if;
            end if;
        end if;
    end process;
    m_axis_tready <= tready;

end RTL;


次に、MT9D111のモデル、mt9d111_model.v を下に示す。(2013/08/27:修正)

// mt9d111_model.v 
// mt9d111 の動作モデル
// RGB565 を出力

`default_nettype none
`timescale 1ns / 1ps

module mt9d111_model # (
    parameter    integer HORIZONTAL_PIXELS    = 800,
    parameter    integer    VERTICAL_LINES        = 600,
    parameter    integer    HBLANK_REG            = 174,     // pixels
    parameter    integer    VBLANK_REG            = 16,    // rows
    parameter    integer    PCLK_DELAY            = 1
)(
    input    wire    xck,
    output    reg        pclk = 1'b1,
    output    reg        href = 1'b0,
    output    reg        vsync = 1'b0,
    output    reg        [7:0]    d = 8'd0,
    input    wire    scl,
    inout    wire    sda,
    input    wire    standby
);

    parameter    [2:0]    INITIAL_STATE =            3'b000,
                        FRAME_START_BLANKING =    3'b001,
                        ACTIVE_DATA_TIME =        3'b011,
                        HORIZONTAL_BLANKING =    3'b010,
                        FRAME_END_BLANKING =    3'b110,
                        VERTICAL_BLANKING =        3'b111;
                        
    reg        [2:0]    mt9d111_cs = INITIAL_STATE;
    reg        [2:0]    fseb_count = 3'd5;
    reg        [15:0]    adt_count = (HORIZONTAL_PIXELS * 2) - 1;
    reg        [15:0]    hb_count = HBLANK_REG - 1;
    reg        [15:0]    fvt_count = VERTICAL_LINES - 1;
    reg        [31:0]    vb_count = VBLANK_REG * (HORIZONTAL_PIXELS + HBLANK_REG) - 1;
    reg        [15:0]    init_count = 10; // 初期化時間
    reg        href_node = 1'b0;
    reg        vsync_node = 1'b0;
    reg        dout_is_even = 1'b0;

    // R, G, B 毎に違った生成多項式のM系列を用意した
    function [7:0] mseqf8_R (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[3] ^ din[2] ^ din[1];
            mseqf8_R = {din[6:0], xor_result};
        end
    endfunction
    
    function [7:0] mseqf8_G (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[4] ^ din[2] ^ din[0];
            mseqf8_G = {din[6:0], xor_result};
        end
    endfunction

    function [7:0] mseqf8_B (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[5] ^ din[2] ^ din[1];
            mseqf8_B = {din[6:0], xor_result};
        end
    endfunction

    reg        [7:0]    mseq8r = 8'd1;
    reg        [7:0]    mseq8g = 8'd1;
    reg        [7:0]    mseq8b = 8'd1;
    
    // pclk の出力
    always @*
        pclk <= #PCLK_DELAY    xck;
        
    // MT9D111 のステート
    always @(posedge pclk) begin
        case (mt9d111_cs)
            INITIAL_STATE : begin
                if (init_count==0) begin
                    mt9d111_cs <= FRAME_START_BLANKING;
                    vsync_node <= 1'b1;
                end
            end
            FRAME_START_BLANKING : begin
                if (fseb_count==0) begin
                    mt9d111_cs <= ACTIVE_DATA_TIME;
                    href_node <= 1'b1;
                end
            end
            ACTIVE_DATA_TIME : begin
                if (adt_count==0) begin
                    if (fvt_count==0)    // frame end
                        mt9d111_cs <= FRAME_END_BLANKING;
                    else
                        mt9d111_cs <= HORIZONTAL_BLANKING;
                    href_node <= 1'b0;
                end
            end
            HORIZONTAL_BLANKING : begin
                if (hb_count==0) begin
                    mt9d111_cs <= ACTIVE_DATA_TIME;
                    href_node <= 1'b1;
                end
            end
            FRAME_END_BLANKING : begin
                if (fseb_count==0) begin
                    mt9d111_cs <= VERTICAL_BLANKING;
                    vsync_node <= 1'b0;
                end
            end
            VERTICAL_BLANKING : begin
                if (vb_count==0) begin
                    mt9d111_cs <= FRAME_START_BLANKING;
                    vsync_node <= 1'b1;
                end
            end
        endcase
    end
                
    // vsync, href 出力、レーシングを防ぐためにpclk よりも出力を遅らせる
    always @* begin
        vsync <= #1    vsync_node;
        href <= #1    href_node;
    end
    
    // Frame Start/End Blanking Counter (6 pixel clocks)
    always @(posedge pclk) begin
        if (mt9d111_cs==FRAME_START_BLANKING || mt9d111_cs==FRAME_END_BLANKING) begin
            if (fseb_count > 0)
                fseb_count <= fseb_count - 3'd1;
        end else
            fseb_count <= 3'd5;
    end
    
    // Initial Counter
    always @(posedge pclk) begin
        if (mt9d111_cs == INITIAL_STATE) begin
            if (init_count != 0)
                init_count <= init_count - 1;
        end
    end
    
    // Active Data Time Counter
    always @(posedge pclk) begin
        if (mt9d111_cs != INITIAL_STATE) begin
            if (mt9d111_cs==ACTIVE_DATA_TIME) begin
                if (adt_count > 0)
                    adt_count <= adt_count - 16'd1;
            end else
                adt_count <= (HORIZONTAL_PIXELS * 2) - 1;
        end
    end
    
    // Horizontal Blanking Counter
    always @(posedge pclk) begin
        if (mt9d111_cs != INITIAL_STATE) begin
            if (mt9d111_cs==HORIZONTAL_BLANKING) begin
                if (hb_count > 0)
                    hb_count <= hb_count - 16'd1;
            end else
                hb_count <= HBLANK_REG - 1;
        end
    end
    
    // Frame Valid Time Counter
    always @(posedge pclk) begin
        if (mt9d111_cs != INITIAL_STATE) begin
            if (mt9d111_cs==ACTIVE_DATA_TIME && adt_count==0) begin
                if (fvt_count > 0)
                    fvt_count <= fvt_count - 16'd1;
            end if (mt9d111_cs == VERTICAL_BLANKING)
                fvt_count <= VERTICAL_LINES - 1;
        end
    end
    
    // Vertical Blanking Counter
    always @(posedge pclk) begin
        if (mt9d111_cs != INITIAL_STATE) begin
            if (mt9d111_cs==VERTICAL_BLANKING) begin
                if (vb_count > 0)
                    vb_count <= vb_count - 32'd1;
            end else
                vb_count <= VBLANK_REG * (HORIZONTAL_PIXELS + HBLANK_REG) - 1;
        end
    end
    
    // Red のM系列符号生成
    always @(posedge pclk) begin
        // if (mt9d111_cs==ACTIVE_DATA_TIME)
            mseq8r <= mseqf8_R(mseq8r);
    end
    
    // Green のM系列符号生成
    always @(posedge pclk) begin
        // if (mt9d111_cs==ACTIVE_DATA_TIME)
            mseq8g <= mseqf8_G(mseq8g);
    end
    
    // Blue のM系列符号生成
    always @(posedge pclk) begin
        // if (mt9d111_cs==ACTIVE_DATA_TIME)
            mseq8b <= mseqf8_B(mseq8b);
    end
    
    // d 出力のODD とEVEN を示す
    always @(posedge pclk) begin
        if (mt9d111_cs==ACTIVE_DATA_TIME)
            dout_is_even <= ~dout_is_even;
        else
            dout_is_even <= 1'b0;
    end
    
    // d 出力、レーシングを防ぐためにpclk よりも出力を遅らせる
    always @(posedge pclk) begin
        if (mt9d111_cs==ACTIVE_DATA_TIME) begin
            if (dout_is_even)
                d <= #1 {mseq8g[4:2], mseq8b[7:3]};
            else
                d <= #1 {mseq8r[7:3], mseq8g[7:5]};
        end
    end

endmodule

`default_nettype wire


最後に、テストベンチの mt9d111_inf_axi_stream_tb.v を下に示す。

`default_nettype none

`timescale 100ps / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   09:51:18 12/31/2012
// Design Name:   mt9d111_inf_axi_master
// Module Name:   D:\HDL\FndtnISEWork\Zynq-7000\ZedBoard\test\VDMA_test\mt9d111_inf_axi_stream_tb.v
// Project Name:  mt9d111_inf_axi_master
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: mt9d111_inf_axi_master
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module mt9d111_inf_axi_stream_tb;

    wire s2mm_aclk;
    wire s2mm_prmry_reset;
    wire s2mm_fsync;
    wire init_done;
    wire pclk_from_pll;
    wire pclk;
    wire href;
    wire vsync;
    wire [7:0] cam_data;
    wire xck;
    wire standby;
    wire pfifo_overflow;
    wire pfifo_underflow;
    
    wire [23:0] m_axis_tdata;
    wire [2:0] m_axis_tstrb;
    wire m_axis_tvalid;
    wire m_axis_tready;
    wire m_axis_tlast;

    // Instantiate the Unit Under Test (UUT)
    mt9d111_inf_axi_stream # (
        .C_M_AXIS_DATA_WIDTH(24)
    ) uut (
        .s2mm_aclk(s2mm_aclk), 
        .s2mm_prmry_reset(s2mm_prmry_reset), 
        .s2mm_fsync(s2mm_fsync),
        .m_axis_tdata(m_axis_tdata), 
        .m_axis_tstrb(m_axis_tstrb), 
        .m_axis_tvalid(m_axis_tvalid), 
        .m_axis_tready(m_axis_tready), 
        .m_axis_tlast(m_axis_tlast), 
        .init_done(init_done), 
        .pclk_from_pll(pclk_from_pll), 
        .pclk(pclk), 
        .xck(xck), 
        .href(href), 
        .vsync(vsync), 
        .cam_data(cam_data), 
        .standby(standby), 
        .pfifo_overflow(pfifo_overflow), 
        .pfifo_underflow(pfifo_underflow)
    );
    
    assign init_done = 1'b1;

    // pclk_from_pll のインスタンス
    clk_gen #(
        .CLK_PERIOD(278),    // 27.8nsec, 約36MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) pclk_from_pll_i (
        .clk_out(pclk_from_pll)
    );
    
    // s2mm_prmry_reset のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(s2mm_prmry_reset)
    );
    
    // MT9D111 モデル
    mt9d111_model #(
        .HORIZONTAL_PIXELS(800),
        .VERTICAL_LINES(600),
        .HBLANK_REG(174),
        .VBLANK_REG(16),
        .PCLK_DELAY(1)
    ) mt9d111_model_i (
        .xck(xck),
        .pclk(pclk),
        .href(href),
        .vsync(vsync),
        .d(cam_data),
        .scl(1'b1),
        .sda(),
        .standby(standby)
    );

    // Instantiate the Unit Under Test (UUT_slave)
    axi4s_slave_bfm #(
        .C_M_AXIS_DATA_WIDTH(24),
        .TREADY_RANDOM_WAIT(1) // m_axis_treadyにランダムなWaitを発生させる=1, Waitしない=0
    ) uut_slave (
        .ACLK(s2mm_aclk), 
        .ARESETN(~s2mm_prmry_reset), 
        .m_axis_tdata(m_axis_tdata), 
        .m_axis_tstrb(m_axis_tstrb), 
        .m_axis_tvalid(m_axis_tvalid), 
        .m_axis_tready(m_axis_tready), 
        .m_axis_tlast(m_axis_tlast)
    );
      
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire


  1. 2013年06月02日 04:29 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:0

カメラ・インターフェース用AXI4-Stream IPの作製4(HDLソース1)

カメラ・インターフェース用AXI4-Stream IPの作製3(シミュレーション)”でシミュレーションしたHDLソースを貼っておきます。なお、バグがあったら予告なく修正します。

まずは、mt9d111_cam_conts.v から下に示す。

// MT9D111 Camera Controller for AXI4-Stream
// mt9d111_cam_contss.v
// 2013/05/29
// 

`default_nettype none

module mt9d111_cam_conts (
    input    wire    pclk,
    input    wire    preset,
    input    wire    pclk_from_pll,
    output    wire    xclk,
    input    wire    line_valid,
    input    wire    frame_valid,
    input    wire    [7:0]    cam_data,
    output    wire    standby,
    output    wire    s2mm_fsync,
    output    wire    pfifo_empty,
    output    wire    pfifo_almost_empty,
    input    wire    pfifo_rd_en,
    output    wire    [24:0]    pfifo_dout,
    output    wire    pfifo_overflow,
    output    wire    pfifo_underflow
);
    
    reg        line_valid_1d, line_valid_2d;
    reg        frame_valid_1d, frame_valid_2d;
    reg        [7:0]    cam_data_1d;
    reg        line_valid_1d_odd;
    reg        line_v_1d_odd_1d, line_v_1d_odd_2d, line_v_1d_odd_3d;
    reg        [23:0]    rgb565, rgb565_2d;
    wire    [24:0]    pfifo_din;
    wire    pfifo_full, pfifo_almost_full;
    reg        s2mm_fsync_node;
    
    assign standby = 1'b0;
    
    // MT9D111 へのクロックを出力 (xclk)
    ODDR #(
        .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
        .INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
        .SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
    ) ODDR_inst (
        .Q(xclk), // 1-bit DDR output
        .C(pclk_from_pll), // 1-bit clock input
        .CE(1'b1), // 1-bit clock enable input
        .D1(1'b1), // 1-bit data input (positive edge)
        .D2(1'b0), // 1-bit data input (negative edge)
        .R(1'b0), // 1-bit reset
        .S(1'b0) // 1-bit set
    );
    
    // 入力信号を一旦ラッチする
    always @(posedge pclk) begin
        if (preset) begin
            line_valid_1d <=    1'b0;
            line_valid_2d <=     1'b0;
            frame_valid_1d <=    1'b0;
            frame_valid_2d <=     1'b0;
            cam_data_1d <=        8'd0;
        end else begin
            line_valid_1d <=    line_valid;
            line_valid_2d <=     line_valid_1d;
            frame_valid_1d <=    frame_valid;
            frame_valid_2d <=    frame_valid_1d;
            cam_data_1d <=        cam_data;
        end
    end
    
    // line_valid_1d が偶数か奇数かをカウント
    always @(posedge pclk) begin
        if (preset)
            line_valid_1d_odd <= 1'b0;
        else begin
            if (line_valid_1d)
                line_valid_1d_odd <= ~line_valid_1d_odd;
            else
                line_valid_1d_odd <= 1'b0;
        end
    end
    
    // rgb565でラッチしているので、line_valid_1d_odd を1クロック遅延する
    always @(posedge pclk) begin
        if (preset) begin
            line_v_1d_odd_1d <= 1'b0;
            line_v_1d_odd_2d <= 1'b0;
            line_v_1d_odd_3d <= 1'b0;
        end else begin
            line_v_1d_odd_1d <= line_valid_1d_odd;
            line_v_1d_odd_2d <= line_v_1d_odd_1d;
            line_v_1d_odd_3d <= line_v_1d_odd_2d;
        end
    end
    
    // rgb565
    always @(posedge pclk) begin
        if (preset)
            rgb565 <= 24'd0;
        else begin
            if (~line_valid_1d_odd)
                rgb565[23:13] <= {cam_data_1d[7:3], 3'b000, cam_data_1d[2:0]};    // cam_data_1d = R7 R6 R5 R4 R3 G7 G6 G5
            else
                rgb565[12:0] <= {cam_data_1d[7:5], 2'b00, cam_data_1d[4:0], 3'b000};    // cam_data_1d = G4 G3 G2 B7 B6 B5 B4 B3
        end
    end
    
    // rgb565_2d, データを2クロック遅らせて、tlastを最後から1つ前のデータに付加する
    always @(posedge pclk) begin
        if (preset)
            rgb565_2d <= 24'd0;
        else begin
            if (line_v_1d_odd_1d)
                rgb565_2d <= rgb565;
        end
    end
    
    assign pfifo_din = {((line_valid_2d & ~line_valid_1d) ? 1'b1 : 1'b0), rgb565_2d}; // line_valid_1d の立ち下がりエッジ
    
    // pixel FIFO をインスタンスする
    pixel_fifo pfifo (
        .clk(pclk),
        .srst(preset), // input rst
        .din(pfifo_din), // input [24 : 0] din
        .wr_en(line_v_1d_odd_3d), // input wr_en
        .rd_en(pfifo_rd_en), // input rd_en
        .dout(pfifo_dout), // output [24 : 0] dout
        .full(pfifo_full), // output full
        .almost_full(pfifo_almost_full), // output almost_full
        .overflow(pfifo_overflow), // output overflow
        .empty(pfifo_empty), // output empty
        .almost_empty(pfifo_almost_empty), // output almost_empty
        .underflow(pfifo_underflow) // output underflow
    );

    // s2mm_fsync
    always @(posedge pclk) begin
        if (preset)
            s2mm_fsync_node <= 1'b0;
        else begin
            if (frame_valid_1d==1'b1 && frame_valid_2d==1'b0) // frame_valid_1d の立ち上がり
                s2mm_fsync_node <= 1'b1;
            else
                s2mm_fsync_node <= 1'b0;
        end
    end
    assign s2mm_fsync = s2mm_fsync_node;
endmodule

`default_nettype wire


トップのVHDLファイルの mt9d111_inf_axi_stream.vhd を下に示す。

-------------------------------------------------------------------------------
-- Filename:          mt9d111_inf_axi_stream.vhd
-- Description: An AXI Stream interface
--
-- VHDL-Standard:   VHDL'93
-------------------------------------------------------------------------------
-- Structure:
--                  mt9d111_inf_axi_stream.vhd
--
-- AXI4-Stream データの出力は2クロックに1回とする
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;

--library unisim;
--use unisim.vcomponents.all;

-------------------------------------------------------------------------------
entity mt9d111_inf_axi_stream is
  generic(
        -- Master AXI Stream Data Width
        C_M_AXIS_DATA_WIDTH : integer range 8 to 1024 := 24
    );
  port (
        s2mm_aclk    : out std_logic;
        s2mm_prmry_reset : in std_logic;
        s2mm_fsync        : out std_logic;

        -- Master Stream Ports
    --    m_axis_aresetn : out std_logic;
        m_axis_tdata   : out std_logic_vector(C_M_AXIS_DATA_WIDTH-1 downto 0);
        m_axis_tstrb   : out std_logic_vector((C_M_AXIS_DATA_WIDTH/8)-1 downto 0);
        m_axis_tvalid  : out std_logic;
        m_axis_tready  : in  std_logic;
        m_axis_tlast   : out std_logic;
        
        init_done        : in std_logic; -- PS部の初期化終了
        
        -- MT9D111 Camera Interface
        pclk_from_pll    : in    std_logic;    -- PLLからMT9D111のxck に出力するクロック
        pclk            : in     std_logic;    -- MT9D111からのピクセルクロック入力
        xck                : out    std_logic;    -- MT9D111へのピクセルクロック出力
        href            : in     std_logic;
        vsync            : in    std_logic;
        cam_data        : in    std_logic_vector(7 downto 0);
        standby            : out    std_logic;    -- STANDBY出力(ディスエーブル、0固定)
        pfifo_overflow    : out    std_logic;    -- pfifo overflow
        pfifo_underflow    : out    std_logic    -- pfifo underflow
    );

end mt9d111_inf_axi_stream;

-------------------------------------------------------------------------------
-- Architecture
-------------------------------------------------------------------------------
architecture implementation of mt9d111_inf_axi_stream is
signal pfifo_empty : std_logic;
signal pfifo_almost_empty : std_logic;
signal pfifo_rd_en : std_logic;
signal pfifo_dout : std_logic_vector(24 downto 0);
signal rst_1d, rst_2d, reset : std_logic;
signal tlast : std_logic;
type tlast_state is (idle_tlast, tlast_assert);
signal tlast_sm : tlast_state;
signal tvalid : std_logic;
type tvalid_state is (idle_tvalid, tvalid_assert);
signal tvalid_sm : tvalid_state;
component mt9d111_cam_conts
    port (
        pclk                : in    std_logic;
        preset                : in    std_logic;
        pclk_from_pll        : in    std_logic;
        xclk                : out    std_logic;
        line_valid            : in    std_logic;
        frame_valid            : in    std_logic;
        cam_data            : in    std_logic_vector(7 downto 0);
        standby                : out    std_logic;
        s2mm_fsync            : out    std_logic;
        pfifo_empty            : out    std_logic;
        pfifo_almost_empty    : out    std_logic;
        pfifo_rd_en            : in    std_logic;
        pfifo_dout            : out    std_logic_vector(24 downto 0);
        pfifo_overflow        : out    std_logic;
        pfifo_underflow        : out    std_logic
    );
end component;
begin
    s2mm_aclk <= pclk;
    
    mt9d111_camc : mt9d111_cam_conts port map(
        pclk                => pclk,
        preset                => reset,
        pclk_from_pll        => pclk_from_pll,
        xclk                => xck,
        line_valid            => href,
        frame_valid            => vsync,
        cam_data            => cam_data,
        standby                => standby,
        s2mm_fsync            => s2mm_fsync,
        pfifo_empty            => pfifo_empty,
        pfifo_almost_empty    => pfifo_almost_empty,
        pfifo_rd_en            => pfifo_rd_en,
        pfifo_dout            => pfifo_dout,
        pfifo_overflow        => pfifo_overflow,
        pfifo_underflow        => pfifo_underflow
    );
    
    -- Synchronization by pclk
    process(pclk) begin
        rst_1d <= s2mm_prmry_reset or not init_done;
        rst_2d <= rst_1d;
    end process;
    reset <= rst_2d;
        
    -- State Machine for tlast
    process(pclk) begin
        if pclk'event and pclk='1' then
            if reset='1' then
                tlast_sm <= idle_tlast;
                tlast <= '0';
            else 
                case tlast_sm is
                    when idle_tlast =>
                        if pfifo_dout(24)='1' and tvalid='1' and m_axis_tready='1' and pfifo_empty='0' then
                            tlast_sm <= tlast_assert;
                            tlast <= '1';
                        end if;
                    when tlast_assert =>
                        if m_axis_tready='1' then
                            tlast_sm <= idle_tlast;
                            tlast <= '0';
                        end if;
                end case;
            end if;
        end if;
    end process;
    m_axis_tlast <= tlast;
    
    -- State Machine for tvalid            
    process(pclk) begin
        if pclk'event and pclk='1' then
            if reset='1' then
                tvalid_sm <= idle_tvalid;
                tvalid <= '0';
            else
                case tvalid_sm is
                    when idle_tvalid =>
                        if pfifo_empty='0' then -- pfifo にデータがある
                            tvalid_sm <= tvalid_assert;
                            tvalid <= '1';
                        end if;
                    when tvalid_assert =>
                        if m_axis_tready='1' and pfifo_almost_empty='1' then -- 次のデータがない場合はidle に戻る
                            tvalid_sm <= idle_tvalid;
                            tvalid <= '0';
                        end if;
                end case;
            end if;
        end if;
    end process;
    m_axis_tvalid <= tvalid;
    
    pfifo_rd_en <= tvalid and m_axis_tready;
    m_axis_tdata <= pfifo_dout(23 downto 0);
    m_axis_tstrb <= (others => '1');
    
end implementation;


pixel_fifo は、”カメラ・インターフェース用AXI4-Stream IPの作製2(pixel_fifo の生成)”を見て下さい。

  1. 2013年06月02日 04:27 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:0

カメラ・インターフェース用AXI4-Stream IPの作製3(シミュレーション)

前回、pixel_fifo を生成した。今回は、HDLファイルを書いて、シミュレーションを行った。書いたHDLファイルは、カメラのコントロールをする mt9d111_cam_conts.v と、その上の階層で、AXI4-Stream IPの mt9d111_inf_axi_stream.vhd、AXI4-Stream Slave のBFM(と言ってもTREADYに応答するだけ)axi4s_slave_BFM.vhd、元々作ってあったMT9D111カメラモジュールのモデルの mt9d111_model.v、それにテストベンチの mt9d111_inf_axi_stream_tb.v だ。
これをProject Navigator のプロジェクトに入れて、ISimでシミュレーションを行った。下に、Project Navigator の画面を示す。
Cam_AXI4_Stream_IP_8_130601.png

シミュレーション波形を示す前に、カメラ・インターフェースAXI4-Stream IPの仕様を下に列挙する。

・MT9D111のvsync の立ち上がりで、s2mm_sync を1にアサートしている。
・MT9D111からのcam_data はRGB565フォーマットなので、8バイト2つ、2クロック分で1つのRGB565データとなる。
・RGB565は、RGB各8ビット長の24ビットに拡張して、pixel_fifo(同期FIFO)に入力する。
・pixel_fifo には、もう1ビットビットを用意してある。それは、AXI4-Stream のTLASTをアサートするために、ラインの最後のデータの1つ前のデータの時に 1 をセットし、その他の場合は 0 をセットする。
・AXI4-Stream プロトコル回路は、pixel_fifo にデータが保存されている場合は、TVALIDをアサートしてストリーム転送を行う。


ISimでシミュレーションした結果を下に示す。(TREADYが1に固定の場合)
Cam_AXI4_Stream_IP_9_130601.png

TLASTがアサートされている付近を下に示す。TLASTはラインの最後のデータであることを示す。
Cam_AXI4_Stream_IP_10_130601.png

TREADYをM系列を使って、アサートするかどうかを決定した場合のシミュレーション波形を下に示す。TREADYが 0 の場合は転送されないので、TVALIDがアサートされる範囲が広いのがわかる。
Cam_AXI4_Stream_IP_11_130601.png

  1. 2013年06月01日 10:01 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:0

カメラ・インターフェース用AXI4-Stream IPの作製2(pixel_fifo の生成)

カメラ・インターフェース用AXI4-Stream IPの作製1(仕様の検討)”で同期FIFOが必要になったので、pixel_fifo を生成した。

pixel_fifo には、RGBそれぞれ8ビットの合計24ビットと、TLASTのために1ビットの合計25ビットを用意する。TLASTのアサートのために同期FIFOのデータの出口で1クロック分、ピクセルデータを遅延することにした。

それでは、CoreGen で作る pixel_fifo の設定を見ていこう。
Cam_AXI4_Stream_IP_1_130530.png

・同期FIFOで、Block RAMを選択した。
Cam_AXI4_Stream_IP_2_130530.png

・25ビット長、512深度を選択した。First-Word Fall-Through を選択した。
Cam_AXI4_Stream_IP_3_130530.png

・almost_full, almost_empty, overflow, underflow を選択した。
Cam_AXI4_Stream_IP_4_130530.png

・リセットは同期リセット(SRST) に変更した。(2013/05/30)
Cam_AXI4_Stream_IP_5_130530.png

・ここもデフォルト値
Cam_AXI4_Stream_IP_6_130530.png

・Block RAMのリソースは、18K BRAMを1個使用する。これで、Generateした。
Cam_AXI4_Stream_IP_7_130530.png

これで、pixel_fifo が生成された。
  1. 2013年05月30日 05:13 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:0

カメラ・インターフェース用AXI4-Stream IPの作製1(仕様の検討)

カメラ、ビデオ表示カスタムIPを AX4-Stream に変更2(カメラ・インターフェースIP)”で仕様を検討したカメラ・インターフェース用のAXI4-Stream IPを作製しようと思う。(カテゴリをAXI4-Stream IPの作製に変更しました)
(AXI4-Streamでは、仕様上、TVALIDはTREADYを待ってはダメだという指摘を受けたので、カメラ・インターフェース用AXI4-Stream IPの仕様を変更します。教えて頂いた ikvzm さんありがとうございました。)

下にカメラ・インターフェース用のAXI4-Stream IPの仕様を示す。

1.カメラ・インターフェース用AXI4-Stream IPは、FIFOを持たない。FIFOはAXI VDMA の C_S2MM_LINEBUFFER_DEPTH (”LogiCORE IP AXI Video Direct Memory Access v5.04a Product Guide PG020 December 18, 2012”の86ページの”C_S2MM_LINEBUFFER_DEPTH”参照)でFIFO深度を決定できるFIFOを使用する。つまり、AXI VDMAの s2mm_buffer_full がアサートされたらデータ転送が間に合わずにエラーということだ。これは、カメラからくるデータの転送速度は決まっているので、これ以下のストリーム転送しか出来ない場合は当然エラーとなる。

1.改 カメラ・インターフェース用AXI4-Stream IPは同期FIFOを持ち、少なくとも同期FIFOがEMPTYでなくなった状態まで、カメラ・データが溜まった後で、TVALIDをアサートする。

2.1.の前提があるので、AXI4-Stream のTREADY の途中でのディアサートは想定しない。最初にカメラのフレームを転送する最初には、TREADY を確認しアサートされていたら、AXI4-Stream でカメラのフレームの画像データを転送する。TREADY がアサートされていない場合は、そのフレームはAXI4-Stream で転送しない。

2.改 同期FIFOを入れることになったので、AXI4-Stream のTREADY の途中でのディアサートにも問題なく対応できる。ただし、待てる範囲はFIFOの容量による。

3.AXI VDMAの s_axis_s2mm_aclk は入力で、カメラ・インターフェース用のAXI4-Stream IPからクロックを入力することができる。カメラからのPCLKを入力することで、カメラ・インターフェース用のAXI4-Stream IPのクロックドメインはPCLKの1つとすることができる。

4.カメラからのデータはRGB565 なので、2クロックで1つのピクセルデータが転送される。よって、AXI4-Stream のTVALID のアサートは s_axis_s2mm_aclk の2クロックについて1回となる。


・最初に、”AR# 37425 12.3 EDK、12.3 ISE - カスタム AXI IP コアの作成方法”から ar37425.zip をダウンロードする。

・ar37425.zip を解凍して、ar37425/axi_stream フォルダをテンプレートとして、カメラ・インターフェース用のAXI4-Stream IP (mt9d111_inf_axi_stream.vhd) を作る。
mt9d111_inf_axi_stream.vhd の entity を下に示す。(2013/06/01:修正)

entity mt9d111_inf_axi_stream is
  generic(
        -- Master AXI Stream Data Width
        C_M_AXIS_DATA_WIDTH : integer range 8 to 1024 := 24
    );
  port (
        s2mm_aclk    : out std_logic;
        s2mm_prmry_reset : in std_logic;
        s2mm_fsync        : out std_logic;

        -- Master Stream Ports
    --    m_axis_aresetn : out std_logic;
        m_axis_tdata   : out std_logic_vector(C_M_AXIS_DATA_WIDTH-1 downto 0);
        m_axis_tstrb   : out std_logic_vector((C_M_AXIS_DATA_WIDTH/8)-1 downto 0);
        m_axis_tvalid  : out std_logic;
        m_axis_tready  : in  std_logic;
        m_axis_tlast   : out std_logic;
        
        init_done        : in std_logic; -- PS部の初期化終了
        
        -- MT9D111 Camera Interface
        pclk_from_pll    : in    std_logic;    -- PLLからMT9D111のxck に出力するクロック
        pclk            : in     std_logic;    -- MT9D111からのピクセルクロック入力
        xck                : out    std_logic;    -- MT9D111へのピクセルクロック出力
        href            : in     std_logic;
        vsync            : in    std_logic;
        cam_data        : in    std_logic_vector(7 downto 0);
        standby            : out    std_logic;    -- STANDBY出力(ディスエーブル、0固定)
        pfifo_overflow    : out    std_logic;    -- pfifo overflow
        pfifo_underflow    : out    std_logic    -- pfifo underflow
    );

end mt9d111_inf_axi_stream;


これで、カメラ・インターフェース用のAXI4-Stream IPを作っていこうと思う。クロックドメインを変換する非同期FIFOは実装しないので、比較的簡単に実装できそうだ。
  1. 2013年05月29日 05:02 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:4