03AXI4總線axi-full-slave(AXI4總線實戰)


軟件版本:vitis2020.2(vivado2020.2)

操作系統:WIN10 64bit

硬件平台:適用XILINX A7/K7/Z7/ZU/KU系列FPGA(米聯客(milianke)MZU07A-EG硬件開發平台)

登錄"米聯客"FPGA社區-www.uisrc.com視頻課程、答疑解惑!

3.1概述

    使用XILINX 的軟件工具VIVADO以及XILINX的7代以上的FPGA或者SOC掌握AXI-4總線結束,並且可以靈活使用AXI-4總線技術完成數據的交換,可以讓我們在構建強大的FPGA內部總線數據互聯通信方面取得高效、高速、標准化的優勢。

    關於AXI4總線協議的部分介紹請閱讀"01AXI4總線axi-lite-slave"。

本文實驗目的:

1:掌握基於VIVADO工具產生AXI協議模板

2:掌握通過VIVADO工具產生AXI-full-slave代碼

3:理解AXI-full-slave中自定義寄存器的地址分配

4:掌握通過VIVADO封裝AXI-full-slave圖形化IP

5:通過仿真驗證AXI-full-slave IP的工作是否正常。

3.2創建axi4-full-slave總線接口IP

新建fpga工程,過程省略

新建完成工程后,單擊菜單欄Tools->Create and Package New IP,開始創建一個AXI4-Full接口總線IP

選擇使用vivado自帶的AXI總線模板創建一個AXI4-FULL接口IP

 

設置IP的名字為saxi_full

模板支持3中協議,分別是AXI4-Full AXI4-Lite AXI4-Stream, 這里選擇ful;

總線包括Master和Slave兩種模式,這里選擇Slave模式

這里選擇Verify Peripheral IP using AXI4 VIP 可以對AXI4-FULL快速驗證

 

 

單擊Finish 后展開VIVADO自動產生的demo,單擊Block Design的工程,可以看到如下2個IP。其中saxi_full_0就是我們自定義的IP,另外一個master_0是用來讀寫我們自定義的saxi_full_0,以此驗證我們的IP正確性。

采用默認地址分配即可

繼續站看代碼看看里面有什么東西

 

3.3程序分析

1:axi-full-slave的axi_awready

當滿足條件(~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)=1的時候表示可以進行一次AXI-FULL的burst寫操作了,這個時候AXI-FULL-SLAVE設置axi_awready <= 1'b1和axi_awv_awr_flag <= 1'b1

    // axi_awready is asserted for one S_AXI_ACLK clock cycle when both

    // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is

    // de-asserted when reset is low.

    always @( posedge S_AXI_ACLK )

    begin

     if ( S_AXI_ARESETN == 1'b0 )

     begin

     axi_awready <= 1'b0;

     axi_awv_awr_flag <= 1'b0;

     end

     else

     begin

     if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)

     begin

     // slave is ready to accept an address and

     // associated control signals

     axi_awready <= 1'b1;

     axi_awv_awr_flag <= 1'b1;

     // used for generation of bresp() and bvalid

     end

     else if (S_AXI_WLAST && axi_wready)

     // preparing to accept next address after current write burst tx completion

     begin

     axi_awv_awr_flag <= 1'b0;

     end

     else

     begin

     axi_awready <= 1'b0;

     end

     end

    end

2:axi-full-slave的axi_awaddr

AXI的burst模式包括3種:

1:fixed burst這種模式下地址都是相同的

2: incremental burst這種模式下地址遞增

3: Wrapping burst 這只模式下地址達到設置的最大地址邊界后返回原來的地址。

