[X]关闭

2-3-02 AXI4总线axi-lite-slave总线协议

文档创建者:uisrc
浏览次数:399
最后更新: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内部总线数据互联通信方面取得高效、高速、标准化的优势。
本文实验目的:
1:学习AXI总线协议包括AXI-FULL、AXI-Lite
2:掌握基于VIVADO工具产生AXI协议模板
3:掌握通过VIVADO工具产生AXI-lite-Slave代码,并且会修改寄存器
4:理解AXI-lite-Slave中自定义寄存器的地址分配
5:掌握通过VIVADO封装AXI-LITE-SLAVE 图形化IP
6:通过仿真验证AXI-LITE IP的工作是否正常。
2 axi-lite介绍
AXI_lite是轻量级的AXI协议,它每次传输的数据和地址的突发长度只有1,也就是burst=1。常用与较少数据量的存储映射通信,比如配置寄存器。
下面把AXI_lite的所有信号罗列出来:
写地址
AW_ADDR
ADDR_WIDTH-1 :0

AW_VALID

AW_READY

AW_PORT
1 : 0
写通道保护信号
写数据
W_DATA
DATA_WIDTH-1 : 0

W_STRB
(DATA_WIDTH/8)-1 : 0
写字节有效位控制
W_VALID

W_READY

写回应
B_RESP
1:0

B_VALID

B_READY

读地址
AR_ADDR
ADDR_WIDTH-1 : 0

AR_VALID

AR_READY

AR_PORT
1:0
读通道保护信号
读数据
R_DATA

R_RESP
1:0

R_VALID

R_READY



介绍一下AW_PORT和AR_PORT,是写/读通道保护信号,[0]表示正常或特权,[1]表示安全或非安全,[2]表示指令或数据。这个信号需要用户在使用中根据需要自行配置,我们在本次实现的AXI_lite中不考虑这个信号。
W_STRB信号是写字节有效位控制,在高速通信协议中,都会有这个信号来代表传输的字节是否有效,PCIE、GTX等协议中都有,1代表有效,0代表无效。如果数据为32位,则STRB = 32/8 = 4位。
3 axi-lite信号之间的依赖关系
图中的单头箭头表示:其指向的信号可以在箭头起始信号置起之前或之后置起(无依赖)
图中的双头箭头表示:其指向的信号必须在箭头起始信号置起之后置起(指向信号依赖起始信号)
读顺序:先传输完毕读地址后(arvalid+arready),slave再给出读数据(rvalid)。
2504661-20231230143122898-1165028229.jpg
读通道顺序(单箭头:无依赖;双箭头:有依赖)
写顺序:写地址和写数据同时传输,然后才能给出bvalid。
2504661-20231230143123362-2014443029.jpg
写通道顺序(单箭头:无依赖;双箭头:有依赖)
4创建axi4-lite-slave总线接口IP
新建fpga工程,过程省略
新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Lite接口总线IP
2504661-20231230143123843-776485025.jpg
2504661-20231230143124272-463653266.jpg
选择使用vivado自带的AXI总线模板创建一个AXI4-Lite接口IP
2504661-20231230143124663-1673494483.jpg
设置IP的名字为saxi_lite
2504661-20231230143125024-598904210.jpg

模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream
2504661-20231230143125455-1302185019.jpg
总线包括Master和Slave两种模式,这里选择Slave模式
2504661-20231230143125858-893767339.jpg
这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-Lite快速验证
2504661-20231230143126293-610046095.jpg
单击Finish 后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中saxi_lite_0就是我们自定义的IP,另外一个master_0是用来读写我们自定义的saxi_lite_0,以此验证我们的IP正确性。
2504661-20231230143126682-483942658.jpg
继续再看代码看看里面有什么东西
2504661-20231230143127129-1224499316.jpg
右击Generate Output Products
2504661-20231230143127547-1818554120.jpg
路径uisrc/03_ip/saxi_lite_1.0/hdl路径下的saxi_lite_v_0_S00_AXI.v就是我们的源码。另外一个saxi_lite_v1_0.v是软件产生了一个接口文件,如果我们自己定义IP可有可无。
2504661-20231230143127883-1562087925.jpg
4程序分析
axi总线信号的关键无非是地址和数据,而写地址的有效取决于AXI_AWVALID和AXI_AWREADY,写数据的有效取决于S_AXI_WVALID和S_AXI_WREADY。同理,读地址的有效取决于AXI_ARVALID和AXI_ARREADY,读数据的有效取决于S_AXI_RVALID和S_AXI_RREADY。所以以下代码的阅读分析注意也是围绕以上4个信号的有效时序。
以下程序我们把关键信号的代码拆分阅读
1:axi-lite-slaveaxi_awready
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_awready <= 1'b0;
          aw_en <= 1'b1;
        end
      else
        begin   
          if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
            begin
              // 当在写地址和数据总线上存在有效的写地址和写数据时,从设备准备好接受写地址。
              // 此设计不需要未完成的事务。
              axi_awready <= 1'b1;
              aw_en <= 1'b0;
            end
            else if (S_AXI_BREADY && axi_bvalid)
                begin
                  aw_en <= 1'b1;
                  axi_awready <= 1'b0;
                end
          else         
            begin
              axi_awready <= 1'b0;
            end
        end
    end


2:axi-lite-slaveaxi_awaddr
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_awaddr <= 0;
        end
      else
        begin   
          if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
            begin
              // 写入地址锁存
              axi_awaddr <= S_AXI_AWADDR;
            end
        end
    end


