01signal.com

带 Xillybus的简单 data acquisition

概述

Xillybus最常见的使用场景是 data acquisition。此页面显示如何开始将 data 从 FPGA 传输到 host。

有关 FPGA内部与 Xillybus 交互的更详细视图,请参阅Xillybus FPGA designer's guide

如果应用程序具有高 data rate,还建议阅读 Linux Windows的Getting Started guide 第 5 章中关于此的指南。

host的侧面

为了理解 Xillybus 是如何工作的,最简单的方法是从电脑端入手: 此命令可用于将 data acquisition 执行到磁盘上的文件中:

$ cat /dev/xillybus_read_32 > capture-file.dat

“cat”是一个标准的 Linux 命令,它从一个文件中读取所有 data 并将这个 data 写入 standard output。在此示例中,输入不是常规文件,而是 device file。此输入由来自 FPGA的 data stream 组成。 standard output有一个 redirection ,所以这个 data 写到磁盘上的一个文件中。

这不是一个人为的例子: 在一些使用场景下,这才是实际使用 Xillybus 换 data acquisition的正确方式。最好使用“dd”以获得特定数量的 data。更常见的是,使用专用计算机程序从 device file读取 data 。每个应用程序都有自己的首选方式来使用 data。

因此,无论您喜欢哪种编程语言,或者您使用 Linux 还是 Windows,都没有关系。从 FPGA 接收 data 的电脑软件只需要和“cat”一样: 打开一个文件,并从中读取。有一个单独的页面介绍了 file I/O的标准编程技术。有关 Xillybus 编程技术的更多详细信息,请参阅 LinuxWindows的编程指南。

适用于 data acquisition的 logic

现在让我们看看 FPGA中发生了什么。 Xillybus IP core 和 application logic 通过 FIFO交互: application logic 将 data 写入 FIFO, Xillybus 确保这个 data 到达 host。如果您不熟悉此概念,则有一个单独的页面解释 FIFOs 的工作原理。

Xillybus的 demo bundle 包含一个名为 xillydemo.v.这是与 IP core接口的 Verilog 代码。 demo bundle中还有一个 VHDL 文件: xillydemo.vhd。但是,下面的示例是在 Verilog中。

Xillybus IP core的 instantiation 发生在 xillydemo.v中。 (或 xillydemo.vhd)。这些是与上面的“cat”命令的 data acquisition 示例相关的部分(其他部分被跳过)。

// Wires related to /dev/xillybus_read_32
   wire        user_r_read_32_rden;
   wire        user_r_read_32_empty;
   wire [31:0] user_r_read_32_data;
   wire        user_r_read_32_eof;
   wire        user_r_read_32_open;

[ ... ]

   xillybus xillybus_ins (
[ ... ]
			  // Ports related to /dev/xillybus_read_32
			  // FPGA to CPU signals:
			  .user_r_read_32_rden(user_r_read_32_rden),
			  .user_r_read_32_empty(user_r_read_32_empty),
			  .user_r_read_32_data(user_r_read_32_data),
			  .user_r_read_32_eof(user_r_read_32_eof),
			  .user_r_read_32_open(user_r_read_32_open),
[ ... ]
			  .bus_clk(bus_clk),
[ ... ]
			  );

IP core的 ports 的含义在Xillybus的 logic API的指南中有详细说明。

xillydemo.v中有一个 FIFO 的 instantiation 。这个 FIFO 是一个 single-clock FIFO,适用于原来 Verilog 代码中演示的 loopback 。对于 data acquisition 应用, dual-clock FIFO 更合适,因为 data acquisition logic 通常依赖于其自身的 clock。

因此,您可以将 xillydemo.v 更改为执行 data acquisition 的 module ,如下所示: 删除 single-clock FIFO 的 instantiation (称为 fifo_32x512)。改为插入:

assign user_r_read_32_eof = 0;

   dualclock_fifo_32 fifo_32
     (
      .rd_clk(bus_clk),

      .rst(!user_r_read_32_open),
      .rd_en(user_r_read_32_rden),
      .dout(user_r_read_32_data),
      .empty(user_r_read_32_empty)

      .wr_clk(capture_clk),

      .wr_en(capture_en),
      .din(capture_data),
      .full(capture_full)
      );

dual-clock FIFO

dualclock_fifo_32 是标准的 dual-clock FIFO。它需要使用 FPGA的开发软件创建为 IP 。这个 FIFO 的深度应该是 512 elements 以上。由于 FPGA 软件之间的差异, ports 的名称可能与上面显示的不同。尽管如此,还是很容易推断出如何连接 FIFO的 signals。

