RF 数据转换器软件驱动 - 真的很简单,不会让人太沮丧

作者:Keith Lumsden,赛灵思应用工程师

嗨,我叫 Keith Lumsden,是赛灵思的一名应用工程师。

很高兴受邀为赛灵思社区的全新设计和调试技术博客撰稿。

我的主要任务是为使用集成到 Zynq®UltraScale™RFSoC 产品中的 RF 数据转换器的客户提供支持。

在我的职业生涯中,我一直从事模拟和混合信号系统、FPGA 架构、I/O 和信号完整性方面的工作。 所以我真的是一个搞硬件的人,让我有时有一点负罪感的是我曾认为嵌入式软件适合其他人来做。

随着射频 (RF) 数据转换器的问世,情况发生了变化。我们现在已经将世界一流的 RF ADC 和 DAC 集成到 Zynq UltraScale +架构中了。因此,传统的射频和模拟工程师不可避免地以前所未有的方式接触到了嵌入式系统。

射频数据转换器解决方案

如果您对数据转换器解决方案很熟悉,那您就会知道它是以 IP 核的形式被封装到 Vivado Design Suite 中的。这让您可以通过赛灵思提供的软件驱动来管理射频模数转换器 (RF-ADC) 和射频数模转换器 (RF-DAC) 块的状态和控制。

《Zynq UltraScale + RFSoC RF 数据转换器 IP 产品指南》(PG269) 提供了有关此 IP 的所有详情,并且还提供了有关该驱动的详细附录。

开始在 RF-ADC 和 RF-DAC 上进行调试

RF 分析仪工具是一个不错的起点。

RF 分析仪是基于 MicroBlaze™ 的设计,具有通信层,可以部署到任何电路板上的任何器件上。它还带有一个 GUI,让您可以将 RF-ADC 接收的内容可视化,并可通过 RF-DAC 来实现激励生成和发射函数。至关重要的是,该应用是通过软件驱动构建的。

如果您试图追踪 RF 系统中的问题,RF 分析仪非常强大,而且由于它独立工作,不依赖于设计或电路板,因此可以用来验证系统的 RF 部分。

一个常见的用例是您希望在系统中调试 RF-ADC 和 RF-DAC,并需要编写一个小应用以在运行时进行测试。鉴于 RF 分析仪和定制设计都需要使用软件驱动,我决定编写一个博客帮助您了解驱动,并展示一下如何开始用它来进行调试。在下一个博客中,我会展示一次拆箱,带您一起来看看 RF 分析仪工具。

有可能您对 RF 数据转换器系统已经很熟悉了,那就把了解驱动想成是在您的知识基础上再增添一点知识,而不是去了解完全未知的事物吧。

