前言
在FPGA - ZYNQ 基于Axi_Lite的PS和PL交互中,介绍了基于基于AXi_Lite的PL和PS交互,接下来构建基于基于Axi_Lite的PS和PL交互。
AXI_GP、AXI_HP和AXI_ACP接口
首先看一下ZYNQ SoC的系统框图,如下图所示。在图中,箭头方向代表主机到从机的方向。
在上图中红色框线中,就是PS和PL之间的交互,将上图简化如下:
可以看到主要有3中接口:AXI_GP、AXI_HP和AXI_ACP接口
GP:General Purpose Port(通用端口),位宽为32 位,适合PS和PL之间中 低速通信,有四个接口(两个从端口,两个主端口);
HP:High Performace Port(高性能端口),位宽为 32 位或者 64 位。适合 PS 和PL之间高速通信,有四个接口(四个都是从端口);
ACP:Accelerator Coherency Port(加速器一致),位宽为 64 位。适合 PS和PL 之间高速通信。
GP接口直接连接到的是中央互联区(central interconnect),然后由中央互联区再连接到OCM interconnect和存储器接口上;而HP接口直接连接到的是OCM interconnect和存储器接口。所以对于GP接口,通常使用他进行控制配置;而对于HP接口,通常使用它进行数据传输交互。
AXI_HP接口
4个AXI HP接口为PL的主机提供了DDR和OCM存储器的高带宽的数据路径。 每个HP接口包括两个的FIFO缓存,用于读写传输。PL到内存互连高速AXI HP端口路由连接到两个DDR内存端口和一个OCM存储器端口。AXI HP接口也被称为AFI (AXI FIFO接口),以强调它们的缓冲功能。 PL电平移位器必须通过LVL SHFTR EN启用后,才能进行PL逻辑通信。
特点
这些接口被设计为在PL主存储器和PS存储器(包括DDR和片上RAM)之间提供一个高吞吐量的数据路径。主要功能包括:
- 可以实现32或64位数据位宽的主接口(每个端口独立编程)。
- 在32位接口模式下,可以进行动态配置位为64位,以实现对齐传输,通过AxCACHE [1]可以进行控制。
- 在32位接口模式下,为未对齐的32位传输自动扩展到64位。
- 可编程的写命令释放阈值。
- PL和PS之间的所有AXI接口的异步时钟域交叉。
- 使用1kb (128 × 64位)数据缓存FIFO来平滑“长延迟”传输,用于读写。
- 从PL端口提供QoS信令。
- 命令和数据FIFO填充级计数可用于PL端。
- 支持标准AXI 3.0接口。
- 可编程命令下发到互连,分别用于读和写命令。
- 14到70个指令范围的高性能从接口读接受能力。(取决于突发长度)
- 8到32个指令范围的高性能从接口写接受能力。(取决于突发长度)
AXI_GP接口
AXI_GP接口直接连接到主互连和从互连的端口,没有任何额外的FIFO缓冲,不像AXI_HP接口使用FIFO缓冲以提高性能和吞吐量。因此,性能受到主互连端口和从互连端口的限制。这些接口仅用于通用用途,并不是为了实现高性能。
特点
AXI GP的特性包括:
- 支持标准AXI协议
- 数据总线宽度只有32位
- 主端口ID位宽为12位
- 主端口发送能力:8位读,8位写
- 从端口ID位宽为6位
- 从端口接受能力:8位读,8位写
AXI_GP、AXI_HP和AXI_ACP接口的主与从
注意:AXI_ACP和AXI_HP接口,PL只能是主机,PS只能是从机!!
接口介绍
AXI_HP
采用AXI_HP接口,PL为主机,PS为从机
PL 发送给PS的数据,通过AXI_HP接口后,进入DDR3控制器,然后写入到DDR3里面。
反过来,PL也可以通过AXI_HP接口读取DDR3里面的数据。
将上图简化:
PS 也可以向DDR3 写入或者读取数据,DDR3 相对于PS 而言就是一个外设。
PS 首先将数据写入到Cache,Cache里面的数据再写入到DDR3 contorller, 最后到DDR3。反过来,PS也可以从DDR3里面读取数据。
PL将数据通过AXI_HP接口写入到DDR3,PS再将数据从DDR3里面读出 来,这样就实现了PL与PS的数据交互。
AXI_GP
采用AXI_GP接口,PL为从机,PS为主机
如果PS端采用AXI_GP接口,且PS端为主机,PL为从机,那么PL的接 口模块相对于PS来说,就是一个外设。(下面的图片加强理解)
既然是外设,那么就会有对应的地址和寄存器。
其中0x4300_0000 为起始地址,又称之为基地址;0x4300_FFFF为结束地址。
其中,基地址和结束地址的值,是由PL端分配的,也就是在PL端可以使用VIVADO软件更改。
第二个地址为0x4300_0004,相比于0x4300_0000 偏移量为4。所以4就是偏移地址。
可以通过 “基地址 + 偏移地址”表示任意的地址。比如基地址为 0x4300_0000,偏移地址为8,表示的地址为0x4300_0008。
- PS 如何通过AXI_GP接口写入数据到PL ?
- PS 先将数据写入到寄存器里面,然后写入到PL。
- PS 如何通过AXI_GP接口从PL端读取数据 ?
- PL 端的数据先通过AXI_GP接口发送PS的寄存器里面,PS从寄存器里面 读取数据。
如下图:
AXI_HP、AXI_GP、AXI_ACP理论带宽
下表介绍了 PS- PL和PS内存接口的理论带宽
注意:Gb/s和GB/s是不一样的,B代表字节,b代表比特。
- Gb/s:每秒传输多少个比特
- GB/s:每秒传输多少个字节
- GB/s = 8 * Gb/s
开发流程
在ZYNQ开发中,FPGA - ZYNQ 基于EMIO的PS和PL交互https://blog.csdn.net/weixin_46897065/article/details/137865852?spm=1001.2014.3001.5501
和 FPGA - ZYNQ 基于Axi_Lite的PS和PL交互https://blog.csdn.net/weixin_46897065/article/details/137937509?spm=1001.2014.3001.5501
中详细介绍了开发流程。
在Creat Block Design 中:搜索ZYNQ以及axi_interconnect 并进行配置
然后进行自动连线,连线完成后如下图:
这里AXI_HP的从机搭建完成。、
然后建立top文件,设计AXI4_FULL主机模块。
代码如下:
`timescale 1ns / 1ps
module top(
inout [14:0] DDR_addr ,
inout [2:0] DDR_ba ,
inout DDR_cas_n ,
inout DDR_ck_n ,
inout DDR_ck_p ,
inout DDR_cke ,
inout DDR_cs_n ,
inout [3:0] DDR_dm ,
inout [31:0] DDR_dq ,
inout [3:0] DDR_dqs_n ,
inout [3:0] DDR_dqs_p ,
inout DDR_odt ,
inout DDR_ras_n ,
inout DDR_reset_n ,
inout DDR_we_n ,
inout FIXED_IO_ddr_vrn ,
inout FIXED_IO_ddr_vrp ,
inout [53:0] FIXED_IO_mio ,
inout FIXED_IO_ps_clk ,
inout FIXED_IO_ps_porb ,
inout FIXED_IO_ps_srstb
);
parameter AXI_DATA_WIDTH = 64;
parameter AXI_ADDR_WIDTH = 32;
parameter USER_WR_DATA_WIDTH = 16;
parameter USER_RD_DATA_WIDTH = 16;
wire PstoPl_clk150m;
wire PstoPl_resetn;
wire user_rd_clk;
wire user_wr_clk;
wire axi_clk;
wire reset;
wire ddr_init_done;
wire user_wr_en;
wire[USER_WR_DATA_WIDTH-1:0] user_wr_data;
wire [AXI_ADDR_WIDTH-1:0] user_wr_base_addr;
wire [AXI_ADDR_WIDTH-1:0] user_wr_end_addr;
wire user_rd_req;
wire [AXI_ADDR_WIDTH-1:0] user_rd_base_addr;
wire [AXI_ADDR_WIDTH-1:0] user_rd_end_addr;
wire user_rd_req_busy;
wire user_rd_valid;
wire user_rd_last;
wire[USER_RD_DATA_WIDTH-1:0] user_rd_data;
wire m_axi_awvalid;
wire m_axi_awready;
wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr;
wire [3:0] m_axi_awid;
wire [7:0] m_axi_awlen;
wire [1:0] m_axi_awburst;
wire [2:0] m_axi_awsize;
wire [2:0] m_axi_awport;
wire [3:0] m_axi_awqos;
wire m_axi_awlock;
wire [3:0] m_axi_awcache;
wire [AXI_DATA_WIDTH-1 :0] m_axi_wdata;
wire [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb;
wire m_axi_wvalid;
wire m_axi_wlast;
wire m_axi_wready;
wire [ 3:0] m_axi_bid;
wire [ 1:0] m_axi_bresp;
wire m_axi_bvalid;
wire m_axi_bready;
wire m_axi_arvalid;
wire m_axi_arready;
wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr;
wire [3:0] m_axi_arid;
wire [7:0] m_axi_arlen;
wire [1:0] m_axi_arburst;
wire [2:0] m_axi_arsize;
wire [2:0] m_axi_arport;
wire [3:0] m_axi_arqos;
wire m_axi_arlock;
wire [3:0] m_axi_arcache;
wire [3:0] m_axi_rid;
wire m_axi_rvalid;
wire m_axi_rready;
wire [AXI_DATA_WIDTH-1:0] m_axi_rdata;
wire [1:0] m_axi_rresp;
wire m_axi_rlast;
user_req_generate #(
.USER_WR_DATA_WIDTH(USER_WR_DATA_WIDTH)
) user_req_generate (
.wr_clk (PstoPl_clk150m),
.rd_clk (PstoPl_clk150m),
.reset (~PstoPl_resetn),
.user_wr_en (user_wr_en),
.user_wr_data (user_wr_data),
.user_rd_req (user_rd_req)
);
axi4_adma_v1 #(
.AXI_DATA_WIDTH(AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),
.USER_RD_DATA_WIDTH(USER_RD_DATA_WIDTH),
.USER_WR_DATA_WIDTH(USER_WR_DATA_WIDTH)
) axi_adma_v1 (
.user_wr_clk (PstoPl_clk150m),
.user_rd_clk (PstoPl_clk150m),
.axi_clk (PstoPl_clk150m),
.reset (~PstoPl_resetn),
.ddr_init_done (1'b1),
.user_wr_en (user_wr_en),
.user_wr_data (user_wr_data),
.user_wr_base_addr (32'h0020_0000), //猫碌路氓搂鈥姑ヅ撀懊ヂ濃偓盲赂聧猫茠陆盲禄?0氓录?氓搂?
.user_wr_end_addr (32'h1000_0000),
.user_rd_req (user_rd_req),
.user_rd_base_addr (32'h0020_0000),
.user_rd_end_addr (32'h1000_0000),
.user_rd_req_busy (user_rd_req_busy),
.user_rd_valid (user_rd_valid),
.user_rd_last (user_rd_last),
.user_rd_data (user_rd_data),
.m_axi_awvalid (m_axi_awvalid),
.m_axi_awready (m_axi_awready),
.m_axi_awaddr (m_axi_awaddr),
.m_axi_awid (m_axi_awid),
.m_axi_awlen (m_axi_awlen),
.m_axi_awburst (m_axi_awburst),
.m_axi_awsize (m_axi_awsize),
.m_axi_awport (m_axi_awport),
.m_axi_awqos (m_axi_awqos),
.m_axi_awlock (m_axi_awlock),
.m_axi_awcache (m_axi_awcache),
.m_axi_wvalid (m_axi_wvalid),
.m_axi_wready (m_axi_wready),
.m_axi_wdata (m_axi_wdata),
.m_axi_wstrb (m_axi_wstrb),
.m_axi_wlast (m_axi_wlast),
.m_axi_bid (m_axi_bid),
.m_axi_bresp (m_axi_bresp),
.m_axi_bvalid (m_axi_bvalid),
.m_axi_bready (m_axi_bready),
.m_axi_arvalid (m_axi_arvalid),
.m_axi_arready (m_axi_arready),
.m_axi_araddr (m_axi_araddr),
.m_axi_arid (m_axi_arid),
.m_axi_arlen (m_axi_arlen),
.m_axi_arburst (m_axi_arburst),
.m_axi_arsize (m_axi_arsize),
.m_axi_arport (m_axi_arport),
.m_axi_arqos (m_axi_arqos),
.m_axi_arlock (m_axi_arlock),
.m_axi_arcache (m_axi_arcache),
.m_axi_rid (m_axi_rid),
.m_axi_rvalid (m_axi_rvalid),
.m_axi_rready (m_axi_rready),
.m_axi_rdata (m_axi_rdata),
.m_axi_rlast (m_axi_rlast),
.m_axi_rresp (m_axi_rresp)
);
mcu_design_wrapper mcu_design_wrapper
(
.DDR_addr (DDR_addr),
.DDR_ba (DDR_ba),
.DDR_cas_n (DDR_cas_n),
.DDR_ck_n (DDR_ck_n),
.DDR_ck_p (DDR_ck_p),
.DDR_cke (DDR_cke),
.DDR_cs_n (DDR_cs_n),
.DDR_dm (DDR_dm),
.DDR_dq (DDR_dq),
.DDR_dqs_n (DDR_dqs_n),
.DDR_dqs_p (DDR_dqs_p),
.DDR_odt (DDR_odt),
.DDR_ras_n (DDR_ras_n),
.DDR_reset_n (DDR_reset_n),
.DDR_we_n (DDR_we_n),
.FIXED_IO_ddr_vrn (FIXED_IO_ddr_vrn),
.FIXED_IO_ddr_vrp (FIXED_IO_ddr_vrp),
.FIXED_IO_mio (FIXED_IO_mio),
.FIXED_IO_ps_clk (FIXED_IO_ps_clk),
.FIXED_IO_ps_porb (FIXED_IO_ps_porb),
.FIXED_IO_ps_srstb (FIXED_IO_ps_srstb),
.PStoPL_resetn (PstoPl_resetn),
.PStoPL_clk150m (PstoPl_clk150m),
.S00_AXI_0_araddr (m_axi_araddr),
.S00_AXI_0_arburst (m_axi_arburst),
.S00_AXI_0_arcache (m_axi_arcache),
.S00_AXI_0_arid (0),
.S00_AXI_0_arlen (m_axi_arlen),
.S00_AXI_0_arlock (0),
.S00_AXI_0_arprot (m_axi_arport),
.S00_AXI_0_arqos (m_axi_arqos),
.S00_AXI_0_arready (m_axi_arready),
.S00_AXI_0_arsize (m_axi_arsize),
.S00_AXI_0_arvalid (m_axi_arvalid),
.S00_AXI_0_awaddr (m_axi_awaddr),
.S00_AXI_0_awburst (m_axi_awburst),
.S00_AXI_0_awcache (m_axi_awcache),
.S00_AXI_0_awid (0),
.S00_AXI_0_awlen (m_axi_awlen),
.S00_AXI_0_awlock (0),
.S00_AXI_0_awprot (m_axi_awport),
.S00_AXI_0_awqos (m_axi_awqos),
.S00_AXI_0_awready (m_axi_awready),
.S00_AXI_0_awsize (m_axi_awsize),
.S00_AXI_0_awvalid (m_axi_awvalid),
.S00_AXI_0_bid (),
.S00_AXI_0_bready (m_axi_bready),
.S00_AXI_0_bresp (m_axi_bresp),
.S00_AXI_0_bvalid (m_axi_bvalid),
.S00_AXI_0_rdata (m_axi_rdata),
.S00_AXI_0_rid (),
.S00_AXI_0_rlast (m_axi_rlast),
.S00_AXI_0_rready (m_axi_rready),
.S00_AXI_0_rresp (m_axi_rresp),
.S00_AXI_0_rvalid (m_axi_rvalid),
.S00_AXI_0_wdata (m_axi_wdata),
.S00_AXI_0_wid (0),
.S00_AXI_0_wlast (m_axi_wlast),
.S00_AXI_0_wready (m_axi_wready),
.S00_AXI_0_wstrb (m_axi_wstrb),
.S00_AXI_0_wvalid (m_axi_wvalid)
);
endmodule
然后生成bitstream,导出硬件,启动SDK
建立新工程
点击空工程 点击finish
接下来返回main.c,向其中添加以下代码:
一直处于接收发送状态。
#include <stdio.h>
#include "xil_printf.h"
int main()
{
while(1)
{
};
return 0;
}
最后,下载验证。
总结
在这里,实现了基于Axi_full的PS和PL交互,详细介绍了AXI_GP、AXI_HP和AXI_ACP接口。