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

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

FPGAの部屋

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

AXI4 Master Interfaceモジュールの作製2(シミュレーション)

AXI4 Master Interfaceモジュールの作製1(仕様の検討)”の続き。

汎用AXI Master IPを目指して、ユーザー回路を非同期FIFOのインターフェースで接続するAXI4 Master Interfaceモジュールが大体出来た。非同期FIFOを使用しているので、例えばユーザー回路がフルHDのピクセルクロックで動作して、AXIバスが200MHzで動作するということが可能になる。AXI Interconnect のクロックレート変換を使うこともできるが、ここはスマートにクロックドメインを分けることにした。ユーザー回路でも、WriteとReadでクロックを別にしてあるので、異なるクロックを使用することができる。

まずは、ISEのプロジェクトから示す。
axi4_master_inf_2_131108.png

下の示す通りのファイル構成となっている。

・axi4_master_inf_tb.v がテストベンチ
・UUTの axi_master_inf.v がトップファイル(AXI4 Master Interfaceモジュール)
・wadfifo が Writeアドレス転送用の非同期FIFO
・アドレス転送が16個ネストできるので、アドレス転送の情報を保存しておく wadfifo_outs
・wdfifo は Writeデータ転送用の非同期FIFO
・radfifo が Readアドレス転送用の非同期FIFO
・アドレス転送が16個ネストできるので、アドレス転送の情報を保存しておく radfifo_outs
・rdfifo が Readデータ転送用の非同期FIFO
・uut_slave の axi_slave_bfm.v がAXI4 Bus Slave の Bus Functional Model


これをシミュレーションしてみた。まだ、axi_slave_bfm.v がアドレス転送のネストが出来ないため、AXI4 Master Interfaceモジュールの性能は十分にはシミュレーション出来ていない。Writeトランザクションの波形を下に示す。
axi4_master_inf_3_131108.png

Readトランザクションの波形を下に示す。
axi4_master_inf_4_131108.png

次は、axi_slave_BFM.v を変更して、ネストされたアドレス転送を受け付けることにする。現在のBFMはメモリを実装していて実際にメモリにWriteし、またメモリのデータをReadしているが、検証をより易しくするために、Writeデータは書きっぱなし、Readデータは、Writeしたデータに関わらずに、0からインクリメントするようにしようと思う。

最後に、現在のテストベンチを貼っておく。(2013/11/22:修正)

`default_nettype none
`timescale 100ps / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date:   03:57:39 11/05/2013
// Design Name:   axi4_master_inf
// Module Name:   D:/HDL/FndtnISEWork/Zynq-7000/ZedBoard/AXI4_bus/axi4_master_inf/axi4_master_inf_tb.v
// Project Name:  axi4_master_inf
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: axi4_master_inf
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////

