uisrc 发表于 2023-12-30 15:10:32

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

软件版本: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-Lite2:掌握基于VIVADO工具产生AXI协议模板3:掌握通过VIVADO工具产生AXI-lite-Slave代码,并且会修改寄存器4:理解AXI-lite-Slave中自定义寄存器的地址分配5:掌握通过VIVADO封装AXI-LITE-SLAVE 图形化IP6:通过仿真验证AXI-LITE IP的工作是否正常。2 axi-lite介绍AXI_lite是轻量级的AXI协议,它每次传输的数据和地址的突发长度只有1,也就是burst=1。常用与较少数据量的存储映射通信,比如配置寄存器。下面把AXI_lite的所有信号罗列出来:
写地址AW_ADDRADDR_WIDTH-1 :0

AW_VALID

AW_READY

AW_PORT1 : 0写通道保护信号
写数据W_DATADATA_WIDTH-1 : 0

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

W_READY

写回应B_RESP1:0

B_VALID

B_READY

读地址AR_ADDRADDR_WIDTH-1 : 0

AR_VALID

AR_READY

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

R_RESP1:0

R_VALID

R_READY



介绍一下AW_PORT和AR_PORT,是写/读通道保护信号,表示正常或特权,表示安全或非安全,表示指令或数据。这个信号需要用户在使用中根据需要自行配置,我们在本次实现的AXI_lite中不考虑这个信号。W_STRB信号是写字节有效位控制,在高速通信协议中,都会有这个信号来代表传输的字节是否有效,PCIE、GTX等协议中都有,1代表有效,0代表无效。如果数据为32位,则STRB = 32/8 = 4位。3 axi-lite信号之间的依赖关系图中的单头箭头表示:其指向的信号可以在箭头起始信号置起之前或之后置起(无依赖)图中的双头箭头表示:其指向的信号必须在箭头起始信号置起之后置起(指向信号依赖起始信号)读顺序:先传输完毕读地址后(arvalid+arready),slave再给出读数据(rvalid)。读通道顺序(单箭头:无依赖;双箭头:有依赖)写顺序:写地址和写数据同时传输,然后才能给出bvalid。写通道顺序(单箭头:无依赖;双箭头:有依赖)4创建axi4-lite-slave总线接口IP新建fpga工程,过程省略新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Lite接口总线IP选择使用vivado自带的AXI总线模板创建一个AXI4-Lite接口IP设置IP的名字为saxi_lite
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream总线包括Master和Slave两种模式,这里选择Slave模式这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-Lite快速验证单击Finish 后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中saxi_lite_0就是我们自定义的IP,另外一个master_0是用来读写我们自定义的saxi_lite_0,以此验证我们的IP正确性。继续再看代码看看里面有什么东西右击Generate Output Products路径uisrc/03_ip/saxi_lite_1.0/hdl路径下的saxi_lite_v_0_S00_AXI.v就是我们的源码。另外一个saxi_lite_v1_0.v是软件产生了一个接口文件,如果我们自己定义IP可有可无。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-slave的axi_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-slave的axi_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-slave的axi_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 )            2'h0:                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )                  if ( S_AXI_WSTRB == 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 == 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 == 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 == 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-slave的axi_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-slave的axi_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-slave的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


8:axi-lite-slave的axi_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 )            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实验结果单击仿真
添加观察信号AXI总线依次写入1 2 3 4,slv_reg0~slv_reg3完成数据寄存
读数据
页: [1]
查看完整版本: 2-3-02 AXI4总线axi-lite-slave总线协议