十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FPGA SoC 的 RISC-V 固件开发全攻略(七):Bootloader 设计详解

FPGA SoC 的 RISC-V 固件开发全攻略(七):Bootloader 设计详解 FPGA SoC 的 RISC-V 固件开发全攻略七Bootloader 设计详解本文是《FPGA SoC 的 RISC-V 固件开发全攻略》专栏第 7 篇。上一篇第 6 篇《Flash 参数持久化双备份循环磨损》 下一篇第 8 篇《Ymodem 协议详解》本文介绍嵌入式系统中 Bootloader 的通用设计方案与关键模块实现思路涵盖启动状态机、Flash 分区管理、双区容错、环境参数持久化、串口升级通道与 APP 跳转。不涉及任何具体芯片或产品的私有实现细节。一、Bootloader 的职责与设计目标Bootloader 是上电后最先运行的固件负责把系统从复位安全地引导到应用正常运行。一个健壮的 Bootloader 通常要满足三个设计目标可恢复应用固件损坏或升级失败时系统不能变砖必须能回到可恢复状态。可升级支持现场固件升级且升级过程中断电不会导致系统无法启动。可容错应用启动失败如崩溃、看门狗复位时能自动回退到备用固件。围绕这三个目标Bootloader 的设计可以拆解为几个核心模块下面逐一展开。二、整体启动流程状态机Bootloader 的主流程通常是一个顺序状态机每一步职责单一、清晰。典型流程如下上电复位 │ ├─ Step 1: 最小系统初始化 │ 关中断 → 初始化调试串口 → 初始化 Flash 驱动 │ ├─ Step 2: 读取环境标志 │ 获取默认启动分区、升级待执行标志、各分区启动失败计数 │ ├─ Step 3: 判断是否进入强制升级模式 │ 入口①: 串口初始化后的窗口期内收到升级命令帧 │ 入口②: 两个应用分区连续启动失败均超阈值 │ ├─ Step 4: 有待执行升级则校验并刷写升级包 │ ├─ Step 5: 启动分区决策当前分区失败则切换备用分区 │ ├─ Step 6: 强制升级模式等待并接收升级包 │ └─ Step 7: 跳转 APP清理上下文 → 跳转应用入口这种顺序步骤 返回值的结构比层层嵌套的if-else更易读、易维护也便于后续增加或合并步骤。三、重要模块一Flash 分区管理Bootloader 的所有数据都落在同一颗 SPI NOR Flash 上通过分区来隔离不同用途的数据。一个典型的通用分区布局如下分区用途Bootloader 区存放 Bootloader 本体只读通常不做自升级APP1 区应用固件主分区APP2 区应用固件备份分区A/B 双区升级包暂存区先接收升级包校验通过后再刷写避免边收边写损坏目标分区参数区掉电不丢失的用户参数Flash EEPROM 模拟环境标志区Bootloader 自身的状态标志启动分区、失败计数等关键设计点升级包先暂存、后刷写升级数据先完整写入暂存区并校验确认无误后再擦写目标分区。这样即使传输中途断电也只是暂存区数据不完整目标分区旧固件不受影响下次上电仍能正常启动。Bootloader 只读、不自升级Bootloader 越简单越稳定把它设计成只读、不做自升级可显著降低变砖风险。四、重要模块二环境参数持久化双备份 循环磨损Bootloader 需要把当前启动哪个分区“各分区失败几次”是否有待执行升级等信息掉电保存。这些数据存放在 Flash 的环境标志区。4.1 双备份防掉电损坏Flash 擦写中途断电会把当前扇区写成垃圾数据。若只存一份环境标志一旦损坏Bootloader 就无法判断启动分区系统变砖。因此采用A/B 双备份同一份数据在相邻两个区域各存一份写的时候两份都写或交替写读的时候取有效的那份。环境标志区 ┌───────────────┐ A 备份 ├───────────────┤ │ ... │ 预留 ├───────────────┤ └───────────────┘ B 备份读取仲裁分别读 A/B 两份校验通过且写入序号seq较大的那份视为最新有效数据若只有一份有效则用该份两份都坏则按首次启动处理直接进入强制升级模式。4.2 循环磨损均衡Flash 有擦写寿命限制若每次都擦写同一个扇区该扇区会提前磨穿。解决方法是把每份备份再拆成多个等大小的 slot循环轮流写入让擦写次数均匀分摊到所有 slot 上成倍延长寿命。写入序号seq的作用每次写入时 seq 单调递增读取时通过比较 seq 判断哪份更新。seq 的仲裁 双备份 多 slot 轮转共同构成了一个可靠的掉电安全持久化方案。这是通用的Flash EEPROM 模拟思想广泛应用于各类嵌入式设备的参数存储。五、重要模块三双区容错A/B 分区 启动成功确认双区容错是 Bootloader 可靠性的核心解决应用固件坏了怎么办的问题。5.1 A/B 双分区维护两个平等的应用分区APP1 / APP2同一时刻只有一个活动分区被启动。当活动分区启动失败时自动切换到另一个分区重试。5.2 启动成功确认协议如何判定启动失败关键是一个启动成功标志boot_success配合看门狗Bootloader 跳转 APP 前 写 boot_success 0 等待 APP 确认 APP 启动并自检通过后 写 boot_success 1 显式确认启动成功 清零该分区失败计数 APP 崩溃 / 看门狗复位未及确认 boot_success 仍为 0 → Bootloader 下次读到 boot_success 0 → 判定上次启动失败该分区失败计数 1核心约定启动失败的唯一判据上次跳转后boot_success未变为 1APP 没来得及确认就复位了。失败计数清零的时机APP 确认成功时或升级刷写新固件成功后新固件不继承旧固件的失败计数。看门狗保证卡死的 APP最终会复位从而触发失败计数。5.3 失败计数与自动回退启动分区决策 ├─ 活动分区失败计数 阈值 → 正常启动该分区 ├─ 活动分区失败计数 ≥ 阈值 → 切换到备用分区 │ ├─ 备用分区失败计数 阈值 → 启动备用分区 │ └─ 备用分区也超阈值 → 双分区都失败 → 进入强制升级模式这个失败计数 自动切换 双失败进强制升级的机制是 A/B 分区 OTA 方案的通用设计。六、重要模块四强制升级模式当系统无法正常启动时双分区都失败需要进入强制升级模式来救砖。6.1 入口条件通常两个入口说明硬件/命令触发上电初始化后在极短的窗口期内收到约定的升级命令帧连续启动失败两个应用分区连续启动失败均超过阈值命令码窗口要设计得足够短如几十毫秒既能捕捉用户的升级指令又不拖慢正常启动时间。6.2 串口升级通道Ymodem强制升级模式最常用的是串口通道协议采用Ymodem文件传输协议自带文件名/大小/CRC16 校验终端工具普遍支持。关于 Ymodem 协议的帧格式与接收实现可参考单独的文章《Ymodem 协议详解》。这里只强调与 Bootloader 结合的两个要点先收后写升级数据先写入升级包暂存区校验通过后再刷写到目标分区。擦除粒度刷写时的擦除操作要分块进行单次擦除耗时需小于上位机的应答超时否则会导致发送方超时重传。七、重要模块五APP 跳转Bootloader 完成决策后把控制权交给 APP。跳转前必须做上下文清理否则 APP 可能因残留状态而运行异常。典型跳转序列voidjump_to_app(uint32_tapp_entry){disable_irq();/* 关闭全局中断 */flush_dcache();/* 刷写数据缓存 */invalidate_icache();/* 失效指令缓存 */fence();/* 内存屏障 */((void(*)(void))app_entry)();/* 跳转应用入口不再返回 */}关键点跳转目标是应用的复位入口reset vector而不是main()。应用的启动代码会重新完成整套初始化设置栈、中断向量表、拷贝代码段到运行区等因此 Bootloader 无需手动搬运镜像或设置栈指针。跳转前写回环境标志含boot_success 0为下一轮启动成功确认做准备。跳转成功后就不可达了APP 已接管系统。不同架构的跳转细节略有差异如 Cortex-M 需手动设置 SP 再跳复位向量而部分 RISC-V 架构直接跳复位入口即可但清理上下文 跳复位入口的思想是通用的。八、总结一个健壮的嵌入式 Bootloader本质上是由几个正交的模块组合而成模块解决的问题Flash 分区管理数据隔离升级包先暂存后刷写环境参数持久化双备份 循环磨损掉电安全双区容错A/B 分区 启动成功确认 失败回退强制升级模式命令触发 串口 Ymodem 救砖APP 跳转上下文清理安全移交控制权设计 Bootloader 时始终围绕三个目标展开可恢复、可升级、可容错。把每个模块的职责理清、边界划清就能得到一个稳定、可维护、可现场升级的启动系统。本文仅介绍 Bootloader 的通用设计思想与工程经验不涉及任何具体项目、芯片或产品的私有实现细节。
返回列表