FC2カウンター FPGAの部屋 2013年11月26日
FC2ブログ

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

FPGAの部屋

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

AXI4 Master アクセスのラプラシアン・フィルタ IP4(シミュレーション2)

AXI4 Master アクセスのラプラシアン・フィルタ IP3(シミュレーション)”の続き。

前回の AXI4 Master アクセスのラプラシアン・フィルタ IP には欠点があった。それは、ラプラシアン・フィルタ結果をDDR3 SDRAMにWrite するときに終了をブロックしていないことだ。もし、AXI4 Write Transaction の終了、次のAXI4 Read Transaction より長く掛かってしまった場合は、write_data FIFOが溢れてしまう危険がある。現在のところwrite_data FIFOがFULLなった時ののデータ停止は行っていない。つまり手抜きです。。。
その代わり、AXI4 Write Transaction の終了までブロックするようにメインのステートマシンを変更した。メインのステートマシンを下に示す。

    // Main State Machine
    always @(posedge ACLK) begin : proc_main_sm
       if(~ARESETN) begin
            main_cs <= IDLE_MAIN;
       end else begin
            case (main_cs)
                IDLE_MAIN :
                    if (init_done & I_ap_start) begin
                        main_cs <= LINE_READ_AND_Y_1ST;
                    end
                LINE_READ_AND_Y_1ST :
                    if (line_read_y_done) begin
                        main_cs <= LINE_READ_AND_Y;
                    end
                LINE_READ_AND_Y :
                    if (line_read_y_done) begin
                        main_cs <= LAP_FILTER_AND_WRITE;
                    end
                LAP_FILTER_AND_WRITE :
                    if (lap_frame_end) begin
                        main_cs <= AP_DONE_ACTIVE;
                    end else if (lap_line_end) begin
                        main_cs <= WAIT_AXI_WRITE_TRANSACTION;
                    end
                WAIT_AXI_WRITE_TRANSACTION :    // Write Transaction が完了するまでWait
                    if (write_fifo_empty)
                         main_cs <= LINE_READ_AND_Y;
                AP_DONE_ACTIVE : begin
                    main_cs <= IDLE_MAIN;
                end

               default : /* default */;
            endcase
       end
    end


WAIT_AXI_WRITE_TRANSACTION ステートが新たに新設したAXI4 Write Transaction の終了までブロックするステートだ。axi_master_inf.v に、write_data FIFO のempty をwrite_clk で同期した write_fifo_empty ポートを増設して、それを見ている。これで write_data が空なのが確認できる。
更に、write_data FIFO の overflow を監視するために、write_fifo_overflow ポートを増やした。これは、write_fifo_full の時に、write_adfifo_wr_ena が来たかどうかを確認している。

下に、AXI4 TransactionのWrite とReadの様子のシミュレーション波形を示す。上の AXI4 Write Transction のデータ・チャネルの長さが、下の AXI4 Read Transaction のデータ・チャネルより長くなっているのが見える。長くなっても、ReadがWriteを待っているのがわかると追う。これは、AXI4 Write データ・チャネルの WREADY をランダムに Waitさせている。これは、AXI4_Slave_BFM の機能だ。
axi4m_lap_filter_14_131126.png

今度は、1フレームのラプラシアン・フィルタに 14.7msec ほどかかっている。全体のシミュレーション波形を示す。write_fifo_overflow はずっと 0 のままである。つまり、FIFOのオーバーフロー・エラーは無い。
axi4m_lap_filter_15_131126.png

因みに、AXI4 Readは、元々ブロックされているので、ブロックする必要はない。これでIPの実装を行うことにする。
下に、テストベンチの lap_filter_axim_tb.v を貼っておく。

`default_nettype none
`timescale 100ps / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date:   18:18:43 11/22/2013
// Design Name:   lap_filter_axim
// Module Name:   D:/HDL/FndtnISEWork/Zynq-7000/ZedBoard/Zed_OOB_Design2_HLS/hw/xps_proj/pcores/lap_filter_axim_v1_00_a/lap_filter_axim/lap_filter_axim_tb.v
// Project Name:  lap_filter_axim
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: lap_filter_axim
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////

