04AXI4總線axi-full-master(AXI4總線實戰)


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

操作系統:WIN10 64bit

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

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

4.1概述

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

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

本文實驗目的:

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

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

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

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

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

4.2創建axi4-full-master總線接口IP

新建fpga工程,過程省略

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

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

 

設置IP的名字為maxi_full

模板支持3中協議,分別是AXI4-Full AXI4-Lite AXI4-Stream,這選擇Full;總線包括Master和Slave兩種模式,這里選擇Master模式

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

 

 

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

采用默認地址分配即可

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

路徑uisrc/03_ip/ maxi_full_1.0/hdl路徑下的maxi_full_v1_0_M00_AXI.v就是我們的源碼。另外一個maxi_full_v1_0.v是軟件產生了一個接口文件,如果我們自己定義IP可有可無。

4.3程序分析

axi總線信號的關鍵無非是地址和數據,而寫地址的有效取決於AXI_AWVALID和AXI_AWREADY,寫數據的有效取決於S_AXI_WVALID和S_AXI_WREADY。同理,讀地址的有效取決於AXI_ARVALID和AXI_ARREADY,讀數據的有效取決於S_AXI_RVALID和S_AXI_RREADY。所以以下代碼的閱讀分析注意也是圍繞以上4個信號的有效時序。

以下程序我們把關鍵信號的代碼拆分閱讀

1:產生初始化信號

    //Generate a pulse to initiate AXI transaction.

    always @(posedge M_AXI_ACLK)                                        

     begin

     // Initiates AXI transaction delay

     if (M_AXI_ARESETN == 0 )

     begin

     init_txn_ff <= 1'b0;

     init_txn_ff2 <= 1'b0;

     end

     else

     begin

     init_txn_ff <= INIT_AXI_TXN;

     init_txn_ff2 <= init_txn_ff;

     end

     end

2:axi-full-master的axi_awvalid

當(~axi_awvalid && start_single_burst_write)==1條件滿足,開始一次寫傳輸,設置axi_awvalid有效。

     always @(posedge M_AXI_ACLK)

     begin

      

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )

     begin

     axi_awvalid <= 1'b0;

     end

     // If previously not valid , start next transaction

     else if (~axi_awvalid && start_single_burst_write)

     begin

     axi_awvalid <= 1'b1;

     end

     /* Once asserted, VALIDs cannot be deasserted, so axi_awvalid

     must wait until transaction is accepted */

     else if (M_AXI_AWREADY && axi_awvalid)

     begin

     axi_awvalid <= 1'b0;

     end

     else

     axi_awvalid <= axi_awvalid;

     end

3:axi-full-slave的axi_awaddr

寫通道地址每當M_AXI_AWREADY && axi_awvalid地址加1

    // Next address after AWREADY indicates previous address acceptance

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     begin

     axi_awaddr <= 'b0;

     end

     else if (M_AXI_AWREADY && axi_awvalid)

     begin

     axi_awaddr <= axi_awaddr + burst_size_bytes;

     end

     else

     axi_awaddr <= axi_awaddr;

     end

4:axi-full-master的axi_wvalid

設置axi_wvalid <= 1'b1開始寫數據。wnext信號有效代碼axi_full_master寫數據有效。

     assign wnext = M_AXI_WREADY & axi_wvalid;

    

    // WVALID logic, similar to the axi_awvalid always block above

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )

     begin

     axi_wvalid <= 1'b0;

     end

     // If previously not valid, start next transaction

     else if (~axi_wvalid && start_single_burst_write)

     begin

     axi_wvalid <= 1'b1;

     end

     /* If WREADY and too many writes, throttle WVALID

     Once asserted, VALIDs cannot be deasserted, so WVALID

     must wait until burst is complete with WLAST */

     else if (wnext && axi_wlast)

     axi_wvalid <= 1'b0;

     else

     axi_wvalid <= axi_wvalid;

     end

5:axi-full-master的axi_master_last

