本帖最后由 FPGA课程 于 2024-10-12 16:21 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://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 的工作是否正常。 2AXI 总线协议介绍
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 协议不再本文中接收, 后面有单独介绍的文章。 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 从机 | 表明从机可以接收写数据 | 2.3 数据有效的情况AXI4 所采用的是一种 READY ,VALID 握手通信机制,简单来说主从双方进行数据通信前,有一个握手的过 程。传输源产生 VLAID 信号来指明何时数据或控制信息有效。而目地源产生 READY 信号来指明已经准备好接受 数据或控制信息。传输发生在 VALID 和 READY 信号同时为高的时候。VALID 和 READY 信号的出现有三种关系。 (1) VALID 先变高 READY 后变高。时序图如下:
在箭头处信息传输发生。 (2) READY 先变高 VALID 后变高。时序图如下:
同样在箭头处信息传输发生。 (3) VALID 和 READY 信号同时变高。时序图如下:
在这种情况下,信息传输立马发生,如图箭头处指明信息传输发生。 2.4 突发式读写
1:突发式写时序图
这一过程的开始时,主机发送地址和控制信息到写地址通道中,然后主机发送每一个写数据到写数据通道中。 当主机发送最后一个数据时,WLAST 信号就变为高。当设备接收完所有数据之后他将一个写响应发送回主机来表 明写事务完成。 2:突发式读的时序图
当地址出现在地址总线后,传输的数据将出现在读数据通道上。设备保持 VALID 为低直到读数据有效。为了 表明一次突发式读写的完成,设备用RLAST 信号来表示最后一个被传输的数据。 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_litev 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
复制代码
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 完成数据寄存
读数据
|