02AXI4總線axi-lite-master(AXI4總線篇)

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

操作系統:WIN10 64bit

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

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

2.1概述

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

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

本文實驗目的:

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

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

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

4:掌握通過VIVADO封裝AXI-lite-master圖形化IP

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

2.2創建axi4-lite-master總線接口IP

新建fpga工程,過程省略

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

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

設置IP的名字爲maxi_lite

模板支持3中協議,分別是AXI4-Full AXI4-Lite AXI4-Stream

總線包括MasterSlave兩種模式,這裏選擇Slave模式

 

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

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

採用默認地址分配即可

繼續站看代碼看看裏面有什麼東西

 

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

2.3程序分析

axi總線信號的關鍵無非是地址和數據,而寫地址的有效取決於AXI_AWVALIDAXI_AWREADY,寫數據的有效取決於S_AXI_WVALIDS_AXI_WREADY。同理,讀地址的有效取決於AXI_ARVALIDAXI_ARREADY,讀數據的有效取決於S_AXI_RVALIDS_AXI_RREADY。所以以下代碼的閱讀分析注意也是圍繞以上4個信號的有效時序。

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

 

1:產生初始化信號

       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-lite-masteraxi_awvalid

start_single_write有效,開始一次寫傳輸,設置axi_awvalid有效。

         always @(posedge M_AXI_ACLK)                                                                      

         begin                                                                       

           //Only VALID signals must be deasserted during reset per AXI spec         

           //Consider inverting then registering active-low reset for higher fmax    

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

             begin                                                                   

               axi_awvalid <= 1'b0;                                                  

             end                                                                     

             //Signal a new address/data command is available by user logic          

           else                                                                      

             begin                                                                    

               if (start_single_write)                                               

                 begin                                                               

                   axi_awvalid <= 1'b1;                                              

                 end                                                                 

            //Address accepted by interconnect/slave (issue of M_AXI_AWREADY by slave)

               else if (M_AXI_AWREADY && axi_awvalid)                                

                 begin                                                               

                   axi_awvalid <= 1'b0;                                              

                 end                                                                 

             end                                                                     

         end

3:axi-lite-slaveaxi_awaddr

         always @(posedge M_AXI_ACLK)                                 

             begin                                                    

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

                 begin                                                

                   axi_awaddr <= 0;                                    

                 end                                                  

                 // Signals a new write address/ write data is        

                 // available by user logic                           

               else if (M_AXI_AWREADY && axi_awvalid)                 

                 begin                                                

                   axi_awaddr <= axi_awaddr + 32'h00000004;           

                                                                      

                 end                                                  

             end 

4:axi-lite-masteraxi_wvalid

M_AXI_WREADY && axi_wvalid同時有效的時候,數據纔是有效的,對於axi-lite_master接口,M_AXI_WREADY && axi_wvalid同時有效的時間窗口是一個時鐘週期。

          always @(posedge M_AXI_ACLK)                                        

          begin                                                                        

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

              begin                                                                     

                axi_wvalid <= 1'b0;                                                    

              end                                                                      

            //Signal a new address/data command is available by user logic             

            else if (start_single_write)                                               

              begin                                                                    

                axi_wvalid <= 1'b1;                                                     

              end                                                                      

            //Data accepted by interconnect/slave (issue of M_AXI_WREADY by slave)     

            else if (M_AXI_WREADY && axi_wvalid)                                        

              begin                                                                    

               axi_wvalid <= 1'b0;                                                     

              end                                                                      

          end  

5:axi-lite-masteraxi_wdata

產生寫測試用的測試數據

         always @(posedge M_AXI_ACLK)                                 

             begin                                                    

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

                 begin                                                

                   axi_wdata <= C_M_START_DATA_VALUE;                 

                 end                                                  

               // Signals a new write address/ write data is          

               // available by user logic                             

               else if (M_AXI_WREADY && axi_wvalid)                   

                 begin                                                

                   axi_wdata <= C_M_START_DATA_VALUE + write_index;   

                 end                                                  

               end       

6:寫次數記錄write_index計數器

