启动与链接脚本
本文档介绍 fw-AC630N_BT_SDK(BD29 平台)的启动与链接脚本体系,包括主链接脚本 sdk.ld 的内存布局、Mask ROM 服务符号绑定机制、启动入口 _start 与代码段组织方式,以及 boot 镜像与预编译库的链接脚本。
Purpose and Scope
本页覆盖以下内容:
- 链接脚本体系:
cpu/bd29/sdk.ld(主链接脚本)、maskrom_stubs.ld/maskrom_stubs_os.ld(Mask ROM 桩)、include_lib/下各预编译库的放置脚本(system_lib_*.ld、btctler_lib_*.ld、btstack_lib.ld、driver_lib.ld、update.ld)。 - 内存布局设计:RAM 分区(ISR 表、ROM RAM、UPDATA 标志区)、Flash 代码区(
code0)起始地址与容量。 - Mask ROM 符号重定向机制:应用直接链接到 ROM 内固化的服务函数地址(中断开关、C 库、NVRAM、SFC、OS/FreeRTOS API 等)。
- 启动入口与段布局:
ENTRY(_start)、startup.o首放、BT 控制器 / BLE LL / LMP 加密代码段的分组。
以下主题属于其他页面,不在本页展开:OTA 升级流程(见 include_lib/update/update.ld 相关页面)、BT 协议栈与控制器实现、OS 调度器(FreeRTOS)内部机制。
Overview
AC630N 是杰理科技(Jieli)面向蓝牙音频/物联网应用的 SoC,其 SDK 采用 Mask ROM + 应用固件 的双层架构:芯片出厂时在 Mask ROM 中固化了一批基础服务函数(C 库、NVRAM、SPI Flash 控制器、RTOS 内核等),应用固件(编译产物)通过链接脚本将这些符号直接绑定到 ROM 中的固定绝对地址,从而大幅减小应用固件体积、并保证 ROM 与 Flash 代码可以无缝互相调用。
cpu/bd29/sdk.ld 是该架构的核心纽带,它同时承担了三项职责:
- 定义存储区域:
code0(Flash 代码区)、ram0(数据区)、irq_table(中断向量表)。 - 导出 ROM 服务符号:把
memcpy、os_task_create、nvram_set_boot_state、chip_reset等符号固定到 Mask ROM 绝对地址。 - 编排代码段:规定
_start入口、startup.o首位、BT 控制器/BLE/LMP 各代码段的存放顺序,并输出段边界符号供运行时(如 OTA、低功耗)使用。
Architecture
flowchart TD
subgraph sg_ROM["Mask ROM (0x100000 区段)"]
ROM_C["C 库 memcpy/strcmp/...<br/>0x103fa8~0x103fcc"]
ROM_IRQ["local_irq_enable/disable<br/>0x103e88/0x103e8c"]
ROM_NV["NVRAM/SFC/CRC<br/>0x103fd4~0x103ff4"]
ROM_OS["OS/FreeRTOS API<br/>0x103eac~0x103fa4"]
ROM_BOOT["boot_arg_list @ 0xbee4<br/>the_debug_isr @ 0x100016"]
end
subgraph sg_LD["链接脚本 (构建期)"]
LD_MAIN["cpu/bd29/sdk.ld"]
LD_STUB["maskrom_stubs.ld<br/>maskrom_stubs_os.ld"]
LD_LIB["include_lib/*.ld<br/>system/btctler/btstack/driver/update"]
end
subgraph sg_APP["应用固件 (Flash code0 @ 0x1E000C0)"]
START["_start (startup.o)"]
TEXT[".text* / .rodata*"]
BT["BTCTLER 控制器段<br/>bt_rf/vendor/hci"]
BLE["BLE LL/HCI 段"]
LMP["LMP + 加密段<br/>auth/bigint/ecdh/hmac"]
DEV["设备表段<br/>gsensor/storage_device"]
end
subgraph sg_BOOT["boot 镜像 (cpu/bd29/tools)"]
UB["uboot.boot / uboot_no_ota.boot<br/>(含 _debug 变体)"]
end
LD_MAIN -->|"ENTRY(_start) + ABSOLUTE 符号"| START
LD_MAIN -->|"段放置"| TEXT
LD_MAIN -->|"段放置"| BT
LD_MAIN -->|"段放置"| BLE
LD_MAIN -->|"段放置"| LMP
LD_MAIN -->|"段放置"| DEV
LD_STUB --> LD_MAIN
LD_LIB --> LD_MAIN
START -->|"调用 (链接至绝对地址)"| ROM_IRQ
TEXT -->|"调用 (链接至绝对地址)"| ROM_C
TEXT -->|"调用 (链接至绝对地址)"| ROM_OS
BT -->|"调用"| ROM_NV
UB -->|"上电加载并跳转"| START
架构说明:sdk.ld 在构建期把应用固件与 Mask ROM 服务函数"缝合"在一起——应用的每次函数调用都编译成对固定 ROM 地址的直接跳转(ABSOLUTE 符号),因此固件中无需携带这些基础代码;运行时由 Mask ROM 中的 bootloader(uboot.boot 镜像,存放于 cpu/bd29/tools/)完成初始化后跳转到应用入口 _start。预编译库(系统库、BT 控制器库、协议栈库、驱动库、升级库)则通过 include_lib/ 下各自的 .ld 片段被精确安放到对应段,保证库内节区与 sdk.ld 中定义的符号/段边界一致。
链接脚本体系
仓库中链接脚本按职责分为四类,共同构成完整的构建期布局方案:
| 类别 | 文件 | 职责 |
|---|---|---|
| 主链接脚本 | cpu/bd29/sdk.ld | 定义 MEMORY 区域、Mask ROM 符号、ENTRY(_start)、全部段布局与边界符号 |
| Mask ROM 桩 | cpu/bd29/maskrom_stubs.ld、cpu/bd29/maskrom_stubs_os.ld | 为 Mask ROM 服务函数提供桩/别名定义(os 变体面向带 OS 的场景) |
| 库放置脚本 | include_lib/system/system_lib_text/data/bss.ld、include_lib/btctrler/btctler_lib_text/data/bss.ld、include_lib/btstack/btstack_lib.ld、include_lib/driver/cpu/bd29/driver_lib.ld、include_lib/update/update.ld | 将预编译库的 text/data/bss 节区放入 sdk.ld 定义的段内 |
| boot 镜像 | cpu/bd29/tools/uboot.boot、uboot_no_ota.boot 及 _debug 变体 | Mask ROM 加载的引导程序二进制,负责初始化并跳转应用 |
这种"一主多从"的设计意图:预编译库与主工程解耦。库以二进制形式发布(如 btctler、btstack),它们各自的节区名由库作者预先约定,sdk.ld 与库放置脚本配合,把这些节区按功能(控制器、协议栈、加密)分组摆放,既便于裁剪(某些段可整段丢弃),也便于运行时按段边界做搬运/校验(如 OTA 时按 BTCTLER_*_CODE_SIZE 计算)。
内存布局
sdk.ld 顶部用一组符号常量定义了整个 RAM 的划分(单位字节):
RAM_LIMIT_L = 0x00000;
RAM_LIMIT_H = 0x0C000;
ISR_SIZE = 0x100;
ISR_BASE = RAM_LIMIT_H - ISR_SIZE;
ROM_RAM_SIZE = 0x400;
ROM_RAM_BEGIN = RAM_LIMIT_H - ROM_RAM_SIZE - ISR_SIZE;
UPDATA_SIZE = 0x80;
UPDATA_BEG = RAM_LIMIT_H - UPDATA_SIZE - ROM_RAM_SIZE - ISR_SIZE;
UPDATA_COPY_BEG = 0x1d000 - UPDATA_SIZE;
RAM_BEGIN = RAM_LIMIT_L;
RAM_END = RAM_LIMIT_H - UPDATA_SIZE - ROM_RAM_SIZE - ISR_SIZE;
RAM_SIZE = RAM_END - RAM_BEGIN;
UPDATA_BREDR_BASE_BEG = 0x1C000;
Source: sdk.ld
RAM 总容量 0xC000(48KB),从高地址向低地址依次保留:ISR 表(0x100)→ ROM RAM(0x400)→ UPDATA 标志区(0x80)→ 应用可用 RAM。ISR 与 ROM RAM 从顶部固定下沉,使得 irq_table 的绝对地址(0xBF00)稳定不变——中断向量表地址固定是嵌入式系统的硬性要求,任何重定位都会导致中断失效。UPDATA 区位于 RAM 最高端,用于记录升级状态/签名,UPDATA_COPY_BEG = 0x1d000 - 0x80 表明其备份位于另一地址,供升级流程在写坏主标志时回退。
MEMORY 区域声明如下:
MEMORY
{
code0(rx) : ORIGIN = 0x1E000C0, LENGTH = (1024 * 1024)
ram0(rwx) : ORIGIN = RAM_BEGIN, LENGTH = RAM_SIZE
irq_table(rw): ORIGIN = 0xbf00, LENGTH = 0x100
}
Source: sdk.ld
code0 是应用代码区:起始地址 0x1E000C0、长度 1MB,位于 SPI Flash 地址空间中(0x1E00000 附近为 Flash 映射区,C0 偏移用于容纳引导头/文件头)。ram0 从 0x00000 开始,长度由上面推导的 RAM_SIZE 决定。irq_table 单独成区且地址固定 0xBF00,与前面 ISR_BASE 的计算完全一致。
flowchart LR
subgraph sg_RAM["RAM 0x00000 ~ 0x0C000 (48KB)"]
R_APP["ram0 应用数据<br/>0x00000 ~ 0xBAFF"]
R_MASK["Mask 参数区<br/>0xBB00 ~ 0xBEDF (_MASK_MEM_BEGIN/_SIZE)"]
R_ARG["boot_arg_list @ 0xBEE4"]
R_IRQ["irq_table 中断向量表<br/>0xBF00 ~ 0xBFFF"]
R_ROM["ROM_RAM 保留<br/>0xC000-0x500 ~ 0xC000-0x100"]
R_UP["UPDATA 升级标志<br/>0xC000-0x80 ~ 0xC000"]
R_APP --- R_MASK --- R_ARG --- R_IRQ --- R_ROM --- R_UP
end
subgraph sg_FLASH["Flash 代码区 code0 (1MB)"]
F_BOOT["引导头/文件头 @ 0x1E00000"]
F_TEXT["_start / startup.o / .text* @ 0x1E000C0"]
F_BT["BT 控制器段 (bt_rf/hci/device_manager)"]
F_BLE["BLE LL/HCI 段"]
F_LMP["LMP 加密段 (ecdh/hmac)"]
end
_MASK_MEM_BEGIN = ABSOLUTE(0xbb00)、_MASK_MEM_SIZE = ABSOLUTE(0x3f0)、boot_arg_list = ABSOLUTE(0xbee4)在 sdk.ld 中定义:0xBB00 起的一段 RAM 是 Mask ROM 与应用共享的参数区,boot_arg_list即引导参数链表首址,位于该区末尾、紧邻中断向量表之前——上电时 ROM 写入引导参数,应用在_start阶段即可读取(如本次启动原因、升级请求等)。
Mask ROM 符号绑定机制
sdk.ld 中段数量最多的部分是绝对地址符号表,其作用是把应用调用的基础函数全部"钉"到 Mask ROM 的固定地址。这样固件里不包含这些函数的实现,链接器直接解析为对 ROM 的跳转。
中断与基础服务(0x103e88 ~ 0x103ff8)
local_irq_enable = ABSOLUTE(0x103e88);
local_irq_disable = ABSOLUTE(0x103e8c);
memmem = ABSOLUTE(0x103fa8);
memcpy = ABSOLUTE(0x103fac);
memmove = ABSOLUTE(0x103fb0);
memcmp = ABSOLUTE(0x103fb4);
memset = ABSOLUTE(0x103fb8);
strcmp = ABSOLUTE(0x103fbc);
strcpy = ABSOLUTE(0x103fc0);
strlen = ABSOLUTE(0x103fc4);
strncmp = ABSOLUTE(0x103fc8);
strstr = ABSOLUTE(0x103fcc);
mask_init = ABSOLUTE(0x103fd0);
wdt_clr = ABSOLUTE(0x103fd4);
nvram_set_boot_state = ABSOLUTE(0x103fd8);
nvram_jumpaddr_set = ABSOLUTE(0x103fdc);
nvram_signature_set = ABSOLUTE(0x103fe0);
sfc_suspend = ABSOLUTE(0x103fe4);
sfc_resume = ABSOLUTE(0x103fe8);
sfc_drop_cache = ABSOLUTE(0x103fec);
chip_crc16 = ABSOLUTE(0x103ff0);
CrcDecode = ABSOLUTE(0x103ff4);
chip_reset = ABSOLUTE(0x103ff8);
Source: sdk.ld
设计意图:这些函数属于"ROM 特权区"——chip_reset、wdt_clr 等涉及芯片级操作,nvram_*、sfc_* 访问外设寄存器,若应用可自由重实现会破坏系统一致性。把它们固定在 ROM 中,还能保证同一 SDK 的所有固件与 ROM 版本严格匹配(地址即 ABI 契约)。
OS / FreeRTOS API(0x103eac ~ 0x103fa4)
os_init = ABSOLUTE(0x103eac);
os_start = ABSOLUTE(0x103eb0);
os_get_curr_tcb_var = ABSOLUTE(0x103eb4);
os_task_create = ABSOLUTE(0x103eb8);
os_time_dly = ABSOLUTE(0x103ec0);
os_time_get = ABSOLUTE(0x103ec4);
os_taskq_pend = ABSOLUTE(0x103ed8);
os_sem_create = ABSOLUTE(0x103f08);
os_mutex_create = ABSOLUTE(0x103f24);
task_queue_post_event = ABSOLUTE(0x103ef0);
Source: sdk.ld
除杰理自研的 os_* 封装外,链接脚本还导出了完整的 FreeRTOS 符号:vTaskSetApplicationTaskTag、xQueueGenericCreateStatic/Send/Receive、uxQueueMessagesWaiting、pcTaskGetName、xPortStartScheduler、vPortYield、vTickISR、OS_ClrPending 等(sdk.ld)。这说明 RTOS 内核整体驻留于 Mask ROM,应用侧(含 BT 协议栈)只是内核的使用者——这与 maskrom_stubs_os.ld 的命名呼应:该桩文件为 OS 相关调用提供类型/包装定义,保证应用链接时不与 ROM 符号冲突。
中断与调试辅助符号
_IRQ_MEM_ADDR = ABSOLUTE(0xbf00);
_MASK_MEM_BEGIN = ABSOLUTE(0xbb00);
_MASK_MEM_SIZE = ABSOLUTE(0x3f0);
boot_arg_list = ABSOLUTE(0xbee4);
the_debug_isr = ABSOLUTE(0x100016);
Source: sdk.ld
_IRQ_MEM_ADDR 与 irq_table 区起始一致;the_debug_isr 指向 ROM 内调试中断服务程序(0x100016 位于 ROM 代码区),用于异常时进入 ROM 的调试/打印路径。
启动入口与段布局
链接脚本明确指定程序入口与代码段的摆放顺序:
ENTRY(_start)
SECTIONS
{
. = ORIGIN(code0);
.text ALIGN(4):
{
PROVIDE(text_rodata_begin = .);
*startup.o(.text)
*(.text*)
*(.rodata*)
*(.LOG_TAG_CONST*)
. = ALIGN(4);
gsensor_dev_begin = .;
KEEP(*(.gsensor_dev))
gsensor_dev_end = .;
. = ALIGN(4);
OMSensor_dev_begin = .;
KEEP(*(.omsensor_dev))
OMSensor_dev_end = .;
. = ALIGN(4);
storage_device_begin = .;
KEEP(*(.storage_device))
storage_device_end = .;
}
}
Source: sdk.ld
关键点:
ENTRY(_start)+*startup.o(.text):startup.o是汇编启动文件(仓库中未提供其源码,属于编译期生成/库内对象),其_start符号即复位入口;把它放在.text首位,保证复位向量/第一条指令必然落在code0起始地址 0x1E000C0。上电后 Mask ROM 跳转到该地址,即进入_start。KEEP()设备表段:gsensor_dev、omsensor_dev、storage_device是由编译期构造的"设备描述符表"(链接期用__attribute__((section))收集各驱动实例)。KEEP防止链接器垃圾回收丢弃,gsensor_dev_begin/end等边界符号供运行时遍历注册表。这种"链接期聚合"模式避免了动态注册的初始化顺序问题。PROVIDE(text_rodata_begin = .):导出代码区起始边界,供 CRC 校验、OTA 搬运等模块计算固件范围。
BT 控制器与协议栈段分组
sdk.ld 继续按功能把控制器代码分组,并输出每组边界与大小符号:
btctler_code_start = .;
BTCTLER_CONTROLLER_CODE_START = .;
*(.bt_rf_const)
*(.bt_rf_code)
*(.vendor_manager_const)
*(.vendor_manager_code)
*(.device_manager_const)
*(.device_manager_code)
*(.hci_controller_const)
*(.hci_controller_code)
*(.hci_interface_const)
*(.hci_interface_code)
BTCTLER_CONTROLLER_CODE_SIZE = ABSOLUTE(. - BTCTLER_CONTROLLER_CODE_START);
BTCTLER_LE_CONTROLLER_CODE_START = .;
*(.ble_rf_const)
*(.ble_rf_code)
*(.ble_ll_const)
*(.ble_ll_code)
*(.ble_hci_const)
*(.ble_hci_code)
*(.classic_hci_const)
*(.classic_hci_code)
BTCTLER_LE_CONTROLLER_CODE_SIZE = ABSOLUTE(. - BTCTLER_LE_CONTROLLER_CODE_START);
BTCTLER_CL_CODE_START = .;
*(.bredr_irq)
*(.bredr_irq_code)
*(.bredr_irq_const)
*(.classic_lmp_const)
*(.classic_lmp_linkbulk_const)
*(.classic_lmp_code)
*(.classic_lmp_linkbulk_code)
LMP_ENC_CODE_START = .;
*(.classic_lmp_auth_const)
*(.classic_lmp_bigint_const)
*(.classic_lmp_crypt_const)
*(.classic_lmp_ecdh_const)
*(.classic_lmp_hmac_const)
*(.classic_lmp_auth_code)
*(.classic_lmp_bigint_code)
*(.classic_lmp_crypt_code)
*(.classic_lmp_ecdh_code)
*(.classic_lmp_hmac_code)
LMP_ENC_CODE_SIZE = ABSOLUTE(. - LMP_ENC_CODE_START);
Source: sdk.ld
设计意图:蓝牙控制器代码(bt_rf、vendor_manager、device_manager、hci_controller)来自预编译的 btctler 库,节区名即"放置协议"。LE 控制器(ble_ll、ble_hci、classic_hci)紧随其后;经典蓝牙 LMP 与加密相关段(auth/bigint/crypt/ecdh/hmac)被单独围出边界——LMP_ENC_CODE_SIZE、BTCTLER_*_CODE_SIZE 这类尺寸符号是 OTA 差分升级与安全校验(按加密段单独处理)的输入。若某功能被裁剪,对应 *(.xxx) 输入为空,段边界符号自动归零,无需改动脚本。
启动流程
BD29 平台的完整启动链路:Mask ROM 上电引导 → bootloader 镜像(uboot.boot)→ 应用入口 _start → OS 初始化 → 业务任务。链接脚本在其中扮演"地址契约"的角色,每一跳转地址都在构建期被固定。
sequenceDiagram
participant PWR as 上电复位
participant ROM as Mask ROM
participant UB as uboot.boot<br/>(cpu/bd29/tools)
participant APP as 应用固件 code0<br/>(0x1E000C0)
participant ROMSVC as ROM 服务函数<br/>(0x103e88~0x103ff8)
PWR->>ROM: 复位向量,初始化最小硬件
ROM->>ROM: 读取 Flash 引导头,定位 uboot 镜像
ROM->>UB: 加载 uboot.boot 到 RAM 并跳转
UB->>UB: 初始化时钟/SFC/看门狗
UB->>ROM: 调用 wdt_clr / sfc_resume (0x103fd4/0x103fe8)
UB->>ROM: nvram_set_boot_state / nvram_jumpaddr_set 记录引导状态
UB->>ROM: 写 boot_arg_list @ 0xbee4(启动原因/升级请求)
UB->>APP: 跳转 0x1E000C0 (_start)
APP->>APP: startup.o 初始化 C 运行环境
APP->>ROM: 调用 local_irq_disable / memset (0x103e8c/0x103fb8)
APP->>APP: 遍历设备表段 (gsensor/storage_device)
APP->>ROM: os_init (0x103eac) / os_start (0x103eb0)
APP->>ROM: 创建业务任务 (os_task_create 0x103eb8)
APP->>ROM: xPortStartScheduler (0x103f90),进入调度
ROMSvc-->>APP: 返回服务结果
流程要点:
- Mask ROM 是唯一的"上帝之手":所有跳转目标(uboot 地址、应用入口、服务函数地址)都是 ROM 或链接脚本固定的绝对地址,应用本身不参与引导决策,因此即使应用固件损坏,ROM 仍能进入升级流程。
uboot_no_ota.boot与uboot.boot:两者对应不同升级能力(no_ota变体不带 OTA 引导逻辑),_debug后缀变体保留调试接口。它们都是二进制镜像,源码未包含在本仓库可见范围内,其行为由 Mask ROM 约定。_start即链接契约:ENTRY(_start)保证 ROM 跳转地址(0x1E000C0)处就是 startup 代码,中间无任何偏移;任何对startup.o顺序的改动都会破坏启动。
配置选项
sdk.ld 顶部的符号常量即"配置面",改动即改变布局(需与 ROM 版本匹配,否则地址失效):
| 符号 | 值(十六进制) | 说明 |
|---|---|---|
RAM_LIMIT_L / RAM_LIMIT_H | 0x00000 / 0x0C000 | RAM 地址上下限(48KB) |
ISR_SIZE / ISR_BASE | 0x100 / 0x0BF00 | 中断向量表大小与基址 |
ROM_RAM_SIZE / ROM_RAM_BEGIN | 0x400 | ROM 与 RAM 交互保留区 |
UPDATA_SIZE / UPDATA_BEG | 0x80 | 升级标志区(RAM 最高端) |
UPDATA_COPY_BEG | 0x1d000 - 0x80 | 升级标志备份地址(Flash 侧) |
UPDATA_BREDR_BASE_BEG | 0x1C000 | 经典蓝牙升级数据基址 |
code0 ORIGIN / LENGTH | 0x1E000C0 / 1MB | 应用代码区(Flash 映射) |
irq_table ORIGIN / LENGTH | 0x0BF00 / 0x100 | 中断向量表区域 |
_MASK_MEM_BEGIN / _MASK_MEM_SIZE | 0x0BB00 / 0x3F0 | ROM 与应用共享参数区 |
boot_arg_list | 0x0BEE4 | 引导参数链表地址 |
符号 API 参考(Mask ROM 服务表)
应用与 ROM 的交互面全部由链接脚本导出,按功能分组列举(地址见上文代码块):
中断控制
local_irq_enable/local_irq_disable:开/关全局中断,临界区原语的基础。
C 运行时库(memmem、memcpy、memmove、memcmp、memset、strcmp、strcpy、strlen、strncmp、strstr):签名与标准 C 库一致;应用不链接任何 libc,全部走 ROM 实现,保证 Flash 代码极简。
芯片/外设服务
mask_init:Mask ROM 侧初始化入口。wdt_clr:喂狗,防止引导阶段看门狗复位。nvram_set_boot_state/nvram_jumpaddr_set/nvram_signature_set:写入启动状态、跳转地址、固件签名,供升级与安全校验。sfc_suspend/sfc_resume/sfc_drop_cache:SPI Flash 控制器挂起/恢复/缓存失效——低功耗与升级时防止 Flash 访问冲突。chip_crc16/CrcDecode:CRC16 校验与解码(固件完整性)。chip_reset:芯片软复位。
OS / FreeRTOS 内核(os_init、os_start、os_task_create、os_task_del、os_time_dly、os_time_get、os_taskq_pend/post/accept/flush/del、task_queue_post_event、os_sem_*、os_mutex_*、vTaskSetApplicationTaskTag、xQueueGeneric*、xPortStartScheduler、vPortYield、vTickISR 等):RTOS 驻留 ROM;应用通过 os_* 封装或直接调用 FreeRTOS API 使用。若某符号缺失(未定义),链接即失败——这是 ROM 版本不匹配时最早暴露的信号。
故障模式、边界情况与并发
- ROM 版本不匹配(最典型的链接期故障):Mask ROM 服务地址是
ABSOLUTE常量,若 SDK 与芯片 ROM 版本不一致,链接产物会在运行期跳转到错误地址(表现:复位、跑飞、wdt超时)。由于是直接绝对地址跳转,链接器无法发现"逻辑"错误,只能靠 ROM 版本号(通常经boot_arg_list或nvram_signature_set校验)在运行时拦截。升级 SDK 时必须同步确认 ROM 版本。 - 段溢出:
ram0长度由RAM_LIMIT_H减去保留区得出,code0固定 1MB。若应用/BT 功能膨胀,链接器报"region overflow",此时需裁剪功能(对应输入节区为空时边界符号自动归零,裁剪是安全的)而非修改地址——修改地址会破坏与 ROM 的地址契约。 irq_table被覆盖:中断向量表在 0xBF00~0xBFFF,紧邻boot_arg_list(0xBEE4)与 Mask 参数区。若 startup 前有代码向 RAM 顶部写数据(例如错误地把UPDATA区当普通变量用),会静默破坏中断表,表现为"偶发死机/中断异常"。这也是保留区被链接脚本"隐藏"(不暴露给ram0)的原因。- 并发/重入:
sfc_suspend/sfc_resume对 Flash 的访问在低功耗与 OTA 场景存在竞争——链接脚本不提供互斥,依赖local_irq_disable/OS 临界区在调用侧串行化;wdt_clr必须周期性调用,任何任务饿死都会触发看门狗复位。 - 升级中断恢复:
UPDATA区(RAM 最高端)与UPDATA_COPY_BEG(Flash 备份)是"双槽"设计;若升级中途断电,ROM 通过nvram_set_boot_state记录的状态决定回退到备份,链接脚本保证该区域地址永远稳定可用。
性能与运维注意事项
- 调用开销:ROM 服务函数经绝对地址直接跳转,无 PLT/重定位开销,与本地调用几乎等价——这是把 C 库/内核放 ROM 的性能红利。
- Flash 代码密度:由于 libc 与内核不在固件内,
code0的 1MB 几乎全部让给业务与蓝牙栈;LMP_ENC_CODE_SIZE等尺寸符号可被构建脚本读取用于生成固件体积报告。 - 调试变体:
uboot.boot_debug/uboot_no_ota.boot_debug保留了调试通道,量产固件应使用非 debug 镜像以减小攻击面与启动时间。 - 布局稳定性:
irq_table、UPDATA、Mask 参数区的绝对地址是跨构建稳定的(由 ROM 契约固定),因此 OTA 升级包可以只携带"段差"数据;任何对这些常量的本地改动都会破坏 OTA 兼容性。
扩展点
- 新增驱动/传感器注册:在
.gsensor_dev、.omsensor_dev、.storage_device节区中追加条目(通过__attribute__((section(...)))定义描述符),KEEP保证其被保留,*_begin/_end符号提供遍历接口——无需修改sdk.ld。 - BT 功能裁剪:删除
*(.classic_lmp_*)等输入节区中的库对象即可整段剔除,BTCTLER_*_CODE_SIZE自动收缩,OTA/校验逻辑无需改动。 - 升级策略选择:引导镜像在
cpu/bd29/tools/下按uboot.boot(带 OTA)与uboot_no_ota.boot(无 OTA)区分,替换引导镜像即可切换升级能力。 - 库放置脚本复用:新增预编译库时,仿照
include_lib/btctrler/btctler_lib_text.ld等文件编写 text/data/bss 放置片段,并在sdk.ld对应分组中登记其节区名。
Related Links
- sdk.ld(主链接脚本)
- maskrom_stubs.ld / maskrom_stubs_os.ld(Mask ROM 桩)
- include_lib/update/update.ld(升级库放置脚本)
- include_lib/system/system_lib_text.ld(系统库放置脚本)
- include_lib/btctrler/btctler_lib_text.ld(BT 控制器库放置脚本)
- cpu/bd29/tools/uboot.boot(引导镜像)
说明:boot 镜像(
.boot)为二进制文件,引导程序源码未包含在本仓库;启动细节(uboot 内部初始化序列)如需深入,请结合芯片 Mask ROM 手册与 OTA/升级相关页面。