Zephyr镜像存储区域配置-机制分析

本文分析Zephyr的配置文件和设备树在存储区域上配置生效机制。

通过Zephyr镜像存储区域配置-基于RT1062的实例知道通过配置文件和设备树可以指定Zephyr镜像的存储区域。而我们知道存储区域是在分散加载文件中指定生效,本文分析配置文件和设备树在分散加载文件中生效机制。最后的分析示例还是基于mm_feater(SOC是RT1062)进行。

存储区域 链接到标题

RT1062的存储区域对应于include/arch/arm/aarch32/cortex_m/scripts/linker.ld中以MEMORY指令指定的region。

MEMORY
    {
    FLASH                 (rx) : ORIGIN = ROM_ADDR, LENGTH = ROM_SIZE
    SRAM                  (wx) : ORIGIN = RAM_ADDR, LENGTH = RAM_SIZE
...
    }

Zephyr将可执行程序运行时使用存储区域分为FLASH和SRAM两部分:

  • FLASH:只读、执行区域。代码和只读数据放在其中。
  • SRAM:读写,执行区域,读写数据放在其中,也可以将可执行代码放入其中执行。

这两片区域的地址ROM_ADDR,ROM_SIZE,RAM_ADDR,RAM_SIZE通过Zephyr的配置项指定 从include/arch/arm/aarch32/cortex_m/scripts/linker.ld中可以看到

#if !defined(CONFIG_XIP) && (CONFIG_FLASH_SIZE == 0)
#define ROM_ADDR RAM_ADDR
#else
#define ROM_ADDR (CONFIG_FLASH_BASE_ADDRESS + CONFIG_FLASH_LOAD_OFFSET)
#endif

#if CONFIG_FLASH_LOAD_SIZE > 0
#define ROM_SIZE CONFIG_FLASH_LOAD_SIZE
#else
#define ROM_SIZE (CONFIG_FLASH_SIZE*1K - CONFIG_FLASH_LOAD_OFFSET)
#endif

#if defined(CONFIG_XIP)
#if defined(CONFIG_IS_BOOTLOADER)
#define RAM_SIZE (CONFIG_BOOTLOADER_SRAM_SIZE * 1K)
#define RAM_ADDR (CONFIG_SRAM_BASE_ADDRESS + \
	(CONFIG_SRAM_SIZE * 1K - RAM_SIZE))
#else
#define RAM_SIZE (CONFIG_SRAM_SIZE * 1K)
#define RAM_ADDR CONFIG_SRAM_BASE_ADDRESS
#endif
#else
#define RAM_SIZE (CONFIG_SRAM_SIZE * 1K - CONFIG_BOOTLOADER_SRAM_SIZE * 1K)
#define RAM_ADDR CONFIG_SRAM_BASE_ADDRESS
#endif

根据CONFIG_XIP,CONFIG_IS_BOOTLOADER的不同ROM/RAM的ADDR和SIZE将由下面几个配置项组合和而成

  • CONFIG_FLASH_BASE_ADDRESS
  • CONFIG_FLASH_SIZE
  • CONFIG_FLASH_LOAD_OFFSET
  • CONFIG_FLASH_LOAD_SIZE
  • CONFIG_SRAM_BASE_ADDRESS
  • CONFIG_SRAM_SIZE
  • CONFIG_BOOTLOADER_SRAM_SIZE