再一次,有一个介绍 FIFOs的页面,如果您还不熟悉的话。

FIFO的 ports 中有几个直接连接到 Xillybus的 IP core: @rd_clk、 @rd_en、 @dout 和 @empty。请注意,这些连接与 demo bundle中的完全相同。 IP core 使用这四个 signals 从 FIFO拉出 data 。这始终是将这些 ports 与 IP core连接的正确方法。

请注意, @rd_clk 连接到 @bus_clk。这个 signal 来自 Xillybus的 IP core。换句话说, IP core 决定了 FIFO的一侧使用的 clock 。

至于 @rst,注意是接 !user_r_read_32_open的。只有在 host上打开相关的 device file 时, @user_r_read_32_open 才为高电平。因此,当文件未打开时, FIFO 将被重置。因此,当 host 打开 device file时, FIFO 为空: 如果 FIFO的存储空间中存在上一个会话的剩余物,则在关闭 device file 时将其删除。

这种行为是我们通常对 data源代码的期望。但是,如果您希望 FIFO 在 device file 关闭时保留其 data ,请将其他东西连接到 @rst。或者可能将 @rst 保持在低位。

请注意, @user_r_read_32_eof 在此示例中为零,在 demo bundle中也是如此。此 signal 可用于将 end-of-file 发送到 host。 API指南中有更多相关信息。

与 application logic的接口

在 data acquisition 应用程序中,总会有某种 application logic 生成 data 以传输到 host。这部分不同于一个应用程序到另一个应用程序,因此与本讨论无关。我们将专注于将此 data 发送到 host。

这部分非常简单: application logic 将这个 data 写入 FIFO。写入 FIFO 的 data 作为连续的 data stream到达 host 上的计算机程序。

因此, application logic 使用标准约定写入 FIFO。在上面的示例中,这显示为 @capture_clk、 @capture_en、 @capture_data 和 @capture_full。这个 logic 只需要将 data 放在 @capture_data 中,控制 @capture_en 就可以正确写入 FIFO了。请注意, application logic 使用其自己的 clock 写入 FIFO。

data flow

这是一个简化的框图,说明了 data flow 从 application logic 到 host上的 user application program 。

Simplified data flow diagram for data acquisition with Xillybus

请注意,此框图中省略了两个技术细节: PCIe block 和 kernel driver 没有展示,因为它们与用户对 data flow的感知无关。 Xillybus 的正确使用方法是忘记这些细节,专注于 application logic 和 application software。

不需要把 data 整理成 packets: application logic 和计算机之间的通信通道是 continuous stream。 IP core 和 driver 确保 data flow 的行为与其他 stream protocols相同,例如 Linux中的程序之间的 pipes 。另一个具有类似行为的协议是 TCP/IP。换句话说, FIFO写入多少 data 并不重要。这个 data 很快就会到达 host上的 user application program 。

将 data 组织成 packets 并将 IP core的 DMA buffers 调整为这些 packets的尺寸是一个常见的错误。这样做没有任何好处。即使 data 在具有恒定尺寸的 packets 中发送,也无需将 IP core 调整为该尺寸。

但是,如果 FIFO 变满了怎么办?

关于 FIFO 的基本规则之一是: 如果 @full port 为高电平,则 @wr_en 必须为低电平。简单来说: 不要写入完整的 FIFO。那么如果发生这种情况怎么办?处理这种情况会使 application logic 变得相当复杂。

简短的回答是 FIFO 永远不应该变满: 在正常操作条件下,会主动防止这种情况发生: IP core 从 FIFO 读取 data ,并将这个 data 复制到 host的 RAM中。这种情况发生得足够快,足以防止 FIFO 充满。通常, FIFO 不需要比 512 data elements更深。

但是,如果 application logic 写入 FIFO 的速度过快, FIFO 可能会变满。换句话说,如果 application logic的平均 data bandwidth 超过 IP core的限制(如针对每种类型的 IP core所宣传的那样),则 IP core 将无法足够快地从 FIFO读取 data 。

另一种可能性是 user application software (例如上例中的“cat”)没有足够快地从 device file读取 data 。结果, host RAM buffer 将变满,这也将阻止 IP core 从 FIFO 读取(因为 IP core 没有任何地方可以写入 data )。结果,出现了 overflow 。发生这种情况是因为 user application software 写得不好。另一个可能的原因与操作系统有关,这将在下面进一步讨论。

