[X]关闭

米联客-XILINX-H3_CZ08_7100] FPGA_AXI总线入门连载-04AXI4 总线 axi-full-master

文档创建者:FPGA课程
浏览次数:50
最后更新:2024-10-12
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 3-FPGA AXI 总线入门
本帖最后由 FPGA课程 于 2024-10-12 16:22 编辑

​软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!



1概述
使用XILINX 的软件工具VIVADO以及XILINX的7代以上的FPGA或者SOC掌握AXI-4总线结束,并且可以灵活使用AXI-4总线技术完成数据的交换,可以让我们在构建强大的FPGA内部总线数据互联通信方面取得高效、高速、标准化的优势。
关于AXI4总线协议的部分介绍请阅读“01AXI4总线axi-lite-slave”。
本文实验目的:
1:掌握基于VIVADO工具产生AXI协议模板
2:掌握通过VIVADO工具产生AXI-full-master代码
3:理解AXI-full-master中自定义寄存器的地址分配
4:掌握通过VIVADO封装AXI-full-slave图形化IP
5:通过仿真验证AXI-full-master IP的工作是否正常。
2创建axi4-full-master总线接口IP
新建fpga工程,过程省略
新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Full接口总线IP
5f5a66b98fe2422e9e635bb5fe619aa0.jpg
选择使用vivado自带的AXI总线模板创建一个AXI4-FULL接口IP
5d7f67bc40a74ad3bb3f5eff596eb56f.jpg
设置IP的名字为maxi_full
f9ba854c332e4b88a332cbd08ed39661.jpg
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream,这选择Full;总线包括Master和Slave两种模式,这里选择Master模式
5768317a9674442a8ceb48bbb0b06a31.jpg
这里选择Verify Peripheral IP using AXI4 VIP可以对AXI4-Lite快速验证
e3174761f6bb41f0b6374551b2fa10cc.jpg
单击Finish后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中maxi_full_0就是我们自定义的IP,另外一个slave_0是用来验证maxi_full_0正确性。
7fdc608774c04c7186fb7a85abe81a5e.jpg
采用默认地址分配即可
c0055c66ebd64bacb2031e9c818592d5.jpg
继续再看代码看看里面有什么东西
3db4d60afaa9413f9dd76b5a479354e6.jpg
路径uisrc/03_ip/ maxi_full_1.0/hdl路径下的maxi_full_v1_0_M00_AXI.v就是我们的源码。另外一个maxi_full_v1_0.v是软件产生了一个接口文件,如果我们自己定义IP可有可无。
3程序分析
axi总线信号的关键无非是地址和数据,而写地址的有效取决于AXI_AWVALID和AXI_AWREADY,写数据的有效取决于S_AXI_WVALID和S_AXI_WREADY。同理,读地址的有效取决于AXI_ARVALID和AXI_ARREADY,读数据的有效取决于S_AXI_RVALID和S_AXI_RREADY。所以以下代码的阅读分析注意也是围绕以上4个信号的有效时序。
以下程序我们把关键信号的代码拆分阅读
1:产生初始化信号
  1.     //Generate a pulse to initiate AXI transaction.
  2.     always @(posedge M_AXI_ACLK)                                             
  3.       begin                                                                        
  4.         // Initiates AXI transaction delay   
  5.         if (M_AXI_ARESETN == 0 )                                                   
  6.           begin                                                                    
  7.             init_txn_ff <= 1'b0;                                                   
  8.             init_txn_ff2 <= 1'b0;                                                   
  9.           end                                                                              
  10.         else                                                                       
  11.           begin  
  12.             init_txn_ff <= INIT_AXI_TXN;
  13.             init_txn_ff2 <= init_txn_ff;                                                                 
  14.           end                                                                     
  15.       end  
复制代码

2:axi-full-masteraxi_awvalid
当(~axi_awvalid && start_single_burst_write)==1条件满足,开始一次写传输,设置axi_awvalid有效。
  1. always @(posedge M_AXI_ACLK)                                   
  2.       begin                                                               
  3.                                                                            
  4.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                          
  5.           begin                                                            
  6.             axi_awvalid <= 1'b0;                                          
  7.           end                                                              
  8.         // If previously not valid , start next transaction               
  9.         else if (~axi_awvalid && start_single_burst_write)                 
  10.           begin                                                            
  11.             axi_awvalid <= 1'b1;                                          
  12.           end                                                              
  13.         /* Once asserted, VALIDs cannot be deasserted, so axi_awvalid      
  14.         must wait until transaction is accepted */                        
  15.         else if (M_AXI_AWREADY && axi_awvalid)                             
  16.           begin                                                            
  17.             axi_awvalid <= 1'b0;                                          
  18.           end                                                              
  19.         else                                                               
  20.           axi_awvalid <= axi_awvalid;                                      
  21.         end   
