当前位置: 首页 > news >正文

Code and Data Relocation in Zephyr

        Code and Data Relocation 是 Zephyr 中的一个强大的功能。此功能允许把某些源文件中代码的 .text、.rodata、.data 和 .bss 字段放置在指定的内存区域中。这里的源文件可以是一个单独的 xxx.c 文件,也可以是一个库文件。指定的内存可以是片内的 RAM 也可以是片外的 QSPI flash。nRF connect SDK 通过脚本 zephyr/scripts/build/gen_relocate_app.py 来实现这一功能。将代码放置到片外 flash 直接通过 XIP 运行,目前只适用于 nRF52840 和 nRF5340 两款芯片。XIP是外设 QSPI 的一部分,通过普通 SPI 扩展的 flash 不能通过 XIP 直接运行代码。由于在 nRF52840 上,  Errata [215] 和 Errata [216] 会对 XIP 的使用造成很大的局限。所以我们更推荐在 nRF5340 上使用这个功能。

准备工作

  • 本文使用 NCS 2.9.1 中的例程代码 zephyr\samples\application_development\code_relocation_nocopy。
  • 硬件使用 nRF5340DK

 

例程代码 code_relocation_nocopy 分析

  • 此例程中主要的文件如下图。其中 main.c 在片内 flash 上运行。ext-code.c 在片外的 QSPI flash 运行。sram_code.c 在片上的 ram 中运行。

 

  • 仔细看 main.c 里的内容。里面只有两个函数 int main(void) 和 void disable_mpu_rasr_xn(void)。函数 disable_mpu_rasr_xn 的作用是配置 MPU 使得代码可以从 SRAM 中运行。main 函数里调用了 function_in_ext_flash() 和 function_in_sram()。
/** This function will allow execute from sram region.  This is needed only for* this sample because by default all soc will disable the execute from SRAM.* An application that requires that the code be executed from SRAM will have* to configure the region appropriately in arm_mpu_regions.c.*/
#ifdef CONFIG_ARM_MPU
#include <cmsis_core.h>
void disable_mpu_rasr_xn(void)
{uint32_t index;/** Kept the max index as 8(irrespective of soc) because the sram would* most likely be set at index 2.*/for (index = 0U; index < 8; index++) {MPU->RNR = index;
#if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE)if (MPU->RBAR & MPU_RBAR_XN_Msk) {MPU->RBAR ^= MPU_RBAR_XN_Msk;}
#elseif (MPU->RASR & MPU_RASR_XN_Msk) {MPU->RASR ^= MPU_RASR_XN_Msk;}
#endif /* CONFIG_ARMV8_M_BASELINE || CONFIG_ARMV8_M_MAINLINE */}
}
#endif /* CONFIG_ARM_MPU */extern void function_in_ext_flash(void);
extern void function_in_sram(void);int main(void)
{
#ifdef CONFIG_ARM_MPUdisable_mpu_rasr_xn();
#endif  /* CONFIG_ARM_MPU */printk("Address of %s function %p\n", __func__, &main);function_in_ext_flash();function_in_sram();printk("Hello World! %s\n", CONFIG_BOARD);return 0;
}

 

  • 再来看 ext_code.c,里面只有一个函数 function_in_ext_flash。        
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>uint32_t var_ext_sram_data = 10U;void function_in_ext_flash(void)
{printk("Address of %s %p\n", __func__, &function_in_ext_flash);printk("Address of var_ext_sram_data %p (%d)\n", &var_ext_sram_data, var_ext_sram_data);
}

 

  • 我sram_code.c里面同样只有一个很简单的函数 function_in_sram。
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>uint32_t var_sram_data = 10U;void function_in_sram(void)
{printk("Address of %s %p\n", __func__, &function_in_sram);printk("Address of var_sram_data %p (%d)\n", &var_sram_data, var_sram_data);
}

 

  • 从刚才的代码里,看上去和普通的代码没有什么不同。为什么函数 function_in_sram 和 function_in_ext_flash 可以分别从 SRAM 和外部 FLASH 里运行呢?带着这个疑问我们再来看 CMakeLists.txt。在CMakeLists.txt 里有这样的调用。

zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY) 。

上面这个函数的作用是把文件 ext_code.c 中的 TXT 字段重定位到 EXTFLASH 所在的区域,执行前不需要先把代码拷贝到 SRAM。

