[X]关闭

2-3-03 AXI4总线axi-lite-master总线协议

文档创建者:uisrc
浏览次数:392
最后更新:2023-12-30
文档课程分类
AMD: FPGA部分(2024样板资料) » 2_FPGA实验篇(仅旗舰) » 3-FPGA AXI 总线入门
软件版本:vitis2021.1(vivado2021.1)
操作系统:WIN10 64bit
硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA
登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!
2.1概述
使用XILINX 的软件工具VIVADO以及XILINX的7代以上的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
2504661-20231230144219262-546262846.jpg
2504661-20231230144219757-1736128385.jpg
选择使用vivado自带的AXI总线模板创建一个AXI4-Lite接口IP
2504661-20231230144220270-1045184781.jpg
设置IP的名字为maxi_lite
2504661-20231230144225653-1765963498.jpg
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream
2504661-20231230144226082-355417751.jpg
总线包括Master和Slave两种模式,这里选择Master模式
2504661-20231230144226463-1424903309.jpg
这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-Lite快速验证
2504661-20231230144226861-1776964529.jpg
单击Finish 后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中maxi_lite_0就是我们自定义的IP,另外一个slave_0是用来验证maxi_lite_0正确性。
2504661-20231230144227235-972982667.jpg
采用默认地址分配即可
2504661-20231230144232590-251706836.jpg

继续再看代码看看里面有什么东西
2504661-20231230144233048-905163490.jpg
路径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_AWVALID和AXI_AWREADY,写数据的有效取决于S_AXI_WVALID和S_AXI_WREADY。同理,读地址的有效取决于AXI_ARVALID和AXI_ARREADY,读数据的有效取决于S_AXI_RVALID和S_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                                                                     
          //用户逻辑发出新地址/数据命令的信号         
        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-masteraxi_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_bready为1,BRESP返回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-masteraxi_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-masteraxi_araddr
      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


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


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                    



整理成流程图,更加容易理解:
2504661-20231230144233537-882568580.jpg
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实验结果
具体的仿真方法省略,以下是仿真结果。
2504661-20231230144233995-512149871.jpg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则