7.3 KiB
gd32e230f8_firmware_merge_tool v2 升级计划(需求规格 / 提示词)
本文可直接作为 AI 辅助重构的提示词使用;也是本次彻底升级的需求基线。 背景仓库:本仓库(main 分支);配套 skill:hulk-mcu-firmware(SKILL.md + tool/ 缓存)。 首个目标工程:CeramixNano-MB(GD32E230F8/64K,双固件 v5.1 布局,BL 侧 dev_fact@512bf7a)。
落地后注记(2026-10-22):内置 profile 已定名
CeramixNano_MB_Dual_FW(本文示例中的ceramixnano_mb_v51为规划期旧名,已废弃);打包产物在 UPDATE/FULL 之外新增 FACTORY 工厂镜像(BL+B 满槽+flag、无 A),不再单独 落盘 B 包文件。
一、背景与目标
现有 gd32e230_firmware_tool.py 是单槽时代的工具:布局硬编码(APP@0x08002000、标志页、校准页),只有两种模式(-f 纯 0xFF 补页、-m 拼 BL+APP+flag)。现已演进为双固件方案(BL + FW_A 运行槽 + FW_B 出厂保底槽 + 记录/CRC),临时脚本(make_v5_update.py、拼 FULL 的临时逻辑)散落各处,需要彻底整合升级。
目标:一个布局可配置、可复用到任意 GD32E230 工程(不同 Flash 型号、不同槽大小、可有/可无保底槽)的固件打包工具:输入编译好的 bin,自动识别槽位、补页、计算长度与 CRC、写记录/trailer/标志,产出完整可烧录镜像与 OTA 包。
二、现状与痛点
- 布局硬编码(0x08002000/末页标志/校准页),换工程即失效;
- 无 A/B 双槽概念,无 trailer/记录/CRC 计算;
- 新增的
-a/--app-addr只是补丁,无法描述 B 记录、锚点等; - GUI(FlashTool)拼接逻辑同样固定 0x08002000;
- 临时脚本(make_v5_update.py 等)逻辑未固化,存在两套真相。
三、核心设计:布局 Profile
用 JSON 配置文件描述一个工程的 Flash 布局(CLI 也可覆盖单项)。示例(CeramixNano-MB v5.1):
{
"profile": "ceramixnano_mb_v51",
"chip": "GD32E230F8",
"flash_base": "0x08000000",
"flash_size": "64K",
"page_size": 1024,
"bl": { "addr": "0x08000000", "size": "8K" },
"slot_a": { "addr": "0x08002000", "size": "36K",
"anchor": "slot_end-4", "record": "{pages16@+0, crc16@+2}" },
"slot_b": { "addr": "0x0800B000", "size": "19K", "fixed_slot": true,
"record": "{magic16@+0=0xC0DE, pages16@+2, crc16@+4}",
"record_addr": "slot_end-8" },
"flag": { "page": "last", "addr": "0x0800FFFC", "value": "0xEEEE" },
"crc": "xmodem"
}
可复用性要求:其他工程仅换 profile——例如保底槽 8K 或 28K、无保底槽(slot_b: null)、BL 更大等;所有地址/大小支持 0x 十六进制与 K 后缀;工具对 profile 做完整性校验(分区不重叠、页对齐、总和=flash_size)。
四、功能规格
4.1 输入与自动识别
- 输入为编译好的 bin:底包(LD/向量配置在 B 槽基址,如 0x0800B000)与升级包(配置在 A 槽基址,如 0x08002000)。
- 工具从向量表自动识别:初始 SP ∈ SRAM、复位向量落在哪个槽区间 → 判定该 bin 属于 A 包还是 B 包;与显式参数冲突时报错。
- BL bin(可选)、底包 bin(可选)作为输入项;BL 需校验 ≤
bl.size。
4.2 计算规则(与 BL dev_fact 侧逐字一致,不得偏离)
- CRC-16/XMODEM(poly 0x1021、init 0x0000、不反转、无 xorout;测试向量
"123456789"→0x31C3——工具启动自检)。 - A 包(OTA/升级包):补 0xFF 至整 N 页,末 4B 写 trailer
{页数N(低16), CRC(高16)},CRC 范围 = N 页内容去掉末 4B(含 0xFF 填充)。 - 槽尾锚点(FULL 镜像用):A 槽末 4B(
slot_a末)写与 trailer 同值——BL 收包后会独立算出同值交叉验证。 - B 包(底包,固定槽):补 0xFF 至恰好填满槽(如 19,456B),槽末 8B 写记录
{magic16, pages16, crc16, 2B 0xFF},CRC 范围 = 槽内容去掉末 8B。若 B 固件 > 槽−8B → 报错拒绝(固定槽不可向后扩展,这是与 A 包的本质区别)。 - flag:
0xEEEE写flag.addr(FULL 镜像)。
4.3 整页扩展规则(重点)
对动态补页的 A 包:设记录长度 R(A=4B trailer),
余量 = N_pages*1024 − body_len;- 若
body_len恰为整页倍数,或余量 < R→ 向后扩展一页(N_pages+1,全 0xFF 补齐后写记录); - 扩展后仍须 ≤
slot_a.size,否则报错。 (B 包为固定槽,不适用扩展,见 4.2;若未来某工程 B 也动态化,须在 profile 中用fixed_slot:false显式声明并用同一规则。)
4.4 产物
*_FULL.bin:整片镜像 = BL + FW_A(补页+trailer+锚点) + 空隙0xFF + FW_B(满槽+记录) + flag;输出布局摘要与 MD5 清单(*.md5.txt)。*_UPDATE.bin:A 包(补页+trailer),供 Ymodem/OTA。*_B.bin(可选):满槽 B 包单独输出(产线备用)。- 兼容模式:
--legacy产出旧格式(纯 0xFF 补页、无 trailer),过渡期用。
4.5 校验(打包前硬门槛)
- 向量表:SP ∈ [0x20000000, 0x20002000];复位向量 Thumb 且落在声明槽内;
- 尺寸:A 包 ≤ 槽;B 包 = 槽(含记录);BL ≤
bl.size; - 写盘后回读复核(重算 CRC/trailer 一致才报成功);
- 输出布局摘要表(各分区起止/大小/页号/记录值)供人工核对。
五、回归基准(本项目实测值,重构后必须逐项复现)
| 用例 | 期望 |
|---|---|
| 1.0.17 原始 bin 19,580B | A 包 20 页/20,480B,trailer=锚点=0xD30F0014 |
| 1.0.18 原始 bin 19,580B | A 包 20 页,trailer=0x6F380014(CRC 0x6F38) |
| V1.0.12 B bin 19,456B(尾部已含 magic) | B 记录 = DE C0 13 00 6F 18 FF FF(pages=19,CRC=0x186F) |
| FULL 镜像 65,536B | 17 项字节校验全过(分区内容/空隙 0xFF/记录/flag) |
| 恰整页用例(构造 body=20480B) | 扩展为 21 页,trailer pages=21 |
| 余量<4B 用例(body=20477B) | 扩展为 21 页 |
六、GUI 与配套同步
- FlashTool(DAPLinkFlash):拼接/补齐弹窗升级为 profile 选择 + 双槽模式,与新 CLI 共用同一打包模块(抽公共库
fw_pack.py,CLI 与 GUI 仅为壳); - 本仓库 README 更新;
AGENTS.md进度同步; - skill
hulk-mcu-firmware:改开发仓库 → push Gitea →python scripts/update_tool.py同步缓存,SKILL.md 流程描述同步改。
七、实施步骤建议
- 抽公共库
fw_pack.py(profile 解析/校验/计算/复核),CLI 改造调用之,先跑通第五节回归基准; - 新增
--profile(默认内置 ceramixnano_mb_v51),保留--legacy; - GUI 接入;
- 文档/skill 同步、Gitea 发布。
八、提示词(可直接粘贴给 AI 会话)
"请重构 gd32e230_firmware_tool.py:按上文第三~五节实现布局 profile 化的双固件打包工具,抽公共库供 GUI 复用。所有 CRC/记录/补页/整页扩展规则必须与第四节逐字一致,并以第五节回归基准作为验收(构造测试 bin 跑通全部用例)。要求:纯标准库、Windows 兼容、输出布局摘要与 MD5 清单、打包后回读复核;保留 --legacy 兼容模式;不要动 FlashTool/ 之外的无关文件;完成后更新 README 与 AGENTS.md。"