2 同步流 (Synchronous Stream) 与异步流 (Asynchronous Stream)
2.1 概述
每个 Xillybus 流都带有一个标志位,用于决定该流是同步(synchronous)还是异步(asynchronous)工作。该标志位的值 在 FPGA 逻辑中固定。
当某个流被标记为异步时,只要相应的设备文件处于打开状态,该流就允许在 FPGA 与主机内核驱动之间传输数据,而无需 用户空间软件的干预。
异步流具有更好的性能,尤其是在数据流连续的情况下。同步流则更易于处理,并且当用户空间应用程序的行为与 FPGA 内 部操作之间需要紧密同步时,同步流是首选方案。
在 IP Core Factory 中生成的定制 IP 核里,每个流是同步还是异步的选择是自动完成的:该工具的用户在启用“自动设置内部 (autoset internals)”时,根据声明的流预期用途信息来决定。如果自动设置选项关闭,则由用户显式选择。
无论哪种方式,从 IP Core Factory 下载的捆绑包中附带的“readme”文件都会指明每个流的同步或异步标志(以及其他属 性)。
在所有演示捆绑包(demo bundles)中,与 xillybus_read_* 和 xillybus_write_* 相关的流都是异步的。xillybus_mem_8 是可 寻址的(seekable),因此是同步的。当使用 XillyUSB 时,相应的 xillyusb_* 文件同样适用。
2.2 异步流的动机
多任务操作系统(如 Linux 和 Microsoft Windows)基于 CPU 时间共享:进程获得 CPU 时间片,由某种调度算法决定在任 何给定时刻哪个进程获得 CPU。
尽管可以为进程设置优先级,但无法保证某个进程会持续运行,也无法保证抢占(preemption)周期有固定时长,即使在多 处理器计算机上也是如此。操作系统的底层假设是任何进程都能承受任意长度的 CPU 饥饿(starvation)周期。面向实时性的应用程序(例 如音频应用和视频播放器)对此问题没有确定的解决方案,它们依赖于操作系统典型的实际行为,并通过 I/O 缓冲来补偿抢占周期。
异步流通过允许数据在应用程序被抢占或忙于其他任务时持续流动来解决这一问题。接下来将讨论这对两个方向上的流的具 体意义。
2.3 从 FPGA 到主机的流
在上行方向(FPGA 到主机),如果流是异步的,FPGA 中的 IP 核会尽可能填充主机驱动的缓冲区。也就是说,当文件打 开、数据可用且缓冲区有空闲空间时,IP 核会主动获取数据。
另一方面,如果流是同步的,则只有主机上的用户空间应用程序有未决的读取文件描述符数据请求时,IP 核才会从用户应用 逻辑(通常是一个 FIFO)中获取数据。换句话说,只有当用户空间应用程序处于 read() 函数调用过程中时,才进行数据获取。
在高带宽应用中应避免使用同步流,主要有以下两个原因:
-
数据流会在应用程序被抢占或执行其他操作时中断,因此物理通道在某些时间段内处于空闲状态。在大多数情况下,这会导 致带宽性能显著下降。
-
在这些时间间隙中,FPGA 内部的 FIFO 可能发生溢出(overflow)。例如,若填充速率为 100 MB/s,一个典型的 2 kByte FPGA FIFO 从空(empty)到满(full)大约只需 0.02 ms。实际上,这意味着用户空间程序的任何抢占都可能导致 FPGA 中 FIFO 的溢 出。
尽管有这些缺点,但当数据在 FPGA 中被采集的时间点很重要时,同步流非常有用。特别是类似内存的接口需要同步接 口。
无论流是同步还是异步,由 FPGA 上的 Xillybus IP 核从应用逻辑接收到的数据,可以立即被主机上的用户空间应用程序读 取。
2.4 从主机到 FPGA 的流
在下行方向(主机到 FPGA),流为异步意味着主机应用程序的 write() 函数调用在大多数情况下会立即返回。更准确地说, 如果数据可以完全存储在驱动缓冲区中,则写入设备文件的函数调用会立即返回。随后,数据会以 FPGA 用户应用逻辑所要求的速率传输到 FPGA,无需主机应用程序软件的参与。
关于 XillyUSB 与其它 Xillybus IP 核在向 FPGA 发送数据(针对 FPGA 的异步流)的及时性上存在细微差异。
对于基于 PCIe 或 AXI 的 IP 核,只有在以下情况之一发生时,数据才会被发送到 FPGA:
-
当前 DMA 缓冲区已满(每个流有多个缓冲区)。
-
应用程序软件显式请求刷新设备文件(参见段落 3.4)。
-
文件描述符正在被关闭。
-
定时器超时,如果在特定时间内没有向该流写入任何数据(通常为 10 ms),则强制自动刷新。
对于 XillyUSB 流,数据几乎是立即发送的。更准确地说,驱动会尝试排队固定大小(通常为 64 kB)的 USB 传输,但如果 存在待发送数据,并且该流没有其他传输排队,则会排队一个较小的传输。因此,对于每个流,最多只有一个排队传输的大小小于固定大 小,但只要存在待发送数据,就至少有一个传输在进行中。这样既实现了 USB 传输的高效利用,又对短数据段实现了快速响应。
综上所述,所有 IP 核(XillyUSB 及其他 Xillybus IP 核)上的异步流行为大致相同,区别在于 XillyUSB 对短数据段的响应时 间更快(没有 10 ms 的延迟)。
另一方面,如果流是同步的,则写入设备文件的底层函数调用在数据全部到达 FPGA 中的用户应用逻辑之前不会返回。在典 型应用中,当 write() 函数调用返回时,表示数据已经到达连接到 FPGA 中 IP 核的 FIFO。
重要
的:
更高级的 I/O 函数(如 fwrite())会引入由库函数创建的缓冲区层。因此,即使在同步流中,fwrite() 及其类似函数也可能在
数据到达 FPGA 之前返回。
在高带宽应用中应避免使用同步流,主要有以下两个原因:
-
数据流会在应用程序被抢占或执行其他操作时中断,因此物理通道在某些时间段内处于空闲状态。在大多数情况下,这会导 致带宽性能显著下降。
-
在这些时间间隙中,FPGA 内部的 FIFO 可能发生下溢(underflow)。例如,若排空速率为 100 MB/s,一个典型的 2 kByte FPGA FIFO 从满(full)到空(empty)大约只需 0.02 ms。实际上,这意味着用户空间程序的任何抢占都可能导致 FPGA 中 FIFO 的下 溢。
尽管有这些缺点,但当应用程序有必要知道数据已到达 FPGA 时,同步流非常有用。例如,当流用于传输必须在某些其他操 作执行之前完成的命令(如硬件配置)时,就需要同步流。
2.5 不确定性 vs. 延迟
一个常见的错误是,为了数据同步而要求异步流具有低延迟。例如,如果应用程序是一个调制解调器,通常需要自然同步接 收和发送的样本。
这往往导致基于一种误解的设计:认为同步不确定性必然小于总延迟。为了保持低不确定性,延迟(以及缓冲区)被尽量减 小,从而导致整个系统面临困难的实时性要求。
使用 Xillybus 时,同步很容易做到完美(达到单个样本的级别),如段落 6.2 所述。因此,延 迟的限制来自于对到达数据快速响应的需求(如果有此需求)。
例如,在调制解调器中,最大延迟会影响应用程序的数据源对发送给它的数据做出响应的速度。在相机应用中,主机可能对 相机编程以调整快门速度来补偿光线变化。具有较大延迟的数据会减慢这个控制环路。
这些是需要考虑的实际问题,而且它们通常比混淆不确定性与延迟而产生的误解所导致的要求宽松得多。
