NXP iMX8QM 通过 SCFW 隔离 AP_M4 核资源
简介
Apalis iMX8QM 使用了 NXP 的 iMX8 Quad Max 处理器。该 CPU 提供 A72 和 A53 Application Processor 和 M4 MCU。文章将介绍如何在硬件层面上隔离 AP 和 MCU 的资源,从而提高系统的可靠性。
System Controller Unit (SCU)System Controller Unit 是 iMX8 Quad Max 处理器上的一个专用 M4 核,连接 PMIC 和控制处理器上的子系统,为处理器的硬件功能提供抽象接口,供子系统使用。其主要管理以下功能和资源:
系统初始化与启动系统控制器通信电源管理资源管理引脚配置定时器中断处理
默认情况下,AP 核和 M4 核均能够使用 Apalis iMX8QM 的所有资源,包括 DDR RAM。SCU 可以通过分区 partition 的方式,将 AP 和 M4 之间,以及两个 M4 核之间所使用的资源从硬件上隔离开。一个分区中除了处理器核心外,还可以有外设、引脚 Pads 和内存区域。处理器核只能访问位于同一个分区内的资源。如果尝试访问其他分区的资源,则会返回总线错误。
当 AP 核和 M4 核在同一个分区时,AP 上的 Linux 虽然可以通过 reserved memory 方式不去使用相关 RAM 区域,但是直接物理地址访问的方式仍起作用。这对于 M4 端也是如此。使用 SCU 的分区,可以从底层规避该问题。除此之外,AP 和 M4 使用独立 Power Domain,在隔离分区情况下,对于 M4 的实时关键任务和 AP 上基于富操作系统 Linux 的应用独立运行提供可能。
在刚启动时,系统会划分三个分区。
SCU:包含所有运行 SCFW 固件所需的 Pads、外设和内存区域SECO:Security Controller 运行所需的相关资源Boot:系统剩余的所有资源如 Pads、外设和内存区域都将会划分到该分区
当系统继续启动时,如果不做进一步的分区,默认情况下 AP 和 M4 核都将运行在 Boot 分区中,它们之间只能通过软件的方式隔离各自的资源。为了实现 AP 和 M4 之间的硬件隔离,可以采用下面分区。
AP0:拥有运行 U-boot,Linux 的分区MCU0 和 MCU1:两个 Cortex-M4 核各自运行的分区Shared:共享资源的分区,例如将一段用于 rpmsg 通信的 RAM 区域划分到其中,AP 和 M4 就能够相互发送消息
分区的创建和资源划分在 SCU 的固件 SCFW 中调整。
SCU 固件 SCFW
SCFW 的下载和编译方法参考Build Custom i.MX 8/8X System Controller Firmware (SCFW)。接下来将针对上面使用 AP0、MCU0、MCU1 和 Shared 分区案例进行说明。按照上面方法下载的 SCFW 源码中,mx8qm_apalis/board.c 已经包含上面分区的实现代码,所以无需额外的修改。在组装 Boot Container 时传入特定的 Flags 值可以启用。
在 platform/board/mx8qm_apalis/board.c 中配置分区和资源。宏定义 PARTITION_NAME 命令系统启动时的设置三个分区名字。
PARTITION_NAME(SC_PT, "SCU"); PARTITION_NAME(SECO_PT, "SECO"); PARTITION_NAME(pt_boot, "BOOT");
检测标志位判断是否需要创建 AP0、MCU0、MCU1 和 Shared 分区。
if (alt_config != SC_FALSE) {
在需要进行分区时,PARTITION_NAME 设置相应分区的名称。
PARTITION_NAME(pt_boot, "AP0"); ... PARTITION_NAME(pt_m4_0, "MCU0"); ... PARTITION_NAME(pt_m4_1, "MCU1"); ... PARTITION_NAME(pt, "Shared");
这里以 MCU0 分区为例,rsrc_list中包含了划分到 MCU0 分区的资源,如 M4 中断、Message Unit、定时器等。
static const sc_rsrc_t rsrc_list[7U] = { SC_R_SYSTEM, SC_R_IRQSTR_M4_0, SC_R_MU_5B, SC_R_MU_7A, SC_R_MU_8B, SC_R_GPT_4, SC_R_SECO_MU_4 };
pad_list 中包含 MCU0 分区所使用的引脚范围。
static const sc_pad_t pad_list[2U] = { RM_RANGE(SC_P_M40_I2C0_SCL, SC_P_M40_GPIO0_01) };
结合 sc_fw_api_qm_b0.pdf 文档,index 7~10 的引脚都将在划分到 MCU0 分区。
sc_rm_mem_list_t 配置了包括 RAM 在内的可使用存储空间。
static const sc_rm_mem_list_t mem_list[2U] = { {0x088000000ULL, 0x0887FFFFFULL}, {0x008081000ULL, 0x008180FFFULL} };
rm_partition_create 使用上面配置的所有资源创建 MCU0 分区。
BRD_ERR(rm_partition_create(pt_boot, &pt_m4_0, SC_FALSE, SC_TRUE, SC_FALSE, SC_TRUE, SC_FALSE, SC_R_M4_0_PID0, rsrc_list, ARRAY_SIZE(rsrc_list), pad_list, ARRAY_SIZE(pad_list), mem_list, ARRAY_SIZE(mem_list)));
组装 Boot Container
NXP i.MX 8QuadMax 处理器使用前面提到的 SCU 来启动系统。启动时除了加载 SCFW,它还可以加载 ATF 和 U-Boot,以及直接加载 M4 核的固件。Boot Container 组装方法参考 Specifics: Build U-Boot for NXP i.MX 8/8X-based SoMs。默认情况下,如网页描述,使用下面命令组装 Boot Container。该方法生成的 flash.bin 文件中并不包含 M4 核的固件,并且 AP 和 M4 在同一个分区。M4 的固件在 U-Boot 启动时,使用 bootaux 命令从 eMMC 上加载后运行。
make SOC=iMX8QM flash_b0
为了使用上面 SCFW 中划分的分区,使用下面命令组装 Boot Container。
make SOC=iMX8QM flash_regression_linux_m4
编译目标来自 imx-mkimage/iMX8QM/soc.mak 中的定义。flash_regression_linux_m4: $(MKIMG) $(AHAB_IMG) \ scfw_tcm.bin u-boot-atf.bin m4_image.bin m4_1_image.bin \ ./$(MKIMG) -soc QM -rev B0 -append $(AHAB_IMG) \ -c -flags 0x00200000 -scfw scfw_tcm.bin \ -ap u-boot-atf.bin a53 0x80000000 \ -p3 -m4 m4_image.bin 0 0x34FE0000 \ -p4 -m4 m4_1_image.bin 1 0x38FE0000 \ -out flash.bin
-flags 0x00200000向 SCFW 的 board.c 传递,SCU 在启动时将会创建 AP0、MCU0、MCU1 和 Shared 的分区。
if (alt_config != SC_FALSE) {
sc_fw_port.pdf 中说明了 Bit 21 SCFW_SC_BD_FLAGS_ALT_CONFIG 为更改 SCFW 配置的位,对应 0x00200000。
上面命令中 m4_image.bin 和 m4_1_image.bin 为两个 M4 各自的固件,从各自的 TCML 运行。因此,在执行上面命令前,需要把两个 M4 的固件复制到 imx-mkimage/iMX8QM 目录下。
如下修改 U-boot 源码中 U-boot/cmd/booti.c,重新编译后将 u-boot.bin 复制到 imx-mkimage/iMX8QM 目录下。
if (dest < gd->ram_base || dest > gd->ram_top) { puts("kernel_comp_addr_r is outside of DRAM range!\n"); //return -EINVAL; }
Linux device tree 更新
接下来的演示中,我们在 M4 上使用 multicore_examples/rpmsg_lite_pingpong_rtos 作为演示,在 AP 上的 Linux 系统中也需要开启 rpmsg 节点并加载驱动。针对 Apalis iMX8 BSP 7 的 imx8qm-apalis-v1.1-ixora-v1.2.dtb 需要打该补丁。
更新 Boot Container 文件
上面命令执行成功后生成的 flash.bin,需要更新到 Apalis iMX8 模块上的 eMMC 上。对于使用 Toradex Easy Installer 进行批量烧写或者重装系统,可以将 flash.bin 重名为 imx-boot,替换原来的烧录文件中 imx-boot。在开发期间,使用 U-Boot 直接更新 flash.bin 会更加方便,免于重装整个系统镜像。
将 flash.bin 复制到的一台 TFTP 服务器的下载目录中。然后在 U-Boot 中配置 Apalis iMX8 和 TFTP 服务器 IP 地址。
Apalis iMX8 # setenv ipaddr 192.168.3.181 Apalis iMX8 # setenv serverip 192.168.3.196 Apalis iMX8 # saveenv
使用 tftpboot 和 mmc 命令将 flash.bin 下载并写入到 eMMC 上。mmc write $loadaddr 0 0x973中最后的数值是写入的 block 数量。tftpboot 在下载时会显示文件大小,例如这里为 1238016 字节。需要写入的 block = (1238016+511)/512,计算结果向上取整,并转为十六进制数值。
Apalis iMX8 # tftpboot $loadaddr flash.bin Using ethernet@5b040000 device TFTP from server 192.168.3.196; our IP address is 192.168.3.181 Filename 'flash.bin'. Load address: 0x95400000 Loading: ################################################## 1.2 MiB 3.4 MiB/s done Bytes transferred = 1238016 (12e400 hex)
Apalis iMX8 # mmc dev 0 1 Apalis iMX8 # mmc write $loadaddr 0 0x973
测试 在 U-Boot 中配置启动时加载的 device tree 文件。 Apalis iMX8 # setenv fdtfile imx8qm-apalis-v1.1-ixora-v1.2.dtb Apalis iMX8 # saveenv Apalis iMX8 # reset 启动后可以在 Linux 和两个 M4 的调试串口上看到启动内容。 Linux 中进入 kernel modules 驱动目录 kernel/drivers/rpmsg,加载 imx_rpmsg_pingpong.ko。两个 M4 上的固件在发现 rpmsg 通道后,自动向 Linux 发送消息。# insmod imx_rpmsg_pingpong.ko ... [ 67.386427] get 91 (src: 0x1f) [ 67.389556] get 101 (src: 0x1e) [ 67.392707] imx_rpmsg_pingpong virtio1.rpmsg-openamp-demo-channel.-1.30: goodbye! [ 67.401771] get 93 (src: 0x1f) [ 67.406364] get 95 (src: 0x1f) [ 67.410982] get 97 (src: 0x1f) [ 67.415594] get 99 (src: 0x1f) [ 67.420212] get 101 (src: 0x1f) [ 67.423388] imx_rpmsg_pingpong virtio3.rpmsg-openamp-demo-channel-1.-1.31: goodbye! 总结
文章介绍了如何使用 SCU 划分不同的分区,将 AP 和 M4 之间做硬件隔离,并使用 SCFW 直接加载 M4 的固件,不仅提高系统可靠性,也将 M4 运行提前到 U-Boot 之前,加快启动速度。更多关于 SCU 的高级高级功能,可以参考 SCFW 的说明文档。

提交
上手测试 Hailo:在 Toradex 模块上加速边缘 AI
Yocto Linux 量产 BSP 镜像定制
ARM 处理器平台 eMMC Flash 存储磨损测试示例
NXP iMX8MP 处理器基于 Linux 关闭 Debug Console 输出
ARM 处理器平台 Ethernet Compliance 测试流程示例