由于项目需要,前几天在调整 NXP RW612 MCU wifi SDK 的构建系统,由原来的 cmake+mingw+make+armgcc 调整为 cmake+kcongfig+ninja+mingw+armcc。构建系统调整完成之后,发现烧录固件设备无法启动,而未调整之前编译出来的固件是正常的。

查找原因

对比二者编译生成的固件镜像文件,旧构建系统生成的固件比新构建系统生成的固件文件大小大一点。首先怀疑的是否是一些宏定义没启用,部分代码被屏蔽了而未编译进去,经过一些测试和检查之后发现不是这个问题。接下来从map文件着手,对比发现map文件有差异。

旧构建系统的map信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.flash_config   0x08000000     0x1000
                0x08000000                . = ALIGN (0x4)
 FILL mask 0xff
                0x00000400                . = 0x400
 *fill*         0x08000000      0x400 ff
                0x08000400                __FLASH_BASE = .
 *(.flash_conf)
 .flash_conf    0x08000400      0xc00 out/libs/libmcux_sdk_module_lib.a(flash_config.c.obj)
                0x08000400                flexspi_config
                0x00001000                . = 0x1000

.interrupts     0x00001000      0x280 load address 0x08001000
                0x00001000                . = ALIGN (0x4)
                0x00001000                __VECTOR_TABLE = .
                0x00001000                __Vectors = .

新构建系统的map信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.flash_config   0x08000000     0x1000
                0x08000000                . = ALIGN (0x4)
 FILL mask 0xff
                0x00000400                . = 0x400
 *fill*         0x08000000      0x400 ff
                0x08000400                __FLASH_BASE = .
 *(.flash_conf)
                0x00001000                . = 0x1000
 *fill*         0x08000400      0xc00 ff

.interrupts     0x00001000      0x280 load address 0x08001000
                0x00001000                . = ALIGN (0x4)
                0x00001000                __VECTOR_TABLE = .
                0x00001000                __Vectors = .

旧的构建系统有将 flash_config.c.obj 的代码 flexspi_config 存储在 .flash_config 这个 section,而新的固件却没有,整个map文件都查不到 flexspi_config 的信息。根据 flash_config.c 的代码在这篇文章痞子衡嵌入式:深扒i.MXRTxxx系列ROM中集成的串行NOR Flash启动SW Reset功能及其应用场合中找到 flexspi_config 的作用:flexspi_config 是固件镜像的启动头 FDCB,bootrom 用来给 FlexSPI 外设初始化 flash 用的。对比两个镜像文件也能看到差异,一个有 FDCB,一个没有。

那么 FDCB 是怎么生成的呢?

查看 RW612 的 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
26
27
28
29
30
31
32
33

/* Entry Point */
ENTRY(Reset_Handler)

HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x400;

/* Specify the memory areas */
MEMORY
{
  m_flash               (RX)  : ORIGIN = 0x08000000, LENGTH = 0x00200000
  m_interrupts          (RX)  : ORIGIN = 0x00001000, LENGTH = 0x00000280
  m_text                (RX)  : ORIGIN = 0x00001280, LENGTH = 0x000B9080
}
/* 0x2012_3000 - 0x2012_FFFF is reserved for BootROM usage */

/* Define output sections */
SECTIONS
{
  .flash_config :
  {
    . = ALIGN(4);
    FILL(0xFF)
    . = 0x400;
    __FLASH_BASE = .;
    KEEP(* (.flash_conf))     /* flash config section */
    . = 0x1000;
  } > m_flash

  /* The startup code goes first into internal ram */

  /*....*/
}

在flash的起始地址定义了一个 4K大小的 .flash_config section,前面填充 1K 0xFF, 后面开始存储 flash_conf 的代码。 在 boards\rdrw612bga\flash_config\flash_config.c 也指定了将代码放到 flash_conf section: attribute((section(".flash_conf"), used))

 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
#if defined(BOOT_HEADER_ENABLE) && (BOOT_HEADER_ENABLE == 1)

#if defined(__ARMCC_VERSION) || defined(__GNUC__)

__attribute__((section(".flash_conf"), used))

#elif defined(__ICCARM__)

#pragma location = ".flash_conf"

#endif

const fc_flexspi_nor_config_t flexspi_config = {
    .memConfig =
        {
            .tag                 = FC_BLOCK_TAG,
            .version             = FC_BLOCK_VERSION,
            .readSampleClkSrc    = 1,
            .csHoldTime          = 3,
            .lookupTable =
                {
                    /* Read */
                    [0] = FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0xEC, FC_RADDR_SDR, FC_FLEXSPI_4PAD, 0x20),
                    [1] = FC_FLEXSPI_LUT_SEQ(FC_DUMMY_SDR, FC_FLEXSPI_4PAD, 0x0A, FC_READ_SDR, FC_FLEXSPI_4PAD, 0x04),
                    /* chip erase */
                    [4 * 11 + 0] =
                        FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x60, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
                },
        },
    .pageSize           = 0x100,
    .sectorSize         = 0x1000,
    .ipcmdSerialClkFreq = 0,
    .blockSize          = 0x8000,
    .fcb_fill[0]        = 0xFFFFFFFF,
};

#endif /* BOOT_HEADER_ENABLE */

那么为什么 FDCB 在新的构建系统下没有生成呢?

我发现二者的差异在于旧构建系统是将flash_config.c放到了构建可执行文件目标的源码里面 add_executable。而新的构建系统是将 flash_config.c 放到构建静态库的源码里面 add_library,然后在构建可执行目标文件的时候通过添加依赖的lib target_link_libraries 加入,在最终链接的时候由于 flash_config.c 的代码未使用而被删除了,即使配置了 attribute((section(".flash_conf"), used)) 也会删除,这个应该是 cmake 的问题。

解决办法有两个:

  1. 将 flash_config.c 放到构建可执行目标的源码里面。
  2. 找个地方调用一下 flash_config.c 里的变量或者函数,避免未使用而被优化。

使用 cmake 构建系统时需要留意这个坑,如果需要自定义 section 指定变量或者函数的存储位置,要留意代码有没有被使用和是否是在构建可执行的目标的源码中,不然即便设置了 attribute((section(".xx"), used)) 也还是会被优化删除。

资料