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以及XILINX7代以上的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;總線包括MasterSlave兩種模式,這裏選擇Master模式

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

 

 

單擊Finish 後展開VIVADO自動產生的demo,單擊Block Design的工程,可以看到如下2IP。其中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_AWVALIDAXI_AWREADY,寫數據的有效取決於S_AXI_WVALIDS_AXI_WREADY。同理,讀地址的有效取決於AXI_ARVALIDAXI_ARREADY,讀數據的有效取決於S_AXI_RVALIDS_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-masteraxi_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-slaveaxi_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-masteraxi_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-masteraxi_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-masteraxi_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-masteraxi_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-slaveaxi_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-slaveaxi_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-masteraxi_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信號不是連續的,所以可以傳輸效率不是最高的

一共進行64burst共計寫了102432bit數據

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

一共進行64burst共計讀了102432bit數據

 

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章