Zephyr 下 esp32c3 引导方式变化和问题处理

ESP32C3 在 Zephyr 3.6 Release 后对引导过程进行了一次修改 f9008b ,移除 ESP-bootlader 不使用"Second stage bootloader",直接使用 ROM 中的"First stage bootloader"对 Zephyr App 进行引导。对于 Mcuboot 的引导方式未发生变化,因此现在 Zephyr 对 esp32c3 的引导方式为下面两种

  • 无 mcuboot: ROM-> Zephyr.bin(App )
  • 有 mcuboot: ROM-> MCUboot -> Zephyr.bin(App )

有 mcuboot 的流程没有变化,在zephyr 下 esp32c3 构建引导之 mcuboot一文中已经说明,本文不做赘述。 无 mcuboot 变为直接被 ROM 引导,本文做分析说明。

无 mcuboot 流程 链接到标题

构建 链接到标题

使用 west build -b esp32c3_zgp zephyr_sample/ 进行无 mcuboot 的构建,当不使用 mcuboot 时,构建 esp32c3 的 zephyr image 会默认选择这种方式,Zephyr 构建会采用下面几步:

  1. 构建 zephyr app,生成 elf
  2. 将 zephyr.elf 按照 ROM 引导的格式加头,分出 segment 转化出 zephyr.bin

以上第 1 步为正常的 zephyr 构建,第 2 步由 zephyr/soc/espressif/esp32c3/CMakeLists.txt 进行描述处理

# 这里 common/loader.c 将会调用 modules/hal/espressif/components/bootloader_support/下的一些 api 做 esp32c3 比较底层的初始化
zephyr_sources (
  vectors.S
  soc_irq.S
  soc_irq.c
  soc.c
  ../common/loader.c
 )

if(NOT CONFIG_BOOTLOADER_MCUBOOT )

  if(CONFIG_BUILD_OUTPUT_BIN )
    # make ESP ROM loader compatible image
    message("ESP-IDF path: ${ESP_IDF_PATH}" )

    set(ESPTOOL_PY ${ESP_IDF_PATH}/tools/esptool_py/esptool.py )
    message("esptool path: ${ESPTOOL_PY}" )

    set(ELF2IMAGE_ARG "" )
    if(NOT CONFIG_MCUBOOT )
      set(ELF2IMAGE_ARG "--ram-only-header" )
    endif()

    # 通过 esptool.py 将 zephyr.elf 转换为 ROM 可以引导的格式
    set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
      COMMAND ${PYTHON_EXECUTABLE}${ESPTOOL_PY}
      ARGS --chip esp32c3 elf2image ${ELF2IMAGE_ARG}
      --flash_mode dio --flash_freq 40m --flash_size ${esptoolpy_flashsize}MB
      -o ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.bin
      ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.elf )
  endif()

endif()

# 使用默认的链接脚本
set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/default.ld CACHE INTERNAL "" )

生成出来的 zephyr.bin 可以用 esptool.py 分析其构成,其入口地址还是 __start

python3 modules/hal/espressif/tools/esptool_py/esptool.py --chip esp32c3 image_info build/zephyr/zephyr.bin
esptool.py v4.7.0
File size: 133056(bytes )
Image version: 1
Entry point: 403814d2
4 segments

Segment 1: len 0x00b4c load 0x3fc87940 file_offs 0x00000018 [DRAM]
Segment 2: len 0x00a4c load 0x3fc88490 file_offs 0x00000b6c [DRAM,BYTE_ACCESSIBLE]
Segment 3: len 0x06a54 load 0x40380000 file_offs 0x000015c0 [IRAM]
Segment 4: len 0x00edc load 0x40386a54 file_offs 0x0000801c [IRAM]
Checksum: 6f(valid )
Validation Hash: 71538ba0ceab253b0ccffcf8a2f28fffa910f66a30a616239c8c22693a7355d5(invalid )

烧写 链接到标题

新的引导方式下只有一个 zephyr.bin, west flash 时会将该文件写到 flash 0 地址处

/home/frank/work/zpro/zephyrproject/.venv/bin/python3 /home/frank/work/zpro/zephyrproject/modules/hal/espressif/tools/esptool_py/esptool.py --chip auto --baud 921600 --before default_reset --after hard_reset write_flash -u --flash_mode dio --flash_freq 40m --flash_size detect 0x0 /home/frank/work/zpro/zephyrproject/build/zephyr/zephyr.bin

启动流程 链接到标题

ROM 启动后从 flash 的 0 地址处读出 header, 根据 header 对 Zephyr app 进行加载,将 iram 段的内容搬运到芯片内部的 iram,然后跳到 iram 中运行:

  • 首先跳到 zephyr/soc/espressif/common/loader.c __start
  • __start 中执行 modules/hal/espressif/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c bootloader_init 进行一些地层硬件的初始化
  • __start 中执行 zephyr/soc/espressif/common/loader.c map_rom_segments 进行 flash 的 mmu map,从这里开始 esp32c3 就可以通过虚拟地址直接访问 flash,执行 flash 中的代码
  • __start 中执行 zephyr/soc/espressif/esp32c3/soc.c __esp_platform_start->z_cstart 走到 zephyr 的 c 环境中,之后就是一般的 zephyr 启动流程

问题处理 链接到标题

当代码更新到直接引导 Zephyr App 后,在一片非官方的 esp32c3 板子下烧写后遇到无法启动的问题