axi_master_last信號,當條件滿足(((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))==1的時候,axi_wlast <= 1'b1。這是VIVADO自帶的模板,但是這里有個bug,那就是必須確保slave可以連續接收數據,假設發送last的時候wnext==0,這樣就不能把最后一個數據正確寫入到slave中了。

    //WLAST generation on the MSB of a counter underflow

    // WVALID logic, similar to the axi_awvalid always block above

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )

     begin

     axi_wlast <= 1'b0;

     end

     // axi_wlast is asserted when the write index

     // count reaches the penultimate count to synchronize

     // with the last write data when write_index is b1111

     // else if (&(write_index[C_TRANSACTIONS_NUM-1:1])&& ~write_index[0] && wnext)

     else if (((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))

     begin

     axi_wlast <= 1'b1;

     end

     // Deassrt axi_wlast when the last write data has been

     // accepted by the slave with a valid response

     else if (wnext)

     axi_wlast <= 1'b0;

     else if (axi_wlast && C_M_AXI_BURST_LEN == 1)

     axi_wlast <= 1'b0;

     else

     axi_wlast <= axi_wlast;

     end

刪除以上代碼,並且添加以下代碼:

     wire wlast = (write_index == C_M_AXI_BURST_LEN-1) && wnext;

     reg wlast_r1 = 1'b0;

     always @(posedge M_AXI_ACLK)

         wlast_r <= wlast;

 

     assign axi_wlast = (wlast_r==1'b0)&&(wlast==1'b1);

另外需要修改reg axi_wlast;為wire axi_wlast;

這樣就可以確保發送wlast的時候數據肯定是有效的。

6:寫次數記錄write_index計數器

    /* Burst length counter. Uses extra counter register bit to indicate terminal

     count to reduce decode logic */

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_write == 1'b1)

     begin

     write_index <= 0;

     end

     else if (wnext && (write_index != C_M_AXI_BURST_LEN-1))

     begin

     write_index <= write_index + 1;

     end

     else

     write_index <= write_index;

     end

7:axi-full-master的axi_wdata

axi_full_master寫數據計數寫數據

    /* Write Data Generator

     Data pattern is only a simple incrementing count from 0 for each burst */

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     axi_wdata <= 'b1;

     //else if (wnext && axi_wlast)

     // axi_wdata <= 'b0;

     else if (wnext)

     axi_wdata <= axi_wdata + 1;

     else

     axi_wdata <= axi_wdata;

     end

8:axi-full-master的axi_bready

設置axi_bready信號

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )

     begin

     axi_bready <= 1'b0;

     end

     // accept/acknowledge bresp with axi_bready by the master

     // when M_AXI_BVALID is asserted by slave

     else if (M_AXI_BVALID && ~axi_bready)

     begin

     axi_bready <= 1'b1;

     end

     // deassert after one clock cycle

     else if (axi_bready)

     begin

     axi_bready <= 1'b0;

     end

     // retain the previous value

     else

     axi_bready <= axi_bready;

     end

9:axi-full-slave的axi_arvalid

Axi_full_master讀通道的分析非常類似,代碼是對稱的。

當(~axi_arvalid && start_single_burst_read)==1條件滿足,開始一次寫傳輸,設置axi_arvalid有效。

    always @(posedge M_AXI_ACLK)

     begin

    

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )

     begin

     axi_arvalid <= 1'b0;

     end

     // If previously not valid , start next transaction

     else if (~axi_arvalid && start_single_burst_read)

     begin

     axi_arvalid <= 1'b1;

     end

     else if (M_AXI_ARREADY && axi_arvalid)

     begin

     axi_arvalid <= 1'b0;

     end

     else

     axi_arvalid <= axi_arvalid;

     end

 

10:axi-full-slave的axi_araddr

讀地址計算

// Next address after ARREADY indicates previous address acceptance

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     begin

     axi_araddr <= 'b0;

     end

     else if (M_AXI_ARREADY && axi_arvalid)

     begin

     axi_araddr <= axi_araddr + burst_size_bytes;

     end

     else

     axi_araddr <= axi_araddr;

     end

11:axi-full-master的axi_rready

讀數據准備好

    /*

     The Read Data channel returns the results of the read request

     In this example the data checker is always able to accept

     more data, so no need to throttle the RREADY signal

     */

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )

     begin

     axi_rready <= 1'b0;

     end

     // accept/acknowledge rdata/rresp with axi_rready by the master

     // when M_AXI_RVALID is asserted by slave

     else if (M_AXI_RVALID)

     begin

     if (M_AXI_RLAST && axi_rready)

     begin

     axi_rready <= 1'b0;

     end

     else

     begin

     axi_rready <= 1'b1;

     end

     end

     // retain the previous value

     end

12:讀次數記錄read_index計數器

讀數據索引計數

assign rnext = M_AXI_RVALID && axi_rready;

    // Burst length counter. Uses extra counter register bit to indicate

    // terminal count to reduce decode logic

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_read)

     begin

     read_index <= 0;

     end

     else if (rnext && (read_index != C_M_AXI_BURST_LEN-1))

     begin

     read_index <= read_index + 1;

     end

     else

     read_index <= read_index;

     end

13:產生對比數據expected_rdata

數據expected_rdata用於和讀出的M_AXI_RDATA進行對比以此驗證數據的正確性。

    //Generate expected read data to check against actual read data

     always @(posedge M_AXI_ACLK)

     begin

        if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)// || M_AXI_RLAST)

            expected_rdata <= 'b1;

        else if (M_AXI_RVALID && axi_rready)

            expected_rdata <= expected_rdata + 1;

        else

            expected_rdata <= expected_rdata;

     end

14:比對數據正確性

讀寫數據比較

    //Check received read data against data generator

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     begin

     read_mismatch <= 1'b0;

     end

     //Only check data when RVALID is active

     else if (rnext && (M_AXI_RDATA != expected_rdata))

     begin

     read_mismatch <= 1'b1;

     end

     else

     read_mismatch <= 1'b0;

     end