在这篇博客中,我会介绍以下内容:

  • 如何构建驱动
  • 数据结构
  • 使用应用编程接口 (API) 来制作一个简单的应用
  • 我们现在还是来制作一个 Baremetal 应用吧。在稍后的博客中,我们会在此基础上介绍如何制作 Linux 应用。

    构建驱动:

    RFDC 驱动的一个优点是它是使用 Libmetal 构建的。Libmetal 是赛灵思开发的开源软件堆栈,提供用来访问器件的、处理器件中断、请求跨 Linux、Realtime OS 和 baremetal 内存的通用用户 API。

    这对我们来说意味着什么?嗯,这意味着我们真正感兴趣的驱动部分是在用户空间实现的,因此我们不必担心与硬件交谈的机制。这还意味着 API 在 Linux 和 Baremetal 应用中很常见,因此您不需要了解两组 API 调用,也不用担心如何将代码从 Baremetal 移植到 Linux。

    XRFdc 驱动程序源代码在下图中显示得更详细。驱动的源代码可以在赛灵思的 SDK 安装中找到,也可以在Github 上单击这里找到。

    在这个图中,在底部您会看到 xrfdc_hw.h 文件。这个报头文件包含您可能会认为是 RF ADC 和 DAC 块的寄存器地址映射。这个文件并不是编写来供用户使用的,而是用于驱动的内部机制。应该不需要用到此文件中的标识符,甚至不需要研究它们,因为它们在编写应用时无法真正帮到您。

    其他报头文件(例如 xrfdc.h 和 xrfdc_mts.h)更重要,因为它们内含以下内容:

  • API 调用所需的所有数据结构。
  • 用于实现 API 调用的代码中的内联(辅助)函数。
  • 用于应用的宏(这很有用,因为您可以在适当的时候使用宏 XRFDC_ADC_TILE/XRFDC_DAC_TILE,而不是将 ADC 块的值 0 或 DAC 块的值 1 传递给 API。这让读取和理解代码变得易如反掌。)
  • 最重要的是,这些文件显示的是将用于您的应用的 API 调用原型。
  • 接下来,您将看到的是 API 调用的源代码。这是我们实现用户 API 调用功能的地方。通常,不需要非常详细地研究各个 API 调用是如何实现的。

  • xrfdc.c:这是用于应用的 API 的主体。
  • xrfdc_intr.c:用于实现管理 RF 数据转换器中断所需的 API 调用。
  • xrfdc_mixer.c:包含 XRFdc 驱动中混频器设置的接口函数。
  • xrfdc_mts.c:包含 XRFdc 驱动的多块同步函数。
  • XRFdc 软件驱动的工作原理

    我们已经看到了驱动源中包含的内容,现在我们来谈谈您需要用它来做什么。

    如上所述,xrfdc.h 报头文件应该是您最常引用的文件。这个文件中提供了数据结构和 API 函数原型。数据结构和 API调用在 (PG269) 的附录 D 中也有详细记录。

    我们先来谈谈数据结构。

    数据结构是一种信息组织方法,用于将有关 RF 数据转换器的信息组织成有意义的组。我喜欢将数据结构看成是一个“容器”。一个全职软件开发者可能会说这是一个 C ++ 术语,不应该这样使用,但这个比喻对我有用。

    我想指向的示例数据结构是 RF-ADC 或 RF-DAC 块中的锁相环 (PLL)。看一下,我们可以看到它描述了我们可能需要知道的关于 PLL 的一切,例如,它是否已启用、它的输入时钟、它输出的示例时钟等。

    一旦这样的结构存在,我们就可以在 API 之间轻松地来回传递它,并且可能还可以从一个 API 中读取,或在另一个 API 中进行修改。

    您还可以在另一个结构中找到结构。

    例如,XRFdc_PLL_Settings 是 XRFdc_DAC_Tile 的一个成员

    由于结构是透明的,因此您可以在代码中单独对它的一个成员进行修改。一个例子是复杂混频器中数控振荡器频率的变化。

    MixerSettings 结构有一个称为 Freq(表示频率)的成员,因此我们可以按如下方式在代码中对它进行更改。
    MixerSettings.Freq = 2000;//MHz

    一旦理解了数据结构的基础知识,我们就需要掌握可以使用的 API 调用了。这些是应用的构建块。

    各个 API 调用机制被抽取并传递给用户。它们在 XRFdc.c 文件中被实现。

    如果您想使用调用,只需要知道三件事。

  • 要使用的 ADC 或 DAC 块中的函数。
  • 您需要将其作为输入传递。
  • 这通常意味着您可以告诉它您在块中需要的 RF 块类型、块 ID 和单个块。
    API 可能需要被传递一个结构才能使用。

  • 您会得到什么作为输出。
  • 例如,它可能会返回结构的内容。

    此驱动中只使用了几种不同类型的 API:

  • 有些管理调用可以用来控制块,例如 XRFdc_StartUp/XRFdc_Shutdown。这些块用来启动或关闭单个 RF-ADC 或 RF-DAC 块。
  • 有些 API 调用可以启用高级状态报告,例如,XRFdc_GetIPStatus/XRFdc_GetBlockStatus。
  • 还有针对各个子块的 Get 和 Set API 调用。例如,您可以分别使用 XRFdc_GetMixerSettings 和 XRFdc_SetMixerSettings 同时用 get 和 set 来进行复杂的混频器设置。
  • 请务必注意,在 IP 中也会配置一些 Get 和 Set 调用,例如复杂的混频器设置。有些调用只能在运行时完成。一个例子是 RF-ADC 阈值标志和正交调制校正 (QMC)。

    最后,其中一些更改将需要重新启动块。更改 PLL 设置就是一个这样的例子。

    你好,RFDC

    我们现在对驱动已经有所了解,可以为 Zynq UltraScale + RFSoC ZCU111 评估板制作第一个非常简单的应用了。

    在本示例中,我打算只捕获一些 ADC 数据。我们可以随时扩展对以后的博客的设计。

    我正在发送一些 IQ 数据模式,我将用混频器将其混合到基带并将其输出到设计中的系统集成逻辑分析器块。

    在本示例中,我们只想展示一些高级状态和管理 API 以及我们先前讨论过的一些 get 和 set API。

    在本示例中,我们只检查 IP 的状态,并确保启动块的方法正确无误。

    然后我们将回读数据路径的状态。

    最后,我们要检查一下混频器的设置。

    在我们实现此设计并获得比特流之后,我们可以导出硬件切换文件 (HDF) 并启动 SDK。如果您想了解有关嵌入式设计和 SDK的更多信息,也许值得花点时间查看本教程

    HDF 文件在 SDK 中创建硬件平台,因此它知道设计中使用的所有外设及其地址。接下来要做的是制作一个电路板支持包。这需要硬件定义,并提供您需要的所有相关驱动和库。

    单击“File > New > Board Support Package”。

    选择刚刚创建的硬件平台,然后单击“下一步”。

    系统会提示您包含某些库。确保您勾选包含 libmetal,然后单击“OK”。在这个阶段,我们拥有制作我们的应用所需的一切。

    现在,您可以单击“File > New > Application Project”创建示例。

    确保指向硬件平台和刚刚创建的 BSP。

    单击“Next”,然后选择一个空白工程。

    我在这篇博客中已提供了我的应用的源代码。您可以导入我的应用并将其用作模板。

    我们一起来看看一些主要功能。

    我们需要先包含 xparameters.h 文件(这个文件中有我们需要的所有硬件参数)和我们前面提到的 xrfdc.h 文件(这个文件中有 API 调用的驱动结构和函数原型)。

    在本示例中,我有一个 ZCU111 电路板,而且我需要在启动时对时钟进行编程。为了实现这一点,我添加了一些文件(这些文件来自驱动程序源中的“examples”文件夹)。

    您将看到我创建 XRFdc 顶层结构的静态实例。这里的想法是我们有一个结构实例,而且我们可以从任何我们可能需要的 API 或函数指向它。

    static XRFdc RFdcInst; /* RFdc driver instance */

    在 main 函数中,我们声明了我们需要的所有结构:
    int Status;
    XRFdc_Config *ConfigPtr;
    XRFdc *RFdcInstPtr = &RFdcInst;
    XRFdc_BlockStatus BlockStatus;
    XRFdc_IPStatus myIPStatus;
    XRFdc_Mixer_Settings MixerSettings = {0};

    请注意,我们只是指向我们刚刚创建的驱动的静态实例。

    下一步是初始化驱动。这一步每次都必须做。简单说来,我们将获得 xrfdc_g 文件中的配置表,并通过 XRFdc_LookupConfig 函数使用 xparameters 中的值和设置来填充表。然后我们将其存储在配置指针 ConfigPtr 中。完成此操作后,我们调用 XRFdc_CfgInitialize API 并通过配置填充 RFdcInstPtr。

    现在我们就可以在我们的应用中使用该驱动了。

    您会看到我将输入时钟编程到 ADC 块。

    我用 XRFdc_GetIPStatus 来检查我启用的 ADC 块的状态。
    Status = XRFdc_GetIPStatus(RFdcInstPtr, &myIPStatus);
    if (Status != XRFDC_SUCCESS) {
    return XRFDC_FAILURE;
    }
    int powerup_status;
    int tile_state;
    powerup_status = myIPStatus.ADCTileStatus[0].PowerUpState;
    tile_state = myIPStatus.ADCTileStatus[0].TileState;
    printf("ADC PowerUp Status: %u\n", powerup_status);
    printf("ADC Tile State: %u\n", tile_state);

    在本示例中,我希望看到块状态为 15。这表示该块已到达其启动状态机的末尾,并且加电状态为 1,这意味着它已启用并处于活动状态。

    我使用的下一个 API 调用是 XRFdc_GetBlockStatus。这应该告诉我们采样频率的设置,以及数字数据路径的配置方式。请注意,我现在在用 XRFDC_ADC_TILE 抽取此 API 调用的块类型。
    Status = XRFdc_GetBlockStatus(RFdcInstPtr, XRFDC_ADC_TILE, 0, 0, &BlockStatus);
    if (Status != XRFDC_SUCCESS) {
    return XRFDC_FAILURE;
    }

    最后,我会使用 XRFdc_GetMixerSettings 并在混频器上打印一些细节。

    我进行了更改,然后用 XRFdc_SetMixerSettings 来写入新设置。

    在此之后,我会生成一个块事件并将所做的更改应用到硬件。

    随后,XRFdc_GetMixerSettings 应该会显示我们已在硬件中将混频器比例从 0 更改为 2 或从 AUTO 更改为 1.0,并且我的混频器设置也已更改。

    那现在我们在 SDK 中的调试器里来运行一下这个应用。

    右键单击应用程序,然后选择“运行方式...”,然后选择“运行配置”。我使用系统调试器,然后也选择对 FPGA 进行编程的选项。

    您也可以选择“Debug As”,这将启用调试透视图并适应代码的步进等。

    我们现在来看看 UART 串行控制台出来的结果。

    (我使用内置的 SDK 终端进行连接,但可以使用任何终端仿真器。)

    您可以看到它执行以下操作:

  • 说“Hello!”
  • 对 ZCU111 电路板上的时钟进行编程。
  • 将块状态设置为 15,这意味着块已完全启动,并且 AXI 流上有有效数据出来。
  • 报告数字数据路径的阻止状态。
  • 显示混频器设置的读取,并确认修改。
  • 最后一次检查显示 RF-ADC 的数据正传递给系统 ILA。

    结语:

    现在您可以开始编写一些应用了。我已经附上了用来构建我的硬件块设计的 Tcl 脚本和用于我的 ZCU111 工程的 XDC 文件。而且,还附上了该应用的 C 代码。

    如果您有可能想要编写来与 RF 数据转换器通信的任何应用,它应该是一个很好的起点。我会鼓励您用自己的裸机应用进行练习。

    我打算在这篇博客的基础添加一些内容,并突出显示 RFSoC 的其他一些不错的设计和调试功能。接下来,我打算展示拆箱并浏览 RF 分析仪工具(这个工具允许您在任何平台上对任何 RFSoC 器件进行调试)。

    下次见!

    文章转载自:赛灵思中文社区论坛

    推荐阅读