本文demo種以下三種模式的具體代碼如下:

    // This process is used to latch the address when both

    // S_AXI_AWVALID and S_AXI_WVALID are valid.

    always @( posedge S_AXI_ACLK )

    begin

     if ( S_AXI_ARESETN == 1'b0 )

     begin

     axi_awaddr <= 0;

     axi_awlen_cntr <= 0;

     axi_awburst <= 0;

     axi_awlen <= 0;

     end

     else

     begin

     if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)

     begin

     // address latching

     axi_awaddr <= S_AXI_AWADDR[C_S_AXI_ADDR_WIDTH - 1:0];

     axi_awburst <= S_AXI_AWBURST;

     axi_awlen <= S_AXI_AWLEN;

     // start address of transfer

     axi_awlen_cntr <= 0;

     end

     else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)

     begin

 

     axi_awlen_cntr <= axi_awlen_cntr + 1;

 

     case (axi_awburst)

     2'b00: // fixed burst

     // The write address for all the beats in the transaction are fixed

     begin

     axi_awaddr <= axi_awaddr;

     //for awsize = 4 bytes (010)

     end

     2'b01: //incremental burst

     // The write address for all the beats in the transaction are increments by awsize

     begin

     axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

     //awaddr aligned to 4 byte boundary

     axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};

     //for awsize = 4 bytes (010)

     end

     2'b10: //Wrapping burst

     // The write address wraps when the address reaches wrap boundary

     if (aw_wrap_en)

     begin

     axi_awaddr <= (axi_awaddr - aw_wrap_size);

     end

     else

     begin

     axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

     axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};

     end

     default: //reserved (incremental burst for example)

     begin

     axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

     //for awsize = 4 bytes (010)

     end

     endcase

     end

     end

    end

3:axi-full-slave的axi_wready

當滿足條件( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)==1 設置axi_wready為1.這里可以看出,S_AXI_WVALID必須在一次burst種持續有效,直到滿足條件(S_AXI_WLAST && axi_wready),否則AXI-FULL-SLAVE會出錯,這一點有別於AXI-LITE-SLAVE每次只讀寫一個數據。

    // axi_wready is asserted for one S_AXI_ACLK clock cycle when both

    // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is

    // de-asserted when reset is low.

    always @( posedge S_AXI_ACLK )

    begin

     if ( S_AXI_ARESETN == 1'b0 )

     begin

     axi_wready <= 1'b0;

     end

     else

     begin

     if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)

     begin

     // slave can accept the write data

     axi_wready <= 1'b1;

     end

     //else if (~axi_awv_awr_flag)

     else if (S_AXI_WLAST && axi_wready)

     begin

     axi_wready <= 1'b0;

     end

     end

    end

4:axi-full-slave的axi_bvalid信號

axi_bvalid用於告知axi master axi-slave端已經完成數據接收了

給出ACK,寫操作LAST信號的下一個時鍾,AXI-SLAVE給出ACK信號

    always @( posedge S_AXI_ACLK )

    begin

     if ( S_AXI_ARESETN == 1'b0 )

     begin

     axi_bvalid <= 0;

     axi_bresp <= 2'b0;

     axi_buser <= 0;

     end

     else

     begin

     if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )

     begin

     axi_bvalid <= 1'b1;

     axi_bresp <= 2'b0;

     // 'OKAY' response

     end

     else

     begin

     if (S_AXI_BREADY && axi_bvalid)

     //check if bready is asserted while bvalid is high)

     //(there is a possibility that bready is always asserted high)

     begin

     axi_bvalid <= 1'b0;

     end

     end

     end

     end

5:axi-full-slave的axi_arready信號

當滿足條件(~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)=1的時候表示可以進行一次AXI-FULL的burst讀操作了,這個時候AXI -FULL-SLAVE設置axi_arready <= 1'b1和axi_arv_arr_flag <= 1'b1

// axi_arready is asserted for one S_AXI_ACLK clock cycle when

    // S_AXI_ARVALID is asserted. axi_awready is

    // de-asserted when reset (active low) is asserted.

    // The read address is also latched when S_AXI_ARVALID is

    // asserted. axi_araddr is reset to zero on reset assertion.

 

    always @( posedge S_AXI_ACLK )

    begin

     if ( S_AXI_ARESETN == 1'b0 )

     begin

     axi_arready <= 1'b0;

     axi_arv_arr_flag <= 1'b0;

     end

     else

     begin

     if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)

     begin

     axi_arready <= 1'b1;

     axi_arv_arr_flag <= 1'b1;

     end

     else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)

     // preparing to accept next address after current read completion

     begin

     axi_arv_arr_flag <= 1'b0;

     end

     else

     begin

     axi_arready <= 1'b0;

     end

     end

    end

6:axi-full-slave的axi_araddr信號