这些配置项的来源和物理意义如下图 CONFIG_FLASH_BASE_ADDRESS/CONFIG_FLASH_SIZE 定义了FLASH的区域范围 CONFIG_FLASH_LOAD_OFFSET/CONFIG_FLASH_LOAD_SIZE 在XIP的情况可以指定ROM使用FLASH的区域,Zephyr将被链接到CONFIG_FLASH_LOAD_OFFSET的地址 CONFIG_SRAM_BASE_ADDRESS/CONFIG_SRAM_SIZE 定义了SDRAM的区域范围 CONFIG_BOOTLOADER_SRAM_SIZE` zephyr被bootloader引导时,运行通过配置在RAM中留出bootloader使用的保留部分

配置项默认来源 链接到标题

arch/Kconfig中定义了FLASH和SRAM地址和大小的默认来源:

  • FLASH是从设备树的zephyr,flash节点读取
  • SDRAM是从设备树的zephyr,sram节点读取
DT_CHOSEN_Z_FLASH := zephyr,flash

config FLASH_SIZE
	int "Flash Size in kB"
	default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_FLASH),0,K) if (XIP && (ARM ||ARM64)) || !ARM

config FLASH_BASE_ADDRESS
	hex "Flash Base Address"
	default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_FLASH)) if (XIP && (ARM || ARM64)) || !ARM

DT_CHOSEN_Z_SRAM := zephyr,sram

config SRAM_SIZE
	int "SRAM Size in kB"
	default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM),0,K)

config SRAM_BASE_ADDRESS
	hex "SRAM Base Address"
	default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_SRAM))

FLASH和SRAM都不建议使用配置文件直接配置。

Kconfig.zephyr中定义了FLASH LOAD的地址和大小的默认来源,在配置了CONFIG_USE_DT_CODE_PARTITION后默认会从设备树的zephyr,code-partition节点读取。也可以不使用设备树在配置文件中直接配置CONFIG_FLASH_LOAD_OFFSET/CONFIG_FLASH_LOAD_SIZE

if HAS_FLASH_LOAD_OFFSET

config USE_DT_CODE_PARTITION
	bool "Link application into /chosen/zephyr,code-partition from devicetree"

DT_CHOSEN_Z_CODE_PARTITION := zephyr,code-partition

config FLASH_LOAD_OFFSET
	# Only user-configurable when USE_DT_CODE_PARTITION is disabled
	hex "Kernel load offset" if !USE_DT_CODE_PARTITION
	default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) if USE_DT_CODE_PARTITION
	default 0

config FLASH_LOAD_SIZE
	# Only user-configurable when USE_DT_CODE_PARTITION is disabled
	hex "Kernel load size" if !USE_DT_CODE_PARTITION
	default $(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) if USE_DT_CODE_PARTITION
	default 0

endif # HAS_FLASH_LOAD_OFFSET

Kconfig.zephyr中定义了BOOTLOADER_SRAM_SIZE的默认值为16K, 只要使用了zephyr bootloader,无论是bootloader运行期还是app运行期,都为bootloader保留了这段空间。

config BOOTLOADER_SRAM_SIZE
	int "SRAM reserved for bootloader"
	default 16
	depends on !XIP || IS_BOOTLOADER
	depends on ARM || XTENSA

可以在配置文件中通过CONFIG_BOOTLOADER_SRAM_SIZE来配置该区域大小,单位为K,如果不需要可以不配置。

mm_feather示例分析 链接到标题

mm_feather使用RT1062,其board综合soc提供的设备树摘录如下

flexram: flexram@400b0000 {
			compatible = "nxp,imx-flexram";
			reg = <0x400b0000 0x4000>;
			interrupts = <38 0>;

			#address-cells = <1>;
			#size-cells = <1>;

			itcm: itcm@0 {
				compatible = "zephyr,memory-region", "nxp,imx-itcm";
				reg = <0x00000000 DT_SIZE_K(128)>;
				zephyr,memory-region = "ITCM";
			};

			dtcm: dtcm@20000000 {
				compatible = "zephyr,memory-region", "nxp,imx-dtcm";
				reg = <0x20000000 DT_SIZE_K(128)>;
				zephyr,memory-region = "DTCM";
			};

			ocram: ocram@20200000 {
				compatible = "mmio-sram";
				reg = <0x20200000 DT_SIZE_K(768)>;
			};
		};

flexspi: spi@402a8000 {
			compatible = "nxp,imx-flexspi";
			reg = <0x402a8000 0x4000>;
			interrupts = <108 0>;
			label = "FLEXSPI";
			#address-cells = <1>;
			#size-cells = <0>;
			ahb-bufferable;
			ahb-cacheable;
			status = "disabled";
		};
		
sdram0: memory@80000000 {
		/* Micron MT48LC16M16A2B4-6AIT:G */
		device_type = "memory";
		reg = <0x80000000 DT_SIZE_M(32)>;
	};

&flexspi {
	reg = <0x402a8000 0x4000>, <0x60000000 DT_SIZE_M(8)>;
	is25wp064: is25wp064@0 {
		compatible = "nxp,imx-flexspi-nor";
		size = <67108864>;
		label = "IS25WP064";
		reg = <0>;
		spi-max-frequency = <133000000>;
		status = "okay";
		jedec-id = [9d 70 17];
	};
};

chosen {
		zephyr,sram = &sdram0;
	};

1. XIP直接从0地址运行镜像 链接到标题

配置选项&设备树 链接到标题

CONFIG_XIP=y
CONFIG_CODE_FLEXSPI=y

分析 链接到标题

** FLASH ** 没有定义zephyr,flash节点,对于rt1062来说在soc/arm/nxp_imx/rt/Kconfig.defconfig.series下会对FLASH_BASE_ADDRESSFLASH_SIZE进行覆盖,不使用zephyr,flash而是

if CODE_FLEXSPI

config FLASH_SIZE
	default $(dt_node_reg_size_int,/soc/spi@402a8000,1,K)

config FLASH_BASE_ADDRESS
	default $(dt_node_reg_addr_hex,/soc/spi@402a8000,1)

endif # CODE_FLEXSPI

if CODE_FLEXSPI2

config FLASH_SIZE
	default $(dt_node_reg_size_int,/soc/spi@402a4000,1,K)

config FLASH_BASE_ADDRESS
	default $(dt_node_reg_addr_hex,/soc/spi@402a4000,1)

endif # CODE_FLEXSPI2

这里我们配置了CONFIG_CODE_FLEXSPI=y,因此会从spi@402a8000第一个寄存器中读取,spi@402a8000的别名为flexspi,其寄存器组为

reg = <0x402a8000 0x4000>, <0x60000000 DT_SIZE_M(8)>;

第一个寄存器是<0x60000000 DT_SIZE_M(8)>, 因此读出CONFIG_FLASH_BASE_ADDRESS=0x60000000, CONFIG_FLASH_SIZE=8M

FLASH LOAD 没有进行相关配置CONFIG_FLASH_LOAD_OFFSET/CONFIG_FLASH_LOAD_SIZE均为0

SRAM 设备树中指定使用zephyr,sram = &sdram0;读出sdram0的寄存器<0x80000000 DT_SIZE_M(32)>CONFIG_SRAM_BASE_ADDRESS=0x80000000,CONFIG_SRAM_SIZE=32M

BOOTLOAD RAM 不会被配置

最后存储区域如下图

2. XIP从非0地址运行镜像 链接到标题

配置选项&设备树 链接到标题

配置选项

CONFIG_XIP=y
CONFIG_CODE_FLEXSPI=y
CONFIG_USE_DT_CODE_PARTITION=y

设备树覆盖

/ {
	chosen {
		zephyr,code-partition = &app_partition;
		zephyr,sram = &ocram;
	};
}

&flexspi {
	reg = <0x402a8000 0x4000>, <0x60000000 DT_SIZE_M(8)>;
	is25wp064: is25wp064@0 {
		compatible = "nxp,imx-flexspi-nor";
		size = <67108864>;
		label = "IS25WP064";
		reg = <0>;
		spi-max-frequency = <133000000>;
		status = "okay";
		jedec-id = [9d 70 17];

        partitions {
			compatible = "fixed-partitions";
			#address-cells = <1>;
			#size-cells = <1>;

			boot_partition: partition@0 {
				label = "bootloader";
				reg = <0x00000000 DT_SIZE_K(128)>;
			};

			app_partition: partition@40000 {
				label = "app";
				reg = <0x00040000 DT_SIZE_M(6)>;
			};
		};
	};
};

分析 链接到标题

** FLASH ** 同前分析会从设备树节点flexspi读寄存器得到CONFIG_FLASH_BASE_ADDRESS=0x60000000, CONFIG_FLASH_SIZE=8M

FLASH LOAD 配置了CONFIG_USE_DT_CODE_PARTITION,会从设备树节点zephyr,code-partition读出,zephyr,code-partition使用了app_partition,也就是读出寄存器<0x00040000 DT_SIZE_M(6)>CONFIG_FLASH_LOAD_OFFSET=0x40000,CONFIG_FLASH_LOAD_SIZE=6M

SRAM 设备树中指定使用zephyr,sram = &ocram;读出ocram的寄存器<0x20200000 DT_SIZE_K(768)>CONFIG_SRAM_BASE_ADDRESS=0x20200000,CONFIG_SRAM_SIZE=768K

BOOTLOAD RAM 不会被配置

最后存储区域如下图

3. 非XIP运行 链接到标题

配置选项&设备树 链接到标题

CONFIG_XIP=n
CONFIG_CODE_SEMC=y
CONFIG_FLASH_SIZE=0

分析 链接到标题

** FLASH ** 对于rt1062在soc/arm/nxp_imx/rt/Kconfig.defconfig.series下会对其进行覆盖CONFIG_CODE_SEMC=y 配置了代码在SDRAM内,则从sdram的节点读取地址和大小:

if CODE_SEMC

config FLASH_SIZE
	default $(dt_node_reg_size_int,/memory@80000000,0,K)

config FLASH_BASE_ADDRESS
	default $(dt_node_reg_addr_hex,/memory@80000000)

endif # CODE_SEMC

可以从sdram0节点中读出CONFIG_FLASH_BASE_ADDRESS=0x80000000, CONFIG_FLASH_SIZE=32M

FLASH LOAD 没有进行相关配置CONFIG_FLASH_LOAD_OFFSET/CONFIG_FLASH_LOAD_SIZE均为0

SRAM 设备树中指定使用zephyr,sram = &sdram0;读出sdram0的寄存器<0x80000000 DT_SIZE_M(32)>CONFIG_SRAM_BASE_ADDRESS=0x80000000,CONFIG_SRAM_SIZE=32M

BOOTLOAD RAM 不会被配置

最后存储区域如下图

4. Bootloader+App 非XIP运行 链接到标题

Bootloader在Flash上XIP运行,将ocram作为SRAM APP在ocram上运行,将ocram作为SRAM bootload启动将Flash上的APP拷贝到ocram上运行

bootloader 链接到标题

配置为

CONFIG_XIP=y
CONFIG_CODE_FLEXSPI=y
CONFIG_USE_DT_CODE_PARTITION=y
CONFIG_BOOTLOADER_SRAM_SIZE=4
CONFIG_IS_BOOTLOADER=y

设备树

/ {
	chosen {
		zephyr,code-partition = &boot_partition;
		zephyr,sram = &ocram;
	};
}

&flexspi {
	reg = <0x402a8000 0x4000>, <0x60000000 DT_SIZE_M(8)>;
	is25wp064: is25wp064@0 {
		compatible = "nxp,imx-flexspi-nor";
		size = <67108864>;
		label = "IS25WP064";
		reg = <0>;
		spi-max-frequency = <133000000>;
		status = "okay";
		jedec-id = [9d 70 17];

        partitions {
			compatible = "fixed-partitions";
			#address-cells = <1>;
			#size-cells = <1>;

			boot_partition: partition@0 {
				label = "bootloader";
				reg = <0x00000000 DT_SIZE_K(128)>;
			};

			app_partition: partition@20000 {
				label = "app";
				reg = <0x00040000 DT_SIZE_K(256)>;
			};
		};
	};
};

按照前面的分析可以得到bootloader的信息 ** FLASH ** CONFIG_FLASH_BASE_ADDRESS=0x60000000, CONFIG_FLASH_SIZE=32M

FLASH LOAD CONFIG_FLASH_LOAD_OFFSET=0, CONFIG_FLASH_LOAD_SIZE=128K

SRAM 设备树中指定使用zephyr,sram = &ocram;读出dtcm的寄存器<0x20200000 DT_SIZE_K(768)>CONFIG_SRAM_BASE_ADDRESS=0x20200000,CONFIG_SRAM_SIZE=768K

BOOTLOAD RAM CONFIG_BOOTLOADER_SRAM_SIZE=4 为4K

App 链接到标题

配置为

CONFIG_XIP=n
CONFIG_CODE_FLEXSPI=n
CONFIG_BOOTLOADER_SRAM_SIZE=4

设备树

/ {
	chosen {
		zephyr,sram = &ocram;
		zephyr,flash = &ocram;
	};
}

参照前面的分析可以得到app的信息 ** FLASH ** 设备树中指定使用zephyr,sram = &ocram;读出dtcm的寄存器<0x20200000 DT_SIZE_K(768)>,CONFIG_FLASH_BASE_ADDRESS=0x20200000, CONFIG_FLASH_SIZE=768K

FLASH LOAD 没有进行相关配置CONFIG_FLASH_LOAD_OFFSET/CONFIG_FLASH_LOAD_SIZE均为0

SRAM 设备树中指定使用zephyr,sram = &ocram;读出dtcm的寄存器<0x20200000 DT_SIZE_K(768)>,CONFIG_SRAM_BASE_ADDRESS=0x20200000,CONFIG_SRAM_SIZE=768K

BOOTLOAD RAM CONFIG_BOOTLOADER_SRAM_SIZE=4

最后存储区域如下图