# SPDX-License-Identifier: Apache-2.0cmake_minimum_required(VERSION 3.20.0)find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(code_relocation_nocopy)FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})# Run ext_code from the external flash (XIP). No need to copy.
zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY)# But still relocate (copy) the data to RAM
zephyr_code_relocate(FILES src/ext_code.c LOCATION RAM_DATA)# sram_code instead runs entirely from SRAM after being copied there.
zephyr_code_relocate(FILES src/sram_code.c LOCATION RAM)

 

  我们再来详细解释一下这个函数  zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY) 。

  • FILES  表明的是目标类型。可选的类型除了 FILES 还可以是 LIBRARY
  • src/ext_code.c 是目标的名字或者路径。如果上一个参数是 LIBRARY 这里就是库的名字。如果上一个参数选 FILES 这里可以是一个或多个源文件的路径。
  • EXTFLASH_TEXT 是 目标区域_字段 目标区域 EXTFLASH 是在 linker.ld 中已经定义的区域。字段 TEXT 表示把 .text 字段的内容重新定位。除了 TEXT 还有 DATA, RODATA, BSS 可以选择。
  • NOCOPY 是附加选项。除了 NOCOPY 还有 NOKEEP。

 

  • 接下来我们来看一下 prj.conf 文件里的内容。其中 CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y 和 CONFIG_CUSTOM_LINKER_SCRIPT="linker_arm_nocopy.ld" 为此代码的链接器指定了一个脚本文件。
CONFIG_CODE_DATA_RELOCATION=y
CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y
CONFIG_CUSTOM_LINKER_SCRIPT="linker_arm_nocopy.ld"
CONFIG_BUILD_NO_GAP_FILL=y
CONFIG_COVERAGE=n
CONFIG_XIP=y
CONFIG_LOG=y
CONFIG_FLASH=y
CONFIG_NORDIC_QSPI_NOR=y
CONFIG_NORDIC_QSPI_NOR_XIP=y

 

我们再来看一下下面的链接脚本 linker_arm_nocopy.ld 里面主要的内容就是定义了 EXTFLASH 的起始地址和长度。 在刚才CMakeLists.txt 里调用 zephyr_code_relocate 时 EXTFLASH  作为输入参数规定了重定位的目标地址。

#include <zephyr/linker/sections.h>
#include <zephyr/devicetree.h>#include <zephyr/linker/linker-defs.h>
#include <zephyr/linker/linker-tool.h>/* On nRF5340, external flash is mapped in XIP region at 0x1000_0000. */#define EXTFLASH_NODE    DT_INST(0, nordic_qspi_nor)
#define EXTFLASH_ADDR    0x10000000
#define EXTFLASH_SIZE    DT_PROP_OR(EXTFLASH_NODE, size_in_bytes, \DT_PROP(EXTFLASH_NODE, size) / 8)MEMORY
{EXTFLASH (rx) : ORIGIN = EXTFLASH_ADDR, LENGTH = EXTFLASH_SIZE
}#include <zephyr/arch/arm/cortex_m/scripts/linker.ld>

 

  • 我们用编译指令 west build -p -b nrf5340dk/nrf5340/cpuapp 编译代码。我们可以看一个名为 EXTFLASH 的外部 flash 分区,大小是 8M bytes。
-- west build: building application
[5/10] Performing build step for 'sdk_291_code_relocation'
[4/166] Generating include/generated/zephyr/version.h
-- Zephyr version: 3.7.99 (C:/ncs/v2.9.1/zephyr), build: v3.7.99-ncs2-1
[166/166] Linking C executable zephyr\zephyr.elf
Memory region         Used Size  Region Size  %age UsedEXTFLASH:          56 B         8 MB      0.00%FLASH:       41580 B         1 MB      3.97%RAM:        9032 B       448 KB      1.97%IDT_LIST:          0 GB        32 KB      0.00%
Generating files from D:/ncs/sdk_291_code_relocation/build/sdk_291_code_relocation/zephyr/zephyr.elf for board: nrf5340dk
[10/10] Generating ../merged.hex

 

  • 编译完成后使用指令 west flash 烧写代码。
(v2.9.1) D:\ncs\sdk_291_code_relocation> west flash
-- west flash: rebuilding
[0/5] Performing build step for 'sdk_291_code_relocation'
ninja: no work to do.
[4/5] cmd.exe /C "cd /D D:\ncs\sdk_291_code_relocation\build\_sysbuild && C:\ncs\toolchains\b620d30767\opt\bin\cmake.exe -E true"
-- west flash: using runner nrfjprog
-- runners.nrfjprog: reset after flashing requested
Using board 1050042793
-- runners.nrfjprog: Flashing file: D:\ncs\sdk_291_code_relocation\build\merged.hex
[ #################### ]   0.000s | Erasing non-volatile memory - Erase successful
[ #################### ]   2.435s | Erase file - Done erasing
[ #################### ]   0.663s | Program file - Done programming
[ #################### ]   0.539s | Verify file - Done verifying
Applying pin reset.
-- runners.nrfjprog: Board with serial number 1050042793 flashed successfully.

 

  •  连接串口查看 log。 log 里面显示了函数和变量所在的地址。其中 function_in_ext_flash 的地址是以 0x10000000 开头的,这个地址是外部 flash 在 XIP 里映射的起始地址。
*** Booting nRF Connect SDK v2.9.1-60d0d6c8d42d ***
*** Using Zephyr OS v3.7.99-ca954a6216c9 ***
Address of main function 0x219
Address of function_in_ext_flash 0x10000001
Address of var_ext_sram_data 0x200000a0 (10)
Address of function_in_sram 0x20000001
Address of var_sram_data 0x200000a4 (10)
Hello World! nrf5340dk

 

 

在 BLE 例程中使用 Code And Data Relocation

分析完刚才的例程,我们总结一下把一个文件里的代码放到外部 flash 运行需要三步:

  1. 在 prj.conf 里添加例程中的配置选项。
  2. 在 CMakeList.txt 里添加需要外部运行的 xxx.c 文件,并用 zephyr_code_relocate 把它放到外部的 flash 里。
  3. 添加 linker script。
  • 下面我们就修改例程 nrf\samples\bluetooth\peripheral_lbs ,把它的部分代码放到外部 flash 里运行。 
  1. 在 prj.conf 里添加例程中的配置选项。
    CONFIG_CODE_DATA_RELOCATION=y
    CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y
    CONFIG_CUSTOM_LINKER_SCRIPT="linker_arm_nocopy.ld"
    CONFIG_BUILD_NO_GAP_FILL=y
    CONFIG_COVERAGE=n
    CONFIG_XIP=y
    CONFIG_FLASH=y
    CONFIG_NORDIC_QSPI_NOR=y
    CONFIG_NORDIC_QSPI_NOR_XIP=y

     

  2. 修改 CMakeList.txt,添加代码 ext_code.c。并且使用 zephyr_code_relocate 把 ext_code.c 中的 TEXT 部分放到了分区 EXTFLASH 中。把 ext_code.c 中的 DATA 部分放到了分区 RAM 中。
    #
    # Copyright (c) 2018 Nordic Semiconductor
    #
    # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    #
    cmake_minimum_required(VERSION 3.20.0)
    find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
    project(NONE)
    # NORDIC SDK APP START
    target_sources(app PRIVATEsrc/main.csrc/ext_code.c
    )
    # Run ext_code from the external flash (XIP). No need to copy.
    zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY)# But still relocate (copy) the data to RAM
    zephyr_code_relocate(FILES src/ext_code.c LOCATION RAM_DATA)
    # NORDIC SDK APP END

     

  3. 添加文件 linker_arm_nocopy.ld, 里面的内容如下。
    #include <zephyr/linker/sections.h>
    #include <zephyr/devicetree.h>#include <zephyr/linker/linker-defs.h>
    #include <zephyr/linker/linker-tool.h>#if defined(CONFIG_NORDIC_QSPI_NOR) && defined(CONFIG_SOC_NRF5340_CPUAPP)/* On nRF5340, external flash is mapped in XIP region at 0x1000_0000. */#define EXTFLASH_NODE    DT_INST(0, nordic_qspi_nor)
    #define EXTFLASH_ADDR    0x10000000
    #define EXTFLASH_SIZE    DT_PROP_OR(EXTFLASH_NODE, size_in_bytes, \DT_PROP(EXTFLASH_NODE, size) / 8)#endifMEMORY
    {EXTFLASH (rx) : ORIGIN = EXTFLASH_ADDR, LENGTH = EXTFLASH_SIZERAM (wx) : ORIGIN = 0x20000000, LENGTH = 0x60000RAM2 (wx) : ORIGIN = 0x20060000, LENGTH = 0x10000
    }#include <zephyr/arch/arm/cortex_m/scripts/linker.ld>

     

        这些修改完全按照 zephyr\samples\application_development\code_relocation_nocopy 里的内容。经过指令 west build -p -b nrf5340dk/nrf5340/cpuapp 编译后,我们可以看到下面的编译信息。里面多了一个分区 EXTFLASH 这个就是外部 flash 的区域。EXTFLASH 中已经使用的56个字节,存放了 ext_code.c 中的 TEXT 部分。

-- west build: building application
[9/20] Performing build step for 'sdk_291_peripheral_lbs'
[6/259] Generating include/generated/zephyr/version.h
-- Zephyr version: 3.7.99 (C:/ncs/v2.9.1/zephyr), build: v3.7.99-ncs2-1
[259/259] Linking C executable zephyr\zephyr.elf
Memory region         Used Size  Region Size  %age UsedEXTFLASH:          56 B         8 MB      0.00%FLASH:      129916 B      1008 KB     12.59%RAM:       26892 B       448 KB      5.86%IDT_LIST:          0 GB        32 KB      0.00%
Generating files from D:/ncs/sdk_291_peripheral_lbs/build/sdk_291_peripheral_lbs/zephyr/zephyr.elf for board: nrf5340dk
[11/20] Performing build step for 'ipc_radio'
[4/202] Generating include/generated/zephyr/version.h
-- Zephyr version: 3.7.99 (C:/ncs/v2.9.1/zephyr), build: v3.7.99-ncs2-1
[202/202] Linking C executable zephyr\zephyr.elf
Memory region         Used Size  Region Size  %age UsedFLASH:      168848 B       256 KB     64.41%RAM:       46560 B        64 KB     71.04%SRAM1:          0 GB        64 KB      0.00%IDT_LIST:          0 GB        32 KB      0.00%
Generating files from D:/ncs/sdk_291_peripheral_lbs/build/ipc_radio/zephyr/zephyr.elf for board: nrf5340dk
[20/20] Generating ../merged_CPUNET.hex

 

        运行编译好的代码,我们可以看到下面的 log 。从 log 中我们可以看到 main 函数的运行地址是 0x755,这是片内 flash 的地址。 函数 function_in_ext_flash 的运行地址是 0x10000001,这个地址是我们 XIP 映射的片外 flash 的地址。

*** Booting My Application v2.9.1-e115fed8f669 ***
*** Using nRF Connect SDK v2.9.1-60d0d6c8d42d ***
*** Using Zephyr OS v3.7.99-ca954a6216c9 ***
Starting Bluetooth Peripheral LBS example
Address of main 0x755
Address of function_in_ext_flash 0x10000001
Address of var_ext_sram_data 0x200001a8 (10)
I: 2 Sectors of 4096 bytes
I: alloc wra: 0, fd0
I: data wra: 0, 1c
I: HW Platform: Nordic Semiconductor (0x0002)
I: HW Variant: nRF53x (0x0003)
I: Firmware: Standard Bluetooth controller (0x00) Version 121.4259 Build 3078678206
I: No ID address. App must call settings_load()
Bluetooth initialized
I: Identity: F2:23:7E:40:9D:67 (random)
I: HCI: version 6.0 (0x0e) revision 0x209a, manufacturer 0x0059
I: LMP: version 6.0 (0x0e) subver 0x209a
Advertising successfully started

 

  • 刚才我们只是把 ext_code.c 这个文件中的 TEXT 放到了外部 flash 中运行。下面我们通过修改 CMakeList.txt 把整个 app 库中的代码放到外部 flash 上。app 库中包含了 main.c 和 ext_code.c 两个文件。我们仍然使用 zephyr_code_relocate 来重定位代码。和之前的区别是,第一个参数使用  LIBRARY 来取代之前的 FILES ,表示现在是要对库进行重定位。
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(NONE)
# NORDIC SDK APP START
target_sources(app PRIVATE
  src/main.c
  src/ext_code.c
)
# Run ext_code from the external flash (XIP). No need to copy.
#zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY)
# But still relocate (copy) the data to RAM
#zephyr_code_relocate(FILES src/ext_code.c LOCATION RAM_DATA)
zephyr_code_relocate(LIBRARY app LOCATION EXTFLASH_TEXT NOCOPY)
zephyr_code_relocate(LIBRARY app LOCATION RAM_DATA)
# NORDIC SDK APP END

 

        编译之后的编译信息显示,和之前相比代码占用外部 flash 空间明显变大。从之前的56个字节,增大到现在的896个字节。

-- west build: building application
[9/20] Performing build step for 'sdk_291_peripheral_lbs'
[6/259] Generating include/generated/zephyr/version.h
-- Zephyr version: 3.7.99 (C:/ncs/v2.9.1/zephyr), build: v3.7.99-ncs2-1
[259/259] Linking C executable zephyr\zephyr.elf
Memory region         Used Size  Region Size  %age UsedEXTFLASH:         896 B         8 MB      0.01%FLASH:      129212 B      1008 KB     12.52%RAM:       26892 B       448 KB      5.86%IDT_LIST:          0 GB        32 KB      0.00%
Generating files from D:/ncs/sdk_291_peripheral_lbs/build/sdk_291_peripheral_lbs/zephyr/zephyr.elf for board: nrf5340dk
[11/20] Performing build step for 'ipc_radio'
[4/202] Generating include/generated/zephyr/version.h
-- Zephyr version: 3.7.99 (C:/ncs/v2.9.1/zephyr), build: v3.7.99-ncs2-1
[202/202] Linking C executable zephyr\zephyr.elf
Memory region         Used Size  Region Size  %age UsedFLASH:      168848 B       256 KB     64.41%RAM:       46560 B        64 KB     71.04%SRAM1:          0 GB        64 KB      0.00%IDT_LIST:          0 GB        32 KB      0.00%
Generating files from D:/ncs/sdk_291_peripheral_lbs/build/ipc_radio/zephyr/zephyr.elf for board: nrf5340dk
[20/20] Generating ../merged_CPUNET.hex

 

        从运行输出的 log ,main 函数的地址大于 0x10000000 ,它运行在 XIP 映射的外部 flash 的地址范围。

*** Booting My Application v2.9.1-e115fed8f669 ***
*** Using nRF Connect SDK v2.9.1-60d0d6c8d42d ***
*** Using Zephyr OS v3.7.99-ca954a6216c9 ***
Starting Bluetooth Peripheral LBS example
Address of main 0x10000165
Address of function_in_ext_flash 0x10000001
Address of var_ext_sram_data 0x200001a8 (10)
I: 2 Sectors of 4096 bytes
I: alloc wra: 0, fd0
I: data wra: 0, 1c
I: HW Platform: Nordic Semiconductor (0x0002)
I: HW Variant: nRF53x (0x0003)
I: Firmware: Standard Bluetooth controller (0x00) Version 121.4259 Build 3078678206
I: No ID address. App must call settings_load()
Bluetooth initialized
I: Identity: F2:23:7E:40:9D:67 (random)
I: HCI: version 6.0 (0x0e) revision 0x209a, manufacturer 0x0059
I: LMP: version 6.0 (0x0e) subver 0x209a
Advertising successfully started

 

  • Code And Data Relocation 可以对 .text, .rodata, .data, .bss 等多个字段重定位。之前的演示中我们展示了对 .text 和 .bss 的重定位。下面我们来演示如何对 .rodata 和 .bss 字段重定位。我们修改了 ext_code.c ,和之前的代码相比下面的代码添加了两个变量。一个是 var_data 另一个是 ext_flash_string。var_data 在编译后会被放到 .bss 字段。ext_flash_string 会被放到 .rodata 字段。
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
uint32_t var_ext_sram_data = 10U;
uint32_t var_data;
const char * const ext_flash_string = "Hello from external flash!";
void function_in_ext_flash(void)
{
    var_data = 0;
    printk("Address of %s %p\n", __func__, &function_in_ext_flash);
    printk("Address of var_ext_sram_data %p (%d)\n", &var_ext_sram_data, var_ext_sram_data);
    printk("Address of var_data %p (%d)\n", &var_data, var_data);
    printk("Address of ext_flash_string %p (%s)\n", &ext_flash_string, ext_flash_string);
}

 

        这里我们来说一下这4个字段都是怎么划分的。

字段名 存储内容 读写权限 典型示例
.text 可执行代码 只读 指令函数
.rodata 只读常量 只读 const 数据、字符串常量
.data 已初始化全局/静态变量 可读写 int x = 5
.bss 未初始化全局/静态变量 可读写 int y

         修改 CMakeList.txt 把 ext_code.c 里的这4个字段都重新定位,其中 text 和 rodata 里的内容被放到了外部的 flash 里。

# NORDIC SDK APP START
target_sources(app PRIVATEsrc/main.csrc/ext_code.c
)
# Run ext_code from the external flash (XIP). No need to copy.
zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY)
# But still relocate (copy) the data to RAM
zephyr_code_relocate(FILES src/ext_code.c LOCATION RAM_DATA)
zephyr_code_relocate(FILES src/ext_code.c LOCATION RAM_BSS)
zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_RODATA NOCOPY)
#zephyr_code_relocate(LIBRARY app LOCATION EXTFLASH_TEXT NOCOPY)
#zephyr_code_relocate(LIBRARY app LOCATION RAM_DATA)
# NORDIC SDK APP END

 

        从运行输出的 log,我们可以看到 rodata 字段的常量 ext_flash_string 的地址为 0x10000080, 在外部 flash 上。 

*** Booting My Application v2.9.1-e115fed8f669 ***
*** Using nRF Connect SDK v2.9.1-60d0d6c8d42d ***
*** Using Zephyr OS v3.7.99-ca954a6216c9 ***
Starting Bluetooth Peripheral LBS example
Address of main 0x755
Address of function_in_ext_flash 0x10000001
Address of var_ext_sram_data 0x200001a8 (10)
Address of var_data 0x20001400 (0)
Address of ext_flash_string 0x10000080 (Hello from external flash!)
I: 2 Sectors of 4096 bytes
I: alloc wra: 0, fd0
I: data wra: 0, 1c
I: HW Platform: Nordic Semiconductor (0x0002)
I: HW Variant: nRF53x (0x0003)
I: Firmware: Standard Bluetooth controller (0x00) Version 121.4259 Build 3078678206
I: No ID address. App must call settings_load()
Bluetooth initialized
I: Identity: F2:23:7E:40:9D:67 (random)
I: HCI: version 6.0 (0x0e) revision 0x209a, manufacturer 0x0059
I: LMP: version 6.0 (0x0e) subver 0x209a
Advertising successfully started

 

    • 我们把 ext_code.c 里的 text 和 rodata 的内容都放在了外部 flash 里,但是 data 和 bss 字段仍然和其它文件中的变量放在相同的 RAM 分区里。有时候我们希望把 ext_code.c 里的变量单独放在一个 RAM 的分区上,下面我们就来演示如何单独划分一个 RAM 分区 RAM2,然后把 ext_code.c 里的变量重新定位到 RAM2 里。

  首先我们先修改 linker_arm_nocopy.ld 来定义分区 RAM2。 RAM2 的起始地址为 0x20060000。

#include <zephyr/linker/sections.h>
#include <zephyr/devicetree.h>#include <zephyr/linker/linker-defs.h>
#include <zephyr/linker/linker-tool.h>#if defined(CONFIG_NORDIC_QSPI_NOR) && defined(CONFIG_SOC_NRF5340_CPUAPP)/* On nRF5340, external flash is mapped in XIP region at 0x1000_0000. */#define EXTFLASH_NODE    DT_INST(0, nordic_qspi_nor)
#define EXTFLASH_ADDR    0x10000000
#define EXTFLASH_SIZE    DT_PROP_OR(EXTFLASH_NODE, size_in_bytes, \DT_PROP(EXTFLASH_NODE, size) / 8)#endifMEMORY
{EXTFLASH (rx) : ORIGIN = EXTFLASH_ADDR, LENGTH = EXTFLASH_SIZERAM (wx) : ORIGIN = 0x20000000, LENGTH = 0x60000RAM2 (wx) : ORIGIN = 0x20060000, LENGTH = 0x10000
}#include <zephyr/arch/arm/cortex_m/scripts/linker.ld>

 

  接下来我们修改 CMakeList.txt 把 ext_code.c 中的 data 和 bss 字段重新定位到 RAM2 里。

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(NONE)
# NORDIC SDK APP START
target_sources(app PRIVATE
  src/main.c
  src/ext_code.c
)
# Run ext_code from the external flash (XIP). No need to copy.
zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_TEXT NOCOPY)
# But still relocate (copy) the data to RAM
zephyr_code_relocate(FILES src/ext_code.c LOCATION RAM2_DATA)
zephyr_code_relocate(FILES src/ext_code.c LOCATION RAM2_BSS)
zephyr_code_relocate(FILES src/ext_code.c LOCATION EXTFLASH_RODATA NOCOPY)

 

  修改完成后编译,烧写,查看 log。我们可以看到 var_ext_sram_data 的地址是 0x20060000, var_data 的地址是 0x200060004。正好在我们之前定义的 RAM2 上。

*** Booting My Application v2.9.1-ce414a3d8014 ***
*** Using nRF Connect SDK v2.9.1-60d0d6c8d42d ***
*** Using Zephyr OS v3.7.99-ca954a6216c9 ***
Starting Bluetooth Peripheral LBS example
Address of main 0x755
Address of function_in_ext_flash 0x10000001
Address of var_ext_sram_data 0x20060000 (10)
Address of var_data 0x20060004 (0)
Address of ext_flash_string 0x10000080 (Hello from external flash!)
I: 2 Sectors of 4096 bytes
I: alloc wra: 0, fd0
I: data wra: 0, 1c
I: HW Platform: Nordic Semiconductor (0x0002)
I: HW Variant: nRF53x (0x0003)
I: Firmware: Standard Bluetooth controller (0x00) Version 121.4259 Build 3078678206
I: No ID address. App must call settings_load()
Bluetooth initialized
I: Identity: F2:23:7E:40:9D:67 (random)
I: HCI: version 6.0 (0x0e) revision 0x209a, manufacturer 0x0059
I: LMP: version 6.0 (0x0e) subver 0x209a
Advertising successfully started

 

外部 flash 的烧写

  之前我们烧写程序都是通过 west flash 来实现的。无论程序是在片内 flash 还是在片外 flash,都是通过这条指令来烧写。下面我们就分析一下这是怎么实现的。

  • 当我们使用 west flash 时 python 脚本会调用指令烧写当前工程目录下的 build/merged.hex 文件。我们打开这个文件看一下,可以找到下面的内容。
    :020000041000EA
    :1000000008B5064A0649074800F012F8BDE808405E
    :10001000054906480A6800F00BB800BF010000104F
    :1000200083B201004BB20100A80100205DB20100C3
    :080030005FF800F02953010004
    :00000001FF

    这个 hex 文件采用的 Intel hex 格式。由多行 ASCII 文本组成,每行称为一个 record。每一行由格式为 :LLAAAATT[DD...]CC 的文本组成。

    1. :记录起始符
    2. LL 数据长度(1 字节),表示 DD 的字节数(十六进制,范围 00FF
    3. AAAA 地址域(2 字节),表示数据加载的起始偏移地址
    4. TT 记录类型(1 字节),定义记录功能(见下表)
    5. DD 数据域(长度由 LL 指定),存储实际二进制数据的十六进制 ASCII 表示
    6. CC 校验和(1 字节),用于验证记录完整性
 
类型(TT) ‌名称 功能说明 ‌数据域(DD)内容
 ‌00 数据记录(Data) 存储实际程序代码或常量数据,需加载到 AAAA 指定地址  二进制数据
 01 文件结束记录(EOF) 标记文件终止,必须出现在末尾  无(LL=00)
02 扩展段地址记录(ESA) 分段内存模型。在纯32位系统中,ESA几乎被ELA完全取代。  
 04 扩展线性地址记录(ELA) 定义后续数据记录的高 16 位线性基址(用于 32 位地址)  高 16 位地址(如 0100
03/05  起始地址记录 指定程序起始执行地址(03 为段地址模式,05 为线性地址模式)  CS 或 EIP 值

依据上面的说明中我们可以解读之前 merged.hex 文件中的内容。可以看出这段数据是在以 0x10000000 起始的外部 flash 空间。:020000041000EA 表示下面的数据的起始地址是 0x10000000。:1000000008B5064A0649074800F012F8BDE808405E 表示在地址 0x10000000 由16个字节的数据。

  merged.hex 里面我们包含了地址位于外部 flash 我们再来看 zephyr\scripts\west_commands\runners\nrf_common.py 这个文件。 当我们执行 west flash 命令行的时候会调用 nrf_common.py 里的 program_hex。我们看一下 program_hex 里的内容。program_hex 会检测 merged.hex 里包含的内容,如果包含片外 flash 的内容,  qspi_erase_opt  会被赋值  ERASE_ALL。接下来调用的参数会根据 qspi_erase_opt 的值来操作外部的 flash。west flash 的底层使用的是 nrfjprog。

        xip_ranges = {'NRF52_FAMILY': (0x12000000, 0x19FFFFFF),'NRF53_FAMILY': (0x10000000, 0x1FFFFFFF),}qspi_erase_opt = Noneif self.family in xip_ranges:xip_start, xip_end = xip_ranges[self.family]if self.hex_refers_region(xip_start, xip_end):qspi_erase_opt = 'ERASE_ALL'# What tool commands do we need to flash this target?if self.family == 'NRF53_FAMILY':# nRF53 requires special treatment due to the extra coprocessor.self.program_hex_nrf53(erase_arg, qspi_erase_opt)else:self.op_program(self.hex_, erase_arg, qspi_erase_opt, defer=True, core=core)self.flush(force=False)def program_hex_nrf53(self, erase_arg, qspi_erase_opt):

 

  看到这里大家会有疑问,外部的 flash 通过 qspi 接口来驱动,不同的 flash 参数不同怎么办?这里有两种方法来修改 flash 驱动的参数。

  1.  用户可以在 CMakeList.txt 中来设定初始化 flash 所需要的参数的文件。下面的 CMakeList.txt 中指定了 Qspi_thingy53.ini 这个文件来描述 flash 驱动的参数。
    cmake_minimum_required(VERSION 3.20.0)
    macro(app_set_runner_args)
      if(CONFIG_BOARD_THINGY53_NRF5340_CPUAPP)
        # Use alternative QSPI configuration file when flashing Thingy53
        board_runner_args(nrfjprog "--qspiini=${CMAKE_CURRENT_SOURCE_DIR}/Qspi_thingy53.ini")
      endif()
    endmacro()
    find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

     

  2.  如果直接使用 nrfjprog 来烧写外部 flash, 用户可以使用 --qspiini 来设定初始化 flash 所需要的参数文件。下面是 nrfjprog 的帮助文档中关于 --qspiini 这个参数的说明。
    --qspiini <file>        Deprecated: Use a TOML file with --config instead.Uses the QSPI settings file specified in the givenfile ini path instead of searching for the defaultconfig.toml file in the installation folder.Must be combined with either --erasepage, --memrd,--memwr, --program, --verify, --readqspi or--qspieraseall commands.

     

  我们来看一下 Qspi_thingy53.ini 这个文件,看一看里面有哪些 flash 相关的参数。

; nrfjprog QSPI configuration file.
[DEFAULT_CONFIGURATION]
; Define the capacity of the flash memory device in bytes. Set to 0 if no external memory device is present in your board.
MemSize = 0x800000
; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO
ReadMode = READ2IO
; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO
WriteMode = PP
; Define the desired AddressMode. Valid options are BIT24 and BIT32
AddressMode = BIT24
; Define the desired Frequency. Valid options are M2, M4, M8, M16 and M32
Frequency = M16
; Define the desired SPI mode. Valid options are MODE0 and MODE3
SpiMode = MODE0
; Define the desired SckDelay. Valid options are in the range 0 to 255
SckDelay = 0x80

  

总结

  本文从例程 zephyr\samples\application_development\code_relocation_nocopy 出发,介绍了 Code and Data Relocation 这个 zephyr 中的使用功能。并通过实操展示如何在常用的 BLE 例程 nrf\samples\bluetooth\peripheral_lbs 上,一步一步把指定的编译字段重新定位到指定的存储区域里。最后我们还简单讲述了如何根据自己的 flash 的硬件规格,来修改驱动参数,外部 flash 的烧写。                                                                                                                                                                                           

 

 
 
 
http://www.hskmm.com/?act=detail&tid=187

相关文章:

  • 产品经理实战指南:用户需求分析全流程详解(含工具链整合)
  • 模板
  • kylin V11安装mysql8.0
  • 【Kubernetes】 PVC 和 PV
  • Docker镜像
  • idea 允许多运行java示例 idea2022版本
  • ROS2环境配置
  • 2025年第五届电子信息工程与计算机科学国际会议(EIECS 2025)
  • P6477 [NOI Online #2 提高组] 子序列问题 题解
  • iframe 跨域通信实战:可视化编辑器的技术实现
  • windows项目下统计代码行数
  • 。。。
  • ETF 简介
  • 实时流式响应的 SSE 技术实现
  • 2025年艺术、教育和管理国际学术会议(ICAEM 2025)- 第五期
  • CF 1048 Div.2 解题报告
  • reLeetCode 热题 100-1 两数之和-扩展1 unordered_map实现 - MKT
  • 读书笔记:什么是对象表?
  • AI 服务路由策略:如何实现智能负载均衡
  • 在SQL语句中的别名
  • 多维度排序算法在企业级应用中的性能优化
  • 正则表达式在代码解析中的高级应用
  • vue3 项目中优雅的使用 SVG 图标(vite-plugin-svg-icons)
  • 自我介绍+软工5问
  • 车道线检测资料
  • 实现Jenkins不同账号只能看到各自任务的权限
  • 6 个最佳无代码 IT 资产管理工具推荐
  • python开发mcp入门
  • 建造者模式进阶:复杂AI服务的优雅构建
  • 代理模式在AI应用中的安全实践:AOP + 限流 + 权限控制