复制代码

3:axi-full-slaveaxi_awaddr
写通道地址每当M_AXI_AWREADY && axi_awvalid地址加1
  1. // Next address after AWREADY indicates previous address acceptance   
  2.       always @(posedge M_AXI_ACLK)                                         
  3.       begin                                                               
  4.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                            
  5.           begin                                                            
  6.             axi_awaddr <= 'b0;                                             
  7.           end                                                              
  8.         else if (M_AXI_AWREADY && axi_awvalid)                             
  9.           begin                                                            
  10.             axi_awaddr <= axi_awaddr + burst_size_bytes;                  
  11.           end                                                              
  12.         else                                                               
  13.           axi_awaddr <= axi_awaddr;                                       
  14.         end   
复制代码

4:axi-full-masteraxi_wvalid
设置axi_wvalid <= 1'b1开始写数据。wnext信号有效代码axi_full_master写数据有效。
  1. assign wnext = M_AXI_WREADY & axi_wvalid;                                   
  2.                                                                                        
  3.     // WVALID logic, similar to the axi_awvalid always block above                     
  4.       always @(posedge M_AXI_ACLK)                                                      
  5.       begin                                                                             
  6.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                                        
  7.           begin                                                                        
  8.             axi_wvalid <= 1'b0;                                                         
  9.           end                                                                           
  10.         // If previously not valid, start next transaction                              
  11.         else if (~axi_wvalid && start_single_burst_write)                              
  12.           begin                                                                        
  13.             axi_wvalid <= 1'b1;                                                         
  14.           end                                                                           
  15.         /* If WREADY and too many writes, throttle WVALID                              
  16.         Once asserted, VALIDs cannot be deasserted, so WVALID                           
  17.         must wait until burst is complete with WLAST */                                 
  18.         else if (wnext && axi_wlast)                                                   
  19.           axi_wvalid <= 1'b0;                                                           
  20.         else                                                                           
  21.           axi_wvalid <= axi_wvalid;                                                     
  22.       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中了。
  1. //WLAST generation on the MSB of a counter underflow                                
  2.     // WVALID logic, similar to the axi_awvalid always block above                     
  3.       always @(posedge M_AXI_ACLK)                                                      
  4.       begin                                                                             
  5.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                                        
  6.           begin                                                                        
  7.             axi_wlast <= 1'b0;                                                         
  8.           end                                                                           
  9.         // axi_wlast is asserted when the write index                                   
  10.         // count reaches the penultimate count to synchronize                           
  11.         // with the last write data when write_index is b1111                           
  12.         // else if (&(write_index[C_TRANSACTIONS_NUM-1:1])&& ~write_index[0] && wnext)  
  13.         else if (((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))
  14.           begin                                                                        
  15.             axi_wlast <= 1'b1;                                                         
  16.           end                                                                           
  17.         // Deassrt axi_wlast when the last write data has been                          
  18.         // accepted by the slave with a valid response                                 
  19.         else if (wnext)                                                                 
  20.           axi_wlast <= 1'b0;                                                            
  21.         else if (axi_wlast && C_M_AXI_BURST_LEN == 1)                                   
  22.           axi_wlast <= 1'b0;                                                            
  23.         else                                                                           
  24.           axi_wlast <= axi_wlast;                                                      
  25.       end        
复制代码
删除以上代码,并且添加以下代码:
  1.       wire  wlast=(write_index==C_M_AXI_BURST_LEN-1)&&wnext;                                                         
  2.       reg   wlast_r1 = 1'b0;
  3.       always @(posedge M_AXI_ACLK)
  4.         wlast_r <= wlast;
  5.       assign   axi_wlast = (wlast_r==1'b0)&&(wlast==1'b1);  
复制代码
另外需要修改reg axi_wlast;为wire axi_wlast;
这样就可以确保发送wlast的时候数据肯定是有效的。
6:写次数记录write_index计数器
  1. /* Burst length counter. Uses extra counter register bit to indicate terminal      
  2.      count to reduce decode logic */                                                   
  3.       always @(posedge M_AXI_ACLK)                                                      
  4.       begin                                                                             
  5.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_write == 1'b1)   
  6.           begin                                                                        
  7.             write_index <= 0;                                                           
  8.           end                                                                           
  9.         else if (wnext && (write_index != C_M_AXI_BURST_LEN-1))                        
  10.           begin                                                                        
  11.             write_index <= write_index + 1;                                             
  12.           end                                                                           
  13.         else                                                                           
  14.           write_index <= write_index;                                                   
  15.       end      
复制代码

7:axi-full-masteraxi_wdata
axi_full_master写数据计数写数据
  1.   /* Write Data Generator                                                            
  2.      Data pattern is only a simple incrementing count from 0 for each burst  */         
  3.       always @(posedge M_AXI_ACLK)                                                      
  4.       begin                                                                             
  5.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
  6.           axi_wdata <= 'b1;                                                            
  7.         //else if (wnext && axi_wlast)                                                  
  8.         //  axi_wdata <= 'b0;                                                           
  9.         else if (wnext)                                                                 
  10.           axi_wdata <= axi_wdata + 1;                                                   
  11.         else                                                                           
  12.           axi_wdata <= axi_wdata;                                                      
  13.         end   
复制代码

8:axi-full-masteraxi_bready
设置axi_bready信号
  1. always @(posedge M_AXI_ACLK)                                    
  2.       begin                                                                 
  3.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                            
  4.           begin                                                            
  5.             axi_bready <= 1'b0;                                             
  6.           end                                                               
  7.         // accept/acknowledge bresp with axi_bready by the master           
  8.         // when M_AXI_BVALID is asserted by slave                           
  9.         else if (M_AXI_BVALID && ~axi_bready)                              
  10.           begin                                                            
  11.             axi_bready <= 1'b1;                                             
  12.           end                                                               
  13.         // deassert after one clock cycle                                   
  14.         else if (axi_bready)                                                
  15.           begin                                                            
  16.             axi_bready <= 1'b0;                                             
  17.           end                                                               
  18.         // retain the previous value                                       
  19.         else                                                               
  20.           axi_bready <= axi_bready;                                         
  21.       end
复制代码

9:axi-full-slaveaxi_arvalid
Axi_full_master读通道的分析非常类似,代码是对称的。
当(~axi_arvalid && start_single_burst_read)==1条件满足,开始一次写传输,设置axi_arvalid有效。
  1. always @(posedge M_AXI_ACLK)                                 
  2.       begin                                                              
  3.                                                                         
  4.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                         
  5.           begin                                                         
  6.             axi_arvalid <= 1'b0;                                         
  7.           end                                                            
  8.         // If previously not valid , start next transaction              
  9.         else if (~axi_arvalid && start_single_burst_read)               
  10.           begin                                                         
  11.             axi_arvalid <= 1'b1;                                         
  12.           end                                                            
  13.         else if (M_AXI_ARREADY && axi_arvalid)                           
  14.           begin                                                         
  15.             axi_arvalid <= 1'b0;                                         
  16.           end                                                            
  17.         else                                                            
  18.           axi_arvalid <= axi_arvalid;                                    
  19.       end           
复制代码

10:axi-full-slaveaxi_araddr
读地址计算
  1. // Next address after ARREADY indicates previous address acceptance  
  2.       always @(posedge M_AXI_ACLK)                                       
  3.       begin                                                              
  4.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                          
  5.           begin                                                         
  6.             axi_araddr <= 'b0;                                          
  7.           end                                                            
  8.         else if (M_AXI_ARREADY && axi_arvalid)                           
  9.           begin                                                         
  10.             axi_araddr <= axi_araddr + burst_size_bytes;                 
  11.           end                                                            
  12.         else                                                            
  13.           axi_araddr <= axi_araddr;                                      
  14.       end   
复制代码

11:axi-full-masteraxi_rready
读数据准备好
  1. /*  The Read Data channel returns the results of the read request        
  2.      In this example the data checker is always able to accept              
  3.      more data, so no need to throttle the RREADY signal             */        
  4.       always @(posedge M_AXI_ACLK)                                          
  5.       begin                                                                 
  6.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                  
  7.           begin                                                            
  8.             axi_rready <= 1'b0;                                             
  9.           end                                                               
  10.         // accept/acknowledge rdata/rresp with axi_rready by the master     
  11.         // when M_AXI_RVALID is asserted by slave                           
  12.         else if (M_AXI_RVALID)                       
  13.           begin                                      
  14.              if (M_AXI_RLAST && axi_rready)         
  15.               begin                                 
  16.                 axi_rready <= 1'b0;                  
  17.               end                                    
  18.              else                                    
  19.                begin                                 
  20.                  axi_rready <= 1'b1;                 
  21.                end                                   
  22.           end                                       
  23.         // retain the previous value                 
  24.       end  
复制代码

12:读次数记录read_index计数器
读数据索引计数
  1. assign rnext = M_AXI_RVALID && axi_rready;                                                                                 
  2.     // Burst length counter. Uses extra counter register bit to indicate   
  3.     // terminal count to reduce decode logic                                
  4.       always @(posedge M_AXI_ACLK)                                          
  5.       begin                                                                 
  6.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_read)                  
  7.           begin                                                            
  8.             read_index <= 0;                                                
  9.           end                                                               
  10.         else if (rnext && (read_index != C_M_AXI_BURST_LEN-1))              
  11.           begin                                                            
  12.             read_index <= read_index + 1;                                   
  13.           end                                                               
  14.         else                                                               
  15.           read_index <= read_index;                                         
  16.       end   