module axi4_master_inf_tb;

    parameter DELAY = 10;

    // Inputs
    wire ACLK;
    wire ARESETN;
    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 [63:0] M_AXI_RDATA;
    wire [1:0] M_AXI_RRESP;
    wire M_AXI_RLAST;
    wire [0:0] M_AXI_RUSER;
    wire M_AXI_RVALID;
    wire write_clk;
    wire write_rst;
    reg write_adfifo_wr_ena;
    reg [31:0] write_adfifo_addr;
    reg [7:0] write_adfifo_awlen;
    reg write_fifo_wr_ena;
    reg [63:0] write_fifo_write_data;
    reg [7:0] write_fifo_wstrb;
    wire read_clk;
    wire read_rst;
    reg read_adfifo_wr_ena;
    reg [31:0] read_adfifo_addr;
    reg [7:0] read_adfifo_arlen;
    wire read_fifo_rd_ena;
    reg init_done;

    // Outputs
    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 [63:0] M_AXI_WDATA;
    wire [7: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 write_adfifo_full;
    wire write_fifo_full;
    wire wirte_fifo_almost_full;
    wire read_adfifo_full;
    wire [63:0] read_fifo_read_data;
    wire read_fifo_empty_n;
    wire read_fifo_almost_empty_n;
    wire wr_resp_err;

    // Instantiate the Unit Under Test (UUT)
    axi4_master_inf #(
        .C_M_AXI_DATA_WIDTH(64)
    ) uut (
        .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(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),
        .write_clk(write_clk),
        .write_rst(write_rst),
        .write_adfifo_wr_ena(write_adfifo_wr_ena),
        .write_adfifo_addr(write_adfifo_addr),
        .write_adfifo_awlen(write_adfifo_awlen),
        .write_adfifo_full(write_adfifo_full),
        .write_fifo_wr_ena(write_fifo_wr_ena),
        .write_fifo_write_data(write_fifo_write_data),
        .write_fifo_wstrb(write_fifo_wstrb),
        .write_fifo_full(write_fifo_full),
        .wirte_fifo_almost_full(wirte_fifo_almost_full),
        .read_clk(read_clk),
        .read_rst(read_rst),
        .read_adfifo_wr_ena(read_adfifo_wr_ena),
        .read_adfifo_addr(read_adfifo_addr),
        .read_adfifo_arlen(read_adfifo_arlen),
        .read_adfifo_full(read_adfifo_full),
        .read_fifo_rd_ena(read_fifo_rd_ena),
        .read_fifo_read_data(read_fifo_read_data),
        .read_fifo_empty_n(read_fifo_empty_n),
        .read_fifo_almost_empty_n(read_fifo_almost_empty_n),
        .wr_resp_err(wr_resp_err),
        .init_done(init_done)
    );

    // Write
    initial begin
        // Initialize Inputs
        write_adfifo_wr_ena = 0;
        write_adfifo_addr = 0;
        write_adfifo_awlen = 0;
        write_fifo_wr_ena = 0;
        write_fifo_write_data = 0;
        write_fifo_wstrb = 0;
        init_done = 0;
        // Wait 100 ns for global reset to finish
        #1000;

        // Add stimulus here
        init_done = 1'b1;

        AXI4_Write_Trans_Auto(32'h11220000, 8'd0, 64'h2233445566778890, 8'hf0);
        AXI4_Write_Trans_Auto(32'h12340000, 8'd7, 64'h123456789ABCDEF0, 8'hff);
        AXI4_Write_Trans_Auto(32'h56780000, 8'd15, 64'h1122334455667788, 8'hff);
        AXI4_Write_Addr_Trans(32'h67890000, 8'd31);
        AXI4_Write_Addr_Trans(32'h789A0000, 8'd63);
        AXI4_Write_Data_Trans(64'h33445566778899A0, 8'hff, 8'd31);
        AXI4_Write_Data_Trans(64'h445566778899AAB0, 8'hff, 8'd63);
    end

    // Read
    assign read_fifo_rd_ena = read_fifo_empty_n;
    initial begin
        // Initialize Inputs
        read_adfifo_wr_ena = 0;
        read_adfifo_addr = 0;
        read_adfifo_arlen = 0;

        // Wait 100 ns for global reset to finish
        #1000;

        // Add stimulus here

        AXI4_Read_Trans_Auto(32'h11220000, 8'd0);
        AXI4_Read_Trans_Auto(32'h12340000, 8'd7);
        AXI4_Read_Trans_Auto(32'h56780000, 8'd15);
        AXI4_Read_Trans_Auto(32'h67890000, 8'd127);
    end

    // Write Channel Task
    task AXI4_Write_Trans_Auto;
        input     [31:0]    write_addr;
        input    [7:0]    awlen;            // 転送長-1
        input    [63:0]    start_wdata;    // wdataは1バーストごとに+1される
        input    [7:0]    wstb;
        integer    i;
        begin
            AXI4_Write_Addr_Trans(write_addr, awlen);
            AXI4_Write_Data_Trans(start_wdata, wstb, awlen);
        end
    endtask

    task AXI4_Write_Addr_Trans;
        input     [31:0]    write_addr;
        input    [7:0]    awlen;            // 転送長-1
        begin
            // アドレスと転送長をWrite
            write_adfifo_addr = write_addr;
            write_adfifo_awlen = awlen;

            while(write_adfifo_full) begin    // fullが0になるまでWait
                @(posedge write_clk);    // 次のクロックへ
                #DELAY;
            end

            @(posedge write_clk);    // 次のクロックへ
            #DELAY;
            write_adfifo_wr_ena = 1'b1;

            @(posedge write_clk);    // 次のクロックへ
            #DELAY;
            write_adfifo_wr_ena = 1'b0;
        end
    endtask

    task AXI4_Write_Data_Trans;
        input    [63:0]    start_wdata;    // wdataは1バーストごとに+1される
        input    [7:0]    wstb;
        input    [7:0]    awlen;
        integer    i;
        begin
            // 転送データをWrite、データは+1する
            write_fifo_wstrb = wstb;
            write_fifo_wr_ena = 1'b1;
            for (i=0; i<awlen+1; i=i+1) begin
                while(write_fifo_full) begin    // fullが0になるまでWait
                    @(posedge write_clk);    // 次のクロックへ
                    #DELAY;
                end

                write_fifo_write_data = start_wdata;
                @(posedge write_clk);    // 次のクロックへ
                #DELAY;
                start_wdata = start_wdata + 1;
            end

            write_fifo_wr_ena = 1'b0;
            @(posedge write_clk);    // 次のクロックへ
            #DELAY;
        end
    endtask


    // Read Channel Task
    task AXI4_Read_Trans_Auto;
        input    [31:0]    read_addr;
        input    [7:0]    arlen;            // 転送長-1
        begin
            // アドレスと転送長をWrite
            read_adfifo_addr = read_addr;
            read_adfifo_arlen = arlen;

            while(read_adfifo_full) begin    // fullが0になるまでWait
                @(posedge read_clk);    // 次のクロックへ
                #DELAY;
            end

            @(posedge read_clk);    // 次のクロックへ
            #DELAY;
            read_adfifo_wr_ena = 1'b1;

            @(posedge read_clk);    // 次のクロックへ
            #DELAY;
            read_adfifo_wr_ena = 1'b0;
        end
    endtask

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

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

    // clk_gen のインスタンス(write_clk, read_clk)
    clk_gen #(
        .CLK_PERIOD(67),    // 6.7nsec, 149.3MHz(本当は148.5MHz)
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) WRITE_CLKi (
        .clk_out(write_clk)
    );
    assign read_clk = write_clk;

    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(1000)    // 100nsec
    ) WRITE_RSTi (
        .reset_out(write_rst),
        .init_done()
    );
    assign read_rst = write_rst;

    // Instantiate the Unit Under Test (UUT_slave)
    axi_slave_bfm # (
        .C_M_AXI_DATA_WIDTH(64),
        .WRITE_RANDOM_WAIT(1),    // Write Transaction のデータ転送の時にランダムなWaitを発生させる=1, Waitしない=0
        .READ_RANDOM_WAIT(1),    //  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(1)
    ) 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月08日 04:30 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0