這個demo中以start_single_wirte信號作爲統計的,如果我們完全自定axi-lite_master代碼可以自行優化,可以寫出更好的代碼。我們這裏是用vivado模板產生的代碼主要線教會大家如何使用現有的手段和軟件工具學習axi4總線。

         always @(posedge M_AXI_ACLK)                                                

         begin                                                                       

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

             begin                                                                    

               write_index <= 0;                                                     

             end                                                                     

             // Signals a new write address/ write data is                           

             // available by user logic                                              

           else if (start_single_write)                                              

             begin                                                                   

               write_index <= write_index + 1;                                       

             end                                                                     

         end   

7:axi-lite-masteraxi_bready

當收到寫通道的axi-lite-slave發回的M_AXI_BVALDI應答信號,設置axi_bready1BRESP返回AXI寫操作是否有錯誤。

         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                                                                  

                                                                             

       //Flag write errors                                                   

       assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]);

8:axi-lite-slaveaxi_arvalid

         always @(posedge M_AXI_ACLK)                                                    

         begin                                                                           

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

             begin                                                                       

               axi_arvalid <= 1'b0;                                                      

             end                                                                          

           //Signal a new read address command is available by user logic                

           else if (start_single_read)                                                   

             begin                                                                        

               axi_arvalid <= 1'b1;                                                      

             end                                                                         

           //RAddress accepted by interconnect/slave (issue of M_AXI_ARREADY by slave)   

           else if (M_AXI_ARREADY && axi_arvalid)                                        

             begin                                                                       

               axi_arvalid <= 1'b0;                                                       

             end                                                                         

           // retain the previous value                                                  

         end  

9:axi-lite-slaveaxi_araddr

         always @(posedge M_AXI_ACLK)                                 

             begin                                                    

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

                 begin                                                 

                   axi_araddr <= 0;                                   

                 end                                                  

                 // Signals a new write address/ write data is        

                 // available by user logic                           

               else if (M_AXI_ARREADY && axi_arvalid)                 

                 begin                                                

                   axi_araddr <= axi_araddr + 32'h00000004;           

                 end                                                   

             end   

10:axi-lite-masteraxi_rready

M_AXI_RVALID && axi_rready同時有效的時候,數據纔是有效的,對於axi-lite_master接口,M_AXI_RVALID && ~axi_rready==1的時候設置axi_rready=1,當axi_rready==1,再設置axi_rready=0

         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 && ~axi_rready)                              

             begin                                                             

               axi_rready <= 1'b1;                                            

             end                                                              

           // deassert after one clock cycle                                   

           else if (axi_rready)                                               

             begin                                                            

               axi_rready <= 1'b0;                                            

             end                                                               

           // retain the previous value                                       

         end                                                                  

                                                                               

       //Flag write errors                                                    

       assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]);

11:axi-lite-masterM_AXI_RDATA

M_AXI_RVALID && axi_rready都有效的時候,對讀取的M_AXI_RDATA數據和expected_rdata數據進行比較。

         always @(posedge M_AXI_ACLK)                                                     

         begin                                                                            

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

           read_mismatch <= 1'b0;                                                         

                                                                                           

           //The read data when available (on axi_rready) is compared with the expected data

           else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata))        

             read_mismatch <= 1'b1;                                                        

           else                                                                           

             read_mismatch <= read_mismatch;                                              

         end

12:產生對比數據expected_rdata

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

         always @(posedge M_AXI_ACLK)                                 

             begin                                                    

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

                 begin                                                

                   expected_rdata <= C_M_START_DATA_VALUE;            

                 end                                                  

                 // Signals a new write address/ write data is         

                 // available by user logic                           

               else if (M_AXI_RVALID && axi_rready)                   

                 begin                                                

                   expected_rdata <= C_M_START_DATA_VALUE + read_index;

                 end                                                  

             end           

13:讀次數記錄read_index計數器

