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-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)。
读通道顺序(单箭头:无依赖;双箭头:有依赖)
写顺序:写地址和写数据同时传输,然后才能给出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[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-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[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实验结果
单击仿真
添加观察信号
AXI总线依次写入1 2 3 4,slv_reg0~slv_reg3完成数据寄存
读数据
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/17936341.html