[X]关闭

2-3-04 AXI4总线axi-full-slave

文档创建者:uisrc
浏览次数:388
最后更新: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视频课程、答疑解惑!
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
2504661-20231230144430500-1814035037.jpg
2504661-20231230144431147-752232671.jpg
选择使用vivado自带的AXI总线模板创建一个AXI4-FULL接口IP
2504661-20231230144431592-291894126.jpg
设置IP的名字为saxi_full
2504661-20231230144432008-887527452.jpg
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream,这里选择full;
总线包括Master和Slave两种模式,这里选择Slave模式
2504661-20231230144432358-947687081.jpg

这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-FULL快速验证
2504661-20231230144432746-1015344926.jpg
单击Finish后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中saxi_full_0就是我们自定义的IP,另外一个master_0是用来读写我们自定义的saxi_full_0,以此验证我们的IP正确性。
2504661-20231230144433112-1449267989.jpg
采用默认地址分配即可
2504661-20231230144433455-1381811728.jpg

继续再看代码看看里面有什么东西
2504661-20231230144433864-846654341.jpg
3程序分析1:axi-full-slaveaxi_awready
当满足条件(~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)=1的时候表示可以进行一次AXI-FULL的burst写操作了,这个时候AXI-FULL-SLAVE设置axi_awready <= 1'b1和axi_awv_awr_flag <= 1'b1
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
    // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
    // de-asserted when reset is low.

    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_awready <= 1'b0;
          axi_awv_awr_flag <= 1'b0;
        end
      else
        begin   
          if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
            begin
              // slave is ready to accept an address and
              // associated control signals
              axi_awready <= 1'b1;
              axi_awv_awr_flag  <= 1'b1;
              // used for generation of bresp() and bvalid
            end
          else if (S_AXI_WLAST && axi_wready)         
          // preparing to accept next address after current write burst tx completion
            begin
              axi_awv_awr_flag  <= 1'b0;
            end
          else        
            begin
              axi_awready <= 1'b0;
            end
        end
    end   


2:axi-full-slaveaxi_awaddr
AXI的burst模式包括3种:
1:fixed burst这种模式下地址都是相同的
2: incremental burst这种模式下地址递增
3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。
本文demo中以下三种模式的具体代码如下:
    //This process is used to latch the address when both
    //S_AXI_ARVALID and S_AXI_RVALID are valid.
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_araddr <= 0;
          axi_arlen_cntr <= 0;
          axi_arburst <= 0;
          axi_arlen <= 0;
          axi_rlast <= 1'b0;
          axi_ruser <= 0;
        end
      else
        begin   
          if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
            begin
              // address latching
              axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
              axi_arburst <= S_AXI_ARBURST;
              axi_arlen <= S_AXI_ARLEN;   
              // start address of transfer
              axi_arlen_cntr <= 0;
              axi_rlast <= 1'b0;
            end  
          else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)        
            begin

              axi_arlen_cntr <= axi_arlen_cntr + 1;
              axi_rlast <= 1'b0;

              case (axi_arburst)
                2'b00: // fixed burst
                 // The read address for all the beats in the transaction are fixed
                  begin
                    axi_araddr       <= axi_araddr;        
                    //for arsize = 4 bytes (010)
                  end  
                2'b01: //incremental burst
                // The read address for all the beats in the transaction are increments by awsize
                  begin
                    axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
                    //araddr aligned to 4 byte boundary
                    axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};  
                    //for awsize = 4 bytes (010)
                  end  
                2'b10: //Wrapping burst
                // The read address wraps when the address reaches wrap boundary
                  if (ar_wrap_en)
                    begin
                      axi_araddr <= (axi_araddr - ar_wrap_size);
                    end
                  else
                    begin
                    axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
                    //araddr aligned to 4 byte boundary
                    axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};  
                    end                     
                default: //reserved (incremental burst for example)
                  begin
                    axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
                    //for arsize = 4 bytes (010)
                  end
              endcase              
            end
          else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )  
            begin
              axi_rlast <= 1'b1;
            end         
          else if (S_AXI_RREADY)  
            begin
              axi_rlast <= 1'b0;
            end         
        end
    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每次只读写一个数据。
    // axi_wready is asserted for one S_AXI_ACLK clock cycle when both
    // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
    // de-asserted when reset is low.

    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_wready <= 1'b0;
        end
      else
        begin   
          if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)
            begin
              // slave can accept the write data
              axi_wready <= 1'b1;
            end
          //else if (~axi_awv_awr_flag)
          else if (S_AXI_WLAST && axi_wready)
            begin
              axi_wready <= 1'b0;
            end
        end
    end      


4:axi-full-slaveaxi_bvalid信号
axi_bvalid用于告知axi master axi-slave端已经完成数据接收了
给出ACK,写操作LAST信号的下一个时钟,AXI-SLAVE给出ACK信号
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_bvalid <= 0;
          axi_bresp <= 2'b0;
          axi_buser <= 0;
        end
      else
        begin   
          if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )
            begin
              axi_bvalid <= 1'b1;
              axi_bresp  <= 2'b0;
              // 'OKAY' response
            end                  
          else
            begin
              if (S_AXI_BREADY && axi_bvalid)
              //check if bready is asserted while bvalid is high)
              //(there is a possibility that bready is always asserted high)  
                begin
                  axi_bvalid <= 1'b0;
                end  
            end
        end
     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
