Zephyr partitions

分区表

  • 在大部分单片机应用程序中,为了实现远程更新需要soc内部flash按照功能进行分区,分区表中需要包含一个bootloader分区和至少两个以上的镜像分区,而单片机本身没有虚拟内存映射的功能,代码的物理地址和逻辑地址必须保持一致,这样才能让程序正常运行。

Zephyr分区表

在Zephyr中,分区表是通过设备树来表示的,在配置阶段,CMake会从设备树获取对应的分区信息,例如 flash 描述,代码存放的分区,bootloader所在分区:

/* zephyr/boards/arm/nucleo_f446ze/nucleo_f446ze.dts */
chosen {
		zephyr,console = &usart3;
		zephyr,shell-uart = &usart3;
		zephyr,sram = &sram0;
		zephyr,flash = &flash0;
		zephyr,canbus = &can1;
	};

&flash0 {

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

		boot_partition: partition@0 {
			label = "mcuboot";
			reg = <0x00000000 DT_SIZE_K(64)>;
			read-only;
		};

		/*
		 * The flash starting at 0x00010000 and ending at
		 * 0x0001ffff (sectors 16-31) is reserved for use
		 * by the application.
		 */
		storage_partition: partition@10000 {
			label = "storage";
			reg = <0x00010000 DT_SIZE_K(64)>;
		};

		slot0_partition: partition@20000 {
			label = "image-0";
			reg = <0x00020000 DT_SIZE_K(128)>;
		};
		slot1_partition: partition@40000 {
			label = "image-1";
			reg = <0x00040000 DT_SIZE_K(128)>;
		};
		scratch_partition: partition@60000 {
			label = "image-scratch";
			reg = <0x00060000 DT_SIZE_K(128)>;
		};
	};
};

链接脚本

链接脚本位置

  • 程序的链接起始地址是通过链接脚本进行设置的,因此我们需要知道在Zephyr中如何查找并使用链接脚本,下面是 zephyr/CMakeLists.txt中对链接脚本的查找:
    • CONFIG_HAVE_CUSTOM_LINKER_SCRIPT 用于控制是否使用用户自定义的链接脚本,CONFIG_CUSTOM_LINKER_SCRIPT 是链接脚本的路径。
      • 首先用户在用户 app 目录下进行查找,如果文件不存在,CMake 会认为 CONFIG_CUSTOM_LINKER_SCRIPT 是一个绝对路径,于是将链接脚本设置为 CONFIG_CUSTOM_LINKER_SCRIPT 的值,然后使用断言判断其是否存在。
    • CONFIG_HAVE_CUSTOM_LINKER_SCRIPT 未使能,CMake首先从 boards 目录下查找链接脚本 linker.ld,如果不存在将使用SOC目录下的 linker.ld
if(CONFIG_HAVE_CUSTOM_LINKER_SCRIPT)
  set(LINKER_SCRIPT ${APPLICATION_SOURCE_DIR}/${CONFIG_CUSTOM_LINKER_SCRIPT})
  if(NOT EXISTS ${LINKER_SCRIPT})
    set(LINKER_SCRIPT ${CONFIG_CUSTOM_LINKER_SCRIPT})
    assert_exists(CONFIG_CUSTOM_LINKER_SCRIPT)
  endif()
else()
  # Try a board specific linker file
  set(LINKER_SCRIPT ${BOARD_DIR}/linker.ld)
  if(NOT EXISTS ${LINKER_SCRIPT})
    # If not available, try an SoC specific linker file
    set(LINKER_SCRIPT ${SOC_DIR}/${ARCH}/${SOC_PATH}/linker.ld)
  endif()
endif()

if(NOT EXISTS ${LINKER_SCRIPT})
  message(FATAL_ERROR "Could not find linker script: '${LINKER_SCRIPT}'. Corrupted configuration?")
endif()

程序链接起始地址

  • 以 nucleo_f103rb 编译 zephyr/samples/basic/blinky 为例,该例程中未使用bootloader,通过在 zephyr/CMakeLists.txt 中添加 message 可获取到对应的链接脚本路径为 zephyr/soc/arm/st_stm32/stm32f1/linker.ld,里面的内容是 #include <zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld>,在这里需要分析的是代码的链接起始地址,因此删除了无关部分,下面是其中的部分内容:
#include <zephyr/linker/sections.h>
#include <zephyr/devicetree.h>

#include <zephyr/linker/devicetree_regions.h>
#include <zephyr/linker/linker-defs.h>
#include <zephyr/linker/linker-tool.h>

/* 使用了XIP功能后代码可以直接在Flash运行,ROMABLE_REGION 代码最终链接的区域 */
#ifdef CONFIG_XIP
#define ROMABLE_REGION FLASH
#else
#define ROMABLE_REGION RAM
#endif
#define RAMABLE_REGION RAM