3:axi-lite-slaveaxi_wready
当满足(~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )==1条件,设置axi_wready有效。
    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 && S_AXI_AWVALID && aw_en )
            begin
              // 当在写地址和数据总线上存在有效的写地址和写数据时,从设备准备好接受写数据。
              // 此设计不需要未完成的事务。
              axi_wready <= 1'b1;
            end
          else
            begin
              axi_wready <= 1'b0;
            end
        end
    end


4:axi-lite-slave的写数据寄存器
axi-lite-slave很重要一点功能就是配合SOC的处理器部分完成一些低速外设,或者寄存器的控制。需要使用多寄存器或者外设,一般在ip代码里面就已经设置好了。前面用vivado的模板产生自定义ip的时候,我们选择了4个32bits寄存器,以下的模板中slv_reg0~ slv_reg3共计4个32bits寄存器。
    assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          slv_reg0 <= 0;
          slv_reg1 <= 0;
          slv_reg2 <= 0;
          slv_reg3 <= 0;
        end
      else begin
        if (slv_reg_wren)
          begin
            case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
              2'h0:
                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                    // 根据写入选通断言相应的字节使能
                    // 从属寄存器0
                    slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
                  end  
              2'h1:
                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                    // 根据写入选通断言相应的字节使能
                    // 从寄存器1
                    slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
                  end  
              2'h2:
                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                    // 根据写入选通断言相应的字节使能
                    // 从寄存器2
                    slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
                  end  
              2'h3:
                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                    // 根据写入选通断言相应的字节使能
                    // 从寄存器3
                    slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
                  end  
              default : begin
                          slv_reg0 <= slv_reg0;
                          slv_reg1 <= slv_reg1;
                          slv_reg2 <= slv_reg2;
                          slv_reg3 <= slv_reg3;
                        end
            endcase
          end
      end
    end   


5:axi-lite-slaveaxi_bvalid信号
axi_bvalid用于告知axi master axi-slave端已经完成数据接收了
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_bvalid  <= 0;
          axi_bresp   <= 2'b0;
        end
      else
        begin   
          if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
            begin
              // indicates a valid write response is available
              axi_bvalid <= 1'b1;
              axi_bresp  <= 2'b0; // 'OKAY' response
            end                   // work error responses in future
          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  


6:axi-lite-slaveaxi_arready
当条件满足(~axi_arready && S_AXI_ARVALID)==1设置axi_arready有效,并且寄存住总线上的地址axi_araddr <= S_AXI_ARADDR
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_arready <= 1'b0;
          axi_araddr  <= 32'b0;
        end
      else
        begin   
          if (~axi_arready && S_AXI_ARVALID)
            begin
              // indicates that the slave has acceped the valid read address
              axi_arready <= 1'b1;
              // Read address latching
              axi_araddr  <= S_AXI_ARADDR;
            end
          else
            begin
              axi_arready <= 1'b0;
            end
        end
    end   


7:axi-lite-slaveaxi_araddr
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_arready <= 1'b0;
          axi_araddr  <= 32'b0;
        end
      else
        begin   
          if (~axi_arready && S_AXI_ARVALID)
            begin
              // indicates that the slave has acceped the valid read address
              axi_arready <= 1'b1;
              // Read address latching
              axi_araddr  <= S_AXI_ARADDR;
            end
          else
            begin
              axi_arready <= 1'b0;
            end
        end
    end


8:axi-lite-slaveaxi_rvalid信号
当条件满足(axi_arready && S_AXI_ARVALID && ~axi_rvalid)==1的时候设置axi_rvalid有效,表示axi-lite-slave总线上的数据是有效的。
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_rvalid <= 0;
          axi_rresp  <= 0;
        end
      else
        begin   
          if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
            begin
              // Valid read data is available at the read data bus
              axi_rvalid <= 1'b1;
              axi_rresp  <= 2'b0; // 'OKAY' response
            end  
          else if (axi_rvalid && S_AXI_RREADY)
            begin
              // Read data is accepted by the master
              axi_rvalid <= 1'b0;
            end               
        end
    end  


9:axi-lite-slave的读数据寄存器
本文实验中,axi-master写入4个寄存器数据,然后读出,通过查看数据是否一致可以确认axi-lite-slave工作是否正常。当slv_reg_rden有效的时候,数据被读入寄存器axi_rdata,当axi_rvalid有效的时候,数据被锁存。
    assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
    always @(*)
    begin
          // Address decoding for reading registers
          case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
            2'h0   : reg_data_out <= slv_reg0;
            2'h1   : reg_data_out <= slv_reg1;
            2'h2   : reg_data_out <= slv_reg2;
            2'h3   : reg_data_out <= slv_reg3;
            default : reg_data_out <= 0;
          endcase
    end

    // Output register or memory read data
    always @( posedge S_AXI_ACLK )
    begin
      if ( S_AXI_ARESETN == 1'b0 )
        begin
          axi_rdata  <= 0;
        end
      else
        begin   
          // When there is a valid read address (S_AXI_ARVALID) with
          // acceptance of read address by the slave (axi_arready),
          // output the read dada
          if (slv_reg_rden)
            begin
              axi_rdata <= reg_data_out;     // register read data
            end  
        end
    end


当我们阅读后分析完以上代码后,可以发现,axi-lite-slave的代码中没有突发长度的处理,每次只处理一个地址的一个数据。并且也没有WLAST和RLAST信号,说明axi-lite-slave适合一些低速的数据交互,但是可以节省一些FPGA的逻辑资源。
5实验结果
2504661-20231230143128336-1963599194.jpg
单击仿真
2504661-20231230143128744-480247973.jpg

添加观察信号
2504661-20231230143129287-1099555676.jpg
AXI总线依次写入1 2 3 4,slv_reg0~slv_reg3完成数据寄存
2504661-20231230143129782-1321873166.jpg

读数据
2504661-20231230143135271-1161730676.jpg

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

本版积分规则