zoukankan      html  css  js  c++  java
  • 03AXI4总线axi-full-slave

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

    操作系统:WIN10 64bit

    硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA(本文使用米联客(milianke)MZU07A-EG开发板)

    登录“米联客”FPGA社区-www.uisrc.com视频课程、答疑解惑!

    3.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的工作是否正常。

    3.2创建axi4-full-slave总线接口IP

    新建fpga工程,过程省略

    新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Full接口总线IP

    选择使用vivado自带的AXI总线模板创建一个AXI4-FULL接口IP

    设置IP的名字为saxi_full

    模板支持3中协议,分别是AXI4-Full AXI4-Lite AXI4-Stream, 这里选择ful;

    总线包括Master和Slave两种模式,这里选择Slave模式

    这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-FULL快速验证

    单击Finish 后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中saxi_full_0就是我们自定义的IP,另外一个master_0是用来读写我们自定义的saxi_full_0,以此验证我们的IP正确性。

    采用默认地址分配即可

    继续站看代码看看里面有什么东西

    3.3程序分析

    1:axi-full-slave的axi_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-slave的axi_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_AWVALID and S_AXI_WVALID are valid.

           always @( posedge S_AXI_ACLK )

           begin

             if ( S_AXI_ARESETN == 1'b0 )

               begin

                 axi_awaddr <= 0;

                 axi_awlen_cntr <= 0;

                 axi_awburst <= 0;

                 axi_awlen <= 0;

               end

             else

               begin   

                 if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)

                   begin

                     // address latching

                     axi_awaddr <= S_AXI_AWADDR[C_S_AXI_ADDR_WIDTH - 1:0]; 

                      axi_awburst <= S_AXI_AWBURST;

                      axi_awlen <= S_AXI_AWLEN;    

                     // start address of transfer

                     axi_awlen_cntr <= 0;

                   end  

                 else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)       

                   begin

                     axi_awlen_cntr <= axi_awlen_cntr + 1;

                     case (axi_awburst)

                       2'b00: // fixed burst

                       // The write address for all the beats in the transaction are fixed

                         begin

                           axi_awaddr <= axi_awaddr;         

                           //for awsize = 4 bytes (010)

                         end  

                       2'b01: //incremental burst

                       // The write address for all the beats in the transaction are increments by awsize

                         begin

                           axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

                           //awaddr aligned to 4 byte boundary

                           axi_awaddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};  

                           //for awsize = 4 bytes (010)

                         end  

                       2'b10: //Wrapping burst

                       // The write address wraps when the address reaches wrap boundary

                         if (aw_wrap_en)

                           begin

                             axi_awaddr <= (axi_awaddr - aw_wrap_size);

                           end

                         else

                           begin

                             axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

                             axi_awaddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};

                           end                     

                       default: //reserved (incremental burst for example)

                         begin

                           axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;

                           //for awsize = 4 bytes (010)

                         end

                     endcase             

                   end

               end

           end      

    3:axi-full-slave的axi_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-slave的axi_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-slave的axi_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

    // axi_arready is asserted for one S_AXI_ACLK clock cycle when

           // 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-slave的axi_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-slave的axi_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[i][(mem_byte_index*8+7) -: 8] <= data_out;

                       end  

                   end   

                         

               end

             end      

           endgenerate

    3.4实验结果

    仿真结果:

  • 相关阅读:
    Java入门学习路线目录索引(持续更新中)
    关于技术面试,面试官会怎么考察?
    什么是REST以及 RESTful?
    程序猿一般可以从什么平台接私活
    Statement常用的方法回顾
    信息网络安全协会学习总结提交规范
    20155322 2017-2018-1 《信息安全系统设计》第五周 MyBash实现
    20155322 2017-2018-1《信息安全系统设计》第五周 学习总结
    2017-2018-1 20155322 20155327 实验一 开发环境的熟悉
    20155322 2017-2018-1《信息安全系统设计》第四周学习总结
  • 原文地址:https://www.cnblogs.com/milinker/p/15142375.html
Copyright © 2011-2022 走看看