[X]关闭

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

文档创建者:FPGA课程
浏览次数:105
最后更新: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-slave代码
3:理解AXI-full-slave中自定义寄存器的地址分配
4:掌握通过VIVADO封装AXI-full-slave图形化IP
5:通过仿真验证AXI-full-slave IP的工作是否正常。
2创建axi4-full-slave总线接口IP
新建fpga工程,过程省略
新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Full接口总线IP
5b14b78b941a486b89ea6754630c0b2f.jpg
c9712c4fe8e64c7caad636c1cb118c1a.jpg
选择使用vivado自带的AXI总线模板创建一个AXI4-FULL接口IP
680adfeffaea446daf510d79d19a0527.jpg
设置IP的名字为saxi_full
0a142720bee041d8b5a4f9b9527be57b.jpg
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream,这里选择full;
总线包括Master和Slave两种模式,这里选择Slave模式
8e82dcd3aa8b436188210ceb25543a00.jpg
这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-FULL快速验证
055719bf29a24b1ca2b7b9e949dafae3.jpg
单击Finish后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中saxi_full_0就是我们自定义的IP,另外一个master_0是用来读写我们自定义的saxi_full_0,以此验证我们的IP正确性。
707b3a85bfd74dd9b7f2c6b6ea9a74ae.jpg
采用默认地址分配即可
86d296f270e24f7a904bb400f2b25747.jpg
继续再看代码看看里面有什么东西
fc5882762d4d4a40810a074118e2ecee.jpg
路径 uisrc/03_ip/ saxi_full_1.0/hdl 路径下的 saxi_full_v1_0_S00_AXI.v 就是我们的源码。另外一个 saxi_full_v1_0.v是软件产生了一个接口文件,如果我们自己定义 IP 可有可无。
3程序分析
1:axi-full-slaveaxi_awready
  1. // axi_awready is asserted for one S_AXI_ACLK clock cycle when both
  2.     // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
  3.     // de-asserted when reset is low.

  4.     always @( posedge S_AXI_ACLK )
  5.     begin
  6.       if ( S_AXI_ARESETN == 1'b0 )
  7.         begin
  8.           axi_awready <= 1'b0;
  9.           axi_awv_awr_flag <= 1'b0;
  10.         end
  11.       else
  12.         begin   
  13.           if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
  14.             begin
  15.               // slave is ready to accept an address and
  16.               // associated control signals
  17.               axi_awready <= 1'b1;
  18.               axi_awv_awr_flag  <= 1'b1;
  19.               // used for generation of bresp() and bvalid
  20.             end
  21.           else if (S_AXI_WLAST && axi_wready)         
  22.           // preparing to accept next address after current write burst tx completion
  23.             begin
  24.               axi_awv_awr_flag  <= 1'b0;
  25.             end
  26.           else        
  27.             begin
  28.               axi_awready <= 1'b0;
  29.             end
  30.         end
  31.     end   
复制代码

2:axi-full-slaveaxi_awaddr
AXI的burst模式包括3种:
1:fixed burst这种模式下地址都是相同的
2: incremental burst这种模式下地址递增
3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。
本文demo中以下三种模式的具体代码如下:
  1. //This process is used to latch the address when both
  2.     //S_AXI_ARVALID and S_AXI_RVALID are valid.
  3.     always @( posedge S_AXI_ACLK )
  4.     begin
  5.       if ( S_AXI_ARESETN == 1'b0 )
  6.         begin
  7.           axi_araddr <= 0;
  8.           axi_arlen_cntr <= 0;
  9.           axi_arburst <= 0;
  10.           axi_arlen <= 0;
  11.           axi_rlast <= 1'b0;
  12.           axi_ruser <= 0;
  13.         end
  14.       else
  15.         begin   
  16.           if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
  17.             begin
  18.               // address latching
  19.               axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
  20.               axi_arburst <= S_AXI_ARBURST;
  21.               axi_arlen <= S_AXI_ARLEN;     
  22.               // start address of transfer
  23.               axi_arlen_cntr <= 0;
  24.               axi_rlast <= 1'b0;
  25.             end   
  26.           else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)        
  27.             begin
  28.             
  29.               axi_arlen_cntr <= axi_arlen_cntr + 1;
  30.               axi_rlast <= 1'b0;
  31.             
  32.               case (axi_arburst)
  33.                 2'b00: // fixed burst
  34.                  // The read address for all the beats in the transaction are fixed
  35.                   begin
  36.                     axi_araddr       <= axi_araddr;        
  37.                     //for arsize = 4 bytes (010)
  38.                   end   
  39.                 2'b01: //incremental burst
  40.                 // The read address for all the beats in the transaction are increments by awsize
  41.                   begin
  42.                     axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
  43.                     //araddr aligned to 4 byte boundary
  44.                     axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
  45.                     //for awsize = 4 bytes (010)
  46.                   end   
  47.                 2'b10: //Wrapping burst
  48.                 // The read address wraps when the address reaches wrap boundary
  49.                   if (ar_wrap_en)
  50.                     begin
  51.                       axi_araddr <= (axi_araddr - ar_wrap_size);
  52.                     end
  53.                   else
  54.                     begin
  55.                     axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
  56.                     //araddr aligned to 4 byte boundary
  57.                     axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
  58.                     end                     
  59.                 default: //reserved (incremental burst for example)
  60.                   begin
  61.                     axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
  62.                     //for arsize = 4 bytes (010)
  63.                   end
  64.               endcase              
  65.             end
  66.           else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )   
  67.             begin
  68.               axi_rlast <= 1'b1;
  69.             end         
  70.           else if (S_AXI_RREADY)   
  71.             begin
  72.               axi_rlast <= 1'b0;
  73.             end         
  74.         end
  75.     end
