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

Creative Commons
本作品采用知识共享署名

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

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

存储区域

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

1
2
3
4
5
6
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中可以看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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节点读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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保留了这段空间。

1
2
3
4
5
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提供的设备树摘录如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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地址运行镜像

配置选项&设备树

1
2
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而是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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,其寄存器组为

1
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地址运行镜像

配置选项&设备树

配置选项

1
2
3
CONFIG_XIP=y
CONFIG_CODE_FLEXSPI=y
CONFIG_USE_DT_CODE_PARTITION=y

设备树覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/ {
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运行

配置选项&设备树

1
2
3
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的节点读取地址和大小:

1
2
3
4
5
6
7
8
9
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

配置为

1
2
3
4
5
CONFIG_XIP=y
CONFIG_CODE_FLEXSPI=y
CONFIG_USE_DT_CODE_PARTITION=y
CONFIG_BOOTLOADER_SRAM_SIZE=4
CONFIG_IS_BOOTLOADER=y

设备树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/ {
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

配置为

1
2
3
CONFIG_XIP=n
CONFIG_CODE_FLEXSPI=n
CONFIG_BOOTLOADER_SRAM_SIZE=4

设备树

1
2
3
4
5
6
/ {
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

最后存储区域如下图