复制代码

13:产生对比数据expected_rdata
数据expected_rdata用于和读出的M_AXI_RDATA进行对比以此验证数据的正确性。
  1. //Generate expected read data to check against actual read data
  2.       always @(posedge M_AXI_ACLK)                     
  3.       begin                                                  
  4.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)// || M_AXI_RLAST)            
  5.             expected_rdata <= 'b1;                           
  6.         else if (M_AXI_RVALID && axi_rready)                  
  7.             expected_rdata <= expected_rdata + 1;            
  8.         else                                                  
  9.             expected_rdata <= expected_rdata;                 
  10.       end
复制代码

14:比对数据正确性
读写数据比较
  1. //Check received read data against data generator                       
  2.       always @(posedge M_AXI_ACLK)                                          
  3.       begin                                                                 
  4.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                  
  5.           begin                                                            
  6.             read_mismatch <= 1'b0;                                          
  7.           end                                                               
  8.         //Only check data when RVALID is active                             
  9.         else if (rnext && (M_AXI_RDATA != expected_rdata))                  
  10.           begin                                                            
  11.             read_mismatch <= 1'b1;                                          
  12.           end                                                               
  13.         else                                                               
  14.           read_mismatch <= 1'b0;                                            
  15.       end           
复制代码

15:读写状态机
读写状态机源码
  1. begin                                                                                         
  2.                     mst_exec_state <= INIT_READ;//                                                              
  3.                   end                                                                                          
  4.                 else                                                                                            
  5.                   begin                                                                                         
  6.                     mst_exec_state  <= INIT_WRITE;                                                              
  7.                     if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active)                       
  8.                       begin                                                                                    
  9.                         start_single_burst_write <= 1'b1;                                                      
  10.                       end                                                                                       
  11.                     else                                                                                       
  12.                       begin                                                                                    
  13.                         start_single_burst_write <= 1'b0; //Negate to generate a pulse                          
  14.                       end                                                                                       
  15.                   end                                                                                          
  16.               INIT_READ:                                                                                       
  17.                 // This state is responsible to issue start_single_read pulse to                                
  18.                 // initiate a read transaction. Read transactions will be                                       
  19.                 // issued until burst_read_active signal is asserted.                                          
  20.                 // read controller                                                                              
  21.                 if (reads_done)                                                                                 
  22.                   begin                                                                                         
  23.                     mst_exec_state <= INIT_COMPARE;                                                            
  24.                   end                                                                                          
  25.                 else                                                                                            
  26.                   begin                                                                                         
  27.                     mst_exec_state  <= INIT_READ;                                                               
  28.                     if (~axi_arvalid && ~burst_read_active && ~start_single_burst_read)                        
  29.                       begin                                                                                    
  30.                         start_single_burst_read <= 1'b1;                                                        
  31.                       end                                                                                       
  32.                    else                                                                                         
  33.                      begin                                                                                      
  34.                        start_single_burst_read <= 1'b0; //Negate to generate a pulse                           
  35.                      end                                                                                       
  36.                   end                                                                                          
  37.               INIT_COMPARE:                                                                                    
  38.                 // This state is responsible to issue the state of comparison                                   
  39.                 // of written data with the read data. If no error flags are set,                              
  40.                 // compare_done signal will be asseted to indicate success.                                    
  41.                 //if (~error_reg)                                                                              
  42.                 begin                                                                                          
  43.                   ERROR <= error_reg;
  44.                   mst_exec_state <= IDLE;                                                               
  45.                   compare_done <= 1'b1;                                                                        
  46.                 end                                                                                             
  47.               default :                                                                                         
  48.                 begin                                                                                          
  49.                   mst_exec_state  <= IDLE;                                                              
  50.                 end                                                                                             
  51.             endcase                                                                                             
  52.           end                                                                                                   
  53.       end //MASTER_EXECUTION_PROC      