/* CONFIG_FLASH_BASE_ADDRESS 的值默认会从设备树中 chosen 节点下 zephyr,flash 获取
   CONFIG_HAS_FLASH_LOAD_OFFSET 用于设置加载地址偏移,改选项在CORTEX-M系列中默认使能,稍后会讲解
   USE_DT_CODE_PARTITION 选项使能后从chosen 节点下 zephyr,code-partition 属性获取偏移信息,否则将通过手动设置
   CONFIG_FLASH_LOAD_OFFSET 为代码偏移地址,可用于代码重定向,CONFIG_FLASH_LOAD_SIZE 为分区大小。
*/
#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

/* ROM_SIZE 在此处代表 flash 可用空间
   CONFIG_FLASH_LOAD_SIZE 为分区大小
   如果 CONFIG_FLASH_LOAD_SIZE 未设置,将使用 FLASH 总大小减去 CONFIG_FLASH_LOAD_OFFSET 作为 ROM_SIZE
 */
#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)
/* 如果编译的程序是作为BOOTLOADER,则将RAM最后一段空间用于BOOTLOADER运行 */
#define RAM_SIZE (CONFIG_BOOTLOADER_SRAM_SIZE * 1K)
#define RAM_ADDR (CONFIG_SRAM_BASE_ADDRESS + \
	(CONFIG_SRAM_SIZE * 1K - RAM_SIZE))
#else
/* 如果编译的程序是不是BOOTLOADER,默认会从从 zephyr,sram 属性中获取RAM大小和起始地址 */
#define RAM_SIZE (CONFIG_SRAM_SIZE * 1K)
#define RAM_ADDR CONFIG_SRAM_BASE_ADDRESS
#endif
#else
/* 如果编译的程序是不是BOOTLOADER,芯片也不支持XIP
   由于Bootloader运行时需要从flash加载到内存中,这种情况下会为Bootloader预留一部分RAM 
*/
#define RAM_SIZE (CONFIG_SRAM_SIZE * 1K - CONFIG_BOOTLOADER_SRAM_SIZE * 1K)
#define RAM_ADDR CONFIG_SRAM_BASE_ADDRESS
#endif

MEMORY
{
	/* ROM_ADDR = (CONFIG_FLASH_BASE_ADDRESS + CONFIG_FLASH_LOAD_OFFSET), ROM_SIZE = CONFIG_FLASH_LOAD_SIZE */
    FLASH (rx) : ORIGIN = ROM_ADDR, LENGTH = ROM_SIZE
    RAM   (wx) : ORIGIN = RAM_ADDR, LENGTH = RAM_SIZE
    LINKER_DT_REGIONS()
    /* Used by and documented in include/linker/intlist.ld */
    IDT_LIST (wx) : ORIGIN = 0xFFFFF7FF, LENGTH = 2K
}

ENTRY(CONFIG_KERNEL_ENTRY)

SECTIONS
{

#include <zephyr/linker/rel-sections.ld>

    /*
     * .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
     * before text section.
     */
    /DISCARD/ :
	{
	*(.plt)
	}

    /DISCARD/ :
	{
	*(.iplt)
	}

    GROUP_START(ROMABLE_REGION)

	__rom_region_start = ROM_ADDR;

    SECTION_PROLOGUE(rom_start,,)
	{

/* Located in generated directory. This file is populated by calling
 * zephyr_linker_sources(ROM_START ...). This typically contains the vector
 * table and debug information.
 */
#include <snippets-rom-start.ld>

	} GROUP_LINK_IN(ROMABLE_REGION)

#ifdef CONFIG_CODE_DATA_RELOCATION

#include <linker_relocate.ld>

#endif /* CONFIG_CODE_DATA_RELOCATION */

    SECTION_PROLOGUE(_TEXT_SECTION_NAME,,)
	{
	__text_region_start = .;

#include <zephyr/linker/kobject-text.ld>

	*(.text)
	*(".text.*")
	*(".TEXT.*")
	*(.gnu.linkonce.t.*)

	/*
	 * These are here according to 'arm-zephyr-elf-ld --verbose',
	 * after .gnu.linkonce.t.*
	 */
	*(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
	. = ALIGN(4);

	} GROUP_LINK_IN(ROMABLE_REGION)

	__text_region_end = .;
}
  • 在CORTEX-M系列中,HAS_FLASH_LOAD_OFFSET 选项默认就是被选中的,其他芯片可能需要手动开启,这种情况下只需要确定 CONFIG_FLASH_LOAD_OFFSET 和 CONFIG_FLASH_LOAD_SIZE 的值即可。
config CPU_CORTEX_M
	bool
	select CPU_CORTEX
	select ARCH_HAS_CUSTOM_SWAP_TO_MAIN
	select HAS_CMSIS_CORE
	select HAS_FLASH_LOAD_OFFSET
	select ARCH_HAS_SINGLE_THREAD_SUPPORT
	select ARCH_HAS_THREAD_ABORT
	select ARCH_HAS_TRUSTED_EXECUTION if ARM_TRUSTZONE_M
	select ARCH_HAS_STACK_PROTECTION if (ARM_MPU && !ARMV6_M_ARMV8_M_BASELINE) || CPU_CORTEX_M_HAS_SPLIM
	select ARCH_HAS_USERSPACE if ARM_MPU
	select ARCH_HAS_NOCACHE_MEMORY_SUPPORT if ARM_MPU && CPU_HAS_ARM_MPU && CPU_HAS_DCACHE
	select ARCH_HAS_RAMFUNC_SUPPORT
	select ARCH_HAS_NESTED_EXCEPTION_DETECTION
	select SWAP_NONATOMIC
	select ARCH_HAS_EXTRA_EXCEPTION_INFO
	select ARCH_HAS_TIMING_FUNCTIONS if CPU_CORTEX_M_HAS_DWT
	select ARCH_SUPPORTS_ARCH_HW_INIT
	select ARCH_HAS_SUSPEND_TO_RAM
	select ARCH_HAS_CODE_DATA_RELOCATION
	imply XIP
	help
	  This option signifies the use of a CPU of the Cortex-M family.
  • 当代码没有使用MCUBOOT的时候,此时 CONFIG_FLASH_LOAD_OFFSET 和 CONFIG_FLASH_LOAD_SIZE 默认为0。
    • 此时 USE_DT_CODE_PARTITION 默认未使能,使能后会从 chosen 节点下 zephyr,code-partition 属性指向的分区获取两个值的大小。
    • 未使能时可通过Kconfig进行手动配置。
  • 当app中使用了MCUBOOT之后,CONFIG_BOOTLOADER_MCUBOOT=y,此时会强制将 USE_DT_CODE_PARTITION 设置为 y,从设备树获取分区信息。
config BOOTLOADER_MCUBOOT
	bool "MCUboot bootloader support"
	select USE_DT_CODE_PARTITION
	imply INIT_ARCH_HW_AT_BOOT if ARCH_SUPPORTS_ARCH_HW_INIT
	depends on !MCUBOOT
	help
	  This option signifies that the target uses MCUboot as a bootloader,
	  or in other words that the image is to be chain-loaded by MCUboot.
	  This sets several required build system and Device Tree options in
	  order for the image generated to be bootable using the MCUboot open
	  source bootloader. Currently this includes:

	    * Setting ROM_START_OFFSET to a default value that allows space
	      for the MCUboot image header
	    * Activating SW_VECTOR_RELAY_CLIENT on Cortex-M0
	      (or Armv8-M baseline) targets with no built-in vector relocation
	      mechanisms

	  By default, this option instructs Zephyr to initialize the core
	  architecture HW registers during boot, when this is supported by
	  the application. This removes the need by MCUboot to reset
	  the core registers' state itself.
  • 下面是 nucleo_h743zi 中的设备树文件,当使能MCUBOOT之后就会以 0x8040000 作为链接起始地址。
model = "STMicroelectronics STM32H743ZI-NUCLEO board";
	compatible = "st,stm32h743zi-nucleo";

	chosen {
		zephyr,console = &usart3;
		zephyr,shell-uart = &usart3;
		zephyr,sram = &sram0;
		zephyr,flash = &flash0;
		zephyr,dtcm = &dtcm;
		zephyr,code-partition = &slot0_partition;
		zephyr,canbus = &can1;
	};

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

		/* 128KB for bootloader */
		boot_partition: partition@0 {
			label = "mcuboot";
			reg = <0x00000000 DT_SIZE_K(128)>;
			read-only;
		};

		/* storage: 128KB for settings */
		storage_partition: partition@20000 {
			label = "storage";
			reg = <0x00020000 DT_SIZE_K(128)>;
		};

		/* application image slot: 256KB */
		slot0_partition: partition@40000 {
			label = "image-0";
			reg = <0x00040000 DT_SIZE_K(256)>;
		};

		/* backup slot: 256KB */
		slot1_partition: partition@80000 {
			label = "image-1";
			reg = <0x00080000 DT_SIZE_K(256)>;
		};

		/* swap slot: 128KB */
		scratch_partition: partition@c0000 {
			label = "image-scratch";
			reg = <0x000c0000 DT_SIZE_K(128)>;
		};

	};
};
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咕咚.萌西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值