软件版本:vitis2020.2(vivado2020.2)
操作系统:WIN10 64bit
硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA(米联客MZU07A-EG开发硬件平台)
登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!
1.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的工作是否正常。
1.2AXI总线协议介绍
1.2.1AXI总线概述
在XIINX FPGA的软件工具vivado以及相关IP中有支持三种AXI总线,拥有三种AXI接口,当然用的都是AXI协议。其中三种AXI总线分别为:
AXI4:(For high-performance memory-mapped requirements.)主要面向高性能地址映射通信的需求,是面向地址映射的接口,允许最大256轮的数据突发传输;
AXI4-Lite:(For simple, low-throughput memory-mapped communication )是一个轻量级的地址映射单次传输接口,占用很少的逻辑单元。
AXI4-Stream:(For high-speed streaming data.)面向高速流数据传输;去掉了地址项,允许无限制的数据突发传输规模。
由于AXI4和AXI4-Lite信号大部分一样,以下只介绍AXI4信号.另外对于AXI4-Stream协议不再本文中接收,后面有单独介绍的文章。
1.2.2AXI-4总线信号功能
1:时钟和复位
信号 |
方向 |
描述 |
ACLK |
时钟源 |
全局时钟信号 |
ARESETn |
复位源 |
全局复位信号,低有效 |
写地址通道信号:
信号 |
方向 |
描述 |
AWID |
主机to从机 |
写地址ID,用来标志一组写信号 |
AWADDR |
主机to从机 |
写地址,给出一次写突发传输的写地址 |
AWLEN |
主机to从机 |
AWLEN[7:0]决定写传输的突发长度。AXI3只支持1~16次的突发传输(Burst_length=AxLEN[3:0]+1),AXI4扩展突发长度支持INCR突发类型为1~256次传输,对于其他的传输类型依然保持1~16次突发传输(Burst_Length=AxLEN[7:0]+1)。 burst传输具有如下规则: wraping burst ,burst长度必须是2,4,8,16 burst不能跨4KB边界 不支持提前终止burst传输 |
AWSIZE |
主机to从机 |
写突发大小,给出每次突发传输的字节数支持1、2、4、8、16、32、64、128 |
AWBURST |
主机to从机 |
突发类型: 2'b00 FIXED:突发传输过程中地址固定,用于FIFO访问 2'b01 INCR :增量突发,传输过程中,地址递增。增加量取决AxSIZE的值。 2'b10 WRAP:回环突发,和增量突发类似,但会在特定高地址的边界处回到低地址处。回环突发的长度只能是2,4,8,16次传输,传输首地址和每次传输的大小对齐。最低的地址整个传输的数据大小对齐。回环边界等于(AxSIZE*AxLEN) 2'b11 Reserved |
AWLOCK |
主机to从机 |
总线锁信号,可提供操作的原子性 |
AWCACHE |
主机to从机 |
内存类型,表明一次传输是怎样通过系统的 |
AWPROT |
主机to从机 |
保护类型,表明一次传输的特权级及安全等级 |
AWQOS |
主机to从机 |
质量服务QoS |
AWREGION |
主机to从机 |
区域标志,能实现单一物理接口对应的多个逻辑接口 |
AWUSER |
主机to从机 |
用户自定义信号 |
AWVALID |
主机to从机 |
有效信号,表明此通道的地址控制信号有效 |
AWREADY |
从机to主机 |
表明"从"可以接收地址和对应的控制信号 |
2:写数据通道信号:
信号名 |
方向 |
描述 |
WID |
主机to从机 |
一次写传输的ID tag |
WDATA |
主机to从机 |
写数据 |
WSTRB |
主机to从机 |
WSTRB[n:0]对应于对应的写字节,WSTRB[n]对应WDATA[8n+7:8n]。WVALID为低时,WSTRB可以为任意值,WVALID为高时,WSTRB为高的字节线必须指示有效的数据。 |
WLAST |
主机to从机 |
表明此次传输是最后一个突发传输 |
WUSER |
主机to从机 |
用户自定义信号 |
WVALID |
主机to从机 |
写有效,表明此次写有效 |
WREADY |
从机to主机 |
表明从机可以接收写数据 |
写响应信号:
信号名 |
方向 |
描述 |
BID |
从机to主机 |
写响应ID tag |
BRESP |
从机to主机 |
写响应,表明写传输的状态 |
BUSER |
从机to主机 |
用户自定义 |
BVALID |
从机to主机 |
写响应有效 |
BREADY |
主机to从机 |
表明主机能够接收写响应 |
3:读地址通道信号:
信号 |
方向 |
描述 |
ARID |
主机to从机 |
读地址ID,用来标志一组写信号 |
ARADDR |
主机to从机 |
读地址,给出一次读突发传输的读地址 |
ARLEN |
主机to从机 |
ARLEN[7:0]决定读传输的突发长度。AXI3只支持1~16次的突发传输(Burst_length=AxLEN[3:0]+1),AXI4扩展突发长度支持INCR突发类型为1~256次传输,对于其他的传输类型依然保持1~16次突发传输(Burst_Length=AxLEN[7:0]+1)。 burst传输具有如下规则: wraping burst ,burst长度必须是2,4,8,16 burst不能跨4KB边界 不支持提前终止burst传输 |
ARSIZE |
主机to从机 |
读突发大小,给出每次突发传输的字节数支持1、2、4、8、16、32、64、128 |
ARBURST |
主机to从机 |
突发类型: 2'b00 FIXED:突发传输过程中地址固定,用于FIFO访问 2'b01 INCR :增量突发,传输过程中,地址递增。增加量取决AxSIZE的值。 2'b10 WRAP:回环突发,和增量突发类似,但会在特定高地址的边界处回到低地址处。回环突发的长度只能是2,4,8,16次传输,传输首地址和每次传输的大小对齐。最低的地址整个传输的数据大小对齐。回环边界等于(AxSIZE*AxLEN) 2'b11 Reserved |
ARLOCK |
主机to从机 |
总线锁信号,可提供操作的原子性 |
ARCACHE |
主机to从机 |
内存类型,表明一次传输是怎样通过系统的 |
ARPROT |
主机to从机 |
保护类型,表明一次传输的特权级及安全等级 |
ARQOS |
主机to从机 |
质量服务QoS |
ARREGION |
主机to从机 |
区域标志,能实现单一物理接口对应的多个逻辑接口 |
ARUSER |
主机to从机 |
用户自定义信号 |
ARVALID |
主机to从机 |
有效信号,表明此通道的地址控制信号有效 |
ARREADY |
从机to主机 |
表明"从"可以接收地址和对应的控制信号 |
4:读数据通道信号:
信号名 |
方向 |
描述 |
RID |
从机to主机 |
一次读传输的ID tag |
RDATA |
从机to主机 |
读数据 |
RRESP |
从机to主机 |
读响应,表明读传输的状态 |
RLAST |
从机to主机 |
表明此次传输是最后一个突发传输 |
RUSER |
从机to主机 |
用户自定义信号 |
RVALID |
从机to主机 |
写有效,表明此次写有效 |
RREADY |
主机to从机 |
表明从机可以接收写数据 |
1.2.3数据有效的情况
AXI4所采用的是一种READY,VALID握手通信机制,简单来说主从双方进行数据通信前,有一个握手的过程。传输源产生VLAID信号来指明何时数据或控制信息有效。而目地源产生READY信号来指明已经准备好接受数据或控制信息。传输发生在VALID和READY信号同时为高的时候。VALID和READY信号的出现有三种关系。
-
VALID先变高READY后变高。时序图如下:
在箭头处信息传输发生。
-
READY先变高VALID后变高。时序图如下:
同样在箭头处信息传输发生。
-
VALID和READY信号同时变高。时序图如下:
在这种情况下,信息传输立马发生,如图箭头处指明信息传输发生。
1.2.4突发式读写
1:突发式写时序图
这一过程的开始时,主机发送地址和控制信息到写地址通道中,然后主机发送每一个写数据到写数据通道中。当主机发送最后一个数据时,WLAST信号就变为高。当设备接收完所有数据之后他将一个写响应发送回主机来表明写事务完成。
2:突发式读的时序图
当地址出现在地址总线后,传输的数据将出现在读数据通道上。设备保持VALID为低直到读数据有效。为了表明一次突发式读写的完成,设备用RLAST信号来表示最后一个被传输的数据。
1.3创建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可有可无。
1.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
// slave is ready to accept write address when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
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
// Write Address latching
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
// slave is ready to accept write data when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
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
// Respective byte enables are asserted as per write strobes
// Slave register 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
// Respective byte enables are asserted as per write strobes
// Slave register 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
// Respective byte enables are asserted as per write strobes
// Slave register 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
// Respective byte enables are asserted as per write strobes
// Slave register 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
assign S_AXI_RVALID = axi_rvalid;
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
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的逻辑资源。
1.5实验结果
单击仿真
添加观察信号
AXI总下依次写入1 2 3 4,slv_reg0~slv_reg3完成数据寄存
读数据