AXI-的讀寫操作幾乎是相對的代碼,AXI的burst模式包括3種:

1:fixed burst這種模式下地址都是相同的

2: incremental burst這種模式下地址遞增

3: Wrapping burst 這只模式下地址達到設置的最大地址邊界后返回原來的地址。

本文demo種以下三種模式的具體代碼如下:

    //This process is used to latch the address when both

    //S_AXI_ARVALID and S_AXI_RVALID are valid.

    always @( posedge S_AXI_ACLK )

    begin

     if ( S_AXI_ARESETN == 1'b0 )

     begin

     axi_araddr <= 0;

     axi_arlen_cntr <= 0;

     axi_arburst <= 0;

     axi_arlen <= 0;

     axi_rlast <= 1'b0;

     axi_ruser <= 0;

     end

     else

     begin

     if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)

     begin

     // address latching

     axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];

     axi_arburst <= S_AXI_ARBURST;

     axi_arlen <= S_AXI_ARLEN;

     // start address of transfer

     axi_arlen_cntr <= 0;

     axi_rlast <= 1'b0;

     end

     else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)

     begin

      

     axi_arlen_cntr <= axi_arlen_cntr + 1;

     axi_rlast <= 1'b0;

      

     case (axi_arburst)

     2'b00: // fixed burst

     // The read address for all the beats in the transaction are fixed

     begin

     axi_araddr <= axi_araddr;

     //for arsize = 4 bytes (010)

     end

     2'b01: //incremental burst

     // The read address for all the beats in the transaction are increments by awsize

     begin

     axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

     //araddr aligned to 4 byte boundary

     axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};

     //for awsize = 4 bytes (010)

     end

     2'b10: //Wrapping burst

     // The read address wraps when the address reaches wrap boundary

     if (ar_wrap_en)

     begin

     axi_araddr <= (axi_araddr - ar_wrap_size);

     end

     else

     begin

     axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

     //araddr aligned to 4 byte boundary

     axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};

     end

     default: //reserved (incremental burst for example)

     begin

     axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;

     //for arsize = 4 bytes (010)

     end

     endcase

     end

     else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )

     begin

     axi_rlast <= 1'b1;

     end

     else if (S_AXI_RREADY)

     begin

     axi_rlast <= 1'b0;

     end

     end

    end

7:axi-full-slave的axi_rvalid信號

在用VIVADO模板產生的demo種,讀操作數據不是連續讀的,通過axi_rvalid設置AXI-SLAVE FULL 讀數據有效。

    always @( posedge S_AXI_ACLK )

    begin

     if ( S_AXI_ARESETN == 1'b0 )

     begin

     axi_rvalid <= 0;

     axi_rresp <= 0;

     end

     else

     begin

     if (axi_arv_arr_flag && ~axi_rvalid)

     begin

     axi_rvalid <= 1'b1;

     axi_rresp <= 2'b0;

     // 'OKAY' response

     end

     else if (axi_rvalid && S_AXI_RREADY)

     begin

     axi_rvalid <= 1'b0;

     end

     end

    end

8:數據保存到bock ram

以下是利用block ram完成數據的保存和回讀

 

// implement Block RAM(s)

    generate

     for(i=0; i<= USER_NUM_MEM-1; i=i+1)

     begin:BRAM_GEN

     wire mem_rden;

     wire mem_wren;

      

     assign mem_wren = axi_wready && S_AXI_WVALID ;

      

     assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid

      

     for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)

     begin:BYTE_BRAM_GEN

     wire [8-1:0] data_in ;

     wire [8-1:0] data_out;

     reg [8-1:0] byte_ram [0 : 15];

     integer j;

      

     //assigning 8 bit data

     assign data_in = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];

     assign data_out = byte_ram[mem_address];

      

     always @( posedge S_AXI_ACLK )

     begin

     if (mem_wren && S_AXI_WSTRB[mem_byte_index])

     begin

     byte_ram[mem_address] <= data_in;

     end

     end

      

     always @( posedge S_AXI_ACLK )

     begin

     if (mem_rden)

     begin

     mem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;

     end

     end

      

     end

     end

    endgenerate

3.4實驗結果

仿真結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM