本文说明如何使用Zephyr的代码和数据重定位功能。
Zephyr目前仅提供静态重定位功能,即在编译期间按照用户的需求将指定文件中的 .text、.rodata、.data 和 .bss 重定位到所需的内存区域。该功能可以在不修改代码的情况下对内存内容重新排序。
基本概念介绍
在gcc的链接加载文件中通过MEMORY指定内存分区, 例如1
2
3
4
5
6
7
8MEMORY
{
FLASH (rx) : ORIGIN = (0x60000000 + 0x0), LENGTH = (8192*1K - 0x0)
RAM (wx) : ORIGIN = 0x80000000, LENGTH = (32768 * 1K)
ITCM : ORIGIN = 0, LENGTH = 131072
DTCM : ORIGIN = 0x20000000, LENGTH = 131072
OCRAM : ORIGIN = 0x20200000, LENGTH = 786432
}
上面是rt1062的一个内存分区结构:
FLASH: 外部8M大小的flash被映射到0x60000000地址,可以当作只读内存区,可执行代码。
RAM: 外部32M大小的SDRAM被映射到0x80000000地址,读写区域, 也可执行代码。
ITCM: rt1062片内内存指令区,大小128K被映射到0地址,可执行代码。
DTCM: rt1062片内内存数据区,大小128K被映射到0x20000000地址,数据读写区域。
OCRAM: rt1062片内内存区,大小768K被映射到0x20200000地址,数据读写区域,也可执行代码。
CPU对这几部分的访问速度为ITCM/DTCM>OCRAM>SDRAM>FLASH
由于ITCM/DTCM/ODRAM/SDRAM都是易丢性存储器,因此代码和数据通常会被保存到Flash上,上电后再由启动代码搬运到易丢失性存储器上。
XIP的默认情况下:.text, .rodata, .data, 都会被保存在Flash上,上电后启动代码会将.data搬运到SDRAM上,并根据Flash上.bss的信息在SRAM进行.bss初始化。
当我们希望.text在SDRAM/ITCM/OCRAM上更快的运行,一般需要bootloader来搬运,但如果只需要部分文件的代码在SDRAM/ITCM/OCRAM,或是.rodata, .data .bss被放到DTCM/OCRAM中读写更快,则可以通过链接脚本指定这些文件的段到特定的内存区域,并在启动代码中对其进行搬运即可。
例如,把一些特定的函数放到ITCM中,对linker.ld进行修改,加载地址在FLASH中,执行地址在ITCM中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 _ITCM_TEXT_SECTION_NAME :
{
. = ALIGN(4);
KEEP(*(.rel.text.strcat))
KEEP(*(.rel.text.strncat))
KEEP(*(.rel.text.strtok_r))
KEEP(*(.text.memchr))
KEEP(*(.text.memcmp))
KEEP(*(.text.memcpy))
KEEP(*(.text.memmove))
KEEP(*(.text.memset))
KEEP(*(.text.strcat))
KEEP(*(.text.strchr))
KEEP(*(.text.strcmp))
KEEP(*(.text.strcpy))
KEEP(*(.text.strlen))
KEEP(*(.text.strncat))
KEEP(*(.text.strncmp))
KEEP(*(.text.strncpy))
KEEP(*(.text.strnlen))
KEEP(*(.text.strrchr))
KEEP(*(.text.strtok_r))
. = ALIGN(4);
}
> ITCM AT > FLASH
__itcm_text_end = .;
__itcm_text_start = ADDR(_ITCM_TEXT_SECTION_NAME);
__itcm_text_size = SIZEOF(_ITCM_TEXT_SECTION_NAME);
__itcm_text_rom_start = LOADADDR(_ITCM_TEXT_SECTION_NAME);
在启动代码中添加对itcm的拷贝1
2z_early_memcpy(&__itcm_text_start, &__itcm_text_rom_start,
(size_t) &__itcm_text_size);
Zephyr的重定位就是将上面的动作通过脚本自动化。
使用说明
Zephyr要对文件做重定位非常容易,按照如下步骤即可
配置启用重定位功能,在prj.conf中加入
1
CONFIG_CODE_DATA_RELOCATION=y
使用
zephyr_code_relocate
将要重定位的文件加入CMakeLists.txt中:1
2zephyr_code_relocate(FILES ${ZEPHYR_BASE}/lib/libc/minimal/source/string/string.c
LOCATION ITCM_TEXT)
zephyr_code_relocate
zephyr_code_relocate
是一个Zephyr定义的CMake函数,专门用于重定位, 支持的参赛有:
FILES
: 一个文件列表,用于指定需要重定位的文件。LIBRARY
: 一个库名,用于指定需要重定位的库。LOCATION
: 一个字符串,指定重定位的目标内存区域,例如SRAM
。- 该函数还支持以下可选参数:
NOCOPY
: 指示文件数据在启动时不需要被复制。PHDR [program_header]
: 添加程序头。在Xtensa平台上使用。
示例:1
2
3
4# 将file1.c和file2.c重定位到SRAM内存区内
zephyr_code_relocate(FILES file1.c file2.c LOCATION SRAM)
# 将my_lib.a重定位到SRAM内城区内
zephyr_code_relocate(LIBRARY my_lib SRAM)
LOCATION
LOCATION
由<area>[_section]
组成,area表示内存区,值的可选范围定义在MEMORY内。section表示被重定位目标那种段,有下面几种选择:
_TEXT
.text代码段_RODATA
.rodata只读数据段_DATA
.data数据段_BSS
.bss未初始化数据段_LITERAL
.literal,给Xtensa用
当未指定_section时表示所有段都将被重定位:1
zephyr_code_relocate(FILES file2.c LOCATION SRAM)
只有代码段被重定位到SRAM中1
zephyr_code_relocate(FILES file2.c LOCATION SRAM_TEXT)
代码段和数据段都被重定位到SRAM中1
2zephyr_code_relocate(FILES file2.c LOCATION SRAM_TEXT)
zephyr_code_relocate(FILES file2.c LOCATION SRAM_DATA)
也可写作1
zephyr_code_relocate(FILES file2.c LOCATION SRAM_TEXT_DATA)
NOCOPY
所有被重定位的段都将发生拷贝,但如果代码段被重定位到Flash中并需要XIP执行,这种情况下我们不希望其被拷贝1
zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY)
自定义内存分区
当zephyr的link.ld文件中的内存分区不能满足你的硬件或者设计时,需要添加自定义分区,自定义分区可以通过自定义link.ld来完成,例如自定义一个linker_arm_sram2.ld添加SRAM2区域1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <zephyr/linker/sections.h>
#include <zephyr/devicetree.h>
#include <zephyr/linker/linker-defs.h>
#include <zephyr/linker/linker-tool.h>
#if defined CONFIG_ARM
#define CONFIG_SRAM2 1
#define _SRAM2_DATA_SECTION_NAME .sram2_data
#define _SRAM2_BSS_SECTION_NAME .sram2_bss
#define _SRAM2_TEXT_SECTION_NAME .sram2_text
#define SRAM2_ADDR (CONFIG_SRAM_BASE_ADDRESS + RAM_SIZE2)
#endif
#define RAM_SIZE2 (CONFIG_SRAM_SIZE * 512)
MEMORY
{
#ifdef CONFIG_SRAM2
SRAM2 (wx) : ORIGIN = (CONFIG_SRAM_BASE_ADDRESS + RAM_SIZE2), LENGTH = RAM_SIZE2
#endif
}
#include <zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld>
通过include原本的linker.ld,加入新的MEMORY区域就可以完成,需要注意不同的soc有不同的zephyr定义linker.ld。另外需要特别注意的是由于zephyr重定位脚本的限制新加入的MEMORY分区名不能有下划线,否则重定位脚本解析会出错,错误示例: SRAM_2。
创建好文件后在prj.conf中指定即可1
2CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y
CONFIG_CUSTOM_LINKER_SCRIPT="linker_arm_sram2.ld"
之后按照前文方法指定你要重定位的文件段到SRAM2区域1
2zephyr_code_relocate(FILES src/test_file3.c LOCATION SRAM2_TEXT)
zephyr_code_relocate(LIBRARY test_lib LOCATION SRAM2)
其它
实例
zephyr提供的测试程序有比较全面的使用演示:https://github.com/zephyrproject-rtos/zephyr/tree/v3.3-branch/tests/application_development/code_relocation
另外示例代码中有nocopy的演示:https://github.com/zephyrproject-rtos/zephyr/tree/v3.3-branch/samples/application_development/code_relocation_nocopy
有效范围
Zephyr目前只有部分soc支持重定位,比较遗憾的是不支持esp32系列
- Cortex-M 全系列
- Cortex-A, Cortex-R 32bit全系列
- 部分risc-v soc
- it8xxx2
- riscv-ite
- gd32vf103
- miv
- mpfs
- neorv32
- opentitan
- sifive-freedom
- starfiv_jh71xx
- telink_b91
- virt
- 部分xtensa soc
- intel_adsp
- sample_controller
对于默认不支持重定位的soc,可以采取自定义linker.ld的方式来做,再根据实际编译链接的情况来处理。
参考
https://docs.zephyrproject.org/latest/kernel/code-relocation.html