复制代码

3:axi-full-slaveaxi_wready
当满足条件( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)==1 设置axi_wready为1.这里可以看出,S_AXI_WVALID必须在一次burst中持续有效,直到满足条件(S_AXI_WLAST && axi_wready),否则AXI-FULL-SLAVE会出错,这一点有别于AXI-LITE-SLAVE每次只读写一个数据。
  1. // axi_wready is asserted for one S_AXI_ACLK clock cycle when both
  2.     // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
  3.     // de-asserted when reset is low.

  4.     always @( posedge S_AXI_ACLK )
  5.     begin
  6.       if ( S_AXI_ARESETN == 1'b0 )
  7.         begin
  8.           axi_wready <= 1'b0;
  9.         end
  10.       else
  11.         begin   
  12.           if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)
  13.             begin
  14.               // slave can accept the write data
  15.               axi_wready <= 1'b1;
  16.             end
  17.           //else if (~axi_awv_awr_flag)
  18.           else if (S_AXI_WLAST && axi_wready)
  19.             begin
  20.               axi_wready <= 1'b0;
  21.             end
  22.         end
  23.     end      
复制代码

4:axi-full-slaveaxi_bvalid信号
axi_bvalid用于告知axi master axi-slave端已经完成数据接收了
给出ACK,写操作LAST信号的下一个时钟,AXI-SLAVE给出ACK信号
  1.     always @( posedge S_AXI_ACLK )
  2.     begin
  3.       if ( S_AXI_ARESETN == 1'b0 )
  4.         begin
  5.           axi_bvalid <= 0;
  6.           axi_bresp <= 2'b0;
  7.           axi_buser <= 0;
  8.         end
  9.       else
  10.         begin   
  11.           if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )
  12.             begin
  13.               axi_bvalid <= 1'b1;
  14.               axi_bresp  <= 2'b0;
  15.               // 'OKAY' response
  16.             end                  
  17.           else
  18.             begin
  19.               if (S_AXI_BREADY && axi_bvalid)
  20.               //check if bready is asserted while bvalid is high)
  21.               //(there is a possibility that bready is always asserted high)   
  22.                 begin
  23.                   axi_bvalid <= 1'b0;
  24.                 end  
  25.             end
  26.         end
  27.      end  
复制代码

5:axi-full-slaveaxi_arready信号
当满足条件(~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)=1的时候表示可以进行一次AXI-FULL的burst读操作了,这个时候AXI -FULL-SLAVE设置axi_arready <= 1'b1和axi_arv_arr_flag  <= 1'b1
  1. // S_AXI_ARVALID is asserted. axi_awready is
  2.     // de-asserted when reset (active low) is asserted.
  3.     // The read address is also latched when S_AXI_ARVALID is
  4.     // asserted. axi_araddr is reset to zero on reset assertion.

  5.     always @( posedge S_AXI_ACLK )
  6.     begin
  7.       if ( S_AXI_ARESETN == 1'b0 )
  8.         begin
  9.           axi_arready <= 1'b0;
  10.           axi_arv_arr_flag <= 1'b0;
  11.         end
  12.       else
  13.         begin   
  14.           if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
  15.             begin
  16.               axi_arready <= 1'b1;
  17.               axi_arv_arr_flag <= 1'b1;
  18.             end
  19.           else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)
  20.           // preparing to accept next address after current read completion
  21.             begin
  22.               axi_arv_arr_flag  <= 1'b0;
  23.             end
  24.           else        
  25.             begin
  26.               axi_arready <= 1'b0;
  27.             end
  28.         end
  29.     end     
复制代码

