嵌入式应用程序实际如何使用设备树 blob?

How are device tree blobs actually used from an embedded application?

我正在尝试了解 device tree 的工作原理。我已经阅读了一些文档并且或多或少地了解什么是 dtsdtsidtb 文件。

我不太明白的是 dtb 文件中的信息是如何从最终应用程序访问的。我一直在阅读 zephyr os 文档和一些 linux 内核来理解这一点,但我仍然不是很清楚。

我现在的理解是最终应用程序(possibly OS 内核)读取并解析 dtb 文件 'manually'。这意味着每个 OS 内核将以自己的方式解析 dtb 文件,这是正确的吗?

然而,据我了解,应用程序没有链接到 dtb 文件,后者在某个内存区域中闪烁,然后是应用程序。以某种方式访问​​它。那么,如果在构建时 ose 符号不可用,如何构建应用程序?

免责声明:我不是 Linux 内核专家。

您的问题已得到解答 here 我想。 是的,每个 OS 内核都会以自己的方式解析 DTB 文件。在 Linux 的情况下, 例如,DTB 将由 u-boot 读取,并且 u-boot 将启动 Linux 内核并传递加载 DTB BLOB 的地址。

在下面的示例中,u-boot 会将内核和 DTB 从 FAT 文件系统加载到内存中,然后将控制权转移到地址 0x80300000 处的内核,同时告诉它它的 DTB BLOB 将在地址 0x815f0000 处可用:

fatload mmc 0:1 0x80300000 zImage
fatload mmc 0:1 0x815f0000 beagle-xm.dtb
bootz 0x80300000 - 0x815f0000

DTB BLOB 包含两个 符号名称及其相关值。该应用程序将使用一些专门用于读取 DTB 格式的代码,例如 libfdt,以从其符号名称中检索值。它现在不需要在构建时了解 DTB 内容。这确实允许相同的编译内核 运行 在不同的硬件上未经修改,DTB 中描述了各种硬件功能。

为了让操作系统有效地访问DTB中包含的信息,信息将使用libfdt从DTB BLOB中提取一次,然后放入一个高效的数据结构中。例如,从 'flatened' 格式转换为 'expanded' 格式。

强烈建议阅读:

请注意,Zephyr 方法不是标准的:当 Open Firmware/Device Tree 方案旨在允许 'late binding' 应用程序和硬件描述时,他们选择了更静态的方法.

Zephyr 实际上使用了一种自定义工具,该工具确实处理 DTB 文件并生成 C header 文件,其中包含遵循某些命名约定的宏形式的相同信息。这可能就是为什么您对 DTB 内容和使用它的应用程序将绑定在一起的确切阶段感到困惑。

不要尝试 ramping-up 使用 Zephyr 的设备树概念。 此外,他们的工具在处理完全有效的 DTB 文件时仍然远非完美和阻塞。

Linux 内核 driver 代码的一个摘录,将 DTB 绑定到 driver:

static const struct platform_device_id mxs_auart_devtype[] = {
    { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART },
    { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART },
    { .name = "as-auart-asm9260", .driver_data = ASM9260_AUART },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, mxs_auart_devtype);

static const struct of_device_id mxs_auart_dt_ids[] = {
    {
        .compatible = "fsl,imx28-auart",
        .data = &mxs_auart_devtype[IMX28_AUART]
    }, {
        .compatible = "fsl,imx23-auart",
        .data = &mxs_auart_devtype[IMX23_AUART]
    }, {
        .compatible = "alphascale,asm9260-auart",
        .data = &mxs_auart_devtype[ASM9260_AUART]
    }, { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids);

在 Zephyr 代码中找到的等价物(虽然 driver 不同):

#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(flexcomm5), nxp_lpc_spi, okay)
    /* Attach 12 MHz clock to FLEXCOMM5 */
    CLOCK_AttachClk(kFRO_HF_to_FLEXCOMM5);
    
    /* reset FLEXCOMM for SPI */
    RESET_PeripheralReset(kFC5_RST_SHIFT_RSTn);
#endif