作者: FPGA入门到精通
AXI DataMover IP是Xilinx提供的一种高性能数据传输模块,它能够实现在AXI4内存映射域和AXI4-Stream域之间的高效数据移动。
DataMover IP的使用可以大大简化数据传输的复杂性,提高设计的性能,这个IP在Xilinx中各种复杂DMA IP中都有应用,比如VDMA、XDMA等等。
本文将从基础概念入手,逐步深入到AXI DataMover IP的配置和使用,帮助读者快速掌握这一强大的IP核。
一、AXI DataMover IP概述
1、DataMover 是什么?
AXI DataMover是一个高度可配置的IP核,它支持在AXI4和AXI4-Stream两种不同的接口之间进行数据传输。
简单理解就是上层程序告诉DataMover,从哪里(起始地址)开始,搬数据(读数据或写数据),搬多少数据(数据长度)!
DataMover有两个数据传输通道:
(1)MM2S(Memory to Stream):从AXI4内存映射域读取数据到AXI4-Stream域。
(2)S2MM(Stream to Memory):从AXI4-Stream域写入数据到AXI4内存映射域。
不管是MM2S还是S2MM的通道,都包含:
命令逻辑FIFO,存储一系列的命令参数,可以排队;
状态逻辑FIFO,存储每个命令执行的结果,可报错;
读写引擎,读写引擎负责执行命令、协议转换。
这两个通道以类似全双工的方式独立运行。AXI DataMover IP 核是一个关键构建块,具有 4 KB 地址边界保护、自动突发分区功能,并且能够使用 AXI4-Stream 协议的几乎全部带宽功能,可以对多个传输请求进行排队。
2、DataMover 命令
这个命令主要参数是读写起始地址、数据长度。
实际的地址宽度与DDR MIG IP有关,用N表示,一般默认取32bit,同时地址宽度也决定了命令长度。
命令格式如下:
具体的内容含义:
命令时序示例:
DataMover 元素的命令接口可以允许命令FIFO 中排队。
3、Status Interface 状态接口与接口时序
状态接口,用于反馈命令执行的结果。
状态接口格式如下:
状态字节描述:
OKAY:值为1表示执行命令成功,值为0表示执行命令失败。
SLVERR:从机错误,值为1表示错误,值为0表示正常。
DECERR:解码错误,一般是地址错误,值为1表示错误,值为0表示正常。
INTERR:内部错误,要求DMA发0个数据,tlast信号到来的时间不对等均会引发内部错误,值为1表示错误,值为0表示正常。
TAG:对应命令中的TAG。
命令时序示例:
除了状态接口可查询执行状态外,另外提供s2mm_err/mm2s_err信号,出现错误时,就会拉高。
需要注意的是,如果DataMover出现报错的话会停止工作,只有将DataMover复位后,才能继续执行新的命令。
二、AXI DataMover IP 使用
1、调用DataMover IP
在vivado中打开IP Catalog,输入DataMover,双击打开DataMover IP。
2、基本参数设置界面
Enable MM2S :此框允许您配置 MM2S 通道选项。
Channel Type:通道类型 – 选择“完整”或“基本”。选择“完整”允许为所有可能的组合和高级功能配置 MM2S 通道。基本模式限制了某些功能,并允许 MM2S 仅用于 32 位或 64 位宽的数据。
Memory Mapped Data Width:内存映射数据宽度 — 指定 AXI4 读取总线的数据宽度(以位为单位)。有效值为 32、64、128、256、512 和 1,024。根据频道类型,这些选项会有所不同。
Stream Data Width:流数据宽度 – 指定 MM2S 流总线的数据宽度(以位为单位)。有效值为 8、16、32、64、128、256、512 和 1,024。此值不能大于内存映射数据宽度。
Maximum Burst Size:最大突发大小
Bytes To Transfer (BTT) Bit Used:要传输的字节数 (BTT) 已用位数 – 指定 MM2S 命令的 BTT 数字段中的有效位数。有效选项为 8 到 23。
Enable xCache and xUser:启用 xCache 和 xUser - 如果要更改 AXI4 接口的 *cache 和 *user 信号,请选择此选项。
Enable MM2S Control Signals, Enable S2MM Control Signals:启用 MM2S 控制信号,启用 S2MM 控制信号 - 启用此功能会暴露 MM2S、S2MM 接口的所有控制和状态信号。启用后,您需要小心连接信号。默认情况下,它们不会公开并绑定到默认状态。
Address Width (32 -64):地址宽度 (32 -64) - 指定地址空间的宽度。它可以是介于 32 和 64 之间的任何值。
3、Advanced Options 高级选项
这是配置MM2S 和 S2MM 通道的高级选项。
Enable Asynchronous Clocks:启用异步时钟 – 此设置允许使用 MM2S 内存映射接口异步操作 MM2S 命令和状态流接口。
Allow Unaligned Transfers:允许未对齐传输 – 此设置启用或禁用 MM2S 数据重新对齐引擎 (DRE)。选中后,DRE 被启用,并允许数据重新对齐到 MM2S 内存映射数据路径上的字节(8 位)级别。对于 MM2S 通道,数据从存储器中读取。如果启用了 DRE,则数据读取可以从任何缓冲区地址偏移量开始,并且读取数据将对齐,以便读取的第一个字节是 AXI4-Stream 上的第一个有效字节输出。对齐或未对齐的内容基于内存映射数据宽度。
Enable Store and Forward:启用存储和转发 – 此设置提供 MM2S 存储和转发功能的包含/省略。此选项仅在“完整”模式下可用。此外,当内存映射数据宽度与流数据宽度不同时,此选项始终处于启用状态。
ID width:ID宽度 — 此值设置ID端口的宽度。将此值设置为 0 将禁用 ID 端口。
ID Value:ID值 — 这是放在ID端口上的值。
4、DataMover的接口
axi_datamover_0 u_axi_datamover_0 (
///////////////mm2s////////////////////
.m_axi_mm2s_aclk(m_axi_mm2s_aclk), // input wire m_axi_mm2s_aclk
.m_axi_mm2s_aresetn(m_axi_mm2s_aresetn), // input wire m_axi_mm2s_aresetn
.mm2s_err(mm2s_err), // output wire mm2s_err
//cmd
.m_axis_mm2s_cmdsts_aclk(m_axis_mm2s_cmdsts_aclk), // input wire m_axis_mm2s_cmdsts_aclk
.m_axis_mm2s_cmdsts_aresetn(m_axis_mm2s_cmdsts_aresetn), // input wire m_axis_mm2s_cmdsts_aresetn
.s_axis_mm2s_cmd_tvalid(s_axis_mm2s_cmd_tvalid), // input wire s_axis_mm2s_cmd_tvalid
.s_axis_mm2s_cmd_tready(s_axis_mm2s_cmd_tready), // output wire s_axis_mm2s_cmd_tready
.s_axis_mm2s_cmd_tdata(s_axis_mm2s_cmd_tdata), // input wire [71 : 0] s_axis_mm2s_cmd_tdata
//sts status
.m_axis_mm2s_sts_tvalid(m_axis_mm2s_sts_tvalid), // output wire m_axis_mm2s_sts_tvalid
.m_axis_mm2s_sts_tready(m_axis_mm2s_sts_tready), // input wire m_axis_mm2s_sts_tready
.m_axis_mm2s_sts_tdata(m_axis_mm2s_sts_tdata), // output wire [7 : 0] m_axis_mm2s_sts_tdata
.m_axis_mm2s_sts_tkeep(m_axis_mm2s_sts_tkeep), // output wire [0 : 0] m_axis_mm2s_sts_tkeep
.m_axis_mm2s_sts_tlast(m_axis_mm2s_sts_tlast), // output wire m_axis_mm2s_sts_tlast
//AXI4-FULL
.m_axi_mm2s_arid(m_axi_mm2s_arid), // output wire [3 : 0] m_axi_mm2s_arid
.m_axi_mm2s_araddr(m_axi_mm2s_araddr), // output wire [31 : 0] m_axi_mm2s_araddr
.m_axi_mm2s_arlen(m_axi_mm2s_arlen), // output wire [7 : 0] m_axi_mm2s_arlen
.m_axi_mm2s_arsize(m_axi_mm2s_arsize), // output wire [2 : 0] m_axi_mm2s_arsize
.m_axi_mm2s_arburst(m_axi_mm2s_arburst), // output wire [1 : 0] m_axi_mm2s_arburst
.m_axi_mm2s_arprot(m_axi_mm2s_arprot), // output wire [2 : 0] m_axi_mm2s_arprot
.m_axi_mm2s_arcache(m_axi_mm2s_arcache), // output wire [3 : 0] m_axi_mm2s_arcache
.m_axi_mm2s_aruser(m_axi_mm2s_aruser), // output wire [3 : 0] m_axi_mm2s_aruser
.m_axi_mm2s_arvalid(m_axi_mm2s_arvalid), // output wire m_axi_mm2s_arvalid
.m_axi_mm2s_arready(m_axi_mm2s_arready), // input wire m_axi_mm2s_arready
.m_axi_mm2s_rdata(m_axi_mm2s_rdata), // input wire [511 : 0] m_axi_mm2s_rdata
.m_axi_mm2s_rresp(m_axi_mm2s_rresp), // input wire [1 : 0] m_axi_mm2s_rresp
.m_axi_mm2s_rlast(m_axi_mm2s_rlast), // input wire m_axi_mm2s_rlast
.m_axi_mm2s_rvalid(m_axi_mm2s_rvalid), // input wire m_axi_mm2s_rvalid
.m_axi_mm2s_rready(m_axi_mm2s_rready), // output wire m_axi_mm2s_rready
//AXI4-Stream
.m_axis_mm2s_tdata(m_axis_mm2s_tdata), // output wire [31 : 0] m_axis_mm2s_tdata
.m_axis_mm2s_tkeep(m_axis_mm2s_tkeep), // output wire [3 : 0] m_axis_mm2s_tkeep
.m_axis_mm2s_tlast(m_axis_mm2s_tlast), // output wire m_axis_mm2s_tlast
.m_axis_mm2s_tvalid(m_axis_mm2s_tvalid), // output wire m_axis_mm2s_tvalid
.m_axis_mm2s_tready(m_axis_mm2s_tready), // input wire m_axis_mm2s_tready
//////////////S2MM/////////////////////
.m_axi_s2mm_aclk(m_axi_s2mm_aclk), // input wire m_axi_s2mm_aclk
.m_axi_s2mm_aresetn(m_axi_s2mm_aresetn), // input wire m_axi_s2mm_aresetn
.s2mm_err(s2mm_err), // output wire s2mm_err
//cmd
.m_axis_s2mm_cmdsts_awclk(m_axis_s2mm_cmdsts_awclk), // input wire m_axis_s2mm_cmdsts_awclk
.m_axis_s2mm_cmdsts_aresetn(m_axis_s2mm_cmdsts_aresetn), // input wire m_axis_s2mm_cmdsts_aresetn
.s_axis_s2mm_cmd_tvalid(s_axis_s2mm_cmd_tvalid), // input wire s_axis_s2mm_cmd_tvalid
.s_axis_s2mm_cmd_tready(s_axis_s2mm_cmd_tready), // output wire s_axis_s2mm_cmd_tready
.s_axis_s2mm_cmd_tdata(s_axis_s2mm_cmd_tdata), // input wire [71 : 0] s_axis_s2mm_cmd_tdata
//sts
.m_axis_s2mm_sts_tvalid(m_axis_s2mm_sts_tvalid), // output wire m_axis_s2mm_sts_tvalid
.m_axis_s2mm_sts_tready(m_axis_s2mm_sts_tready), // input wire m_axis_s2mm_sts_tready
.m_axis_s2mm_sts_tdata(m_axis_s2mm_sts_tdata), // output wire [7 : 0] m_axis_s2mm_sts_tdata
.m_axis_s2mm_sts_tkeep(m_axis_s2mm_sts_tkeep), // output wire [0 : 0] m_axis_s2mm_sts_tkeep
.m_axis_s2mm_sts_tlast(m_axis_s2mm_sts_tlast), // output wire m_axis_s2mm_sts_tlast
//AXI4-FULL
.m_axi_s2mm_awid(m_axi_s2mm_awid), // output wire [3 : 0] m_axi_s2mm_awid
.m_axi_s2mm_awaddr(m_axi_s2mm_awaddr), // output wire [31 : 0] m_axi_s2mm_awaddr
.m_axi_s2mm_awlen(m_axi_s2mm_awlen), // output wire [7 : 0] m_axi_s2mm_awlen
.m_axi_s2mm_awsize(m_axi_s2mm_awsize), // output wire [2 : 0] m_axi_s2mm_awsize
.m_axi_s2mm_awburst(m_axi_s2mm_awburst), // output wire [1 : 0] m_axi_s2mm_awburst
.m_axi_s2mm_awprot(m_axi_s2mm_awprot), // output wire [2 : 0] m_axi_s2mm_awprot
.m_axi_s2mm_awcache(m_axi_s2mm_awcache), // output wire [3 : 0] m_axi_s2mm_awcache
.m_axi_s2mm_awuser(m_axi_s2mm_awuser), // output wire [3 : 0] m_axi_s2mm_awuser
.m_axi_s2mm_awvalid(m_axi_s2mm_awvalid), // output wire m_axi_s2mm_awvalid
.m_axi_s2mm_awready(m_axi_s2mm_awready), // input wire m_axi_s2mm_awready
.m_axi_s2mm_wdata(m_axi_s2mm_wdata), // output wire [511 : 0] m_axi_s2mm_wdata
.m_axi_s2mm_wstrb(m_axi_s2mm_wstrb), // output wire [63 : 0] m_axi_s2mm_wstrb
.m_axi_s2mm_wlast(m_axi_s2mm_wlast), // output wire m_axi_s2mm_wlast
.m_axi_s2mm_wvalid(m_axi_s2mm_wvalid), // output wire m_axi_s2mm_wvalid
.m_axi_s2mm_wready(m_axi_s2mm_wready), // input wire m_axi_s2mm_wready
.m_axi_s2mm_bresp(m_axi_s2mm_bresp), // input wire [1 : 0] m_axi_s2mm_bresp
.m_axi_s2mm_bvalid(m_axi_s2mm_bvalid), // input wire m_axi_s2mm_bvalid
.m_axi_s2mm_bready(m_axi_s2mm_bready), // output wire m_axi_s2mm_bready
//AXI4-Stream
.s_axis_s2mm_tdata(s_axis_s2mm_tdata), // input wire [31 : 0] s_axis_s2mm_tdata
.s_axis_s2mm_tkeep(s_axis_s2mm_tkeep), // input wire [3 : 0] s_axis_s2mm_tkeep
.s_axis_s2mm_tlast(s_axis_s2mm_tlast), // input wire s_axis_s2mm_tlast
.s_axis_s2mm_tvalid(s_axis_s2mm_tvalid), // input wire s_axis_s2mm_tvalid
.s_axis_s2mm_tready(s_axis_s2mm_tready) // output wire s_axis_s2mm_tready
);
三、代码使用示例
写命令代码示例
always@(posedge clk or posedge reset) begin
if(reset) begin
s_axis_s2mm_cmd_tvalid <= 0;
burst_addr <= 0;
end else begin
if(s_axis_s2mm_cmd_tready&&s_axis_s2mm_cmd_tvalid)
s_axis_s2mm_cmd_tvalid <= 1'b0;
else if(fifo_in_wr_req) //输入fifo已缓存一定数据
s_axis_s2mm_cmd_tvalid <= 1'b1;
end
end
assign s_axis_s2mm_cmd_tdata = s_axis_s2mm_cmd_tvalid ? {9'd0,burst_addr,2'b01,6'd0,1'b1,BTT[22:0]} : 72'b0;
参考上面的代码编写读写命令代码。