4. 启动流程与初始化

RH850 上电后并不是直接进入 main(),而是经历一条严格受硬件约束的启动链:复位采样、向量取指、启动代码搬运、C 运行时建立、时钟切换、看门狗处理,最后才进入应用。任何一环配置错误,都会表现为“程序不跑”“跑飞”“偶发复位”或“调试器连不上”。理解这条链的先后依赖,是 ECU 集成阶段定位启动类问题的前提。

一、复位向量与启动入口

RH850 复位释放后,内核从固定复位向量地址取指。该地址通常位于 Code Flash 起始区域,向量表中存放的是启动代码入口地址,而不是 main。以 GHS/Multi 工具链为例,复位入口一般由启动汇编文件提供,并通过链接脚本固定在向量区:

/* 链接脚本片段:复位向量定位 */
MEMORY {
  VECTORS (rx) : ORIGIN = 0x00000000, LENGTH = 0x1000
  TEXT    (rx) : ORIGIN = 0x00001000, LENGTH = 0x001F0000
}
SECTIONS {
  .reset_vector : { KEEP(*(.reset_vector)) } > VECTORS
  .text         : { *(.text*) *(.rodata*) } > TEXT
}

复位向量区必须被 KEEP 保护,否则链接器优化可能将其回收,导致复位后取到非法地址。此外,RH850 的部分型号存在多组向量(如异常向量、中断向量),启动阶段至少要保证复位向量与初始异常向量有效。

二、启动代码与 C 运行时初始化

启动代码(Startup Code)承担从“裸机状态”到“可运行 C 环境”的过渡,典型动作顺序如下:

  1. 关闭全局中断,设置初始栈指针 SP,避免异常发生时无栈可用。
  2. 初始化必要系统寄存器,如 PSW、EBASE、中断控制相关基址。
  3. 将 .data 段从 Flash 搬运到 RAM,将 .bss 段清零。
  4. 完成 C 运行时所需环境后,调用 main()。

其中 .data 搬运与 .bss 清零是“变量初值不对”的高频根因。若搬运源地址、目标地址或长度配置错误,全局变量会呈现随机值。下面是一个典型的启动搬运逻辑示意:

/* 启动代码中的段初始化示意 */
extern uint32 _data_load, _data_start, _data_end;
extern uint32 _bss_start, _bss_end;

void startup_copy(void) {
    uint32 *src = &_data_load;
    uint32 *dst = &_data_start;
    while (dst < &_data_end) { *dst++ = *src++; }
    dst = &_bss_start;
    while (dst < &_bss_end) { *dst++ = 0u; }
}

三、栈与堆设置

RH850 通常支持多栈结构,至少包括系统栈与用户栈,部分工程还会为中断、任务分配独立栈。栈设置的核心是“位置、大小、对齐、边界保护”。栈顶一般放在 RAM 高端,向下增长;堆则从 RAM 低端向上增长,两者之间必须保留足够余量,防止相互踩踏。

对象典型位置关键参数常见问题
系统栈 SPRAM 高端大小、8/16 字节对齐中断嵌套溢出
用户栈RAM 次高端与系统栈隔离任务切换异常
堆 HeapRAM 低端起始、结束、增长方向与栈碰撞
栈保护字栈边界魔术字校验溢出无法及时发现
栈溢出在 RH850 上不一定立即触发异常,可能先破坏相邻变量,表现为“某个无关变量被改写”。建议在栈边界放置保护字,并在周期任务中校验。

四、时钟初始化与看门狗配置

时钟初始化决定 CPU、外设与总线的运行频率,必须在访问依赖时钟的外设之前完成。典型顺序是:选择时钟源(内部高速/外部晶振/PLL)→ 配置 PLL 倍频 → 等待锁定 → 切换系统时钟 → 配置外设分频。若在 PLL 未锁定时切换,CPU 可能跑飞。

/* 时钟初始化顺序示意 */
CLK_SRC_SEL = EXT_CRYSTAL;      /* 选择外部晶振 */
PLL_CTRL    = PLL_ENABLE | PLL_MUL_20;
while (!(PLL_STATUS & PLL_LOCKED));
SYS_CLK_SEL = PLL_OUT;          /* 切换系统时钟 */
PERIPH_DIV  = DIV_2;            /* 外设分频 */

看门狗(WDT)在启动阶段尤其敏感:若在启动代码中过早喂狗或未及时喂狗,会导致启动过程中反复复位。正确做法是:启动早期先确认 WDT 的初始状态,必要时在时钟稳定后按窗口要求配置并开启,再进入主循环喂狗。对于安全启动场景,WDT 往往被配置为“超时即复位”,并配合复位原因寄存器区分上电复位、看门狗复位与外部复位。

五、安全启动要点与典型误区

安全启动关注的是“启动链的可信与可诊断”。在 ECU 集成中,至少应做到:复位原因可读、关键段校验可做、看门狗状态可查、启动阶段有最小化日志或标志位。常见误区包括:

  • 复位向量未加 KEEP,被链接器优化后取指异常。
  • .data 搬运长度按字节计算却按字搬运,导致末尾数据错位。
  • 栈顶未对齐,触发 RH850 的对齐异常或性能下降。
  • 时钟切换前就访问外设,读回值恒为 0 或总线挂死。
  • 看门狗在启动阶段被意外关闭,量产后失去复位保护。
调试启动问题时,可先在复位入口点灯或翻转一个 GPIO,再逐步在段初始化、时钟切换、WDT 配置后增加标志,用“启动里程碑”快速定位卡死位置。
RH850 启动链:从复位向量到 main() 复位释放 取复位向量 启动代码 关中断 / 设 SP C 运行时 .data / .bss 进入 main() 应用初始化 栈与堆设置 SP 对齐 / 边界保护 时钟初始化 PLL 锁定 / 分频 看门狗配置 窗口 / 超时复位 安全启动检查点 复位原因寄存器 · 关键段校验 · WDT 状态 · 启动里程碑标志 任一检查失败 → 记录复位原因并进入安全状态 关键依赖:SP 必须在任何 C 代码前有效;时钟必须在访问外设前稳定;WDT 必须在启动阶段可控 调试建议:在复位入口、段初始化后、时钟切换后、main 入口分别设置里程碑标志

把启动链理解为“有向依赖图”而非简单顺序表,能帮助我们在集成阶段快速判断:某个外设读不到、某个变量初值异常、某次复位无法解释,究竟卡在向量、段初始化、栈、时钟还是看门狗环节。启动代码一旦稳定,后续驱动与 ECU 集成才有可靠的地基。