Files
GD32_BOOT/docs/review/GD32_BOOT_专家评审_2026-10-19.html
T

103 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>GD32_BOOT 专家评审 2026-10-19</title><style>
body{font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;max-width:1180px;margin:0 auto;padding:24px;color:#1f2328;line-height:1.6;background:#fafbfc}
h1{border-bottom:3px solid #0b5cad;padding-bottom:8px}h2{margin-top:36px;border-left:5px solid #0b5cad;padding-left:10px}
table{border-collapse:collapse;width:100%;font-size:13.5px;background:#fff;margin:10px 0}th,td{border:1px solid #d0d7de;padding:6px 8px;vertical-align:top;text-align:left}th{background:#eef3f8}
.loc{font-family:Consolas,monospace;font-size:12px;word-break:break-all}.b{padding:2px 8px;border-radius:10px;color:#fff;font-size:12px;white-space:nowrap}
.crit{background:#b00020}.high{background:#e36209}.mid{background:#9a6700}.low{background:#57606a}
.cards{display:flex;gap:12px;flex-wrap:wrap}.card{flex:1;min-width:140px;background:#fff;border:1px solid #d0d7de;border-radius:8px;padding:12px;text-align:center}.card b{font-size:26px;display:block}
pre{background:#fff;border:1px solid #d0d7de;padding:12px;overflow:auto;font-size:12.5px}.note{background:#fff8c5;border:1px solid #d4a72c;padding:10px;border-radius:6px}
</style></head><body>
<h1>GD32_BOOT 专家评审报告</h1>
<p>仓库:cbb/GD32_BOOT 分支:GD32C103 评审基线提交:<code>05069e3</code>(2026-07-26,“移除CAN DMA相关代码并优化编译配置”) 评审日期:2026-10-19 报告提交:2026-10-20</p>
<div class="cards"><div class="card"><b style="color:#b00020">3</b>致命</div><div class="card"><b style="color:#e36209">9</b>严重</div><div class="card"><b style="color:#9a6700">11</b>一般</div><div class="card"><b style="color:#57606a">6</b>建议</div><div class="card"><b>5.0</b>综合评分 / 10</div></div>
<h2>1. 项目概述与架构</h2>
<p>GD32C103CB(Cortex-M4,128 KB Flash / 32 KB SRAM,120 MHz HXTAL PLL)上的 CAN-FD Bootloader,C++(AC6 + microlib,CMSIS csolution 构建)。功能:上电判定标志位 → 直接跳 APP / 10 s 延迟窗口监听 OTA / 从备份区恢复 / 停留 OTA 模式;OTA 采用 AES-128 握手派生会话密钥,48 字节/包加密传输,每包带密钥 CRC32;另含深度睡眠与 PB8 EXTI 唤醒。</p>
<table><tr><th>Flash 区域</th><th>地址范围</th><th>大小</th><th>用途</th></tr>
<tr><td>BOOT</td><td class="loc">0x08000000-0x08005FFF</td><td>24 KB</td><td>Bootloader(当前使用 0x3398 ≈ 13 KB)</td></tr>
<tr><td>APP_BACKUP</td><td class="loc">0x08006000-0x08011FFF</td><td>48 KB</td><td>恢复镜像(BOOT 只读不写)</td></tr>
<tr><td>APP_LATEST</td><td class="loc">0x08012000-0x0801DFFF</td><td>48 KB</td><td>运行镜像,OTA 写入目标</td></tr>
<tr><td>OTA 标志页</td><td class="loc">0x0801E000-0x0801E3FF</td><td>1 KB</td><td>6 个 32 位标志:LATEST/BACKUP 的 AVAL/EN/CHECK(0=有效,0xFFFFFFFF=无效)</td></tr>
<tr><td>睡眠配置页</td><td class="loc">0x0801E400-0x0801E7FF</td><td>1 KB</td><td>追加式睡眠使能记录</td></tr>
<tr><td>(flash.cpp 预留)</td><td class="loc">0x0801F800-0x0801FFFF</td><td>2 KB</td><td>EEPROM 模拟,未参与构建</td></tr></table>
<pre>上电 → systick/USART0/CAN-FD 初始化 → check_sleep()(使能则深睡,PB8 唤醒后软复位)
├─ LATEST.CHECK=0 且 EN=0 → jumpToApplication()
├─ LATEST.CHECK=0 且 EN=0xFFFF.. → 10 s 窗口:发 WAIT 心跳(0x103),收到握手则进入 OTA 循环,否则跳 APP
└─ 否则:BACKUP.CHECK=0 且 AVAL=0 → move_app_data() 备份→主区 → 跳 APP
否则 → OTA 循环
OTA 状态机:IDLE --REQ[00 AC]--> WAITING_RANDOM --REQ[16B nonce]--> WAITING_CONFIRM
--REQ[16B AES(K_s, nonce^0x55)]--> CHECKING_FIRMWARE --DATA[4B size]--> 擦除 --> RECEIVING
--DATA[64B: 会话号4|包号4|密文48|IFEND4|CRC4]...尾帧 IFEND=0xFFFFFFFF--> FINISHED → 跳 APP
CAN ID(扩展帧):0x100 REQ / 0x101 DATA / 0x102 ACK / 0x103 WAIT / 0x000 CANCEL;1 Mbps 仲裁 + 2 Mbps 数据(BRS)</pre>
<h2>2. 评审范围与方法</h2>
<ul><li>范围:Robot_V2/main.*、device/Can.*、sys/(OTA、aes128、initial、systick、flash、config)、RTE 中的中断文件/启动文件/链接脚本/区域定义、cproject/csolution 配置、Release map 文件;GigaDevice 标准外设库仅在被调用处核对行为(fmc_page_erase、can_message_receive 等)。</li>
<li>排除:out/、tmp/ 构建产物(仅在仓库卫生项中指出,map 用于核对 ROM/RAM 与符号是否链接)。</li>
<li>专家视角:MCU 固件架构、Bootloader/升级可靠性、中断与实时性、CAN-FD 协议、安全与故障保护、代码质量、构建与仓库卫生。</li>
<li>方法:逐行静态阅读 + 交叉核对库实现与 map 符号;行号以基线提交 05069e3 为准。未上板实测,标注“需实测”的条目为推断风险。</li></ul>
<h2>3. 既有评审记录检查</h2>
<div class="note">全仓(含历史提交)未发现任何 .md/.html 评审、审计或报告文件;Gitea issue 因访问令牌无 read:issue 权限无法读取。本报告是该仓库的第一份评审记录,因此没有可修订的原评审文件。作为替代,第 5 节逐条核对了提交信息中声明的修复/功能在当前代码中是否成立。</div>
<h2>4. 问题清单</h2>
<table><tr><th>编号</th><th>严重度</th><th>领域</th><th>文件:行号</th><th>现象</th><th>风险</th><th>修改建议</th></tr>
<tr><td>F-01</td><td><span class="b crit">致命</span></td><td>安全</td><td class="loc">Robot_V2/sys/config.h:70-72;sys/OTA.cpp:531-560</td><td>AES-128 主密钥 K_master 以明文常量写在头文件并提交进仓库,所有设备共用同一密钥;会话密钥 K_session = AES(K_master, nonce_server) 完全由上位机提供的随机数决定,设备侧不贡献随机性。</td><td>任何拿到仓库或任一固件镜像的人即可伪造合法上位机;设备无法区分新会话与录制的旧会话,可重放旧握手与旧固件(降级攻击)。</td><td>主密钥移出源码(读保护的 OTP/选项字节或按设备 UID 派生的设备密钥);握手加入设备侧随机数(双向 nonce);开启 Flash 读保护(SPC)。</td></tr>
<tr><td>F-02</td><td><span class="b crit">致命</span></td><td>安全/升级可靠性</td><td class="loc">sys/OTA.cpp:423-478</td><td>固件没有整体签名或整镜像校验:仅有每包 4 字节“带密钥 CRC32”,CRC32 是线性函数,不是 MAC;AES-ECB 逐块加密,相同明文块产生相同密文,可被剪切/拼接/重排。</td><td>知道密钥即可构造任意固件;即便不知道密钥,ECB 也允许在包内替换整块。被篡改或损坏的镜像会被当成合法固件写入并激活。</td><td>改为“整镜像 SHA-256 + ECDSA/Ed25519 签名”或至少 AES-CMAC/HMAC;加密改为 AES-CTR/GCM 并带包序号作 nonce;结束时校验总长度与整体摘要后才置可用标志。</td></tr>
<tr><td>F-03</td><td><span class="b crit">致命</span></td><td>升级可靠性</td><td class="loc">sys/OTA.cpp:369-373, 453, 466-471</td><td>① writeApplicationData 写 Flash 失败只 printf,不返回错误、不上报,随后照常回 ACK;② CRC 校验失败的包仍会继续检查尾帧标志 receive_IFEND,若尾帧本身 CRC 错误也照样置 FINISHED 并跳转;③ 结束时不校验 received_size 与 expected_size(received_size 从未累加)。</td><td>丢包、写失败、尾帧损坏都可能导致残缺镜像被 jumpToApplication 置为“可用+激活”(OTA.cpp:232-233),只做向量表两字检查就跳转,设备可能变砖或运行半截固件。</td><td>写失败要回错误帧并让上位机重发;尾帧必须 CRC 通过;累加已写字节并在结束时比对 expected_size 与整镜像校验值,全部通过后才写可用标志。</td></tr>
<tr><td>S-01</td><td><span class="b high">严重</span></td><td>Flash/断电安全</td><td class="loc">sys/OTA.cpp:41-124(OTA_SET)</td><td>6 个 OTA 标志共用 0x0801E000 一页。把某位从 0 改回 1 时整页擦除后再逐项重写,无双页/日志结构,重写结果不检查。</td><td>擦除后、重写前掉电,所有标志(包括备份可用标志)全部变回 0xFFFFFFFF:主区与备份都被判无效,设备只能停在 OTA 模式等待上位机。</td><td>改为双页乒乓或追加式记录(序号+CRC),先写新记录再作废旧记录;写后回读校验。</td></tr>
<tr><td>S-02</td><td><span class="b high">严重</span></td><td>Flash 驱动用法</td><td class="loc">sys/OTA.cpp:18-27, 54, 86, 98, 307-309;sys/initial.cpp:208</td><td>write_ADDRESS 每次写完都 fmc_lock()。OTA_SET 内先调用 write_ADDRESS(会上锁)再 fmc_page_erase;eraseApplicationArea 连续调用 3 次 OTA_SET,第 2、3 次进入时 FMC 已被上锁;EXTI 唤醒中断里调用 OTA_SET 前根本没有解锁。GD32 库在锁定时写 FMC_CTL 无效而 fmc_ready_wait 仍返回 READY。</td><td>页擦除可能被静默跳过却返回成功,标志位停留在旧值(例如“激活”标志清不掉),状态机与实际 Flash 状态不一致。</td><td>由 OTA_SET 自己负责一次 unlock/lock,write_ADDRESS 不再加解锁;擦除后回读确认 0xFFFFFFFF;所有 Flash 操作统一入口。</td></tr>
<tr><td>S-03</td><td><span class="b high">严重</span></td><td>通信协议</td><td class="loc">sys/OTA.cpp:623-634</td><td>getDeviceSerialNumber 把寄存器地址常量(0x1FFFF7E8 等)本身当作 UID 数值,没有解引用。</td><td>所有设备上报的“UID”完全相同,UID 在握手中失去意义,无法做设备绑定或按设备派生密钥。</td><td>改为 *(volatile uint32_t*)DEVICE_SERIAL_ADDR_xxx 读取。</td></tr>
<tr><td>S-04</td><td><span class="b high">严重</span></td><td>通信协议</td><td class="loc">sys/config.h:49-53;device/Can.cpp:105-119, 196-222</td><td>协议没有节点地址:所有设备使用相同的固定 CAN ID(0x100-0x103),硬件过滤器全通(mask=0)。</td><td>同一总线上多块处于 BOOT 的板子会同时响应握手、同时擦写、同时回 ACK,ACK 冲突/交错,升级结果不可预测;也无法单独升级某一节点。</td><td>在 ID 或负载中加入节点号(可由 UID 派生),过滤器只收本节点与广播 ID。</td></tr>
<tr><td>S-05</td><td><span class="b high">严重</span></td><td>CAN 接收/实时性</td><td class="loc">device/Can.cpp:196-222, 150-163(communication_check)</td><td>接收只有单个 receive_message 缓冲,ISR 每收一帧整体覆盖,计数器 can0_receive_count 只是计数;主循环取出时读到的永远是最后一帧,且计数自减在主循环、自增在 ISR,无临界区。ISR 还接收 ACK/WAIT ID——其他 BOOT 节点每 10 ms 发送的 WAIT 心跳会覆盖本该处理的数据帧。</td><td>连续两帧到达(握手应答+数据、多节点心跳)就会丢帧或同一帧被处理两次;提交 26cf5af 声称的“修复BOOT丢帧”并未根治。</td><td>改为环形接收队列(深度≥4,ISR 入队/主循环出队,单生产者单消费者),ISR 只接收 REQ/DATA/CANCEL。</td></tr>
<tr><td>S-06</td><td><span class="b high">严重</span></td><td>CAN 接收</td><td class="loc">device/Can.cpp:203-212</td><td>temp_message 是未初始化的栈变量,can_message_receive 只在扩展帧时写 rx_efid;收到标准帧时 rx_efid 为栈上残值。CANFD_OTA_CANCEL_ID = 0。</td><td>总线上任意标准帧都可能被判成某个 OTA ID,残值为 0 时会被当成“取消 OTA”,中途终止升级。</td><td>temp_message 先清零或调用 can_struct_para_init;先判断 rx_ff==CAN_FF_EXTENDED 再比较 ID;取消命令不要用 ID 0。</td></tr>
<tr><td>S-07</td><td><span class="b high">严重</span></td><td>故障保护</td><td class="loc">整个工程(无 fwdgt/wwdgt 使用);device/Can.cpp:73;RTE/.../gd32c10x_it.c:54-98</td><td>没有启用任何看门狗;CAN auto_bus_off_recovery = DISABLE 且无软件恢复;HardFault/BusFault 等全部是死循环。</td><td>任何卡死(总线 bus-off、Flash 等待、异常)后设备永久停机,只能人工断电;BOOT 是最后一道防线,不能没有自恢复。</td><td>启用独立看门狗并在主循环喂狗(跳 APP 前保持启用,APP 接管);开启 bus-off 自动恢复;Fault 处理改为记录原因后系统复位。</td></tr>
<tr><td>S-08</td><td><span class="b high">严重</span></td><td>调试代码进入发布版</td><td class="loc">sys/config.h:17;sys/OTA.cpp:311-337, 324;253-280</td><td>DEBUG_OTA=1 是当前发布配置。① eraseApplicationArea 在 __disable_irq() 后擦除失败分支调用 delay_1ms(5000),该延时依赖 SysTick 中断 → 永久死等;② 擦除循环每页在关中断状态下 printf;③ jumpToApplication 关闭 USART0 时钟后仍 printf(jumping3/jumping5),fputc 轮询 TBE。</td><td>擦除失败时设备永久挂死(无看门狗兜底);跳转路径存在挂在 fputc 的风险(与外设时钟关闭后寄存器读值有关,需实测);关中断时间长导致 CAN 溢出。</td><td>发布版置 DEBUG_OTA=0;关中断区段内禁止 printf/延时;跳转前先输出日志再关时钟。</td></tr>
<tr><td>S-09</td><td><span class="b high">严重</span></td><td>备份恢复</td><td class="loc">sys/OTA.cpp:657-742;sys/flash.cpp:25</td><td>move_app_data 用硬件 CRC 校验拷贝结果,但 RCU_CRC 时钟只在 FlashEEPROM::init() 中开启,而 flash.cpp 不在构建清单、init() 从未被调用;main.cpp:88 也忽略 move_app_data 返回值,直接跳转。</td><td>CRC 外设无时钟时读回值恒定,src_crc==dst_crc 恒成立,校验形同虚设;拷贝失败也会跳转到半截镜像。另外拷贝前先擦除主区,拷贝中掉电则主区损坏,只能依赖备份再次恢复。</td><td>调用前 rcu_periph_clock_enable(RCU_CRC);检查返回值,失败时进入 OTA 模式而不是跳转;备份区自身应有长度+CRC/签名头。</td></tr>
<tr><td>G-01</td><td><span class="b mid">一般</span></td><td>升级流程</td><td class="loc">sys/OTA.cpp:140-185;config.h:74</td><td>超时计数:非 IDLE 状态每次 process() 只 delay_1us(100) 后 ota_time++,OTA_TIMEOUT=10000 → 实际约 1 s,与注释“10 秒”不符;delay_1us 基于空循环估算,随编译优化变化。</td><td>上位机准备/擦除较慢时容易误超时;行为依赖优化级别。</td><td>超时统一基于 SysTick 毫秒计数(记录时间戳比较)。</td></tr>
<tr><td>G-02</td><td><span class="b mid">一般</span></td><td>升级流程</td><td class="loc">sys/OTA.cpp:142-153, 319-329</td><td>页擦除失败只发错误帧并 break,process() 仍把状态置为 RECEIVING_FIRMWARE。</td><td>在擦除不完整的区域继续接收写入,产生 PGERR 或混合镜像。</td><td>eraseApplicationArea 返回结果,失败则回到 IDLE。</td></tr>
<tr><td>G-03</td><td><span class="b mid">一般</span></td><td>升级流程</td><td class="loc">sys/OTA.cpp:456-457</td><td>offset = receive_packet_num * 48 为 u32 乘法,可溢出回绕后通过 offset+48 ≤ APP_MAX_SIZE 检查。</td><td>异常包序号会写入意外偏移。</td><td>先检查 receive_packet_num &lt; APP_MAX_SIZE/48 再计算偏移;同时要求序号单调连续。</td></tr>
<tr><td>G-04</td><td><span class="b mid">一般</span></td><td>A/B 设计</td><td class="loc">main.cpp:24-101;sys/OTA.cpp:221-234</td><td>所谓 A/B 实为“主区 + 恢复区”:BOOT 从不写备份区;新固件的 CHECK 标志只能由 APP 写入(隐含约定,无文档)。若 APP 未写 CHECK,下次复位会走备份分支,用备份覆盖刚升级的新固件。</td><td>升级后“回退”行为取决于 APP 是否遵守未文档化约定,容易出现“升级后重启又变回旧版”。</td><td>书面定义 BOOT/APP 标志握手协议(试运行→确认→回滚计数);或真正双槽并只切换启动槽。</td></tr>
<tr><td>G-05</td><td><span class="b mid">一般</span></td><td>跳转</td><td class="loc">sys/OTA.cpp:250-271</td><td>切换到 IRC8M 后未等待 SCSS 生效,也未关闭 PLL/HXTAL、未复位 RCU 寄存器;SysTick 关闭后未清除 SCB-&gt;ICSR 挂起位。</td><td>APP 的 SystemInit 一般能兜底,但时钟残留状态与 APP 预期不一致时会出现难查的启动问题。</td><td>调用 rcu_deinit() 或按 SystemInit 逆序复位时钟,清 PendSV/SysTick 挂起。</td></tr>
<tr><td>G-06</td><td><span class="b mid">一般</span></td><td>CAN 发送</td><td class="loc">device/Can.cpp:376-409;Can.h:5-43;Can.cpp:122</td><td>CAN0_TX_IRQHandler 未在 extern &quot;C&quot; 中声明,C++ 名字修饰后不会覆盖启动文件的弱符号(map 中无该函数);NVIC 也从未使能 CAN0_TX_IRQn。</td><td>发送队列只入队不出队:邮箱满时消息进入队列后永远不会发送,提交 d019b41 的“发送队列机制”实际不工作。</td><td>在 extern &quot;C&quot; 中声明 TX 中断函数并 nvic_irq_enable(CAN0_TX_IRQn);或在主循环轮询出队。</td></tr>
<tr><td>G-07</td><td><span class="b mid">一般</span></td><td>CAN-FD</td><td class="loc">device/Can.cpp:315-334</td><td>DLC 长度映射有缺档:分支为 21-23→24、29-32→32,长度恰为 24 以及 25-28 没有任何分支,直接返回失败(应为 21-24→24、25-32→32)。</td><td>发送 24~28 字节报文会被静默丢弃(当前协议最大 36/64 字节,未来扩展易踩坑)。</td><td>用查表把长度向上取整到 {12,16,20,24,32,48,64}。</td></tr>
<tr><td>G-08</td><td><span class="b mid">一般</span></td><td>睡眠/唤醒</td><td class="loc">sys/initial.cpp:147-177, 199-255</td><td>唤醒中断中执行 Flash 擦写与 OTA_SET(未解锁,见 S-02);地址循环用 address &gt;= SLEEP_CONFIG_START 递减,起点为 0x0801E400 时边界写法易错;enter_deep_sleep 返回后无处理(醒来靠 EXTI 软复位)。</td><td>ISR 内长时间 Flash 操作;唤醒后标志写入可能失败。</td><td>ISR 只置标志,Flash 操作放主流程;睡眠标志使用与 OTA 标志相同的可靠记录格式。</td></tr>
<tr><td>G-09</td><td><span class="b mid">一般</span></td><td>代码质量</td><td class="loc">sys/OTA.h:12;OTA.h:18</td><td>成员 u32 ota_stateota_time(明显拼接笔误,未使用);isActive() 返回 ota_state 非零,WAITING_RANDOM 等早期状态即被视为“进入 OTA”。</td><td>可读性差;延迟启动期间收到一帧握手就永久停在 OTA 循环,即使之后超时回 IDLE 也不会再跳 APP(main.cpp:61-67)。</td><td>删除笔误成员;OTA 循环中 IDLE 持续超时后重新走启动判定。</td></tr>
<tr><td>G-10</td><td><span class="b mid">一般</span></td><td>代码质量</td><td class="loc">sys/OTA.cpp:18(inline)与 sys/initial.cpp:183</td><td>write_ADDRESS 在两个翻译单元各定义一次(一个 inline、一个外部链接),违反 ODR。</td><td>链接器选择哪份实现不确定,后续改动其中一份会产生隐蔽差异。</td><td>只保留一份,放到 flash 模块并在头文件声明。</td></tr>
<tr><td>G-11</td><td><span class="b mid">一般</span></td><td>代码质量</td><td class="loc">sys/OTA.cpp:676, 709, 735 与 config.h:17</td><td>move_app_data 内用 OTA_DEBUG,而全工程定义的是 DEBUG_OTA,这些日志永远不会输出。</td><td>恢复失败时没有任何诊断信息。</td><td>统一宏名。</td></tr>
<tr><td>A-01</td><td><span class="b low">建议</span></td><td>构建</td><td class="loc">sys/flash.h:5-14 / sys/flash.cpp(未加入 cproject.yml)</td><td>flash.cpp/flash.h 未参与构建,且 FLASH_EEPROM_OK 枚举与 config.h 的 constexpr 同名冲突;EEPROM 地址 0x0801F800 与其他区无文档说明。</td><td>死代码误导维护者,一旦加入构建即编译失败。</td><td>删除或整合进统一 Flash 存储模块。</td></tr>
<tr><td>A-02</td><td><span class="b low">建议</span></td><td>内存配置</td><td class="loc">RTE/.../startup_gd32c10x.s:42;regions_GD32C103CB.h:96-97;map</td><td>启动文件 Stack 0x800 + scatter ARM_LIB_STACK 0x200 + HEAP 0xC00 三套配置并存(map 中均占用 RAM),microlib 下实际 MSP 来自向量表(2 KB)。</td><td>浪费 ~3.5 KB RAM,且栈大小以哪处为准不明确。</td><td>只保留一处栈定义;BOOT 不用 malloc 时把 HEAP 设为 0。</td></tr>
<tr><td>A-03</td><td><span class="b low">建议</span></td><td>构建配置</td><td class="loc">Robot_V2.csolution.yml:42-47</td><td>Debug 为 optimize none,Release 为 size;ROM 使用 0x3398/0x6000(约 54%),余量可接受。BOOT 版本号写死在 config.h,未写入镜像固定位置。</td><td>上位机无法读取 BOOT 版本;Debug/Release 时序差异(空循环延时)大。</td><td>在固定偏移放置版本/构建信息结构;延时不依赖优化级别。</td></tr>
<tr><td>A-04</td><td><span class="b low">建议</span></td><td>仓库卫生</td><td class="loc">.gitignore;out/、tmp/(65 个文件)</td><td>out/、tmp/ 构建产物(axf/hex/map/.o/CMake 缓存等)在 .gitignore 生效前已被跟踪,至今仍在库中,每次提交都产生大量噪声 diff;compile_commands.json 含本机路径 G:/Robot_v2/BOOT。</td><td>仓库膨胀、评审困难、泄露本机目录结构。</td><td>git rm -r --cached out tmp(由维护者执行);发布固件改用 Release 附件。</td></tr>
<tr><td>A-05</td><td><span class="b low">建议</span></td><td>仓库卫生</td><td class="loc">Robot_V2.cbuild-idx.yml、*.cbuild-pack.yml、.vscode/*.json、.continue/mcpServers/new-mcp-server.yaml、RTE/*.base@1.0.0</td><td>生成文件与 IDE 本地配置被跟踪(.gitignore 已声明忽略但未从索引移除);.continue 下是未填写的模板。</td><td>多人协作时频繁冲突。</td><td>从索引移除并保持忽略;保留 cbuild-pack.yml(锁定包版本)即可。</td></tr>
<tr><td>A-06</td><td><span class="b low">建议</span></td><td>文档/测试</td><td class="loc">仓库根目录</td><td>无 README、无协议文档(帧格式、状态机、标志位定义、BOOT/APP 约定)、无主机侧工具说明、无任何测试(AES 可用 FIPS-197 向量做单元测试)。</td><td>知识只在作者脑中,协议变更无法验证。</td><td>补 README + docs/protocol.md(状态机图、帧表、Flash 分区表);为 AES/CRC/标志模块加主机侧单元测试。</td></tr></table>
<h2>5. 提交声明跟踪表(替代既有评审跟踪)</h2>
<table><tr><th>提交</th><th>声明</th><th>当前状态</th><th>核对说明</th></tr><tr><td class="loc">2f48255 / 9c7145e</td><td>OTA 功能测试通过;安全 OTA,AES 加密和 CRC 校验</td><td><b>部分成立</b></td><td>加解密与 CRC 已实现,但主密钥明文入库、无签名、ECB 模式、CRC 非 MAC(F-01、F-02);UID 读取错误(S-03)。</td></tr>
<tr><td class="loc">c7c4451</td><td>支持 A/B 分区逻辑</td><td><b>部分成立</b></td><td>实为主区+恢复区,BOOT 不写备份区;确认依赖 APP 写 CHECK 的隐含约定(G-04)。</td></tr>
<tr><td class="loc">aefb7d4</td><td>修复备份 APP 区域恢复功能</td><td><b>部分成立</b></td><td>恢复路径可执行,但 CRC 外设无时钟导致校验无效、返回值被忽略(S-09)。</td></tr>
<tr><td class="loc">26cf5af</td><td>修复 BOOT 丢帧问题,优化标志位存储</td><td><b>未根治</b></td><td>单缓冲接收仍会被后续帧/心跳覆盖(S-05);标志页整页擦写断电风险仍在(S-01)。</td></tr>
<tr><td class="loc">d019b41</td><td>CAN FD 协议栈优化和发送队列机制</td><td><b>不成立(队列未生效)</b></td><td>TX 中断函数名被 C++ 修饰、NVIC 未使能,队列不会出队(G-06);DLC 映射有缺档(G-07)。</td></tr>
<tr><td class="loc">1e6d47d</td><td>完整的系统休眠和唤醒机制</td><td><b>基本成立,有缺陷</b></td><td>可休眠并通过 PB8 EXTI 唤醒复位;唤醒 ISR 内做 Flash 操作且未解锁(G-08、S-02)。</td></tr>
<tr><td class="loc">46d4bfe</td><td>APP 延迟启动模式支持 OTA 请求检测</td><td><b>成立,有缺陷</b></td><td>10 s 窗口工作正常;一旦进入早期握手状态即永久停在 OTA 循环(G-09)。</td></tr>
<tr><td class="loc">05069e3</td><td>移除 CAN DMA、-Oz/ROM 护栏、-fno-exceptions</td><td><b>成立</b></td><td>DMA 代码已移除;Release 为 size 优化,ROM 占用 0x3398/0x6000;但 jumpToApplication 仍关闭 RCU_DMA0(无害残留)。</td></tr></table>
<h2>6. 优先级修复路线图</h2>
<table><tr><th>阶段</th><th>内容</th><th>对应问题</th></tr>
<tr><td><b>P0 立即(发布前必须)</b></td><td>发布版关闭 DEBUG_OTA;修 UID 解引用;写失败/尾帧 CRC/长度校验失败一律不置可用标志;OTA_SET 解锁/上锁统一并回读校验;move_app_data 开 CRC 时钟并检查返回值;启用独立看门狗与 bus-off 恢复</td><td>F-03, S-02, S-03, S-07, S-08, S-09</td></tr>
<tr><td><b>P1 短期(1-2 周)</b></td><td>接收环形队列 + 帧格式判断 + 只收本节点 ID;协议加入节点地址;标志区改双页/追加式记录;修 TX 中断与 DLC 映射;超时改为 SysTick 时间戳</td><td>S-01, S-04, S-05, S-06, G-01, G-02, G-03, G-06, G-07</td></tr>
<tr><td><b>P2 中期(安全加固)</b></td><td>密钥移出源码并按 UID 派生;双向 nonce 防重放;整镜像签名(ECDSA/Ed25519)+ AES-CTR/GCM;版本号防降级;开启读保护</td><td>F-01, F-02</td></tr>
<tr><td><b>P3 持续</b></td><td>书面 BOOT/APP 标志协议与回滚策略;清理 out/tmp 与生成文件;README + 协议文档;AES/CRC 单元测试;统一栈堆配置与调试宏</td><td>G-04, G-05, G-08-G-11, A-01-A-06</td></tr></table>
<h2>7. 总结评分</h2>
<table><tr><th>维度</th><th>评分 /10</th><th>说明</th></tr>
<tr><td>架构清晰度</td><td>6</td><td>模块划分简单明了,分区与状态机易懂;BOOT/APP 约定未文档化</td></tr>
<tr><td>升级可靠性</td><td>4</td><td>有恢复区与延迟启动;写失败/断电/残缺镜像均可能被激活</td></tr>
<tr><td>安全性</td><td>3</td><td>有加密握手的意图,但密钥入库、无签名、ECB、可重放</td></tr>
<tr><td>通信与实时性</td><td>5</td><td>CAN-FD 配置正确、BRS 生效;单缓冲接收、无寻址、发送队列失效</td></tr>
<tr><td>故障保护</td><td>3</td><td>无看门狗、无 bus-off 恢复、Fault 死循环</td></tr>
<tr><td>代码质量</td><td>6</td><td>注释充分、可读;存在 ODR 重复定义、宏名不一致、死代码</td></tr>
<tr><td>构建与仓库卫生</td><td>5</td><td>csolution 配置规范、ROM 余量足;构建产物与生成文件入库、无 README/测试</td></tr>
<tr><td><b>综合</b></td><td><b>5.0</b></td><td>功能链路已打通、可用于台架;量产前须完成 P0+P1,面向现场设备须完成 P2</td></tr></table>
<p style="color:#57606a;font-size:12px">本报告仅新增于 docs/review/,未修改任何源码。由 Hark 生成。</p>
</body></html>