// S_AXI_ARVALID is asserted. axi_awready is
    // de-asserted when reset (active low) is asserted.
    // The read address is also latched when S_AXI_ARVALID is
    // asserted. axi_araddr is reset to zero on reset assertion.

    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_arready <= 1'b0;
          axi_arv_arr_flag <= 1'b0;
        end
      else
        begin   
          if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
            begin
              axi_arready <= 1'b1;
              axi_arv_arr_flag <= 1'b1;
            end
          else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)
          // preparing to accept next address after current read completion
            begin
              axi_arv_arr_flag  <= 1'b0;
            end
          else        
            begin
              axi_arready <= 1'b0;
            end
        end
    end      


6:axi-full-slaveaxi_araddr信号
AXI-的读写操作几乎是相对的代码,AXI的burst模式包括3种:
1:fixed burst这种模式下地址都是相同的
2: incremental burst这种模式下地址递增
3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。
本文demo种以下三种模式的具体代码如下:


//This process is used to latch the address when both
    //S_AXI_ARVALID and S_AXI_RVALID are valid.
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_araddr <= 0;
          axi_arlen_cntr <= 0;
          axi_arburst <= 0;
          axi_arlen <= 0;
          axi_rlast <= 1'b0;
          axi_ruser <= 0;
        end
      else
        begin   
          if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
            begin
              // address latching
              axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
              axi_arburst <= S_AXI_ARBURST;
              axi_arlen <= S_AXI_ARLEN;   
              // start address of transfer
              axi_arlen_cntr <= 0;
              axi_rlast <= 1'b0;
            end  
          else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)        
            begin

              axi_arlen_cntr <= axi_arlen_cntr + 1;
              axi_rlast <= 1'b0;

              case (axi_arburst)
                2'b00: // fixed burst
                 // The read address for all the beats in the transaction are fixed
                  begin
                    axi_araddr       <= axi_araddr;        
                    //for arsize = 4 bytes (010)
                  end  
                2'b01: //incremental burst
                // The read address for all the beats in the transaction are increments by awsize
                  begin
                    axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
                    //araddr aligned to 4 byte boundary
                    axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};  
                    //for awsize = 4 bytes (010)
                  end  
                2'b10: //Wrapping burst
                // The read address wraps when the address reaches wrap boundary
                  if (ar_wrap_en)
                    begin
                      axi_araddr <= (axi_araddr - ar_wrap_size);
                    end
                  else
                    begin
                    axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
                    //araddr aligned to 4 byte boundary
                    axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};  
                    end                     
                default: //reserved (incremental burst for example)
                  begin
                    axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
                    //for arsize = 4 bytes (010)
                  end
              endcase              
            end
          else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )  
            begin
              axi_rlast <= 1'b1;
            end         
          else if (S_AXI_RREADY)  
            begin
              axi_rlast <= 1'b0;
            end         
        end
    end   




7:axi-full-slaveaxi_rvalid信号
在用VIVADO模板产生的demo中,读操作数据不是连续读的,通过axi_rvalid设置AXI-SLAVE FULL读数据有效。
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_rvalid <= 0;
          axi_rresp  <= 0;
        end
      else
        begin   
          if (axi_arv_arr_flag && ~axi_rvalid)
            begin
              axi_rvalid <= 1'b1;
              axi_rresp  <= 2'b0;
              // 'OKAY' response
            end  
          else if (axi_rvalid && S_AXI_RREADY)
            begin
              axi_rvalid <= 1'b0;
            end            
        end
    end


8:数据保存到bock ram
以下是利用block ram完成数据的保存和回读
    // implement Block RAM(s)
    generate
      for(i=0; i<= USER_NUM_MEM-1; i=i+1)
        begin:BRAM_GEN
          wire mem_rden;
          wire mem_wren;

          assign mem_wren = axi_wready && S_AXI_WVALID ;

          assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid

          for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)
          begin:BYTE_BRAM_GEN
            wire [8-1:0] data_in ;
            wire [8-1:0] data_out;
            reg  [8-1:0] byte_ram [0 : 15];
            integer  j;

            //assigning 8 bit data
            assign data_in  = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];
            assign data_out = byte_ram[mem_address];

            always @( posedge S_AXI_ACLK )
            begin
              if (mem_wren && S_AXI_WSTRB[mem_byte_index])
                begin
                  byte_ram[mem_address] <= data_in;
                end  
            end   

            always @( posedge S_AXI_ACLK )
            begin
              if (mem_rden)
                begin
                  mem_data_out[(mem_byte_index*8+7) -: 8] <= data_out;
                end  
            end   

        end
      end      
    endgenerate


4实验结果
仿真结果:
2504661-20231230144434340-213613529.jpg

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

本版积分规则