FC2カウンター FPGAの部屋 カメラ-AXI4-Stream出力IPの作製4(シミュレーション用HDLソースの公開)
FC2ブログ

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

FPGAの部屋

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

カメラ-AXI4-Stream出力IPの作製4(シミュレーション用HDLソースの公開)

”カメラ-AXI4-Stream出力IPの作製3(論理合成用HDLソースの公開)”の続き。

今回は、カメラ-AXI4-Stream出力IPのシミュレーション用 Verilog HDL のソースコードを貼っておく。

まずは、mt9d111_inf_axis_tb.v から貼っておく。

`default_nettype none

`timescale 100ps / 1ps

// mt9d111_inf_axis_tb.v
// 2015/05/11
//

module mt9d111_inf_axis_tb;
    parameter integer C_S_AXI_LITE_ADDR_WIDTH = 9; // Address width of the AXI Lite Interface
    parameter integer C_S_AXI_LITE_DATA_WIDTH = 32; // Data width of the AXI Lite Interface

    parameter integer C_M_AXIS_DATA_WIDTH = 32; // AXI4 Stream Master Interface
    
    parameter DELAY = 1;
    
    // Inputs
    wire ACLK;
    wire ARESETN;
    wire pclk_from_pll;
    wire pclk;
    wire href;
    wire vsync;
    wire [7:0] cam_data;
    wire m_axis_tready;

    // Outputs
    wire xck;
    wire standby;
    wire pfifo_overflow;
    wire pfifo_underflow;
    wire [C_M_AXIS_DATA_WIDTH-1:0] m_axis_tdata;
    wire [(C_M_AXIS_DATA_WIDTH/8)-1:0] m_axis_tstrb;
    wire m_axis_tvalid;
    wire m_axis_tlast;
    wire m_axis_tuser;

    // AXI Lite Write Address Channel
    reg                                        s_axi_lite_awvalid = 1'b0;
    wire                                    s_axi_lite_awready;
    reg        [C_S_AXI_LITE_ADDR_WIDTH-1: 0]    s_axi_lite_awaddr = 0;
    reg        [3-1:0]                            s_axi_lite_awport = 1'b0;

    // AXI Lite Write Data Channel
    reg                                        s_axi_lite_wvalid =1'b0;
    wire                                    s_axi_lite_wready;
    reg        [C_S_AXI_LITE_DATA_WIDTH-1: 0]    s_axi_lite_wdata = 0;
    
    // AXI Lite Write Response Channel
    wire    [1:0]                            s_axi_lite_bresp;
    wire                                    s_axi_lite_bvalid;
    reg                                        s_axi_lite_bready = 1'b0;

    // AXI Lite Read Address Channel
    reg                                        s_axi_lite_arvalid = 1'b0;
    wire                                    s_axi_lite_arready;
    reg        [C_S_AXI_LITE_ADDR_WIDTH-1: 0]    s_axi_lite_araddr = 1'b0;
    reg        [3-1:0]                            s_axi_lite_arport = 0;
    
    // AXI Lite Read Data Channel
    wire                                    s_axi_lite_rvalid;
    reg                                        s_axi_lite_rready = 1'b0;
    wire    [C_S_AXI_LITE_DATA_WIDTH-1: 0]    s_axi_lite_rdata;
    wire    [1:0]                            s_axi_lite_rresp;

    integer i;

    // Instantiate the Unit Under Test (UUT)
    mt9d111_inf_axis # (
        .C_M_AXIS_DATA_WIDTH(C_M_AXIS_DATA_WIDTH)
    ) uut (
        .s_axi_lite_aclk(ACLK),
        .m_axis_aclk(ACLK), 
        .axi_resetn(ARESETN), 
        
        .s_axi_lite_awvalid(s_axi_lite_awvalid),
        .s_axi_lite_awready(s_axi_lite_awready),
        .s_axi_lite_awaddr(s_axi_lite_awaddr),
        //.s_axi_lite_awport(s_axi_lite_awport),
        .s_axi_lite_wvalid(s_axi_lite_wvalid),
        .s_axi_lite_wready(s_axi_lite_wready),
        .s_axi_lite_wdata(s_axi_lite_wdata),
        .s_axi_lite_bresp(s_axi_lite_bresp),
        .s_axi_lite_bvalid(s_axi_lite_bvalid),
        .s_axi_lite_bready(s_axi_lite_bready),
        .s_axi_lite_arvalid(s_axi_lite_arvalid),
        .s_axi_lite_arready(s_axi_lite_arready),
        .s_axi_lite_araddr(s_axi_lite_araddr),
        //.s_axi_lite_arport(s_axi_lite_arport),
        .s_axi_lite_rvalid(s_axi_lite_rvalid),
        .s_axi_lite_rready(s_axi_lite_rready),
        .s_axi_lite_rdata(s_axi_lite_rdata),
        .s_axi_lite_rresp(s_axi_lite_rresp),

        .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),
        .m_axis_tuser(m_axis_tuser),
        
        .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 m_axis_tready = 1'b1;

    // ACLK のインスタンス
    clk_gen #(
        .CLK_PERIOD(100),    // 10nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(ACLK)
    );

    // 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)
    );

    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ARESETN)
    );

    // MT9D111 モデル
    mt9d111_model #(
        // .HORIZONTAL_PIXELS(800),
        // .VERTICAL_LINES(600),
        // .HBLANK_REG(174),
        // .VBLANK_REG(16),
        .HORIZONTAL_PIXELS(50),
        .VERTICAL_LINES(10),
        .HBLANK_REG(10),
        .VBLANK_REG(5),
        .PCLK_DELAY(1)
    ) mt9d111_model_i (
        .xck(xck),
        .pclk(pclk),
        .href(href),
        .vsync(vsync),
        .d(cam_data),
        .scl(1'b1),
        .sda(),
        .standby(standby)
    );

    initial begin
        // Initialize Inputs
        s_axi_lite_awaddr = 0;
        s_axi_lite_awport = 0;
        s_axi_lite_wvalid = 0;
        s_axi_lite_wdata = 0;
        s_axi_lite_wvalid = 0;
        s_axi_lite_bready = 0;
        s_axi_lite_araddr = 0;
        s_axi_lite_arport = 0;
        s_axi_lite_arvalid = 0;
        s_axi_lite_rready = 0;

        // Wait Reset rising edge
        @(posedge ARESETN);
        
        for (i=0; i<10; i=i+1) begin
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end
        
        // Add stimulus here
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_0000, 32'h1200_0000);
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_RADC1(32'h0000_0000);
        #DELAY;
        
        // @(posedge ACLK);    // 次のクロックへ        
        // #DELAY;
        // AXI_MASTER_WADC2(32'h0000_0004, 32'h0000_0001);    // one_shot mode
        // @(posedge ACLK);    // 次のクロックへ        
        // #DELAY;
        // AXI_MASTER_RADC2(32'h0000_0004);
        
        // @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_WADC2(32'h0000_0004, 32'h0000_0003); // one_shot trigger
  //       @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_RADC2(32'h0000_0004);
        
        // @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_WADC2(32'h0000_0004, 32'h0000_0003); // one_shot trigger
  //       @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_RADC2(32'h0000_0004);
    end
            
    // Write Transcation 1
    task AXI_MASTER_WADC1;
        input    [C_S_AXI_LITE_ADDR_WIDTH-1:0]    awaddr;
        input    [C_S_AXI_LITE_DATA_WIDTH-1:0]    wdata;
        begin
            s_axi_lite_awaddr    = awaddr;
            s_axi_lite_awvalid    = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_awvalid = 1'b0;
            s_axi_lite_wdata = wdata;
            s_axi_lite_wvalid = 1'b1;
            @(posedge ACLK);    // 次のクロックへ, s_axi_lite_wready は常に 1
            
            #DELAY;
            s_axi_lite_wvalid = 1'b0;
            s_axi_lite_bready = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
                
            s_axi_lite_bready = 1'b0;
        end
    endtask

    // Write Transcation 2
    task AXI_MASTER_WADC2;
        input    [C_S_AXI_LITE_ADDR_WIDTH-1:0]    awaddr;
        input    [C_S_AXI_LITE_DATA_WIDTH-1:0]    wdata;
        begin
            s_axi_lite_awaddr    = awaddr;
            s_axi_lite_awvalid    = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_awvalid = 1'b0;
            s_axi_lite_wdata = wdata;
            s_axi_lite_wvalid = 1'b1;
            @(posedge ACLK);    // 次のクロックへ, s_axi_lite_wready は常に 1
            
            #DELAY;
            s_axi_lite_wvalid = 1'b0;        
            @(posedge ACLK);    // 次のクロックへ
            
            #DELAY;        
            s_axi_lite_bready = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
                
            s_axi_lite_bready = 1'b0;
        end
    endtask
    
    // Read Transcation 1    
    task AXI_MASTER_RADC1;
        input    [31:0]    araddr;
        begin
            s_axi_lite_araddr    = araddr;
            s_axi_lite_arvalid     = 1'b1;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_araddr    = 0;
            s_axi_lite_arvalid     = 1'b0;
            s_axi_lite_rready = 1'b1;

            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            s_axi_lite_rready = 1'b0;
        end
    endtask
    
    // Read Transcation 2   
    task AXI_MASTER_RADC2;
        input    [31:0]    araddr;
        begin
            s_axi_lite_araddr    = araddr;
            s_axi_lite_arvalid     = 1'b1;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_araddr    = 0;
            s_axi_lite_arvalid     = 1'b0;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            s_axi_lite_rready = 1'b1;

            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            s_axi_lite_rready = 1'b0;
        end
    endtask
    
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


次に、カメラの動作モデルの mt9d111_model.v を貼っておく。

// 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'b1,
    output    reg        [7:0]    d = 8'd0,
    input    wire    scl,
    inout    wire    sda,
    input    wire    standby
);

    parameter    [2:0]    FRAME_START_BLANKING =    3'b000,
                        ACTIVE_DATA_TIME =        3'b001,
                        HORIZONTAL_BLANKING =    3'b011,
                        FRAME_END_BLANKING =    3'b010,
                        VERTICAL_BLANKING =        3'b110;
                        
    reg        [2:0]    mt9d111_cs = VERTICAL_BLANKING;
    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        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)
            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
    
    // Active Data Time Counter
    always @(posedge pclk) 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
    
    // Horizontal Blanking Counter
    always @(posedge pclk) 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
    
    // Frame Valid Time Counter
    always @(posedge pclk) begin
        if (mt9d111_cs==ACTIVE_DATA_TIME && adt_count==0)
            fvt_count <= fvt_count - 16'd1;
        else if (mt9d111_cs==FRAME_END_BLANKING)
            fvt_count <= VERTICAL_LINES - 1;
    end
    
    // Vertical Blanking Counter
    always @(posedge pclk) 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
    
    // 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

  1. 2015年05月13日 04:15 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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