复制代码

整理成流程图,更加容易理解:
a0fc332c45b445ee9eda3a70f1ef5d20.jpg
16:正在写burst_write_active
burst_write_active代表正在写操作
  1. always @(posedge M_AXI_ACLK)                                                                              
  2.       begin                                                                                                     
  3.         if(M_AXI_ARESETN==0||init_txn_pulse==1'b1)                                                                                 
  4.           burst_write_active<=1'b0;                                                                                                
  5.         //The burst_write_active is asserted when a write burst transaction is initiated                        
  6.         else if (start_single_burst_write)                                                                     
  7.           burst_write_active <= 1'b1;                                                                           
  8.         else if (M_AXI_BVALID && axi_bready)                                                                    
  9.           burst_write_active <= 0;                                                                              
  10.       end  
复制代码

  
17:写完成writes_done
写数据完成writes_done信号
  1.       always @(posedge M_AXI_ACLK)                                                                              
  2.       begin                                                                                                     
  3.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
  4.           writes_done <= 1'b0;                                                  
  5.         //The writes_done should be associated with a bready response                                          
  6.         //else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)
  7.         else if (M_AXI_BVALID && (write_burst_counter[C_NO_BURSTS_REQ]) && axi_bready)                          
  8.           writes_done <= 1'b1;                                                                                 
  9.         else                                                                                                   
  10.           writes_done <= writes_done;                                                                           
  11.         end
复制代码

18:正在读burst_read_active
读burst_read_active代表正在读数据
  1. // burst_read_active signal is asserted when there is a burst write transaction                           
  2.       // is initiated by the assertion of start_single_burst_write. start_single_burst_read                     
  3.       // signal remains asserted until the burst read is accepted by the master                                 
  4.       always @(posedge M_AXI_ACLK)                                                                              
  5.       begin                                                                                                     
  6.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                
  7.           burst_read_active <= 1'b0;                                                                  
  8.         //The burst_write_active is asserted when a write burst transaction is initiated                        
  9.         else if (start_single_burst_read)                                                                       
  10.           burst_read_active <= 1'b1;                                                                           
  11.         else if (M_AXI_RVALID && axi_rready && M_AXI_RLAST)                                                     
  12.           burst_read_active <= 0;                                                                              
  13.         end         
复制代码

19:读完成reads_done
读数据完成reads_done信号
  1.       always @(posedge M_AXI_ACLK)                                                                              
  2.       begin                                                                                                     
  3.         if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)     
  4.           reads_done <= 1'b0;                                                                                   
  5.         //The reads_done should be associated with a rready response                                            
  6.         //else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)
  7.         else if (M_AXI_RVALID && axi_rready && (read_index == C_M_AXI_BURST_LEN-1) && (read_burst_counter[C_NO_BURSTS_REQ]))
  8.           reads_done <= 1'b1;                                                                                   
  9.         else                                                                                                   
  10.           reads_done <= reads_done;                                                                             
  11.         end
复制代码

20:IP的更新
由于修改了代码,需要先更新IP状态,完成IP更新
c0f1dbca58bd41ae962b42080be86261.jpg
730b8ced166f4ffca7147cbca47b89a2.jpg
4实验结果
仿真结果
d71c90c4095748cbb73cd821e70b1d88.jpg
一次axi4写操作burst lenth=16如下图所示,由于WREADY信号不是连续的,所以可以传输效率不是最高的
f466f965ec95487c85f6b93d983cb274.jpg
一共进行64次burst共计写了1024个32bit数据
26db8dbbd0a941c99719b45d293fbf75.jpg
一次读操作的burst lenth也是16如下图,但是可以看到读数据时连续的,所以效率最高
0afc90583f644d27ab27b8ba544959e8.jpg
一共进行64次burst共计读了1024个32bit数据

69cea601706b4584bfe64b8569ffdd5c.jpg
可以看到读出的数据RDATA和expected_rdata一致,所以代码正确。

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

本版积分规则