ESP-ROM: esp32c3-api1-20210207 Build: Feb 7 2021 rst:0x1(POWERON),boot:0xc(SPI_FAST_FLASH_BOOT ) SPIWP:0xee mode: DIO, clock div:2 load:0x3fc8a5b0,len:0xe50 load:0x3fc8b400,len:0xb9c load:0x40380000,len:0x96c4 load:0x403896c4,len:0xedc SHA-256 comparison failed: Calculated: 9fcc4d0e045225bc2b1fe1bac1a9aab696ac525f028aeaabd8ad4a1e39aa3633 Expected: 0000000020400000000000000000000000000000000000000000000000000000 Attempting to boot anyway… entry 0x403815c8 I(71)boot: ESP Simple boot I(71)boot: compile time May 23 2024 21:41:43 I(71)boot: Multicore bootloader I(71)spi_flash: detected chip: mxic I(73)spi_flash: flash io: dio W(76)spi_flash: Detected size(4096k)larger than the size in the binary image header(2048k). Using the size in the binary image header. I bootloader_flash: XM25QHxxC startup flow E bootloader_flash: XMC flash startup fail E(95)boot.esp32c3: failed when running XMC startup flow, reboot! [esp32c3] [ERR] HW init failed, aborting Guru Meditation Error: Core 0 panic’ed(Illegal instruction )

从 log 可以看得出来是 XMC flash startup fail,原因是这片板子上使用了 mxic 的 flash,但 esp32c3 仍然在走 modules/hal/espressif/components/bootloader_support/bootloader_flash/src/bootloader_flash.c bootloader_flash_xmc_startup->is_xmc_chip_strict, 该流程在 CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 配置时才会走, 因此在 prj.conf 中配置 CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=n,但编译完后依然无法启动。

通过查看 modules/hal/espressif/zephyr/esp32c3/include/sdkconfig.h 发现将 CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 写死为 1,优先级高于 zephyr kconfig 产生的

# ifdef CONFIG_MCUBOOT

# define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1
# define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1
# define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000
# define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000
# define CONFIG_EFUSE_MAX_BLK_LEN 256

# endif /* CONFIG_MCUBOOT*/

# ifdef CONFIG_ESP_SIMPLE_BOOT
# define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1
# endif /* !CONFIG_BOOTLOADER_MCUBOOT */

删除 sdkconfig.h#define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1,通过 prj.conf 中 CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=n 来控制,重新编译后可正常引导运行

ESP-ROM: esp32c3-api1-20210207
Build: Feb  7 2021
rst:0x1(POWERON),boot:0xc(SPI_FAST_FLASH_BOOT )
SPIWP:0xee
mode: DIO, clock div:2
load:0x3fc87930,len:0xb4c
load:0x3fc88480,len:0xa4c
load:0x40380000,len:0x6a44
load:0x40386a44,len:0xedc
SHA-256 comparison failed:
Calculated: eda76abff957bf3d83774c79b7f852456a71fc44c54dae6c17351fd61df6cb82
Expected: 00000000f0700000000000000000000000000000000000000000000000000000
Attempting to boot anyway...
entry 0x403814d2
I(62)boot: ESP Simple boot
I(62)boot: compile time May 24 2024 121:24:40
I(62)boot: Multicore bootloader
I(62)spi_flash: detected chip: mxic
I(64)spi_flash: flash io: dio
W(67)spi_flash: Detected size(4096k)larger than the size in the binary image header(2048k). Using the size in the binary image header.
I(79)boot: chip revision: v0.3
I(82)boot.esp32c3: SPI Speed : 40MHz
I(86)boot.esp32c3: SPI Mode : SLOW READ
I(90)boot.esp32c3: SPI Flash Size : 4MB
I(94)boot: Enabling RNG early entropy source...
[esp32c3] [INF] DRAM: lma 0x00000020 vma 0x3fc87930 len 0xb4c(2892 )
[esp32c3] [INF] DRAM: lma 0x00000b74 vma 0x3fc88480 len 0xa4c(2636 )
[esp32c3] [INF] IRAM: lma 0x000015c8 vma 0x40380000 len 0x6a44(27204 )
[esp32c3] [INF] IRAM: lma 0x00008014 vma 0x40386a44 len 0xedc(3804 )
[esp32c3] [INF] padd: lma 0x00008f08 vma 0x00000000 len 0x70f0(28912 )
[esp32c3] [INF] IMAP: lma 0x00010000 vma 0x42010000 len 0x3078(12408 )
[esp32c3] [INF] padd: lma 0x00013080 vma 0x00000000 len 0xcf78(53112 )
[esp32c3] [INF] DMAP: lma 0x00020000 vma 0x3c000000 len 0x798(1944 )
[esp32c3] [INF] Image with 8 segments
[esp32c3] [INF] DROM segment: paddr=00020000h, vaddr=3c000000h, size=007A0h(1952)map
[esp32c3] [INF] IROM segment: paddr=00010000h, vaddr=42010000h, size=03076h(12406)map

*** Booting Zephyr OS build v3.6.0-4510-g531c457550ca ***
Hello World! esp32c3_devkitm/esp32c3

参考 链接到标题

zephyr 下 esp32c3 构建引导之 esp-bootloader zephyr 下 esp32c3 构建引导之 mcuboot