在前一篇基础篇的文章中,我们对 DataMover 这一 IP 有了基础的认识,本文我们将会进一步了解 TA。
所以还是你
我们前文中讨论过,一般意义上的 DMA 由 CPU 控制,在 Xilinx 嵌入式系统中, CPU 通过 AXI-Lite 总线控制 DMA 的初始化,发送以及接收数据。但其实 CPU 控制的 DMA 也是由 DataMover 组成的。所以还是你,DataMover。
来自 xilinx pg021
比如上图中 AXI 接口的 DMA 中,有一部分逻辑用于解析来自 CPU 的 AXI-L 总线,并将其转换为 DataMover 的 cmd/stat 逻辑,真正意义上的数据搬移操作还是由 DataMover 完成。从 Mapped Memory 的 AXI 总线到逻辑的 AXIS 总线。
这里我们刷新了对 DataMover 的认识, DataMover 不只是 DMA 的一种类别,实际上他也是其他 DMA 的组成部分,是 DMA IP 的核心组成部分。
S2MM (发送)命令中的 EOF 字段
在前文中我们提到 DataMover 命令中的 EOF 字段需要设置为 1 ,那么 EOF 字段代表什么意思,设置成 0 可不可以?
可以的,亲。
我们本文中首先从 S2MM ,也就是发送的角度来看 EOF 字段的作用。遮去的部分是 EOF 字段在 MM2S 中的作用以及 DRE 开启时的作用,这里我们先暂时无视。
在 indeterminate BTT 关闭的情况,一般情况下都会被关闭,当命令中设定的最后一个数据到来时,TLAST 会置起,这种模式下该 bit 应该置 1 。意思就是说,如果使用 TLAST 来表示一次命令所属的数据传输结束,即 TLAST 和属于该命令的最后一个数据的 TVALID 信号同时到来,需要将 EOF 位置 1 。
但也可以将 EOF 置 0,不使用 TLAST 来表示一个命令所属的数据传输结束,而只是让 DataMover 通过计数的方式来顺序区分不同命令的数据。
使用 tlast 或者不使用 tlast
PS: 这段内容笔者还不是非常确定,我们一般是将 EOF 置 1 ,使用 tlast 来表示一个命令的数据结束,但 EOF 设置为 0 也是可行的。欢迎大家一起来讨论,如果有新的见解也会及时更新。
Debug 一次有问题的 DataMover 传输
当我们进行了一次有问题的 DataMover,我们可能会得到一次的错误的数据,或者 DataMover 会因为先前错误的传输,进入错误状态,通过置低 cmd 或者 data 总线的 ready 信号,拒绝接下来的传输。
前文中我们提到,DataMover 除了数据/命令总线接口之外,还有一组传输完成情况汇报接口: sts 总线接口。sts 接口传输的是 DataMover 对每次传输,即每个传输命令的完成情况。通过 sts 接口我们可以判断传输的成功与否,或者失败的原因。
sts 接口的数据信号共 8 bit,这 8bit 分别用于表示成功或者失败的原因:
来自 Xilinx pg022
其中低 4 bit TAG 用于标识这是哪次命令的响应结果。最高位为 1 表示此次传输成功,剩下 3 bit 为 1 表示不同的失败原因。真可谓成功的传输响应相同,而失败的传输响应各自不同。
来自 Xilinx pg022
从字面意思上看,Slave Error 代表从机错误,DataMover 从 AXI 总线的回复信号中读取到从机发送的 response error 信号,再将其回复给发起传输的命令。
来自 Xilinx pg022
Decode Error 是笔者当时遇到过的问题,这个问题一般发生在非 1-对-1 的拓扑中,DataMover 可能连接到一个 AXI Interconnect ,比如一个 1-对-2 的拓扑结构,DataMover 作为两个从机的主机,通过Interconnect 连接到这两个从机,比如两个 BRAM 。
Interconnect 会读取用户为两个从机设定的地址空间,对接收到的 DataMover AXI 数据包中的地址进行解析,将 DM 的 AXI 数据包转发给相应的从机。但当地址无法解析到任何从机时,interconnect 会回复 decode error,DM 将其回复给 cmd 逻辑,使开发者检查自己的地址映射。
Internal Error 是一类比较经过遇到的问题。这类问题一般是由于命令中的 BTT (Byte to Transfer) 传输字节数字段与 s2MM 总线中实际发送的数据不一致导致的。
我们首先来看在 s2MM 总线上是如何完成一次数据传输的。
其中 tvalid 信号和 tready 信号同时为高代表 tdata 传输有效,tlast 在 tdata 传输有效时置起,表示当前的有效数据是本次传输的最后一个有效数据,表示这一次传输结束。此次传输传输了 6 个有效数据。
所谓一次传输指的是对应一条 DataMover 命令的数据传输,如果命令中的 BTT 为 6*数据位宽,那么这是一次正常的传输。但如果 BTT 为 4*数据位宽或者 8* 数据位宽,那么这种情况下,传输的状态为错误,tlast 过晚或者过早置起。所以说 internal error 又被称为因为 tlast 在不合适的时机置起造成的错误。出现错误时一般需要开发者检查自己的 s2MM 总线逻辑,尤其是产生 tlast 的逻辑。
命令中的 BTT 字段也会造成内部错误,当 BTT 字段为 0 时,触发 internal error 。需要在动态设置 BTT 的逻辑中增加对 0 的检查。
举个内部错误的栗子
如图是一次失败的错误,在写入命令和数据后,sts 总线上得到 DataMover 对此次传输的回应:0x10 ,对照上表可知,此时发生了内部错误,BTT 数量和实际在 AXIS 总线上发送数据的数量不一致。
(这里出错的原因是因为,BTT 的位宽是可以设置的,我的命令是按照 BTT 为 23 bit 组成的,但实际 BTT 设置成了 16 bit,所以实际 BTT 与我设想的 BTT 不一致)
多次传输错误的命令 -_-,可以看到 cmd_ready 和 s2mm_tready 都置低了。从图中还可以发现多次命令却只有一次 sts 响应。这是因为 DataMover 也只将第一次的 axis 操作转换为了 axi 操作写入了 slave,但在发生内部错误后,根本就没有再对后续的命令和数据进行响应了!
DataMover 的错误状态,只能通过 AXI 总线上的 arestn 信号清除。
监控 DataMover 的传输状态
一般来说,我们只会在调试状态下引出 sts 接口,在生产环境中我们只需要监控 DataMover 本身的状态即可。当产生多次传输错误后,DataMover 会置低命令总线上的 cmd_tready 信号,拒绝接收后续的命令以及产生新的传输。所以我们一般在设计中添加一个监控信号:
wire datamover_cmd_error = ~cmd_tready && cmd_tvalid
在逻辑希望产生传输请求,但 DataMover 始终拒绝接收的情况下,DataMover 的状态就出了问题。
同时 DataMover 还有两个错误信号:s2mm/mm2s_err 当这两个信号置起的时候,也代表传输出现了错误。
搭建仿真调试环境
Xun Lu 曾经说过:“全面完整的功能仿真可以节省大量综合和上板调试的时间。”对于 DataMover 也是同样的道理。和很多 IP 一样,Xilinx 也为 DataMover 提供了完整的仿真支持。
但在搭建仿真环境时,有一个问题:在 DataMover 的 slave 端接何种存储介质?
笔者认为当你要仿真 DataMover 的控制逻辑以及数据输入逻辑时,接一个 BRAM 就足矣,此时要仿真的是 DataMover 对用户逻辑的响应,存储介质的特性此时关系不大,并且连接 BRAM 在搭建速度和仿真速度上都有优势。
但如果你的设计是需要通过 MIG 来访问 DDR,并关心访问的延迟与数据带宽,那建议还是搭建一个 DDR 仿真环境,可以从 MIG 的示例工程中抽取一个 ddr 模型来搭建环境,实测仿真速度还是可以接受的。
结语
本文关注了 DataMover 一些更深的细节,包括 DataMover 与其他 DMA 的关系;DM 命令中 EOF 状态在 S2MM 中的用法;DM 的错误响应以及如何搭建仿真调试环境等话题。在后续的文章中,我们将继续讨论一些进阶的话题,包括不对齐传输等等。
PS:目前文章的组织有一些混乱,后续会整理,欢迎大家关注更新。