15aa08552c
用户裁定:该报告大部分内容已闭环,可删除;仍有价值的迁到合适文档。删前逐条核对, 确认绝大多数结论在仓内早已有权威副本(LV_DRAW_SW_COMPLEX / osFlagsNoClear / SPI 分频 8 / 分层反向依赖存量基线等),只迁真正独有的四类内容: 1. 暂缓理由与重开条件(三条量化门槛)→ RAM 使用分析与优化方案 §4,并明确「本节是唯一 权威」,其它文档只留指针;同时把 10 处指向报告 §5.3 的重开条件引用改指该节。 2. 2026-10-22「常驻 vs 动态」三项诊断(静态前五块 43,462 B = 58%;动态面只有 heap_4, 故栈余量=CCM 余量;栈深主因是日志/配置写链)→ 同文档 §2.5。 3. `_fp_digits` 1,018 B 删不掉的机理与 F-6 根因修法(Q10 宏改单精度,−332 B,4.49e6 例 逐位等价)→ §2.6.4。 4. 「UI 侧缓冲进 CCM」要按 DMA 可达性二分(LVGL 池可进,帧缓冲/推流缓冲不可)→ 暂缓清单。 编号侧(报告曾是 6 个族的属主登记处): - 原报告 §9 的 26 行「号 → 现在详述于」对照表迁到 docs/编号规范.md 新增 §4.5,6 族 owner_docs 改指该节。规范正文含大量写法示例,直接当定义集会误伤,故 check_ids.py 新增 `_def_owners`:snapshot_only 族剔除规范正文,只恢复可判定、不参与悬空/撞号/裸形 /紧邻判定(实测若不分,裸形 S1 误报 238 条、S-3 与登记簿 S-3 误判重复定义)。 - 11 个已死号(N-03b/N-10~N-13/N-20/N-21/N-24/N-26/S-1/S-2)在 id_aliases.json 登记 status=void 并写明去处——它们被约 110 处代码注释与文档引用,原先只靠「报告里顺带提过 一句」才算有定义,报告一删就集体悬空。N-21/S-1/S-2 一号两义,注记已分别标明。 - 顺带修 id_aliases.json 的 note 被脚本累积重复 26 次;修 registry 里 3 处仍指向改名前的 任务卡文件名(W1-采集保护收尾.md / W8-数据可信.md),以及 iec104_master.py 两处同类注释。 门禁:check_ids --strict 通过(可判定 8747/10410 = 84.02%,基线由 82.6% 抬到 84.0%); check_doc_links --all --sections-strict 通过(177 文档);pre-commit 相关钩子全过。
12 KiB
12 KiB
bsp-err — BSP 错误码与超时命名约定
一句话:它管
Drivers/BSP/**对外 API 的四件事——用什么类型表达失败、超时宏叫什么、 有没有把返回值写进注释、调用点有没有真的去看返回值。它不管错误码用得对不对(见 §6)。
- 配置:
.pre-commit-config.yaml第 579 行起(entry: python scripts/check_bsp_err.py、language: system、pass_filenames: false)。 - 规则脚本:
scripts/check_bsp_err.py(纯标准库;文件头就是规则正文)。 - 约定正文:
Drivers/BSP/bsp_err.h(header-only,无.c→ 不占体积、不需双端注册)。 - 背景与整改史:S-5 条目的完整记录已随审查报告删除,现存依据 = 登记簿 S-5 行与
Drivers/BSP/bsp_err.h。
1. 它在查什么
四项检查,每项一个自描述标签(就是基线键的第二字段,键格式 文件|标签|名字):
| 标签 | 判据 | 拿它挡的具体写法 |
|---|---|---|
err-type |
公开 API 的返回类型不得是 HAL_StatusTypeDef;也不得是 bool(除非是谓词形状) |
用 bool 表达成败——"为什么失败"整条丢掉;用 HAL 枚举——把 HAL 焊进调用方的 include 链 |
timeout-name |
#define 出来的 *TIMEOUT* 宏不得取负值 |
名字叫 TIMEOUT 实际是返回码(本仓曾有 GD5F_BUSY_TIMEOUT = -2、TPAFE5160_BUSY_TIMEOUT = -2,已改) |
return-doc |
非 void 的公开 API,声明上方紧邻注释块里必须出现"返回值"(匹配时忽略空格) |
只写函数功能不写成败含义 |
consume-result |
返回错误码的函数,调用点必须接住返回值或显式 (void) 丢弃;裸调用即违规 |
HAL_SPI_Transmit(&hspi1, cmd, 3, T); 后面照旧 return OK |
consume-result 的"哪些函数算返回错误码"是自动取名单,不手写(手写名单必然与代码脱节):
- 头文件里返回
bsp_err_t的公开 API(契约规定bsp_err_t就是错误码); - 返回
int/int32_t且其文档块出现"错误码 / 失败 / BSP_ERR /_FAIL"的函数; - 已知返回
HAL_StatusTypeDef的 HAL 族(HAL_{SPI,I2C,UART}_{Transmit,Receive,TransmitReceive}及其_DMA/_IT变体、HAL_I2C_Mem_{Read,Write}); - 与 2 同一套启发式,再扫一遍
.c里的 static 定义——只扫头文件等于只守跨编译单元的边界。
2. 为什么要它
- 这一层今天六套约定并存:0/−1、自研枚举
CH395F_STATUS_*、芯片寄存器原生码CH395F_ERR_*(与前一套同模块并存)、HAL_StatusTypeDef、bool,外加第三方 dhara 的dhara_error_t与 FatFs 的FRESULT。真正值钱的不是码名好看,而是"能不能重试" 以前只写在个别注释里——现在BSP_ERR_CAN_RETRY(e)把它变成跨模块可判定。 - 它上线当天就照出真窟窿:
consume-result落地即暴露 37 条存量丢弃,其中一处是bbt_rebuild重新打开 ECC 的那两行返回值全丢——SPI 一抖 ECC 就保持关闭,而 ECC 关闭时 ECC 检查恒读"无错",整条链再没有任何信号。修法与运维判据见docs/测试说明/存储维护与容量恢复.md§4.3。 - 没有工具会替我们做这件事,两条实测负结果(本门禁存在的正当性全在这两条上):
①
__attribute__((warn_unused_result))在 ARMCC v5 上完全不生效——属性挂上、调用点 裸调不接返回值,构建仍 0 错 0 警告(在rs485_transmit上做过实验); ② cppcheck 也照不出来——按本仓自己的--enable=warning,style,performance,portability跑gd5f2gq5ue.c,对 14 处丢弃HAL_SPI_*返回值、以及"未初始化局部量直接进P_FAIL判定"一条不报(它假定被调函数会写出参,而失败路径根本不写)。
3. 触发面
files: ^Drivers/BSP/|^scripts/check_bsp_err\.py$|^\.pre-commit-config\.yaml$
pass_filenames: false # 恒扫全库:要按 git 清单建公开头索引,不能只看暂存的那几个文件
- 扫描对象来自
git ls-files Drivers/BSP:err-type/timeout-name/return-doc看.h(对外 API 面),consume-result扫.c(33 个文件)。 lftpd/排除:它的226/425/450/550是 FTP 协议码,不是内部错误码。net_*BSD 兼容层不纳入统一码(用户裁定 2026-10-23,见Drivers/BSP/bsp_err.h边界纪律 3): 调用方要的就是"返回字节数 + 每任务 errno 槽"那个形状,模拟器的 Winsock 后端照同一形状实现。 ⚠ 但**"不纳入码"不等于"可以丢弃返回值"**——consume-result照样管net_close这类调用。- 因为
files:覆盖到.pre-commit-config.yaml与脚本自身,改门禁必然触发它自己。
4. 报错怎么读
$ python scripts/check_bsp_err.py
BSP 约定:命中 38(在册 38 / 新增 0),基线共 38
| 输出 | 意思 | 做什么 |
|---|---|---|
新增 N>0 + [FAIL] |
出现了基线之外的新违规 | 按提示改代码(返回 bsp_err_t / 时长宏带 _MS / 补「返回值:」/ 接住返回值或写 (void)) |
[提醒] 有 N 条基线已还清 |
你把存量修掉了 | 跑 --prune 拿到清单,从脚本的 BASELINE 里删掉那些行,再跑一次确认 0 提醒 |
命中 0 |
干净 | — |
| 本地跑出与暂存无关的假报警 | pre-commit 会 stash 未暂存改动,钩子看到的是"没有你改动"的树 | 先 git add 再跑钩子(本仓踩过:rs485 试点期被假报一批) |
三个只读旗标:--verbose(列出全部命中并标注在册/新增)、--prune(只报已消失、可删的基线)、
--emit-baseline(打印当前全部键,重建基线用)。脚本刻意没有 --write:基线是人审后手改的,
不提供"一条命令把现状洗成合法"的入口。退出码 0 通过 / 1 有新增 / 2 用法或扫描失败。
5. 好处
- 把"约定"从口头搬到机器上:新代码想沿用旧习惯,提交就被拦,不需要人在评审里记着。
- 存量与新增分得开:基线让历史债务不阻塞日常工作,"只减不增 +
--prune验收"保证债只会变小。 - 可审计:
--verbose就是债务清单;每条基线是文件|标签|名字,能对着代码定位。 - 零运行时成本:header-only 契约 + 纯标准库脚本,不进固件、不需要构建开关。
6. 限制与盲区(读这篇最重要的部分:绿灯不等于没问题)
- 不管错误码用得对不对。它只判通道类型、命名、有没有文档、有没有被消费。把
BSP_ERR_TIMEOUT用在"参数非法"上,它一声不响——那是语义问题,机器判不了。 - 范围只有
Drivers/BSP。App/**、simulator/**、Src/**不在面内;应用层的同类问题 今天仍然只能靠评审。 consume-result只认单行整句的裸调用(foo(...);一行写完,行尾注释允许)。跨行写的调用 ——参数换行、或;在下一行——按正则形状测不到。这是判据的物理限度,不是漏配。- 名单是启发式:靠"返回类型 + 注释关键词"认函数。注释里不写"错误码/失败"的函数会漏
(假阴性);反过来,把谓词写成
foo_ok()这种不在豁免形状里的名字就会被判违规(假阳性, 见 §7 的两次收窄)。判据依赖命名形状,改名就能绕过规则——这是所有基于正则的门禁共同的洞。 - HAL 族是固定清单(§1 第 3 条)。清单外的返回
HAL_StatusTypeDef的函数(如 FLASH、SD 卡 那批)不在名单里;将来用到就得多补一行正则,属于人工维护的盲区。 err-type/return-doc只看头文件里的声明形状:用宏包出来的 API、函数指针表里的 API、 通过typedef起的函数别名,都不在它的视野里。return-doc的判据是"紧邻注释块里出现『返回值』字样",散文里出现这三个字也会放行。 这不是假想:本仓gd5f_write_enable、gd5f_read_status曾长期"通过",原因竟是它们的 函数说明里顺口写了"返回值"三个字,而不是真有一行返回值说明。一条会因为散文里出现关键词 而放行的规则,等于在奖励运气——已改为忽略空格后匹配(规范 V1.0 允许列对齐写法「返 回 值:」, 门禁不规定排版),但"关键词出现即放行"的性质没有变,只能靠评审看内容。- 基线键带出现序号(
gd5f_read_status#2)。含义是"该文件里该函数的第 2 处丢弃",不是位置。 于是:删掉靠前的丢弃会让后面的序号整体前移,--prune只报"消失的键",不会告诉你某处只是 搬了家;反过来,同函数新增一处丢弃会多出一个新序号 → 会被判新增(这点是好的,实测过)。 - 改标签名 = 改基线键。今天把
R1~R4换成现名时,必须脚本与 38 条基线一起换,中途跑--prune会把改名读成"38 条全还清 + 38 条新账"。同理,今后任何标签改名都是一次原子改动, 不能分两个提交。 - 不提供重试 helper,也不检查重试策略(有上限吗、喂看门狗了吗、跨就绪屏障等待了吗)——
见
Drivers/BSP/bsp_err.h边界纪律 5:一个通用while包装太容易被拿去包住无限等待。
7. 豁免与基线
- 基线在脚本里的
BASELINE常量(不是外部台账文件),只减不增,没有--write。 - 当前在册 38 条(2026-10-23):
·
consume-result37 条 ——gd5f2gq5ue.c28(14 处丢弃HAL_SPI_*状态:HAL_SPI_Transmit×11、HAL_SPI_Receive×3;14 处丢弃自家原语:gd5f_write_enable×4、gd5f_read_status×3、gd5f_bbt_save×1、gd5f_block_erase×1、gd5f_page_read×1、gd5f_program_exec×1、gd5f_program_load×1、gd5f_read_from_cache×1、gd5f_wait_busy×1)、ui_net_sink.c6(全是net_close)、rs485.c2(rs485_receive_start)、nand_ftl.c1; ·err-type1 条 ——bool ui_content_page_open():要改签名并处理 3 个调用点(三处今天全部 丢弃返回值,即"按 ENTER 开页失败是静默的"),与"补注释"不是一档事,见报告变更记录。 - 形状白名单(写进规则,不塞基线):
· 谓词
is_/has_/can_/should_/need_/valid_——返回值是"提问"而不是"办成没有"; · setter(?:^|_)set_...$——返回的整数是状态回声而不是调用成败(实跑误判出net_set_errno八处); ·mark_block_bad——今天返回void,预先豁免,防它将来加错误码返回时把整批调用点变成违规。 - **原则:规则按真实反例收窄,不拿基线洗假阳性。**本仓第一版
err-type误判 7 个正当谓词, 改窄而不是入基线;consume-result两次同类处理见上面两条。
8. 改这个门禁本身
- 改正则先做正反例自测:写一段一次性 python,把"应当命中"和"不应命中"的样例各列几条,
exec脚本头部拿到正则再断言(今天三次修正则都是这么验的,6 条样例级别即可)。 - 假阳性只能改规则;把它加进
BASELINE是洗规则,会把真问题一起豁免掉。 - 加新的 must-use HAL 族:改
HAL_STATUS_RE,同时补一条正反例自测。 - 改标签名:脚本与基线一次改完(§6 第 9 条),随后
--emit-baseline复核数量不变。 - 门禁自己的注释里不要写只有本次会话懂的代号;要引用规范就写
文件.md+ 紧邻编号。
9. 变更记录
| 日期 | 变更 |
|---|---|
| 2026-10-23 | 首版三项检查(错误通道 / 超时宏命名 / 必须写返回值)随 S-5 契约落地,存量基线 19 条 |
| 2026-10-23 | 还掉 18 条缺「返回值:」;return-doc 改为忽略空格匹配(此前两条因散文里出现"返回值"而误判通过) |
| 2026-10-23 | 新增第四项 consume-result,上线暴露 37 条存量丢弃;名单扩到 .c 内 static 定义;修两次判据(行尾注释漏判、setter 误判);同日修掉 bbt_rebuild 的 ECC 开关丢弃 |
| 2026-10-23 | 四项由 R1~R4 改名为语义标签(该裸形在本仓与"专项风险"编号撞车,且编号门禁因同号不同义白名单不会报);基线 38 条随改名一起换 |