2 定义定制 IP 核
2.1 概览
IP Core Factory 是一个向导式的 Web 应用程序,可用于从头定义定制 IP 核,或以 demo bundle 中的内核配置为起点。
对于绝大多数应用场景,建议保持“Autoset internals”(自动设置内部参数)选项启用,依靠 IP Core Factory 来设定每个流 的属性。一个非常常见的错误是为了调整流参数而关闭此选项,但这几乎总会导致性能下降。
特别地,如果 IP 核未能达到预期的数据速率性能,问题很可能出在其他地方。此时建议参考以下两份指南,它们讨论了如何 实现 IP 核的完整性能:
另一个常见错误是关闭“Autoset internals”以调整 DMA 缓冲区的大小,使其匹配待传输数据包的大小。这一点在第 2.7 节中讨论。
使用该工具时,还有几点值得强调:
-
IP 核所针对的 FPGA 系列必须正确选择,因为 IP 核是以网表(netlist)形式交付的。
-
务必将每个设备文件的“use”(用途)属性设置为与实际用途相符的描述。这确保流的属性被正确配置。
-
对于 XillyUSB IP 核,应将“Expected bandwidth”(预期带宽)属性准确设置为该流所请求的最大带宽,因为数据速率会被限 制在该值。对于其他变体(PCIe 和 AXI),此属性仅影响性能调优。应使用实际的数值,而不是通过夸大需求来获得更好的结果。这种夸大 可能导致真正需要某些有限资源的其他流出现性能下降。
本节的其余部分将讨论一些设备文件的属性。
2.2 设备文件名称
每个流都被赋予一个名称,该名称用作主机上创建的设备文件的名称。
名称总是采用 xillybus_* 的形式,例如 xillybus_mystream。对于 XillyUSB,名称类似 xillyusb_NN_*,其中 NN 是一个索 引——当主机只连接一个 XillyUSB 设备时,通常为两个零。
在 Linux 系统上,该流作为普通文件打开,例如 /dev/xillybus_mystream。 在 Windows 中,同一个流显示为 \\.\xillybus\_mystream。
一个设备文件可以代表两个方向相反的流,它们只是碰巧共享该设备文件的名称。这两个流可以分别以任一方向打开,也可 以以读写方式打开。通常应避免使用此功能以防止混淆,但当设备文件被传递给期望双向管道的软件时,这会很有用。
2.3 数据宽度
数据宽度是指从 FPGA 中的 FIFO 读取或写入的字的位数。可选宽度为 32 位、16 位或 8 位。对于修订版 B/XL/XXL 的 Xillybus IP 核(这些版本在第 4 节中讨论)以及 XillyUSB,允许更宽的数据宽度。
当某个流需要高带宽性能,且 IP 核版本为 A(针对 PCIe)或任何针对 AXI 的 IP 核时,数据宽度必须设置为 32 位:16 位 和 8 位数据宽度会导致显著的性能下降,使得底层传输(例如 PCIe 总线传输)利用效率低下。
原因在于,这些字通过 Xillybus 内部数据路径以总线时钟速率进行传输。结果,传输一个 8 位字所占用的时间槽与传输一个 32 位字相同,这使得其效率实际上慢了四倍。
这也会影响同一时刻竞争底层传输的其他流,因为数据路径被较慢的数据元素所占用。
较新版本的 IP 核以及 XillyUSB 具有不同的内部数据路径结构,因此没有此限制。
无论如何,良好的实践是让主机应用程序中的 I/O 操作粒度与数据宽度匹配,例如,如果数据宽度为 32 位,则调用 read() 和 write() 函数时数据长度应为 4 的倍数。
数据宽度的选择不当可能导致不期望的行为。例如,如果从主机到 FPGA 的链路是 32 位宽,在主机端写入 3 字节数据,驱 动程序会无限期地等待第 4 个字节,然后才将数据发送到 FPGA。
2.4 用途
“用途”(Use)属性帮助生成 IP 核的工具为每个流提供最适合其预期应用的属性。
务必将“Use”设置为最匹配流用途的选项,以获得最佳性能。
以下是每个选项的简要说明:
-
图像采集/视频回放(Frame grabbing / video playback):如果该流用于视频数据应用,请选择此项。
-
数据采集/回放(Data acquisition / playback):如果你打算将该流连接到 DAC/ADC 或其他持续产生或消耗数据的设备,请 选择此项。
-
与协处理器数据交换(Data exchange with coprocessor):如果该流用于硬件加速(即 FPGA 代替 CPU 执行任务以提高性 能),请选择此项。当流需要高数据速率,但允许数据流偶尔短暂停止时,此选项适用。
-
外部硬件桥接(Bridge to external hardware):当 FPGA 借助流控制外部硬件时,此选项适用。例如,如果流中的数据包含 另一个组件的固件。
-
硅内逻辑验证数据(Data for in-silicon logic verification):如果该流用于向逻辑传输应用数据或从逻辑接收应用数据,以验 证该逻辑的功能是否正确,请选择此项。
-
命令与状态(Command and status):如果该流用于向 FPGA 发送命令或收集 FPGA 的状态信息,请选择此项。
-
短消息传输(Short message transport):如果该流包含短信息段(可能用于发送消息),此选项适用。
-
地址/数据接口(Address / data interface):如果你想能够对流使用 lseek(),请选择此项。选择此选项后,FPGA 端的接口 会添加一个地址输出。
此选项有三种变体,每种提供不同数量的地址线:5 根、16 根或 32 根。
关于可寻址流(seekable streams)的主题在 Xillybus FPGA 设计者指南中有进一步说明。
-
通用(General purpose):如果以上选项均不适用你的应用,则应选择此项。
当使用“Autoset internals”时,工具会根据上述选择的选项决定该流是同步流还是异步流。如果选择以下选项之一,则流为同 步流:命令与状态、短消息传输、地址/数据接口或桥接外部硬件。对于所有其他选项,流为异步流。
2.5 同步流与异步流
此属性在选择了“Autoset internals”选项后,会根据“用途”设置自动设定。
在大多数情况下,异步流(asynchronous stream)适用于连续数据流,而同步流(synchronous stream)适用于命令、控 制数据和获取状态信息。
对于同步流,所有 I/O(包括 FPGA 中的数据流)仅发生在 read() 或 write() 函数调用发起与返回之间。这提供了对事件发生 时间的完全控制,但在 CPU 处理其他事务时,数据传输资源处于闲置状态。建议阅读以下两篇文档中第 2 节关于此主题的详细说明:
使用同步流使软件编程更直观,但会对带宽利用率产生负面影响。使用异步流时,即使操作系统在一定时间内将 CPU 从用 户空间进程中剥夺,也能维持连续的数据流。
总结一下,以下是指导性问题:
-
对于下游流(主机到 FPGA):是否允许 write() 操作在数据到达 FPGA 之前就返回?
-
对于上游流(FPGA 到主机):是否允许 Xillybus IP 核在主机端发起 read() 操作之前就开始从 FPGA 的用户应用逻辑中获 取数据?
如果对应的答案为“否”,则需要同步流。否则,异步选项通常是首选,但需要理解对数据流的控制较少,并且稍微不那么直 观。
2.6 缓冲时间
Xillybus 在 FPGA 与主机之间维持一种连续数据流的幻象。DMA 缓冲区的存在对 FPGA 端的用户应用逻辑以及主机端的应用软件都是透 明的。它们仅用于控制数据流的效率及其保持连续性的能力,尤其是在高数据速率下。
数据采集(data acquisition)和数据回放(data playback)等应用需要在 FPGA 端有连续的数据流,否则数据会丢失。为了 维持这种流,用户空间应用程序需要足够频繁地调用 read() 或 write() 函数,以防止 DMA 缓冲区因 FPGA 的活动而变满(full)或变空 (empty)。
然而,确保这些函数调用足够频繁存在一个问题:常见的操作系统(如 Linux 和 Windows)可能会在理论上任意长的时间内 剥夺用户空间应用程序的 CPU 使用权。而 FPGA 会持续填充或清空驱动程序的缓冲区。因此,DMA 缓冲区必须足够大,以在 CPU 被短暂 剥夺的情况下维持连续数据流。
为了讨论方便,这里定义缓冲时间(buffering time)为:当数据以该流预期的速率填充缓 冲区(且在此期间没有数据被清空)时,流从所有 DMA 缓冲区均为空(empty)的状态变为所有缓冲区均为满(full)的状态所需的时 间。
在设置 Xillybus 流时,如果启用了“Autoset internals”(推荐),Web 应用程序中会出现一个标题为“Buffering”(缓冲)的选 择框。在此处选择所需的缓冲时间。
对于需要保持连续性的异步流,所选时间应反映 CPU 可能被用户空间应用程序剥夺的最大预期时间。
选择“Maximum”(最大值)会告知分配缓冲区的算法尝试分配尽可能多的 RAM,同时仅对其他流给予少量考虑。
给定所需的缓冲时间 t 和预期带宽 W,算法将尝试为驱动程序的 DMA 缓冲区分配总 RAM 量 M,基于以下公式:
M = t x W
然而,实际缓冲区大小始终是 2 的幂次方(2^N)。也可能无法分配足够的内存来满足所需的缓冲时间。
因此,务必在 IP 核的 README 文件中查看分配的缓冲区大小,并验证其是否可接受。手动设置缓冲区大小(即关 闭“Autoset internals”)可能是必要的,以强制在流之间实现更合适的 RAM 分配,使其更适合目标应用。
2.7 DMA 缓冲区的大小
建议让工具通过启用 Web 应用程序中的“Autoset internals”自动设置 DMA 缓冲区的参数(参见上文第 2.6 节)。在某些情况下,自动设置可能不适用 于应用,此时可以手动设置 DMA 缓冲区的大小和数量。
对于异步流,缓冲区的参数有显著影响,这在以下两个文档中标题为“Continuous I/O at high rate”(高速率下的连续 I/O)部 分中讨论:
完全没有必要将 DMA 缓冲区的大小适配到预期的 read() 和 write() 函数调用的大小。正如这两份指南所解释的,DMA 缓冲 区的大小在 read() 和 write() 函数调用中是无关且透明的。特别是,如果 FPGA 中的 IP 核已经有足够的数据到达,read() 函数调用会立即返 回(无论 DMA 缓冲区的填充水平如何)。这得益于 FPGA 与主机之间的一种机制,允许 FPGA 提交部分填充的 DMA 缓冲区。当有助于立 即完成 read() 函数调用时,就会使用这种机制。
同样地,从主机到 FPGA 的数据可以通过显式请求来确保立即到达 FPGA。
一个常见的错误是将 DMA 缓冲区的大小与预期的数据交换模式联系起来。使用 Xillybus 则无需这样做,这也再次说明为什 么“Autoset internals”是设置 DMA 缓冲区大小的首选。
为了保持连续性,更多的 RAM 更好,因为 DMA 缓冲区的总空间可以保持数据流连续,即使 CPU 被应用程序剥夺时也是如 此。做出正确决策还涉及其他因素,这些因素在上文引用的编程指南中有详细说明。
然而,当 DMA 缓冲区的总大小过大时,存在缓冲延迟(buffering delay)的风险,这是 由于能够存储大量数据所致。结果,当一端填充缓冲区的速度快于另一端清空它们时,数据可能会在相当长的时间后才到达另一端。这可以 通过 Xillybus FPGA 设计者指南中标题为“Monitoring the amount of buffered data”(监控缓冲数据量)部分提到的技术来控制。
对于 XillyUSB IP 核,每个流只有一个缓冲区,它作为一个由驱动程序管理的大 FIFO 运行。其他 IP 核(PCIe 和 AXI)为每 个流维护多个 DMA 缓冲区,因此需要定义其大小和数量。因此,DMA 缓冲区的有效大小是每个 DMA 缓冲区的大小乘以数量。
相应地,如果对于基于 PCIe/AXI 的 IP 核关闭了“Autoset internals”,则需要指定 DMA 缓冲区的数量和每个缓冲区的大小。 需要考虑以下几点:
-
每个 DMA 缓冲区的大小在从主机到 FPGA 的流中有其自身意义:当这些缓冲区填满时,数据会被发送到 FPGA(除非软件 显式请求刷新,或者流空闲超过 10 毫秒)。因此,每个 DMA 缓冲区的大小会影响数据流量的典型延迟。
-
对于慢速流(小于 10 MBytes/s),推荐的 DMA 缓冲区数量为 4。当需要更高带宽时,选择缓冲区数量以达到合适的总体 DMA 缓冲区分配。对于高带宽流,合适的 DMA 缓冲区数量在 16 到 64 之间,前提是这能使每个缓冲区的大小为 128 kBytes 或更小。
-
所有流的 DMA 缓冲区总分配不应超过 512 MBytes,除非主机上使用了增强型驱动程序。否则,操作系统可能拒绝分配超过 此限制的内存,导致驱动程序初始化失败。
-
每次缓冲区填满时,会向主机发送一个硬件中断。鉴于预期的数据速率,应计算中断率并将其保持在处理器可接受的范围内 (每秒不超过几千次)。
-
当可以通过增加 DMA 缓冲区的数量来达到总大小时,每个 DMA 缓冲区的大小不应超过 128 kBytes。
对于同步流,驱动程序的 DMA 缓冲区问题不那么重要。对于这类流,经验法则是:为某个流分配的缓冲区总 RAM 量应与预 期的 read() 和 write() 函数调用的数据长度处于同一数量级。如上所述,并不需要使缓冲区大小适应这些 函数调用,但将缓冲区做得比这更大通常没有必要,只会浪费内核 RAM。
2.8 DMA 加速
对于基于 PCIe 的 IP 核,从主机到 FPGA 的流可能需要 DMA 数据传输加速。
为了实现该方向的数据交换,PCIe 总线协议规定 FPGA 应向主机发出数据请求,并等待数据到达。请求在总线上传输、被 主机排队和处理、以及数据返回的过程中会产生固有的延迟。这种往返时间间隙会导致总线效率下降,有时会将单个流的带宽降低到 40%。
为了解决这个问题,会发送多个数据请求,以便在连续传输期间主机队列中始终有一个请求。由于来自不同请求的数据可能 以随机顺序到达,因此必须将其存储在 FPGA 上的 RAM 缓冲区中,以便向应用逻辑呈现有序的数据流。
FPGA 中的每个缓冲区用于存储一段请求的数据。当前 DMA 加速的可选设置如下:
-
无(None)。FPGA 上不存储任何数据。仅当上一个数据请求的所有数据都到达后,才发送下一个数据请求。
-
4 个段,每段 512 字节。在 FPGA 上分配 2048 字节的块 RAM。任何时候最多可同时有四个数据请求处于活动状态。
-
8 个段,每段 512 字节。在 FPGA 上分配 4096 字节的块 RAM。任何时候最多可有八个数据请求处于活动状态。
-
修订版 B 及更高版本的 IP 核还提供 16 个段(每段 512 字节)的选项。
从请求到数据到达的往返时间取决于主机的硬件。因此实际带宽性能可能有所不同。
在 IP Core Factory 中使用“Autoset internals”时,加速资源的自动分配基于典型 PC 计算机硬件上的测量结果,在极少数情况 下可能需要手动调整。
