作者:李凡
来源:https://zhuanlan.zhihu.com/p/82129170
何为 DataMover
DataMover 很有趣的名字,他是谁,数据搬运工?那可得跟我们代码搬运工好生亲近下。-\_-
DataMover 是 DMA 的一种形式。Direct Memory Access 对我们来说是一个更熟悉的名字。在不需要 CPU 干预的情况下,DMA 可以进行数据的搬运,包括但不仅限于将数据从外部存储,比如 DDR,搬运到内部寄存器,或者搬运到外部存储的另一个位置。这些都只需要 CPU 一句话的事:
CPU:DMA, 帮我搬个数据!
DMA:BOSS,你只需要告诉我从哪搬(起始地址),搬多少(字节长度),搬到哪!
然后 CPU 就可以爱干嘛干嘛,数据的搬运,和 DDR 打交道就全权由 DMA 负责了。
我们今天讨论的 DataMover 和上述典型的 DMA 的区别就在于,他不是由 CPU 来分配任务,而是由 FPGA 逻辑通过命令总线给出任务:从哪搬,搬到哪。
DataMover 的接口
那么 DataMover 是如何进行他的工作呢,我们可以从他的端口来了解。这里以 DM 的读通道为例。DataMover 共有三路接口(status 一般在调试中用于观察状态),一路 AXI 总线,两路 AXIS 总线。
AXI DataMover 读通道
读通道,将数据从如 DDR 这样的外部存储,搬运到 FPGA 的逻辑模块中。DDR 在 FPGA 上通过 MIG IP 访问,即 Memory 访问接口。MIG 提供了一个 AXI4 Slave 接口。DataMover 的 Master 接口连接到 MIG 的 Slave 接口,AXI4 协议提供了一种基于地址的访问 DDR 能力。关于 AXI4 对存储介质的地址访问,可以参考以下的文章,该文章中访问的是 BRAM ,但总线操作和访问 DDR 类似。
https://zhuanlan.zhihu.com/p/46672113
连接DDR示意图
如何创建 DDR MIG 在各个开发板的教程中都有提及,Step by Step 设置自己的开发板上搭载的 DDR 芯片信息和引脚即可。如果你有钱....有幸能用 Xilinx 的评估板,那么 MIG 还可以一键建立,自动导入硬件和 DDR 信息。
(我们实验室就是这么有钱...有幸有很多板子,包括 Xilinx ZCU102/VCU709/ KCU116 等等,如果对 FPGA 开发有兴趣,欢迎在今年以及未来的考/保研中加入我们实验室,我们有不错的硬件条件和项目。PS:坐标东部沿海211,省部共建国家重点实验室 )
无论是哪款开发板,都可以通过包含在 vivado 中的 MIG 中的示例工程对 MIG 进行仿真以及上板调试。
盗图自 xilinx PPT
从 MIG 中读取的 DDR 数据会以 AXI-Stream 总线的方式提供给逻辑部分。 AXI-S 总线相比 上述的 AXI4-Full 协议,信号更少,逻辑也比较简单。你可以从以下的文章中了解 AXI-S 的定义:https://zhuanlan.zhihu.com/p/49834243
应用 AXI 协议没有定义说的那么复杂,以下是读取一段数据的示例。
简单的AXIS时序
tvalid 为高表示数据有效,tlast 置起表示当前是当次传输的最后一个数据,比如表示一次 DDR 读操作的结束。tkeep 表示数据中的有效字节,作用和 AXI4-Full 中的 strb 作用相同。
前文提到 DataMover 有两路 AXI-S 总线,一路总线是数据输出总线,将从 DDR 读取的数据输出。另一路为命令输入总线。
DataMover 命令
前文提到 DataMover 是由逻辑控制,控制的方式是通过命令输入 AXIS 总线输入 DataMover 命令。
DataMover 命令控制的就是读写操作的起始地址,传输字节长度。命令有如下的格式,根据地址宽度的不同,命令长度不同,一般地址宽度,N,取 32 比特,命令长度 72 比特。
命令格式
一般只需要关注三个字段,EOF 字段设为 1 ,其他字段可以暂时填 0,这些填充固定值的字段将在后续的文章中进行分析。
- BTT:Byte to Transfer
- 传输字节数
- SADDR: start address
- 起始地址
- Type:
- 突发传输类型,这个字段在过去的 IP 核版本中没有启用(ISE 时代),默认为 incr
- 字段为 1 时:突发传输类型为 incr ,数据会保存在以起始地址开始递增的地址中
- 字段为 0 时:突发传输类型为 fixed, 数据均保存在起始地址中,覆盖旧值
当地址为32bit宽时,cmd 为32(低32位)+32(地址)+4(TAG)+4(RSVD)=72 bit
AXI DataMover 写通道
写通道的逻辑与读通道相同,只是数据的方向相反。
仿真
这部分中将演示 DataMover 基本的读写方式,输入由 testbench 产生。后续的文章中会讨论一些更加实用的逻辑,应用于板级调试中。
读操作
通过 testbench 写入命令,这里定义了一个写入命令的 task,将起始地址和字节长度组装成一个命令。这里 #UI\_CLK\_PERIOD; 代表延时一个周期
//task: read channel
task mm2s_cmd;
input logic[22:0] btt;
input logic[31:0] saddr;
logic [71:0] cmd = {
4'b0000,
4'b0000,
saddr,
1'b0,//DRR
1'b1,//EOF
6'b000000,//DSA
1'b1,//type 1:incr 0:fixed
btt
};
begin
S_AXIS_MM2S_CMD_0_tdata=cmd;
S_AXIS_MM2S_CMD_0_tvalid=1;
#UI_CLK_PERIOD;
S_AXIS_MM2S_CMD_0_tdata=0;
S_AXIS_MM2S_CMD_0_tvalid=0;
end
endtask
调用 task,指定传输的长度以及起始地址。
mm2s\_cmd(23'd64,32'h00000000);
读数据时序
读取到 64 字节数据,因为数据位宽 32 字节,所以读到两个有效信号脉冲。
写操作
写入命令和读命令类似,写命令可以在写数据之前或者之后,没有严格的对应关系。通过 AXIS 总线写入数据, DataMover 内部有一定缓存空间,相当于向一个 FIFO 写入数据。定义了一个写入指定长度的 task ,写入 data\_num*32 bit 数据
//task:write data
task s2mm_data;
input logic [31:0] data_num;
begin
S_AXIS_S2MM_1_tvalid=1;
S_AXIS_S2MM_1_tdata=256'h12345678123456781234567812345678;
S_AXIS_S2MM_1_tkeep=32'hfffffffff;
S_AXIS_S2MM_1_tlast=0;
repeat(data_num-1)begin
#UI_CLK_PERIOD;
end
S_AXIS_S2MM_1_tlast=1;
#UI_CLK_PERIOD;
S_AXIS_S2MM_1_tdata=0;
S_AXIS_S2MM_1_tkeep=0;
S_AXIS_S2MM_1_tlast=0;
S_AXIS_S2MM_1_tvalid=0;
end
endtask
写命令与写数据时序
这里写入了两个32位宽的数据,在第二个数据处, tlast 信号置起,表示传输的最后一个有效数据,tlast 清除表示此次传输结束。
结语
本文是 DataMover 的基础篇,初步介绍了 IP 核的用途和用法,并通过 testbench 调用 IP 实现了读写操作。但没有涉及比较复杂的部分,比如未对齐传输等。如何使用这些更复杂的应用,以及如何将 DataMover 使用在实际项目中,我们将在后续的文章中进行讨论。
参考资料
推荐阅读
关注此系列,请关注专栏FPGA的逻辑