這個demo中以start_single_read信號作爲統計的,如果我們完全自定axi-lite_master代碼可以自行優化,可以寫出更好的代碼。我們這裏是用vivado模板產生的代碼主要線教會大家如何使用現有的手段和軟件工具學習axi4總線。

         always @(posedge M_AXI_ACLK)                                                    

         begin                                                                           

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

             begin                                                                       

               read_index <= 0;                                                           

             end                                                                         

           // Signals a new read address is                                              

           // available by user logic                                                     

           else if (start_single_read)                                                   

             begin                                                                       

               read_index <= read_index + 1;                                              

             end                                                                         

         end   

14:axi-lite-master的狀態機

         always @(posedge M_AXI_ACLK)                                 

             begin                                                    

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

                 begin                                                

                   expected_rdata <= C_M_START_DATA_VALUE;            

                 end                                                  

                 // Signals a new write address/ write data is        

                 // available by user logic                           

               else if (M_AXI_RVALID && axi_rready)                   

                 begin                                                

                   expected_rdata <= C_M_START_DATA_VALUE + read_index;

                 end                                                  

             end                                                       

         //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_write <= 1'b0;                                                

               write_issued  <= 1'b0;                                                     

               start_single_read  <= 1'b0;                                                

               read_issued   <= 1'b0;                                                     

               compare_done  <= 1'b0;                                                     

               ERROR <= 1'b0;

             end                                                                           

           else                                                                           

             begin                                                                        

              // state transition                                                          

               case (mst_exec_state)                                                      

                                                                                          

                 IDLE:                                                             

                 // This state is responsible to initiate

                 // AXI transaction when init_txn_pulse is asserted

                   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 last_write 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 && ~axi_wvalid && ~M_AXI_BVALID && ~last_write && ~start_single_write && ~write_issued)

                           begin                                                           

                             start_single_write <= 1'b1;                                  

                             write_issued  <= 1'b1;                                       

                           end                                                             

                         else if (axi_bready)                                             

                           begin                                                          

                             write_issued  <= 1'b0;                                       

                           end                                                            

                         else                                                             

                           begin                                                           

                             start_single_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 last_read 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 && ~M_AXI_RVALID && ~last_read && ~start_single_read && ~read_issued)

                          begin                                                           

                            start_single_read <= 1'b1;                                     

                            read_issued  <= 1'b1;                                         

                          end                                                             

                        else if (axi_rready)                                               

                          begin                                                           

                            read_issued  <= 1'b0;                                         

                          end                                                              

                        else                                                              

                          begin                                                           

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

                          end                                                             

                      end                                                                 

                                                                                          

                  INIT_COMPARE:                                                           

                    begin

                        // 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.           

                        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 

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

15:最後一個寫數據

         always @(posedge M_AXI_ACLK)                                                     

         begin                                                                            

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

             last_write <= 1'b0;                                                          

                                                                                           

           //The last write should be associated with a write address ready response      

           else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY)               

             last_write <= 1'b1;                                                           

           else                                                                           

             last_write <= last_write;                                                    

         end                                                                               

                                                                                          

         //Check for last write completion.                                               

                                                                                           

         //This logic is to qualify the last write count with the final write             

         //response. This demonstrates how to confirm that a write has been               

         //committed.                                                                      

                                                                                          

         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 (last_write && M_AXI_BVALID && axi_bready)                             

             writes_done <= 1'b1;                                                         

           else                                                                           

             writes_done <= writes_done;                                                  

         end      

16:最後一個讀數據

         always @(posedge M_AXI_ACLK)                                                     

         begin                                                                            

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

             last_read <= 1'b0;                                                           

                                                                                          

           //The last read should be associated with a read address ready response        

           else if ((read_index == C_M_TRANSACTIONS_NUM) && (M_AXI_ARREADY) )             

             last_read <= 1'b1;                                                           

           else                                                                            

             last_read <= last_read;                                                      

         end                                                                              

                                                                                           

       /*                                                                                 

        Check for last read completion.                                                   

                                                                                           

        This logic is to qualify the last read count with the final read                  

        response/data.                                                                    

        */                                                                                 

         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 read ready response               

           else if (last_read && M_AXI_RVALID && axi_rready)                              

             reads_done <= 1'b1;                                                           

           else                                                                           

             reads_done <= reads_done;                                                    

           end 

2.4實驗結果

具體的仿真方法省略,以下是仿真結果。

 

 

 

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