CH582 A/B OTA 启动与 WCH-Link 排障:特权级、Flash 擦除态和调试现场
CH582 A/B OTA 启动与 WCH Link 排障 记录日期:2026 10 14。工程: /home/dwh/project/deskscreen/binbot slave 。 背景与当前结论 目标是在 CH582 工程中接入 LatticeMCU,支持 Server → RV1106 → UART → MC
CH582 A/B OTA 启动与 WCH-Link 排障
记录日期:2026-10-14。工程:/home/dwh/project/deskscreen/binbot_slave。
背景与当前结论
目标是在 CH582 工程中接入 LatticeMCU,支持 Server → RV1106 → UART → MCU 的 A/B OTA,复用现有设备 OTA 流程。这里的 ESP32 只是流程参考,不需要改动 ESP32 固件。
截至本次硬件验证,生产 Bootloader 已成功跳转到 A 槽 0x4000,应用计时器和三个任务持续推进,两次软件复位后仍正常启动。当前运行的是已确认的 A 槽,没有待试运行镜像。
物理 Server → RV1106 → UART → MCU 链路、A/B 升级切换、试运行确认、回滚、物理断电及更新中断恢复,仍需板上验证。主机仿真和软件测试通过,不能代替这些实机测试。
1. 跳转后应用启动失败:Bootloader 在用户态运行
现象与直接证据
分阶段检查表明,Bootloader 能完成时钟初始化、加载元数据、验证 A 槽、提交启动记录,并执行显式跳转。但应用尚未进入 main 就出现异常:
| 项目 | 捕获值 |
|---|---|
mcause |
2,非法指令 |
mepc |
0x4aec |
| 故障指令 | 0xbc029073,即 csrw 0xbc0,t0 |
mstatus |
0x88 |
| 芯片配置 | 0xd9,当次仍可重新连接调试 |
原因
普通 SDK 启动文件设置 mstatus=0x88,其中 MPP 为 0,执行 mret 后以用户态运行 Bootloader 的 main。Bootloader 随后普通跳转到应用启动入口,不会自动恢复机器态。应用启动代码一开始访问机器态 CSR 0xbc0,因此触发非法指令异常。
关键是先保证 Bootloader 的执行权限正确。仅修改应用启动代码后半段的 mstatus,无法解决它在到达该设置之前已经访问机器态 CSR 的问题。
修复与验证
官方 EXAM/IAP/UART_IAP/ 和 EXAM/IAP/APP/ 的启动文件均使用 0x1888,其中 MPP 为 3,mret 后保持机器态。
工程新增并维护 startup/startup_ch58x.S,保留厂商中断向量顺序和启动指令,将 Bootloader、A 槽和 B 槽统一设为 mstatus=0x1888;不直接修改 SDK。独立 recovery 仍保留原先已验证的启动文件和镜像。
先只修正 Bootloader 模式、保持应用字节不变,诊断应用就能进入 main,为原因提供了对照证据。随后统一修正两端启动,完成初始化、主循环、看门狗和生产镜像验证。
经验:Bootloader 到应用的跳转不仅涉及链接地址、栈和中断向量,还必须核对进入应用时的 CPU 特权级以及应用启动文件的 CSR 访问顺序。
2. 元数据损坏:逻辑 FF 不等于原生擦除态
现象
曾出现完整烧录 CRC 校验通过,但 Bootloader 追加新启动记录后校验失败、留在失败循环的情况。Flash 写接口返回成功,并不表示写入内容一定正确。
本次 CH582 的程序 Flash 在 SDK 擦除后,读回的原生擦除字是 0xf3f9bda9,不是通常假设的 0xffffffff。
复现实验
用实际 128 字节元数据记录测试:
- SDK 擦除后,直接写入页起点和偏移
0x80,均逐字节一致。 - SDK 擦除后,先写入 256 字节逻辑 FF,再追加记录,偏移
0x80的记录出现两个字损坏,存储的 CRC 未随之变化。 - SDK 写调用仍返回 0。该实验复现了同类损坏,但没有精确复现最早版本字段由 1 变 5 的每个细节。
结论:已编程的逻辑 FF 不能当作仍可直接追加写入的擦除空间。
修复
工厂镜像的未使用区域改用原生擦除字填充;Flash 后端向追加日志提供自身的擦除字,日志据此判断空记录。主机后端仍可以使用 0xffffffff。
修复后,通过主机测试、打包回归、元数据连续追加与页轮换测试;实机 Flash 诊断覆盖 16/60 MHz、看门狗关闭/开启组合,连续追加记录均匹配。
经验:不能在通用存储层硬编码“擦除态就是 FF”。应基于目标芯片和实际编程路径验证擦除、填充、重写语义,并检查写后内容。
3. 调试器本身会改变现场
WCH OpenOCD 连接会改写 s1
本次使用的厂商 OpenOCD 在重新连接等待请求的诊断应用时,将 s1 改为 0xffffff82,正好是符号扩展后的 CH582 ID。反汇编确认 wait_request(n) 正依赖 s1 保存请求参数。恢复已知的参数后,同一诊断程序继续完成初始化。
这是本次工具与设备组合上观察到的行为,不能推广为所有 OpenOCD 版本的通用结论。
后续采用同一调试连接内“复位 → 运行 → 检查”的方式验证生产固件,避免新连接破坏任意运行现场后直接继续执行。仅在明确知道诊断函数所需参数时修复寄存器,不猜测生产代码中寄存器原值。
看门狗与受保护寄存器写入
CPU 暂停检查时,看门狗可能继续计时。通过多次 USB 内存写实现“解锁 → 修改看门狗控制”,不能满足以 CPU 时钟计数的保护窗口,因此不能依赖这种方法。
新增 tools/ch_link_watchdog.tcl:将解锁和控制写入放入 Debug Module 的 8 条指令程序缓冲区执行,保存并恢复 t0/t1,校验控制位读回。实机验证了启用 0x02、暂停 0x00 和恢复原控制状态。
有界诊断让 MCU 自己运行指定时间并在检查前关闭看门狗;生产验证在暂停后调用上述原子操作,检查结束恢复看门狗并继续运行。
先前某次超时是否确实由看门狗触发,没有完整现场,不能仅凭超时反推。新的方法解决了受保护写入时序,并已用于成功的运行验证。
经验:连接、暂停和检查都可能影响被测程序。设计可控的阶段、超时、异常留存和明确的完成标记,才能区分固件故障与调试操作影响。
4. WCH-Link 连接排障要区分阶段
本次探针为 WCH-Link-CH549,RV 模式,固件 2.12,USB VID:PID 为 1a86:8010。
使用 WCH 定制 OpenOCD 和它的 bin/wch-riscv.cfg。配置中使用 adapter driver wlinke、transport select sdi,不能用同名通用 JTAG 配置替代。
参数顺序必须是:
openocd -f <厂商 bin/wch-riscv.cfg> -c "chip_id CH58x" ...
chip_id 在创建厂商 target 后才注册,因此不能放在配置加载之前。
排查顺序:
- 检查 USB 是否枚举、是否透传到当前 Linux/虚拟机。
- 检查当前 USB 节点的用户访问权限。持久 udev/plugdev 规则可解决重新枚举后的权限丢失。
- 确认 OpenOCD 已识别探针型号、模式和版本。
- 确认目标芯片初始化完成,例如出现
wlink_init ok和Target successfully examined。 - 再读取芯片 ID、配置、PC、复位状态和固件数据。
Ready for Remote Connections 是配置在 init 前打印的,不能证明已经连接芯片。WCH-Link failed to connect with riscvchip 表示目标连接阶段失败,仅凭它不能判定硬件损坏、代码保护或固件已关闭调试。
曾尝试 1000 kHz 并成功运行,但日志同时提示 unknow speed config,实际生效速度未确认,不能认定“降速就是修复原因”。
本次成功使用的 Linux 环境:
OpenOCD:
/home/dwh/software/toolchains/mrs-2.5.0/OpenOCD/bin/openocd
配置:
/home/dwh/software/toolchains/mrs-2.5.0/OpenOCD/bin/wch-riscv.cfg
LD_LIBRARY_PATH:
/home/dwh/software/toolchains/OpenOCD/OpenOCD/lib
该 Linux OpenOCD 为厂商 0.11.0+dev-snapshot,构建时间 2026-06-17。Windows MRS 1.80.2 使用的厂商构建为 2026-07-23;两者不是同一个二进制,但当时使用的 SDI 配置文件哈希一致。
5. 如何判断“确实启动成功”
烧录完整 CRC 一致,只证明写入结果,不能单独证明应用正常运行。一次看到有效元数据,也可能只是 Bootloader 写过记录。
本次生产固件验证在同一个连接内完成三个运行周期,其中包括两次软件复位。每个周期取两次快照,检查时间和任务计数推进,并检查周期内未发生意外重启:
| 检查项 | 实测结果 |
|---|---|
| 全部 448 KiB 初始编程 CRC32 | df81c98d,匹配 |
| Bootloader → 应用 | A 槽,入口 0x4000 |
| 毫秒计时 | 各周期内持续增加 |
| input / heartbeat / protocol 任务 | 三个运行计数都增加 |
| OTA valid | 始终为 1 |
| boot counter | 1 → 2 → 3 |
| 元数据 generation | 2 → 3 → 4 |
| 槽状态字 | 0x00ff0000:active A、confirmed A、pending FF |
| 调试配置 | 始终 0xd9 |
| 看门狗 | 运行时启用,检查后恢复 |
最后一组计数为 0x993 / 0x9 / 0x98f,tick 为 0x132d。这证明了本次短时运行和软件复位后的启动,不等价于长时间稳定性或断电恢复测试。
当前生产镜像身份:
build/binbot_slave_factory.bin SHA-256:
4ce74e7b0ac829ec274698d8475639039d92652569c6b91062e8f15573bb2478
build/binbot_slave.ota SHA-256:
deae4b1f6445b96ff255d8db9eb5e83d60fdf3177760802e480f5dc626108e36
完整 CRC 在应用运行前验证。后续 Bootloader 会追加元数据,运行后的全 Flash CRC 合理地发生变化,不能直接和初始工厂镜像 CRC 比较并认定烧录损坏。
RAM 地址必须从本次 ELF/map 文件取得,不能复用 Windows 原始 UART 示例或旧版本地址。本次生产构建的 tick 是 0x20000ee0,OTA valid 是 0x20000fa8,这些只是当前构建的定位信息。
6. A/B 布局与后续验证
| 区域 | 地址范围 | 大小 |
|---|---|---|
| Bootloader | 0x00000..0x03fff |
16 KiB |
| A 槽 | 0x04000..0x38fff |
212 KiB |
| B 槽 | 0x39000..0x6dfff |
212 KiB |
| 元数据 | 0x6e000..0x6ffff |
8 KiB,两页 |
A/B 分别链接到自己的执行地址,OTA 包含两份槽镜像,RV1106 选择非当前槽下发。Bootloader 不通过该 OTA 协议更新。元数据采用 128 字节追加记录和 CRC 提交字。
后续实机验证需要确认 RV1106 当前地址,以及 MCU UART1 的 PA8 RX/PA9 TX 与 RV1106 UART 的实际接线;默认设备配置是 /dev/ttyS4、115200 8-N-1,但默认配置不是接线证据。
建议按 A → B 更新、试运行确认、B → A 更新、试运行失败回滚、传输/写入/元数据提交时断电的顺序补齐测试。
7. 尚未查明的问题
用户曾报告重新开启两线仿真后恢复烧录,但没有捕获关闭前后的完整配置和导致关闭的具体操作。已检查构建没有直接调用 UserOptionByteConfig、UserOptionByteClose_SWD、UserOptionByte_Active,这不能排除其他意外写入或工具行为。
某些恢复操作后的快照还出现 Bootloader 区域被擦除,但具体擦除来源未查明。不能据此断言是掉电擦除、OTA 主动关闭调试、硬件故障,或把它和已经确认的启动特权级问题混为同一个原因。
证据与可复用工具
证据目录:/home/dwh/project/deskscreen/binbot_slave/build/hardware/after-power-cycle-2qOOHWdk/。
app-check-startup-fault.log:非法指令、故障 PC、CSR 指令和配置。app-check-iap-install.log、app-check-iap-boot-stages.log、app-check-iap-enter.log:修正 IAP 特权级后的对照。app-check-iap-app-stages-repair.log:已知诊断参数恢复后的初始化。app-check-bounded-stages.log:初始化、真实主循环、关闭/开启看门狗的有界诊断均通过。watchdog-atomic-check.log、watchdog-atomic-enable-pause.log:原子看门狗控制。production-runtime-check.log:工厂镜像 CRC、三个运行周期和两次软件复位。
工程内可复用:tools/ch_link_check.tcl、tools/ch_link_watchdog.tcl、tools/ch_link_runtime.tcl。生产烧录脚本支持 CH_LINK_AFTER_PROGRAM,可在同一调试连接内运行安装后验证。
使用这些脚本时必须匹配本次 ELF 的符号地址,并理解复位、Flash 写入和元数据追加的影响。现有 probe/diagnose 包装尚未全部适配重新连接改写寄存器这一发现,不应把其连接后直接 resume 当成可靠的生产运行验证。