host 上 RAM buffer 的大小取决于 Xillybus IP Core。例如,对于 xillybus_read_32 和 xillybus_write_32 (在 IP core 中是 demo bundle的一部分),此尺寸为 4 MBytes 。 IP Core Factory 允许创建需要更大 buffers的自定义 IP cores 。

综上所述: 防止 overflow 是为 IP core选择正确参数的问题: 首先,这款 IP core 应该能够处理 data rate。除此之外, host RAM buffer 应该足够大。这确保即使 user application program 不从 device file读取 data , data flow 也可以继续。

尽管如此,如果 FIFO 变满,则常见原因是 system design出现错误。选择 overflow 的一个常见原因是高估了计算机处理 data rate的能力。

特别是,如果将 data 写入磁盘上的文件(如上面的“cat”命令),最大 data rate 可能比人们想象的要慢。原因是操作系统通常有一个很大的 disk cache (可能有很多 Gigabytes)。如果用比 disk cache小的 data 量来测量磁盘的 data rate ,结果会过于乐观: 在实际发生之前,操作系统会假装已经完成将 data 写入磁盘。实际上, data 只到 cache,实际写入磁盘发生在后面。只有在处理大量 data 时才会显示此错误。

CPU的剥夺

不幸的是, overflow有一个不可避免的可能性: 允许操作系统(Linux 或 Windows)无限期剥夺 CPU 的任何 user-space process 。也就是说,读取 data 的计算机程序可以突然停止工作一段时间,然后再恢复正常运行。这个时间段没有限制。允许任何 non-real-time operating system 像这样随机暂停 processes 。

然而,这些操作系统仍然可以使用 data acquisition 。这主要是因为长期剥夺 CPU 通常被认为是一种不良特征。所以这些停顿通常很短。

在这些暂停期间, IP core 继续填充 host 上的 RAM buffer (凭借 DMA,因此不需要 processor的干预)。当计算机程序取回 CPU 时,它可以快速消耗所有已经积累的 data 。补偿 10 ms 暂停的 RAM buffer 通常就足够了。但是,在IP Core Factory上创建自定义 IP core 时,可以请求更大的 buffer 。

尽管如此,仍有可能暂停时间过长。结果, RAM buffer 将变满,因此 FPGA 上的 FIFO 将变满。这个 overflow 的结果就是 data 会丢失。这永远不应该发生,也可能永远不会发生。但如果是这样呢?

检测 overflow

建议的解决方案是在 FIFO 变满时终止 data stream : logic 在连续 data的最后一个元素之后立即向 host 发送 EOF (end-of-file)。因此,让我们考虑一下如果 host 使用“cat”命令使用 data 会发生什么情况,如上所示:

$ cat /dev/xillybus_read_32 > capture-file.dat

通常,此命令将继续执行,直到使用 CTRL-C停止为止。但如果 FIFO 在 FPGA中变满,则此命令将正常终止,就像完成复制常规文件后一样。输出文件将包含在 FIFO 变满之前收集的所有 data 。

总结一下这个方法: 所有写入 capture-file.dat 的 data 都保证无错误且连续。如果 data acquisition 系统由于 CPU 剥夺而无法保持连续性,则结果是较短的输出文件。但是文件的内容是可以依赖的。

为了实现此解决方案,请将 dualclock_fifo_32的 instantiation 替换为:

eof_fifo fifo_32
     (
      .rd_clk(bus_clk),

      .rst(!user_r_read_32_open),
      .rd_en(user_r_read_32_rden),
      .dout(user_r_read_32_data),
      .empty(user_r_read_32_empty)

      .wr_clk(capture_clk),

      .wr_en(capture_en),
      .din(capture_data),
      .full(),
      .eof(user_r_read_32_eof)
      );

eof_fifo 的定义在单独的页面上给出。

请注意, @user_r_read_32_eof 连接到此 FIFO的 @eof port。这就是 logic 在必要时将 EOF 发送到 host 的方式。另请注意,没有任何东西连接到此 FIFO的 @full port: 无需再监视该信号。如果 FIFO 已满,则没有什么可做的。 EOF 机制确保 host 在消耗完所有有效数据后重新启动数据流。

Data playback

反方向呢?像这样的事情怎么样?

$ cat playback-data.dat > /dev/xillybus_write_32

这工作原理相同: “cat”命令从磁盘上的文件读取并将 data 写入 device file。在 FPGA上, IP core 将这个 data 写入一个 FIFO。 application logic 从 FIFO读取 data 。同样的想法,只是方向相反。

这是一个简化的框图,说明了从 data flow 、 host 上的 user application program 到 application logic。