6:axi-full-slaveaxi_araddr信号
AXI-的读写操作几乎是相对的代码,AXI的burst模式包括3种:
1:fixed burst这种模式下地址都是相同的
2: incremental burst这种模式下地址递增
3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。
本文demo种以下三种模式的具体代码如下:
  1. //This process is used to latch the address when both
  2.     //S_AXI_ARVALID and S_AXI_RVALID are valid.
  3.     always @( posedge S_AXI_ACLK )
  4.     begin
  5.       if ( S_AXI_ARESETN == 1'b0 )
  6.         begin
  7.           axi_araddr <= 0;
  8.           axi_arlen_cntr <= 0;
  9.           axi_arburst <= 0;
  10.           axi_arlen <= 0;
  11.           axi_rlast <= 1'b0;
  12.           axi_ruser <= 0;
  13.         end
  14.       else
  15.         begin   
  16.           if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
  17.             begin
  18.               // address latching
  19.               axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
  20.               axi_arburst <= S_AXI_ARBURST;
  21.               axi_arlen <= S_AXI_ARLEN;     
  22.               // start address of transfer
  23.               axi_arlen_cntr <= 0;
  24.               axi_rlast <= 1'b0;
  25.             end   
  26.           else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)        
  27.             begin
  28.             
  29.               axi_arlen_cntr <= axi_arlen_cntr + 1;
  30.               axi_rlast <= 1'b0;
  31.             
  32.               case (axi_arburst)
  33.                 2'b00: // fixed burst
  34.                  // The read address for all the beats in the transaction are fixed
  35.                   begin
  36.                     axi_araddr       <= axi_araddr;        
  37.                     //for arsize = 4 bytes (010)
  38.                   end   
  39.                 2'b01: //incremental burst
  40.                 // The read address for all the beats in the transaction are increments by awsize
  41.                   begin
  42.                     axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
  43.                     //araddr aligned to 4 byte boundary
  44.                     axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
  45.                     //for awsize = 4 bytes (010)
  46.                   end   
  47.                 2'b10: //Wrapping burst
  48.                 // The read address wraps when the address reaches wrap boundary
  49.                   if (ar_wrap_en)
  50.                     begin
  51.                       axi_araddr <= (axi_araddr - ar_wrap_size);
  52.                     end
  53.                   else
  54.                     begin
  55.                     axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
  56.                     //araddr aligned to 4 byte boundary
  57.                     axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
  58.                     end                     
  59.                 default: //reserved (incremental burst for example)
  60.                   begin
  61.                     axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
  62.                     //for arsize = 4 bytes (010)
  63.                   end
  64.               endcase              
  65.             end
  66.           else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )   
  67.             begin
  68.               axi_rlast <= 1'b1;
  69.             end         
  70.           else if (S_AXI_RREADY)   
  71.             begin
  72.               axi_rlast <= 1'b0;
  73.             end         
  74.         end
  75.     end   
复制代码

7:axi-full-slaveaxi_rvalid信号
在用VIVADO模板产生的demo中,读操作数据不是连续读的,通过axi_rvalid设置AXI-SLAVE FULL读数据有效。
  1. always @( posedge S_AXI_ACLK )
  2.     begin
  3.       if ( S_AXI_ARESETN == 1'b0 )
  4.         begin
  5.           axi_rvalid <= 0;
  6.           axi_rresp  <= 0;
  7.         end
  8.       else
  9.         begin   
  10.           if (axi_arv_arr_flag && ~axi_rvalid)
  11.             begin
  12.               axi_rvalid <= 1'b1;
  13.               axi_rresp  <= 2'b0;
  14.               // 'OKAY' response
  15.             end   
  16.           else if (axi_rvalid && S_AXI_RREADY)
  17.             begin
  18.               axi_rvalid <= 1'b0;
  19.             end            
  20.         end
  21.     end
复制代码

8:数据保存到bockram
以下是利用block ram完成数据的保存和回读
  1.   // implement Block RAM(s)
  2.     generate
  3.       for(i=0; i<= USER_NUM_MEM-1; i=i+1)
  4.         begin:BRAM_GEN
  5.           wire mem_rden;
  6.           wire mem_wren;
  7.    
  8.           assign mem_wren = axi_wready && S_AXI_WVALID ;
  9.    
  10.           assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid
  11.          
  12.           for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)
  13.           begin:BYTE_BRAM_GEN
  14.             wire [8-1:0] data_in ;
  15.             wire [8-1:0] data_out;
  16.             reg  [8-1:0] byte_ram [0 : 15];
  17.             integer  j;
  18.          
  19.             //assigning 8 bit data
  20.             assign data_in  = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];
  21.             assign data_out = byte_ram[mem_address];
  22.          
  23.             always @( posedge S_AXI_ACLK )
  24.             begin
  25.               if (mem_wren && S_AXI_WSTRB[mem_byte_index])
  26.                 begin
  27.                   byte_ram[mem_address] <= data_in;
  28.                 end   
  29.             end   
  30.          
  31.             always @( posedge S_AXI_ACLK )
  32.             begin
  33.               if (mem_rden)
  34.                 begin
  35.                   mem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;
  36.                 end   
  37.             end   
  38.                   
  39.         end
  40.       end      
  41.     endgenerate
复制代码

4实验结果
仿真结果:
5447bcaaacbd47dc8cf389cee8e647ea.jpg

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

本版积分规则