15:讀寫狀態機

讀寫狀態機源碼

//implement master command interface state machine

     always @ ( posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 1'b0 )

     begin

     // reset condition

     // All the signals are assigned default values under reset condition

     mst_exec_state <= IDLE;

     start_single_burst_write <= 1'b0;

     start_single_burst_read <= 1'b0;

     compare_done <= 1'b0;

     ERROR <= 1'b0;

     end

     else

     begin

     // state transition

     case (mst_exec_state)

     IDLE:

     // This state is responsible to wait for user defined C_M_START_COUNT

     // number of clock cycles.

     if ( init_txn_pulse == 1'b1)

     begin

     mst_exec_state <= INIT_WRITE;

     ERROR <= 1'b0;

     compare_done <= 1'b0;

     end

     else

     begin

     mst_exec_state <= IDLE;

     end

     INIT_WRITE:

     // This state is responsible to issue start_single_write pulse to

     // initiate a write transaction. Write transactions will be

     // issued until burst_write_active signal is asserted.

     // write controller

     if (writes_done)

     begin

     mst_exec_state <= INIT_READ;//

     end

     else

     begin

     mst_exec_state <= INIT_WRITE;

     if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active)

     begin

     start_single_burst_write <= 1'b1;

     end

     else

     begin

     start_single_burst_write <= 1'b0; //Negate to generate a pulse

     end

     end

     INIT_READ:

     // This state is responsible to issue start_single_read pulse to

     // initiate a read transaction. Read transactions will be

     // issued until burst_read_active signal is asserted.

     // read controller

     if (reads_done)

     begin

     mst_exec_state <= INIT_COMPARE;

     end

     else

     begin

     mst_exec_state <= INIT_READ;

     if (~axi_arvalid && ~burst_read_active && ~start_single_burst_read)

     begin

     start_single_burst_read <= 1'b1;

     end

     else

     begin

     start_single_burst_read <= 1'b0; //Negate to generate a pulse

     end

     end

     INIT_COMPARE:

     // This state is responsible to issue the state of comparison

     // of written data with the read data. If no error flags are set,

     // compare_done signal will be asseted to indicate success.

     //if (~error_reg)

     begin

     ERROR <= error_reg;

     mst_exec_state <= IDLE;

     compare_done <= 1'b1;

     end

     default :

     begin

     mst_exec_state <= IDLE;

     end

     endcase

     end

     end //MASTER_EXECUTION_PROC

整理成流程圖,更加容易理解:

16:正在寫burst_write_active

burst_write_active代表正在寫操作

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     burst_write_active <= 1'b0;

      

     //The burst_write_active is asserted when a write burst transaction is initiated

     else if (start_single_burst_write)

     burst_write_active <= 1'b1;

     else if (M_AXI_BVALID && axi_bready)

     burst_write_active <= 0;

     end

17:寫完成writes_done

寫數據完成writes_done信號

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     writes_done <= 1'b0;

    

     //The writes_done should be associated with a bready response

     //else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)

     else if (M_AXI_BVALID && (write_burst_counter[C_NO_BURSTS_REQ]) && axi_bready)

     writes_done <= 1'b1;

     else

     writes_done <= writes_done;

     end

18:正在讀burst_read_active

讀burst_read_active代表正在讀數據

// burst_read_active signal is asserted when there is a burst write transaction

     // is initiated by the assertion of start_single_burst_write. start_single_burst_read

     // signal remains asserted until the burst read is accepted by the master

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     burst_read_active <= 1'b0;

    

     //The burst_write_active is asserted when a write burst transaction is initiated

     else if (start_single_burst_read)

     burst_read_active <= 1'b1;

     else if (M_AXI_RVALID && axi_rready && M_AXI_RLAST)

     burst_read_active <= 0;

     end

19:讀完成reads_done

讀數據完成reads_done信號

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     reads_done <= 1'b0;

    

     //The reads_done should be associated with a rready response

     //else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)

     else if (M_AXI_RVALID && axi_rready && (read_index == C_M_AXI_BURST_LEN-1) && (read_burst_counter[C_NO_BURSTS_REQ]))

     reads_done <= 1'b1;

     else

     reads_done <= reads_done;

     end

20:IP的更新

由於修改了代碼,需要先更新IP狀態,完成IP更新

4.4實驗結果

仿真結果

一次axi4寫操作burst lenth=16如下圖所示,由於WREADY信號不是連續的,所以可以傳輸效率不是最高的

一共進行64次burst共計寫了1024個32bit數據

一次讀操作的burst lenth也是16如下圖,但是可以看到讀數據時連續的,所以效率最高

一共進行64次burst共計讀了1024個32bit數據

 

可以看到讀出的數據RDATA和expected_rdata一致,所以代碼正確。

 


免責聲明!

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



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