Simplified data flow diagram for data playback with Xillybus

与 data acquisition类似,没有丢失 data的风险: IP core 已满时不会写入 FIFO 。结果, host的 RAM 中的 buffer 也可能变满。发生这种情况时, host 上的 user application program 会等待(通过休眠),直到 application logic 从 FIFO读取足够的 data 。

与 data acquisition 的另一个相似之处是,只要 user application program 继续足够快地写入 data , FIFO 就永远不会变空。为了保证这一点,同样的考虑是相关的: IP core的规格以及 user application program 必须支持所需的 data rate。

当满足这些条件时, application logic 可以在需要 data 时从 FIFO 消耗 data 。 underflow 永远不应该发生。

Asynchronous streams 与 Synchronous streams

这个主题没有直接关系,但仍然值得简要讨论一下。

在 data acquisition 应用程序中,主要目标是保持连续的 data flow。相应地, IP core 尽快将 data 从 FIFO 移动到 host的 RAM buffer 。 host 上的 user application program 在给定时刻请求 data 并不重要(通过对 read() 的函数调用或类似方式): 只要 device file 打开并且 FIFO中有 data , data flow 就会继续。

这意味着 host 没有办法从 FPGA 控制 data flow (除了打开和关闭 device file,或者诉诸特定应用程序的解决方案)。然而,在大多数现实生活中的 data acquisition 应用程序中,不需要控制 data flow: data flow 是打开 device file的结果,这很好。从 FIFO读取 data 的每个元素的确切时间并不重要。

行为类似的 device file 称为asynchronous stream (在 Xillybus的术语中)。

但是,在其他应用中,收集 data 的时间很重要。例如, FPGA 上的 application logic 可能会从 FIFO发送 status register 而不是 data 的内容。这在 demo bundle 中进行了演示,其中 device file 名为 xillybus_mem_8。在这种情况下,控制何时在 FPGA收集 data 非常重要: host 从 device file 读取数据以获取有关当前状态的信息,而不是过去某个未知时间的状态。

Xillybus 有 synchronous streams 用于此类应用程序: IP core 总是从 FPGA收集尽可能少的 data 。换句话说, IP core 仅在响应 host上对 read() (或类似)的函数调用时收集 data 。因此, host 控制何时从 FPGA收集 data 。

synchronous streams 的缺点是 data flow的停顿。这些暂停的主要问题是,当 data flow 暂时停止时, FPGA 上的 FIFO 可能会变满。这些暂停也降低了 data flow的效率,因此最大 data rate 变得更低。但是,这两个缺点仅与 data acquisition 应用程序相关。无论如何,这样的应用程序应该使用 asynchronous stream 。

关于反方向的 device files , asynchronous streams 和 synchronous streams也有区别。在这个方向上,区别在于 write() 函数调用的返回: 对于 asynchronous streams,一旦将 data 写入 RAM buffer, write() 就会返回。所以在大多数情况下, write() 根本不休眠。另一方面,对于 synchronous streams, write() 会等到 data 在 FPGA上交付。当使用通信通道发送命令时,这一点很重要。但再一次,这对 data acquisition 应用程序不利。

在 demo bundle中,只有 /dev/xillybus_mem_8 是 synchronous stream。其他四个 device files 是 asynchronous streams。

IP Core Factory中,选择 synchronous stream 还是 asynchronous streams 取决于应用程序的选择( drop-down menu 为“use”)。例如,如果您选择“Data acquisition / playback”,该工具将生成一个 asynchronous streams。如果您选择“Command and status”,您将获得 synchronous stream。也可以通过关闭“Autoset internals”来手动选择它。

有关 asynchronous streams 和 synchronous streams的更多信息,请参阅 programming guide for Linux (或programming guide for Windows )中的第 2 部分。关于 IP Core Factory,请参考 guide to defining a custom Xillybus IP core

概括

使用 Xillybus可以简单快速地创建一个简单但实用的 data acquisition 系统: application software 归结为使用标准 Linux 命令(“cat”)。在 FPGA方面,与 Xillybus的 IP core 的交互仅包括将 data 写入 FIFO。

使用 Xillybus 收集的 data 保证无错误且连续。但是,由于操作系统的性质,无法保证永远不会出现 overflow 。由于这是不可避免的,因此最佳方法是保证在发生这种情况时检测到 overflow 。 Xillybus 通过向 host发送 EOF 来为此目的提供一种简单的机制。

此页面由英文自动翻译。 如果有不清楚的地方,请参考原始页面
Copyright © 2021-2024. All rights reserved. (6f913017)