FC2カウンター FPGAの部屋 カメラ・インターフェース用AXI4-Stream IPの作製5(HDLソース2)
fc2ブログ

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

FPGAの部屋

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

カメラ・インターフェース用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

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/2479-625de500
この記事にトラックバックする(FC2ブログユーザー)