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_ADDRESS和FLASH_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
最后存储区域如下图