module lap_filter_axim_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 DELAY = 1;

    // Inputs
    wire s_axi_lite_aclk;
    wire ACLK;
    wire ARESETN;
    reg s_axi_lite_awvalid;
    reg [8:0] s_axi_lite_awaddr;
    reg s_axi_lite_wvalid;
    reg [31:0] s_axi_lite_wdata;
    reg [3:0] s_axi_lite_wstrb;
    reg s_axi_lite_bready;
    reg s_axi_lite_arvalid;
    reg [8:0] s_axi_lite_araddr;
    reg s_axi_lite_rready;
    wire M_AXI_AWREADY;
    wire M_AXI_WREADY;
    wire [0:0] M_AXI_BID;
    wire [1:0] M_AXI_BRESP;
    wire [0:0] M_AXI_BUSER;
    wire M_AXI_BVALID;
    wire M_AXI_ARREADY;
    wire [0:0] M_AXI_RID;
    wire [31:0] M_AXI_RDATA;
    wire [1:0] M_AXI_RRESP;
    wire M_AXI_RLAST;
    wire [0:0] M_AXI_RUSER;
    wire M_AXI_RVALID;

    // Outputs
    wire s_axi_lite_awready;
    wire s_axi_lite_wready;
    wire [1:0] s_axi_lite_bresp;
    wire s_axi_lite_bvalid;
    wire s_axi_lite_arready;
    wire s_axi_lite_rvalid;
    wire [31:0] s_axi_lite_rdata;
    wire [1:0] s_axi_lite_rresp;
    wire [0:0] M_AXI_AWID;
    wire [31:0] M_AXI_AWADDR;
    wire [7:0] M_AXI_AWLEN;
    wire [2:0] M_AXI_AWSIZE;
    wire [1:0] M_AXI_AWBURST;
    wire M_AXI_AWLOCK;
    wire [3:0] M_AXI_AWCACHE;
    wire [2:0] M_AXI_AWPROT;
    wire [3:0] M_AXI_AWQOS;
    wire [0:0] M_AXI_AWUSER;
    wire M_AXI_AWVALID;
    wire [31:0] M_AXI_WDATA;
    wire [3:0] M_AXI_WSTRB;
    wire M_AXI_WLAST;
    wire [0:0] M_AXI_WUSER;
    wire M_AXI_WVALID;
    wire M_AXI_BREADY;
    wire [0:0] M_AXI_ARID;
    wire [31:0] M_AXI_ARADDR;
    wire [7:0] M_AXI_ARLEN;
    wire [2:0] M_AXI_ARSIZE;
    wire [1:0] M_AXI_ARBURST;
    wire [1:0] M_AXI_ARLOCK;
    wire [3:0] M_AXI_ARCACHE;
    wire [2:0] M_AXI_ARPROT;
    wire [3:0] M_AXI_ARQOS;
    wire [0:0] M_AXI_ARUSER;
    wire M_AXI_ARVALID;
    wire M_AXI_RREADY;
    wire interrupt;
    wire write_fifo_overflow;


    // Instantiate the Unit Under Test (UUT)
    lap_filter_axim # (
            .C_S_AXI_LITE_ADDR_WIDTH(C_S_AXI_LITE_ADDR_WIDTH),
            .C_S_AXI_LITE_DATA_WIDTH(C_S_AXI_LITE_DATA_WIDTH)
        ) uut (
        .s_axi_lite_aclk(s_axi_lite_aclk),
        .M_AXI_ACLK(ACLK),
        .ARESETN(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_wvalid(s_axi_lite_wvalid),
        .s_axi_lite_wready(s_axi_lite_wready),
        .s_axi_lite_wdata(s_axi_lite_wdata),
        .s_axi_lite_wstrb(s_axi_lite_wstrb),
        .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_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_AXI_AWID(M_AXI_AWID),
        .M_AXI_AWADDR(M_AXI_AWADDR),
        .M_AXI_AWLEN(M_AXI_AWLEN),
        .M_AXI_AWSIZE(M_AXI_AWSIZE),
        .M_AXI_AWBURST(M_AXI_AWBURST),
        .M_AXI_AWLOCK(M_AXI_AWLOCK),
        .M_AXI_AWCACHE(M_AXI_AWCACHE),
        .M_AXI_AWPROT(M_AXI_AWPROT),
        .M_AXI_AWQOS(M_AXI_AWQOS),
        .M_AXI_AWUSER(M_AXI_AWUSER),
        .M_AXI_AWVALID(M_AXI_AWVALID),
        .M_AXI_AWREADY(M_AXI_AWREADY),
        .M_AXI_WDATA(M_AXI_WDATA),
        .M_AXI_WSTRB(M_AXI_WSTRB),
        .M_AXI_WLAST(M_AXI_WLAST),
        .M_AXI_WUSER(M_AXI_WUSER),
        .M_AXI_WVALID(M_AXI_WVALID),
        .M_AXI_WREADY(M_AXI_WREADY),
        .M_AXI_BID(M_AXI_BID),
        .M_AXI_BRESP(M_AXI_BRESP),
        .M_AXI_BUSER(M_AXI_BUSER),
        .M_AXI_BVALID(M_AXI_BVALID),
        .M_AXI_BREADY(M_AXI_BREADY),
        .M_AXI_ARID(M_AXI_ARID),
        .M_AXI_ARADDR(M_AXI_ARADDR),
        .M_AXI_ARLEN(M_AXI_ARLEN),
        .M_AXI_ARSIZE(M_AXI_ARSIZE),
        .M_AXI_ARBURST(M_AXI_ARBURST),
        .M_AXI_ARLOCK(M_AXI_ARLOCK),
        .M_AXI_ARCACHE(M_AXI_ARCACHE),
        .M_AXI_ARPROT(M_AXI_ARPROT),
        .M_AXI_ARQOS(M_AXI_ARQOS),
        .M_AXI_ARUSER(M_AXI_ARUSER),
        .M_AXI_ARVALID(M_AXI_ARVALID),
        .M_AXI_ARREADY(M_AXI_ARREADY),
        .M_AXI_RID(M_AXI_RID),
        .M_AXI_RDATA(M_AXI_RDATA),
        .M_AXI_RRESP(M_AXI_RRESP),
        .M_AXI_RLAST(M_AXI_RLAST),
        .M_AXI_RUSER(M_AXI_RUSER),
        .M_AXI_RVALID(M_AXI_RVALID),
        .M_AXI_RREADY(M_AXI_RREADY),
        .interrupt(interrupt),
        .write_fifo_overflow(write_fifo_overflow)
    );

    initial begin : initial_state
        integer i;

        // Initialize Inputs
        s_axi_lite_awaddr = 0;
        s_axi_lite_awvalid = 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_arvalid = 0;
        s_axi_lite_rready = 0;
        s_axi_lite_wstrb = 0;

        // Wait Reset rising edge
        @(posedge ARESETN);

        for (i=0; i<10; i=i+1) begin
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end

        s_axi_lite_wstrb = 4'b1111;

        // Add stimulus here
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_0014, 32'h1000_0000);    // cam_addr write
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_0010, 32'h0000_0001);    // cam_addr_ap_vld = 1
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_001C, 32'h2000_0000);    // lap_addr write
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_0018, 32'h0000_0001);    // lap_addr_ap_vld = 1
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_0008, 32'h0000_0001);    // IP Interrupt Enable Registe(ap_done=1)
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_0000, 32'h0000_0001);    // ap_start = 1
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_RADC1(32'h0000_0000);

        forever begin
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            AXI_MASTER_RADC1(32'h0000_0000);
        end
    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);    // 次のクロックへ
            #DELAY;
            s_axi_lite_wvalid = 1'b0;
            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

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

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

    // Instantiate the Unit Under Test (UUT_slave)
    axi_slave_bfm # (
        .C_M_AXI_DATA_WIDTH(32),
        .WRITE_RANDOM_WAIT(1),    // Write Transaction のデータ転送の時にランダムなWaitを発生させる=1, Waitしない=0
        .READ_RANDOM_WAIT(0),    //  Read Transaction のデータ転送の時にランダムなWaitを発生させる=1, Waitしない=0
        .READ_DATA_IS_INCREMENT(1),    // ReadトランザクションでRAMの内容をReadする = 0(RAMにWriteしたものをReadする)、Readデータを+1する = 1(データは+1したデータをReadデータとして使用する
        .RUNDAM_BVALID_WAIT(0)
    ) uut_slave (
        .ACLK(ACLK),
        .ARESETN(ARESETN),
        .M_AXI_AWID(M_AXI_AWID),
        .M_AXI_AWADDR(M_AXI_AWADDR),
        .M_AXI_AWLEN(M_AXI_AWLEN),
        .M_AXI_AWSIZE(M_AXI_AWSIZE),
        .M_AXI_AWBURST(M_AXI_AWBURST),
        .M_AXI_AWLOCK({1'b0, M_AXI_AWLOCK}),
        .M_AXI_AWCACHE(M_AXI_AWCACHE),
        .M_AXI_AWPROT(M_AXI_AWPROT),
        .M_AXI_AWQOS(M_AXI_AWQOS),
        .M_AXI_AWUSER(M_AXI_AWUSER),
        .M_AXI_AWVALID(M_AXI_AWVALID),
        .M_AXI_AWREADY(M_AXI_AWREADY),
        .M_AXI_WDATA(M_AXI_WDATA),
        .M_AXI_WSTRB(M_AXI_WSTRB),
        .M_AXI_WLAST(M_AXI_WLAST),
        .M_AXI_WUSER(M_AXI_WUSER),
        .M_AXI_WVALID(M_AXI_WVALID),
        .M_AXI_WREADY(M_AXI_WREADY),
        .M_AXI_BID(M_AXI_BID),
        .M_AXI_BRESP(M_AXI_BRESP),
        .M_AXI_BUSER(M_AXI_BUSER),
        .M_AXI_BVALID(M_AXI_BVALID),
        .M_AXI_BREADY(M_AXI_BREADY),
        .M_AXI_ARID(M_AXI_ARID),
        .M_AXI_ARADDR(M_AXI_ARADDR),
        .M_AXI_ARLEN(M_AXI_ARLEN),
        .M_AXI_ARSIZE(M_AXI_ARSIZE),
        .M_AXI_ARBURST(M_AXI_ARBURST),
        .M_AXI_ARLOCK(M_AXI_ARLOCK),
        .M_AXI_ARCACHE(M_AXI_ARCACHE),
        .M_AXI_ARPROT(M_AXI_ARPROT),
        .M_AXI_ARQOS(M_AXI_ARQOS),
        .M_AXI_ARUSER(M_AXI_ARUSER),
        .M_AXI_ARVALID(M_AXI_ARVALID),
        .M_AXI_ARREADY(M_AXI_ARREADY),
        .M_AXI_RID(M_AXI_RID),
        .M_AXI_RDATA(M_AXI_RDATA),
        .M_AXI_RRESP(M_AXI_RRESP),
        .M_AXI_RLAST(M_AXI_RLAST),
        .M_AXI_RUSER(M_AXI_RUSER),
        .M_AXI_RVALID(M_AXI_RVALID),
        .M_AXI_RREADY(M_AXI_RREADY)
    );

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,
    output    reg        init_done
);
    begin
        initial begin
            reset_out = RESET_STATE;
            init_done = 1'b0;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
            init_done = 1'b1;
        end
    end
endmodule

`default_nettype wire

  1. 2013年11月26日 04:20 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0