GD5F页读回归主线单发令(settle与二次发令移除、3.1V误判更正归一Trap05)、新增Phase11复现用例(16轮零复现)、附台架环境与审计文档
This commit is contained in:
@@ -155,7 +155,7 @@ ST-LINK_CLI -c SWD HOTPLUG -Dump 20000000 1000000 dump.bin :: 转储 SRAM
|
||||
| `-P <文件> [地址] [ske] [skpv]` | 编程(ske=跳擦除 skpv=跳校验) |
|
||||
| `-V [after_programming]` | 校验 |
|
||||
| `-ME` / `-SE <扇区>` | 全片擦除 / 扇区擦除(⚠️ RDP≠0 时 `-ME` 会被拒) |
|
||||
| `-TVolt` | 目标 VDD 电压 |
|
||||
| `-TVolt` | 目标 VDD 电压(⚠️ 需 VDD 感应脚接至目标;本机台架 3 线连接(SWDIO/SWCLK/GND)此脚未接,读数无效,供电判据以万用表实测为准) |
|
||||
| `-Q` / `-Log` / `-NoPrompt` | 静默 / 生成跟踪日志 / 免交互确认 |
|
||||
| `-List` | 枚举所有已连接探针(固件版本 + 串号) |
|
||||
|
||||
@@ -192,7 +192,7 @@ ST-LINK_CLI -c SWD -P "MDK-ARM\STM32F407-Demo\STM32F407-Demo.hex" -Rst -Run
|
||||
|
||||
ⓞ 注意:`UR`(复位态连接)在本机不可用(nRST 未接线)——跑飞救援能力受限,能做的只有"正常连接 + LPM + 全擦重烧";若板子连 SWD 都握不上手,需先补 nRST 线。
|
||||
|
||||
**D. 运行中检查**:`-c SWD HOTPLUG -SCore` / `-R32` / `-TVolt` / `-Dump`。
|
||||
**D. 运行中检查**:`-c SWD HOTPLUG -SCore` / `-R32` / `-TVolt`(⚠️ 本机 3 线连接下读数无效,见命令表注记)/ `-Dump`。
|
||||
|
||||
## 6. `reset.bat`(已落工程 `MDK-ARM\reset.bat`,2026-09-19 上板验证)
|
||||
|
||||
@@ -226,43 +226,3 @@ ST-LINK_CLI -c SWD -P "MDK-ARM\STM32F407-Demo\STM32F407-Demo.hex" -Rst -Run
|
||||
| SWD 通信不稳 | 不传 `SWCLK` 用默认时钟(本机实测 4MHz);检查长线/夹子接触 |
|
||||
| 误改 option bytes | `-rOB` 读回现状;`-OB RDP=0` 等回写后需再复位生效;**RDP=2 会锁死调试+全片擦除,操作前务必确认** |
|
||||
| 多板混淆 | `reset.bat list` 拿串号,`-c SN=<串号>` 指定 |
|
||||
|
||||
## 8. 与 J-Link 命令的对照(备查)
|
||||
|
||||
| 操作 | ST-LINK_CLI(本机已装) | JLinkExe(SEGGER,未安装) |
|
||||
|---|---|---|
|
||||
| 复位并运行 | `-c SWD -Rst -Run` | `-device STM32F407xG -if SWD -speed 4000 -reset -go` |
|
||||
| 复位停核 | `-c SWD -Rst -Halt`(ⓞ 必须显式 `-Halt`) | `... -resetHalting` |
|
||||
| nRST 脉冲 | ❌ 本机不可用(nRST 未接线 + v3.5.0 不支持 `RM=`) | 需 nRST 接线 + J-Link 引脚控制脚本 |
|
||||
| 烧写并运行 | `-c SWD -P x.hex -Rst -Run`(实测 6.5s/1MB) | `-loadfile x.axf -reset -go` |
|
||||
| 运行中检查 | `-c SWD HOTPLUG -SCore` | 需先 `-noReset` 连接或用 GDBServer |
|
||||
|
||||
## 9. 上板实测记录(2026-09-19)
|
||||
|
||||
- 目标:STM32F407ZGTx(CPUID 0x413,Flash 1MB),V2 CPU 板
|
||||
- 探针:ST-LINK/V2,FW **V2J37S7**,SN `0B250D12A218303030303032`;**接线 3 根(SWDIO/SWCLK/GND),nRST 未接**
|
||||
- VDD 实测 **3.136V**(偏低,正常 3.3V——当前走 PC 端口供电,长期/大电流场景建议改 5V 供电)
|
||||
- 串口 **COM7** @115200(板子重插后重新枚举,`serial_config.json` 已同步)
|
||||
- 板内固件:测试前半段为较早的 ui-research 线构建(引脚集未知);13:59 起为 **v2-on-ui 构建(ui-research + V2 引脚补丁,10:03 编译)**
|
||||
|
||||
| # | 时间 | 命令 | 结果 |
|
||||
|---|---|---|---|
|
||||
| 1 | 13:40 | `-List` | ✅ 识别探针(V2 / V2J37S7 / SN 如上) |
|
||||
| 2 | 13:41 | `-c SWD HOTPLUG -SCore` | ✅ `Core is Running`(连接不扰运行中目标) |
|
||||
| 3 | 13:41 | `-c SWD HOTPLUG -R32 0x08000000 4` | ✅ 首字=0x2001E8B8(192K SRAM 的 IRAM2 区)→ Flash 程序有效 |
|
||||
| 4 | 13:41 | `-c SWD HOTPLUG -TVolt` | ✅ 3.136V |
|
||||
| 5 | 13:43~13:55 | `-c SWD -Rst -Run`(多次,含 `reset.bat` 默认分支) | ✅ 每次均重启成功:`App init OK` / `Network Init` / `net init: OK` 齐备,PHY 100M FULL,UI port/menu init OK,0 条 FAIL/ERROR/HardFault |
|
||||
| 6 | 13:44 | `-c SWD -Rst -SCore` | ⓞ 复位后核为 `Running`(不停核)→ 停核必须加 `-Halt` |
|
||||
| 7 | 13:44 | `-c SWD -Rst -Halt -SCore -CoreReg` | ✅ 停核:SP=0x2001E8B8、PC=0x08004664(启动代码中段)、xPSR=0x21000000 |
|
||||
| 8 | 13:45 | `-c SWD RM=Hrst -HardRst PULSE=100`(4 种语法排列) | ❌ v3.5.0.0 全部拒 `RM=`(`Unknown debug protocol or option`)→ CLI 层无 nRST 能力(且本机 nRST 未接线) |
|
||||
| 9 | 13:4x | `reset.bat list / (默认)/ hard / halt / run` | ✅ 全分支 exit=0;`hard` 分支按设计回退软件复位(RESET_NOTE);`halt` 断开后核自动恢复运行 |
|
||||
| 10 | 13:59 | `reset.bat flash`(= `-P <hex> -Rst -Run`) | ✅ `.hex` 直接支持:**1MB 编程 6.5s**,`Programming Complete` → `MCU Reset` → `Application started`,exit=0 |
|
||||
| 11 | 13:59 | 烧录后启动观察 | ⚠️ **V2 引脚固件启动异常**:日志到 `[APP] RS485 init: OK` 后中断(无 GD5F/TPAFE/存储/UI 后续日志,无二次重启);核状态回读:`Core is Running`,**PC=0x08000FA0**(启动区死循环,LR=0x08000FA5,SP 正常推进)→ 根因 2026-09-20 闭环:芯片上是合并前旧向量表镜像(DMA2_Stream3 中断落空槽 → `B .` 死循环,PC=0x08000FA0 即该死循环),且此后所有"重下载"均静默失败(芯片从未更新)→ 详见 §10 |
|
||||
|
||||
**结论**:
|
||||
|
||||
1. **命令行复位成立**:`ST-LINK_CLI -c SWD -Rst -Run` = 板子重启(多次实测全部成功、开机日志完整)——这就是"命令控制板子重启",3 线接线完全够用。
|
||||
2. **本机复位能力 = 软件系统复位**:nRST 未接线 + v3.5.0 CLI 不支持 `RM=`,nRST 引脚级操作不可用(`reset.bat hard` 自动回退,不影响脚本闭环);需要时补 nRST 线 + 升级工具链。
|
||||
3. 停核 = `-Rst -Halt`(停点不保证是复位向量,实测 0x08004664);一次性连接语义——断开即恢复运行。
|
||||
4. **烧录路径打通**:`-P <hex>` 实测可用(1MB ≈ 6.5s,比 UV4 快得多),`reset.bat flash` 可作为快速烧录通道。
|
||||
5. ✅ **问题已闭环(2026-09-20)**:09/19 启动卡死(PC=0x08000FA0)根因 = 芯片一直是合并前旧向量表镜像(09/19 之后所有下载均静默失败:裸 `-P` 不带 `-Rst -Run` 不落地)+ 内核启动前 `osMessageQueueNew` 代码缺陷;修复已提交(v2-on-ui `82411cf`),V0.1 板完整启动已验证(63 行日志 + 全片 `-CmpFile` 一致,见 §10)。
|
||||
|
||||
@@ -160,10 +160,12 @@ if (ok) {
|
||||
无 reformat 行、`[CFG] load: OK (523B)`(config.ini 跨复位存活)、VERDICT PASS。
|
||||
|
||||
**调查中被证伪的假设(重要反面记录)**:
|
||||
1. **低 VDD"幻影编程"假设——证伪**:怀疑 3.1V 供电下 program 状态报成功
|
||||
但 cell 未 latch。裸 NAND 持久性自检(块 2040/2041:擦→写 4 页图案→
|
||||
1. **"幻影编程"假设——证伪(低 VDD 归因同时作废)**:当时怀疑"3.1V 欠压供电"下
|
||||
program 状态报成功但 cell 未 latch。裸 NAND 持久性自检(块 2040/2041:擦→写 4 页图案→
|
||||
立即回读→跨复位再回读 seq 递增)实测 4/4 全过:芯片、驱动原语、供电
|
||||
在同一台架上的持久性完全正常。
|
||||
在同一台架上的持久性完全正常。**2026-09-21 更正**:实测供电为 3.3V 标称,
|
||||
"3.1V 欠压"前提不成立(3.1V 数字源自 ST-LINK VDD 感应脚未接线的悬空读数),
|
||||
本假设的低 VDD 归因作废;4/4 结果本身不受影响。
|
||||
2. **"journal 混合页/数据损坏"假设——误读**:所谓"LPN16 恒为 `RRaA`
|
||||
垃圾、LPN0 全零、LPN15 全 FF"实为**合法卷内容**——本移植
|
||||
`f_mkfs(FM_FAT32)` 创建的是 **MBR 分区卷**(分区起点 LBA 63):
|
||||
@@ -172,8 +174,10 @@ if (ok) {
|
||||
0x41615252 "RRaA" 是合法签名!)。判定"损坏"前默认了几何=整盘 FAT32,
|
||||
把合法结构当成了花屏。
|
||||
3. 三个先前修复(mkfs 前清 FTL 树 ac2087e、page_read 二次发令 38b70ad、
|
||||
读后 2ms settle)**都没有终结循环**——它们是合理硬化(全部保留),
|
||||
但不是根因。
|
||||
读后 2ms settle dc6c206)**都没有终结循环**——它们是合理硬化,
|
||||
但不是根因。(2026-09-21:2ms settle 已移除——其"低 VDD"前提与
|
||||
"混合页"证据双双证伪,见本条 1 更正与 ST-LINK 文档命令表注记;
|
||||
二次发令同日移除——Phase 11 对照复现(16 轮编程擦除风暴 × 48 次立即/延迟整页字节比对)零复现 13h 空操作 / WIP 提前清零,页读回归主线单发令形状;mkfs 清树保留。)
|
||||
|
||||
**定界决定性手段**:
|
||||
- 裸 NAND 跨复位持久性自检 → 排除介质/驱动/电源,指向文件系统层;
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
# 错误码传播审计报告(全项目)
|
||||
|
||||
> **模块名称**:全项目错误码/返回值/状态传播审计
|
||||
> **文档目的**:以 2026-09-21 网络错误码事故(errno 覆盖 → 远端显示误断连)为起点,对全项目做系统性
|
||||
> 错误码传播缺陷普查:哪些机制会把"成功"传给调用方(而状态实际是失败)、哪些通道会让错误码丢失/串扰/
|
||||
> 永不可达,并按 P0/P1/P2 给出修复计划。**本文档只读审计,不修改任何代码。**
|
||||
> **日期**:2026-09-21
|
||||
> **审计方法**:按 9 类缺陷模式(P1 值拷贝队列陷阱 / P2 共享全局错误变量竞态 / P3 返回值与错误码混同 /
|
||||
> P4 IPC/HAL 状态未检查 / P5 事件标志语义 / P6 通知值宽度与符号 / P7 ISR 与跨上下文共享状态 / P8 外设与
|
||||
> 库特定约定 / P9 超时与错误混同)逐子系统审查;三个并行审计组(配置/日志 IPC、驱动层、net 消费方/模拟器)
|
||||
> + 父审计组(net 核心/事件标志/UI/启动路径)交叉覆盖,**全部关键发现经父审计组逐行代码复核**(含芯片手册
|
||||
> 原文核对)后才入册。
|
||||
> **配套文档**:`docs/项目审计改进清单.md`(交叉引用见 §6)、`docs/问题记录/*_Trap_Records.md`(模块陷阱史)、
|
||||
> `docs/网络配置说明.md`、`AGENTS.md`(09-21 教训已固化条目)。
|
||||
|
||||
---
|
||||
|
||||
## 0. 摘要
|
||||
|
||||
**结论**:09-21 的确定性缺陷族(值拷贝回传字段、就绪位消费、EOF 关监听)已修复并在位;本次全项目普查
|
||||
**新确认 59 项**错误码传播缺陷:**P0×2、P1×22、P2×35**。2 项 P0 均为"值说成功、状态说失败"的静默
|
||||
数据丢失路径,均可在生产触发且当前零告警。(2026-09-21 复核修订:E-32 经手册 §4.8"出厂配置 82H"证伪由 P0 降 P2;
|
||||
E-60"write_enable WRTC2 与手册矛盾"经三方复核(手册表头 D7~D0 顺序 + DFRobot 官方库/社区代码同序列 0x10←0x80、0x0F←0x84)证伪——0x84 本已含 D2=WRTC2,代码与手册一致,详见 §7-19)
|
||||
|
||||
| 级别 | 含义 | 数量 | 编号 |
|
||||
|---|---|---|---|
|
||||
| P0 | 生产可触发的活跃缺陷(静默数据丢失) | 2 | E-01, E-31 |
|
||||
| P1 | 故障条件下的潜在陷阱(触发需特定时序/工况,后果重) | 22 | E-02~E-10, E-33~E-45 |
|
||||
| P2 | 健壮性/一致性/可维护性 | 35 | E-11~E-30, E-32, E-46~E-59 |
|
||||
|
||||
### P0 速览
|
||||
|
||||
| 编号 | 位置 | 一句话 |
|
||||
|---|---|---|
|
||||
| E-01 | `App/cfg_file.c:1151` | `cfg_file_poll` 的"文件超长=损坏"检查是死代码(读长被钉死在阈值)→ >4096B 配置文件被截断解析当有效 → 界外配置静默回工厂默认 → 基线粘住错误状态 → 下次开机 load 守卫判"损坏" → **用户配置文件整体销毁** |
|
||||
| E-31 | `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c:250-252` | `dhara_map_resume` **任何**失败(含单次瞬态页读失败)→ `dhara_map_clear` → **整卷逻辑映射静默丢弃** → FatFs 探针判卷无效 → 整卷重格式化 → 全部用户数据丢失;dhara 本可区分"真空白"(合法)与 ECC/损坏(必须上报),glue 层把错误分类全坍缩 |
|
||||
|
||||
### P1 速览(22 项)
|
||||
|
||||
| 编号 | 子系统 | 一句话 |
|
||||
|---|---|---|
|
||||
| E-02 | net 执行器 | **队列头阻塞**:任一消费者的阻塞 recv(30s)在 netTask 上下文空转 → 全体消费者的队列消息停摆最长 30s(事件仍处理);FTP 客户端空闲 >30s 即可触发 |
|
||||
| E-03 | net 层 | 全局 `s_net_errno` 跨任务覆写竞态(09-21 修复的**残留**):恢复与读取之间隔任务切换,健康连接可被误拆(ui_sink :460 每 10ms 读) |
|
||||
| E-04 | UI 远端推流 | `remo_send` 丢弃 `net_send` 返回值 → 3205B 响应弱网短发送**静默截断**(09-21 事故机制的调用方一侧未闭环) |
|
||||
| E-05 | net 层 | 3 任务直调 `net_socket/net_bind/net_getsockname` 跨任务写 netTask 属主的 socket 表 → alloc/close 交错可双分同一 fd(SPI 铁律未破,表属主破了) |
|
||||
| E-06 | lftpd | `lftpd_inet_accept_timeout`:`elapsed+=100` 计时在队列阻塞下失真(名义 5s → 实际十几分钟)+ 跨任务 state 预检误判 → PASV 偶发假 425 |
|
||||
| E-07 | 存储 | `f_close`(本移植 FTL 的提交点)返回值 3 处丢弃 → 配置原子写/日志追加**假成功**(f_write 可 FR_OK 而数据仅在 cache),掉电静默丢失 |
|
||||
| E-08 | 日志 | preboot 日志数组"单生产者免锁"前提在内核后不成立(队列惰性创建前,多任务并发无同步读-改-写)→ 自检记录丢失/重复 |
|
||||
| E-09 | 配置 | UI 读-改-写 RAM 配置副本 × defaultTask poll 原地更新竞态 → 陈旧写回可抹掉 FTP 外部编辑(P1-3 残余,从引擎静态态转移到 RAM 层) |
|
||||
| E-10 | NAND | init 单次无重试 + 内部超时/扫描失败全吞 → 冷启动瞬态 SPI 毛刺 = 好芯片判"NAND fault"整次开机无存储;反向慢芯片 = 假 OK + 垃圾坏块表 |
|
||||
| E-33 | CH395F | DMA 数据路径 `void` 无错误通道:HAL 失败/1s 超时静默丢发;**收路径无条件 memcpy 陈旧 DMA 缓冲冒充新数据** |
|
||||
| E-34 | CH395F | 字节级命令路径 `HAL_MAX_DELAY`(无限超时)且返回值未检查 → SPI2 状态机卡死 = netTask 永久挂死(系统无看门狗) |
|
||||
| E-35 | 测试 | CH395F 套件 Phase5 块长 4093B > DMA 缓冲 2052B(MSS 2048→1024 后常量未同步)→ 整块静默丢弃,失败签名误导为 "rx stall" → 质量门禁失效 |
|
||||
| E-36 | RS485 | ISR 重武装清零 `rx_size` → 文档化的 `rs485_rx_get_size` 对任务**恒返回 0**("无数据"误判族;消费者休眠中,首个接入即触发) |
|
||||
| E-37 | RS485 | `rs485_init` 吞掉 RX 武装失败(`(void)`)→ UART 状态异常时 485 接收终身静默死亡 |
|
||||
| E-38 | SD2506 | 负温度解码 `(int8_t)(val\|0xF0)` 在 ≤-17°C 全错(芯片用二补码;-30°C 会报 -14°C)且返回 0=OK 无指示(=既有 P2-5,本次升级 P1) |
|
||||
| E-39 | SD2506 | ARST 在 CTR3(11H) bit7,驱动**从未写过 CTR3**(全库 grep 证实)→ ARST 恒为复位值 0 → `clear_alarm` 的读清除契约**永久不可达**(首次报警后 INTAF 永久锁存,API 返回 0 但什么都没清) |
|
||||
| E-40 | FTL | `dhara_nand_erase/prog` 把**瞬态**(BUSY_TIMEOUT/ERROR)与单元缺陷(E_FAIL/P_FAIL)混同为坏块并持久化 → 容量永久损失(09-21 容量 95248→95200 事故的机制性同根) |
|
||||
| E-41 | GD5F | `gd5f_bbt_save` 编程失败仍推进 BBT 版本并返回 OK(`void` 化 API 结构性丢失持久化失败信息)(=既有 P2-3) |
|
||||
| E-42 | GD5F | `gd5f_bbt_scan` 两个读返回值全弃 → 坏块判定基于上一块/未初始化 `spare[0]`(好块判坏或坏块放行) |
|
||||
| E-43 | GD5F | `gd5f2gq5ue_erase` 缺容量边界检查(read/write 有,Trap 03 文档与代码不符)→ 越界 erase 的行地址回绕可**静默擦掉块 0**(journal/BTT 所在) |
|
||||
| E-44 | TPAFE | `read_all` 无任何数据有效性校验——头文件承诺的 FRSTDATA 首通道校验**未实现**(宏有定义、代码零引用)→ 芯片死亡时返回 OK + 常量垃圾 |
|
||||
| E-45 | TPAFE | `s_ready` 三写者:`start_conv_irq` 触发前置 0 → 重触发即清除未消费帧(电平标志坍缩,高速采样系统性丢样) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与方法
|
||||
|
||||
### 1.1 起点
|
||||
|
||||
2026-09-21 台架事故:`net_msg_t` 按值拷入队列 → netTask 对 `result` 字段的回写在调用方不可见 →
|
||||
"错误码"读到的是栈垃圾(恒 0)→ `ui_net_sink` 把每次空轮询的 `WOULDBLOCK` 误判为真错误 → 远端连接
|
||||
建立即被 `net_close`(监听 fd 同毁,端口"连接被拒",RemoDispBus 7003 无画面)。修复(`c5a9e8c`/
|
||||
`a9aa8ce`):错误码改搭 `xTaskNotify()` 通知值回传(失败=负的具体错误码,成功=字节数/描述符),包装器
|
||||
在**调用方上下文**恢复全局 errno,成功路径不清 errno。AGENTS.md 已固化"勿给 `net_msg_t` 加回传字段"铁律。
|
||||
|
||||
本次审计回答的问题是:**同一个"值说成功、状态说失败"的缺陷族,在项目的其他子系统里还有多少活体?**
|
||||
|
||||
### 1.2 缺陷模式定义(审计用分类法)
|
||||
|
||||
| 模式 | 定义 | 09-21 原型 |
|
||||
|---|---|---|
|
||||
| P1 | 值拷贝队列陷阱:结构体按值入队,消费端字段回写对生产端不可见(指针字段安全) | `net_msg_t.result` 回传字段 |
|
||||
| P2 | 共享全局错误变量跨任务竞态:写者与读者不在同一执行流,中间隔任务切换 | `s_net_errno` |
|
||||
| P3 | 返回值/错误码混同:0 重载、短写、多套码系(FR_/HAL/负 errno/芯片原生码)互译缺失 | "FR_OK=成功"掩盖短写 |
|
||||
| P4 | IPC/HAL 状态未检查:队列 put、HAL 调用、FTL 提交的返回值被丢弃或 `(void)` 化 | — |
|
||||
| P5 | 事件标志语义错位:状态位被消费型等待清掉 / 命令位被状态型等待挂着 | `NET_READY` 被 `WaitAll` 消费 |
|
||||
| P6 | 通知值宽度/符号/通道碰撞 | 通知值复用 |
|
||||
| P7 | ISR/跨上下文共享状态无同步(表属主、双缓冲、重武装竞态) | — |
|
||||
| P8 | 外设/库特定约定违背(写保护、提交点、DMA 缓冲契约、字面量方言) | FatFs FA_OPEN_APPEND 无 FA_WRITE |
|
||||
| P9 | 超时与错误混同(超时判成功/判错误、无限超时、超时处置副作用) | — |
|
||||
|
||||
### 1.3 覆盖范围与复核方式
|
||||
|
||||
| 审计组 | 范围 | 状态 |
|
||||
|---|---|---|
|
||||
| 父审计组 | `net_socket.c` 全文(1979 行)、`net_select.c`、`ui_task.c`、`net_task.c`、`freertos.c`、`main.c`、UI 层(`ui_port.c`/`ui_net_sink.c`/`ui_page_net_cfg.c`/`ui_page_runlog`/`ui_page_selftest`)、cmsis_os2.c 事件组实现、`lv_conf.h` | 完成(自审) |
|
||||
| SA1 | `cfg_file.c`/`cfg_file_store_mcu.c` 全文、`sys_log_mcu.c`、`ringbuf.c`、`app_cfg.c`/`app_main.c` 配置段、`freertos.c` defaultTask 全路径 | 完成,关键项已复核 |
|
||||
| SA2 | `ch395f.c/h`、`rs485.c/h`、`sd2506.c/h`、`gd5f2gq5ue.c/h`、`nand_ftl.c`、`tpafe5160.c/h` 全文 + `Lib/dhara`(error/journal/map)、HAL UART/SPI 错误路径、`app_main.c` 诊断段、三份 Trap_Records | 完成,关键项已复核 |
|
||||
| SA3 | lftpd 5 文件、`time_sync.c`、`ui_net_sink*`、`test/net_test_task.c`、`simulator/sim/net_socket_sim.c` + 全库 grep 消费方地图 | 完成,关键项已复核 |
|
||||
|
||||
复核方式:每项 P0/P1 的**核心机制**由父审计组独立读码/读手册确认后才入册(本文件 §3 各条的
|
||||
"位置"行即为复核落点);P2 条目采信审计组带行号的报告。全库 grep 核验了全部 `net_*`/`osEvent*`/
|
||||
`xTaskNotify`/`f_open|close|write`/`0xNNH` 字面量调用面。
|
||||
|
||||
---
|
||||
|
||||
## 2. 机制基线(各子系统错误码传播契约,审计基准)
|
||||
|
||||
| 子系统 | 现行契约(修复后状态) | 审计基准 |
|
||||
|---|---|---|
|
||||
| **net 层** | 队列消息只携带**输入**(fd/长度/指针);错误码经通知值回传(负=错误码,≥0=字节数/fd);包装器仅**失败**时在调用方上下文恢复 `s_net_errno`,成功**永不**清除(`net_socket.c:1609` 文档化);`net_msg_t` 禁止加回传字段(AGENTS.md 铁律);指针字段安全(`net_accept` 地址回写为先例) | P1 族已消除;残留 = P2 全局竞态(E-03)与直调 API 的表属主(E-05) |
|
||||
| **事件标志** | CMSIS 事件组 = FreeRTOS 事件组(`xEventGroupWaitBits`);**就绪位=状态位**须 `osFlagsNoClear`;**命令位**(CFG_SAVE/NET_RECONFIG)=可消费,单一属主 | 5 处屏障等待全部 NoClear 在位(§7-2);命令位消费存在非原子窗口(E-17,无载荷→自愈) |
|
||||
| **配置引擎** | 静态态单属主(defaultTask);队列条目**纯输入型**(段号+路径指针,无回传字段)→ 结构性免疫 P1;CFG_SAVE = 可消费唤醒位 + `s_save_pending` volatile 锁存 + `s_save_result` 单字节状态机回传 UI | 全时序窗口(含 <500ms 双保存合并)无丢失(§7-3);RAM 副本读-改-写竞态残留(E-09) |
|
||||
| **syslog** | 队列条目纯输入型(id + 静态串指针);defaultTask 唯一写者;内核前 post 走 `s_preboot[8]` 静态数组 → 队列惰性创建(首次 drain 时) | 输入型结构免疫 P1;preboot 数组内核后并发写(E-08) |
|
||||
| **CH395F** | socket 命令回芯片原生 8 位状态(0x00 成功 / 0x10 BUSY 兼"进行中"与"轮询超时");init 族三值 OK/TIMEOUT/ERROR;**DMA 数据路径 `void` 无错误通道**;字节路径返回值未检查 + `HAL_MAX_DELAY` | E-33/E-34/E-46/E-47 |
|
||||
| **RS485** | `HAL_StatusTypeDef` 返回;RX 数据**只能**经 `rs485_on_rx` ISR 钩子观察(getter 恒 0);init 吞武装结果 | E-36/E-37/E-48~E-50 |
|
||||
| **SD2506(RTC)** | int 返回:0=OK / -1=参数 / -2=I2C;**0 仅表 I2C 事务成功 ≠ 芯片接受了写**(WRTC 写保护层,驱动无回读校验);上电默认写禁止;18H 充电寄存器**非易失**(出厂 82H、上电即充电使能);写允许序列与手册 L392"三者全 1"矛盾待台架确认 | E-32(P2)/E-38/E-39/E-51/E-52/E-60 |
|
||||
| **GD5F + FTL** | int32 码:0 / -1 ERROR / -2 BUSY_TIMEOUT / -3 ECC / -4 PROGRAM_FAIL / -5 ERASE_FAIL / -6 ID_MISMATCH;**仅 -4/-5 是单元缺陷(可判坏块),-1/-2 是瞬态**;`dhara_error_t` 分类完整(TOO_BAD/ECC/CORRUPT_MAP…)但 **glue 层映射坍缩**;`f_close` = FTL 提交点 | E-31/E-40/E-41/E-42/E-43/E-53~E-57 |
|
||||
| **TPAFE5160** | 阻塞 API:0 / -1 参数 / -2 BUSY(10ms,唯一硬超时);IRQ 模式 = ISR 双缓冲 + `s_ready`;**FRSTDATA 校验承诺未实现** | E-44/E-45/E-58 |
|
||||
| **net 消费方** | lftpd(队列+直调混合,控制通道阻塞 recv);timeSync(全队列,从不读 errno → 免疫 E-03);ui_sink(队列 + DONTWAIT + NoClear + EOF 不关监听 = 全项目唯一完全合规消费方,作修复参照);test(直调+队列混合) | E-02/E-04/E-06/E-20~E-24 |
|
||||
| **模拟器** | 单线程 Winsock 直调,无队列/线程/阻塞模式/代次/回调触发;成功即清 errno(与 MCU 相反) | 16 项保真度差异(附录 D);09-21 类缺陷在 sim **结构性不可复现** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 发现明细
|
||||
|
||||
> 每条:位置(复核落点)→ 模式 → 触发 → 影响 → 修复方向。P2 条目压缩为一行式。
|
||||
|
||||
### 3.1 P0(生产可触发,静默数据丢失)
|
||||
|
||||
#### E-01 `cfg_file_poll` 超长文件检查是死代码 → 用户配置文件销毁链
|
||||
- **位置**:`App/cfg_file.c:1151`(`if (len > CFG_FILE_MAX_LEN)`);机制根 `:504`(`s_load_file` 读长钉死 `CFG_FILE_MAX_LEN`,`cfg_file.h:26` =4096);对照组:load 守卫 `:984-993`、save 守卫 `:1032-1041` 均在位,**唯 poll 缺**
|
||||
- **模式**:P3(阈值检查写在恒假的位置)
|
||||
- **触发**:`/config.ini` 超过 4096B——文档化的 FTP 编辑工作流 + 外科手术式保存补写(`:774-797`,输出上限 `CFG_OUT_MAX`=5120 > 4096,无回检)都可达
|
||||
- **影响链**(已逐环复核):poll 读被截断的前 4096B → 界外段/键当"缺失"静默回工厂默认(`:1164-1183` 原地段复位 + 重解析)→ 回调触发网络按工厂 IP 重建 → 基线刷新(`:1189-1197`)把错误状态"粘住"→ 任何后续保存只按 RAM 副本(截断视图)重写 → **界外内容永久丢失** → 下次开机 `cfg_file_load` 的 `st.size > 4096` 守卫判"损坏" → **整份用户文件销毁**重生工厂模板。全程零错误日志
|
||||
- **修复方向**:poll 在 `s_load_file` **之前**查 `cfg_store_stat` 的 `st.size > CFG_FILE_MAX_LEN`,走与 load 相同的"工厂默认 + 重建模板 + **自检留痕**"分支;save 路径加 `out_len ≤ CFG_FILE_MAX_LEN` 守卫(超限拒绝保存并报错,而非写出下一颗雷)
|
||||
|
||||
#### E-31 FTL:`dhara_map_resume` 任何失败 → 整卷映射静默丢弃
|
||||
- **位置**:`Drivers/BSP/GD5F2GQ5UE/nand_ftl.c:250-252`(`if (dhara_map_resume(&s_map, &err) < 0) { DBG_INFO("No valid map found..."); dhara_map_clear(&s_map); }`);错误源 `Lib/dhara/journal.c:222-248`(find_checkblock 逐块扫描,单次读失败透传 `*err` 并继续,任一子步骤失败 → resume 返回 -1);分类能力 `Lib/dhara/error.h:20-31`
|
||||
- **模式**:P3(错误类别坍缩)+ P8(软错误升级为终态数据丢失事件)
|
||||
- **触发**:resume 扫描(大量页读)中**任何一次**瞬态读失败——ECC glitch / SPI 瞬断 / WIP 轮询超时(2026-09-20 台架瞬态读异常,其低 VDD 归因与"混合页"解读后续均被证伪——见 GD5F Trap 05;曾为它引入的 2ms settle 已于 2026-09-21 移除,就绪判定回归 WIP 轮询)
|
||||
- **影响**:全部逻辑→物理映射静默丢弃 → FatFs 探针判卷无效 → **整卷重格式化**(2026-09-20 `.fschk` 重格式化循环为同族事故)→ 全部用户数据丢失;仅一行 info 级日志。dhara 本可区分 `DHARA_E_TOO_BAD`(真空白芯片,正当 fresh)与 `DHARA_E_ECC`/`BAD_BLOCK`/`CORRUPT_MAP`(真实完整性故障)——glue 层丢弃了全部区分信息
|
||||
- **修复方向**:按 `err` 分类——仅 `DHARA_E_TOO_BAD`/无 checkpoint 判 fresh;其余错误**返回失败给 mount 层**,由自检上报、禁止静默 `dhara_map_clear`(重格式化须显式确认/自愈策略明确)
|
||||
|
||||
### 3.2 P1(22 项)
|
||||
|
||||
#### E-02 netTask 队列头阻塞:一个消费者的阻塞调用冻结全体
|
||||
- **位置**:`Drivers/BSP/NET/net_socket.c:357`(`net_process_messages` 一次取一条,处理完才取下一条)、`:1019-1067`(`net_recv_locked` 阻塞等待循环,30s 超时在 netTask 内空转)、`:961-984`(send 每 chunk 1s 停滞等待);触发源 `App/time_sync.c:84`(flags=0 阻塞 recv)、`lftpd_inet.c:106-109`(read_line 阻塞 recv——**FTP 客户端控制连接空闲 >30s 即触发,常见工况**)、`:135/:156`(阻塞 send)
|
||||
- **模式**:P4 变体(串行化头阻塞,非丢消息)
|
||||
- **影响**:最长 30s 全网队列停摆——uiTask 100Hz 轮询、FTP 收发/accept、timeSync 全部排在队列里得不到处理;事件处理不受影响(等待循环内仍调 `net_poll`,`:1020`),受影响的**恰是队列**;与 E-06 叠加 → PASV 假 425;STOR 上传方向芯片 2KB 缓冲满 + 重传 8×500ms=4s 耗尽 → 必断
|
||||
- **修复方向**:消费端阻塞 recv 改 `DONTWAIT` 分片 + 调用方重投 + 总超时(ui_sink 模式推广到 lftpd/timeSync);或 `net_process_messages` 在长阻塞间隙按 tick 继续排空队列;`NET_RECV_TIMEOUT_MS` 按服务可容忍量级分离(既有 P2-8 是本条的子集)
|
||||
|
||||
#### E-03 全局 `s_net_errno` 跨任务覆写竞态(09-21 修复的残留)
|
||||
- **位置**:写方 = `net_socket.c:39` 单全局;netTask 侧 `:362/365/375/385/410` + **notify 值构造 `:419-423`**(`s_notified` 分支读同一全局);6 个队列包装器在**调用方上下文** `:660/758/818/1233/1286/1333`(仅失败路径写);6 个直调 API 在**调用方上下文**失败时写(`net_socket :442/449`、`net_bind :482-538`、`net_sendto/net_recvfrom`、`net_getsockname :1953-1964`),**成功不写** → stale 滞留(`:1609` 文档化)。读端:`ui_net_sink.c:460`(uiTask 100Hz,BelowNormal 被抢占窗口最大)、`test/net_test_task.c:230/381`
|
||||
- **模式**:P2
|
||||
- **触发**:包装器写回 errno → 调用方读之间的任何任务切换窗口内,netTask 处理下一条消息(任何其它消费者失败)或他任务直调 API 失败 → 覆写
|
||||
- **影响**:uiTask 空闲轮询的 `WOULDBLOCK`(正常态)被误读为真错误 → `net_close(g_client)` **拆掉健康远端连接**(单连接 = 监听同毁 → 端口拒连);测试判读漂移(flaky)。09-21 主失败模式(空轮询误判)已除,跨任务覆写仍在
|
||||
- **修复方向**:错误码不再经共享全局——包装器把通知值里的精确码经**出参/返回值**直接交回调用方(通道已有,只差"还原到调用方栈");netTask 侧 locked 函数改以局部变量/出参携带结果码(消除 `:419-423` 的全局读)
|
||||
|
||||
#### E-04 `remo_send` 丢弃 `net_send` 返回值 → 弱网静默截断
|
||||
- **位置**:`Drivers/BSP/UI/ui_net_sink.c:117`(`net_send(g_client, p, 4+len+1, 0);` 返回值未用)
|
||||
- **模式**:P3(假定 `net_send` 恰好返回 len)
|
||||
- **触发**:LCDMEM 响应 3205B > `NET_SEND_CHUNK_MAX`(1KB),须 4 个 chunk;客户端拥塞时某 chunk 停滞 1s(`NET_SEND_TIMEOUT_MS`)→ 短写/0(net 层已打 "short send" 日志,`net_socket.c:987-992`)→ 响应帧截断
|
||||
- **影响**:客户端 CRC 不过判"全程无字节" → 断连;**无应用层日志、无客户端清理**。= 09-21 生产事故的机制在调用方一侧仍未闭环
|
||||
- **修复方向**:校验 `ret == len`;短发送 → `DBG_ERROR` + `net_close(g_client)` 强制客户端重连补帧(或入队重试),至少让截断可观测
|
||||
|
||||
#### E-05 socket 表跨任务写竞态(表属主规则被直调 API 打破)
|
||||
- **位置**:直调点 `lftpd_inet.c:13,25,50`、`lftpd.c:398,640`、`time_sync.c:40,53`、`ui_net_sink.c:350,360`(`net_socket`/`net_bind`/`net_getsockname`,3 个并发任务);被侵入结构 `net_socket.c:38`(`s_net_socks[]`)、`:1732-1742`(`alloc_socket` 线性扫描置 `in_use`)、`:42/:503-509`(`s_dynamic_port` 全局计数器)、`:436-462/:476-548`(写控制块);对照注释 `:346`("独占 CH395F 和 s_net_socks 访问权")
|
||||
- **模式**:P7(表属主变体)。**已逐函数核验:TCP 直调路径不触 SPI**(`net_bind` 仅 UDP 分支开 socket 走 SPI,`:511-542`)→ SPI 铁律未破
|
||||
- **触发**:`alloc_socket` 扫描/置位 与 netTask 的 `net_close_locked`(`:1167` memset 整块)或事件写 state 在同一 tick 边界交错(ftp/timeSync 同为 Normal 优先级,可时间片抢占)
|
||||
- **影响**:双任务分到同一 fd,或刚释放的 fd 被复用而芯片侧尚未 CLOSED → 两个服务配置同一 CH395F socket → 偶发"端口行为错乱/连接串扰",难复现
|
||||
- **修复方向**:`net_socket`/`net_bind`/`net_getsockname` 并入队列(新增消息类型,与 `net_listen` 同模式);或至少 alloc+init 段临界区 + `s_dynamic_port` 收归 netTask
|
||||
|
||||
#### E-06 `lftpd_inet_accept_timeout` 计时失真 + 跨任务状态预检
|
||||
- **位置**:`Drivers/BSP/NET/lftpd/lftpd_inet.c:77-98`(`:93` `elapsed += 100`;`:87-91` `net_get_sock` + `state != LISTENING` 预检)
|
||||
- **模式**:P3 + P2
|
||||
- **触发**:(a) `elapsed+=100` 假定每轮 ~100ms,而 `net_accept` 是队列往返——被 E-02 冻结时单次可达 30s → 名义 5s 超时实际**十几分钟**;(b) ftpTask 跨任务读 netTask 属主表:数据客户端"连接即断"时 state=CLOSED(auto_relisten 尚未执行)的窗口内被读到 → 误判 "data socket closed" → 提前 -1 → **425 假失败**
|
||||
- **修复方向**:超时改墙钟(`HAL_GetTick` 起点)计量;去掉/降级 state 直读(以 accept 返回值 + WOULDBLOCK 为准;保留则经队列或临界区快照)
|
||||
|
||||
#### E-07 `f_close`(FTL 提交点)返回值 3 处丢弃 → 假成功
|
||||
- **位置**:`App/cfg_file_store_mcu.c:83`(原子写:`/config.TMP` + `f_write` 后 `(void)f_close`,只查 fr/bw 就 rename 转正——本移植 `f_write` 可 FR_OK 而数据仅在 cache,**close 才经 `ftl_flush_cache` → dhara 提交**);`App/log/sys_log_mcu.c:110,117`(rc 判定只覆盖 `f_write`+bw → 假成功还**误清 `s_fault_reported` 去重标志** `:127`,后续真失败被静默);`Src/freertos.c:278`(卷探针 `(void)f_close(&__chk)` → "假健康"窗口)
|
||||
- **模式**:P4 + P8
|
||||
- **触发**:瞬态 NAND 写失败(2026-09-20 台架实证,供电实测 3.3V 标称;原"欠压工况"表述已更正)
|
||||
- **影响**:配置/日志报"成功"而数据未提交 → **掉电即静默丢失**;日志去重被假成功解除 → 真故障不报
|
||||
- **修复方向**:close 返回非 FR_OK 即判写失败(配置:unlink 临时文件、不 rename;日志:不清去重标志;探针:归一 FR_DENIED)
|
||||
|
||||
#### E-08 preboot 日志数组并发写
|
||||
- **位置**:`App/log/sys_log_mcu.c:39-40`(`s_preboot[8]` + volatile cnt)、`:218-223`(队列**惰性创建**于 `syslog_drain`,defaultTask 上下文,`Src/freertos.c:378` 首调)、`:265-273`(preboot 分支无同步读-改-写)
|
||||
- **模式**:P7
|
||||
- **触发**:内核启动后 ~ 首次 drain 前(defaultTask 存储 bring-up 慢时可达数十秒)的 post 落 preboot 数组——netTask 10s 屏障超时后的 "comms fault"(`net_task.c:126`)与 defaultTask 启动段失败 post(`freertos.c:316/322/343`)**同处"无队列时代"并发**
|
||||
- **影响**:自检记录丢失/重复(有界,NULL 守卫挡崩溃)——恰是最需要日志的故障窗口
|
||||
- **修复方向**:preboot 分支加临界区;或队列创建提前(首个任务上下文的 post 点前)
|
||||
|
||||
#### E-09 UI 读-改-写 × poll 原地更新竞态(P1-3 残余)
|
||||
- **位置**:`Drivers/BSP/UI/ui_page_net_cfg.c:181`(`net_cfg_t cfg = *net_cfg_get();`)→ 改一字节 → `:186`(`net_cfg_set(&cfg)`,`net_cfg.c:296-301` 整 19B 无锁 memcpy 到 `&s_cfg`)× defaultTask poll 的段原地更新(`cfg_file.c:1171-1182`,`s_parse` 直接写 `p_data=&s_cfg`)
|
||||
- **模式**:P7(跨任务无同步读-改-写)
|
||||
- **触发**:FTP 外部编辑(poll 原地刷新 RAM 副本)与 UI 页面读-写窗口跨唤醒 tick
|
||||
- **影响**:陈旧写回**抹掉外部编辑** → 下次保存旧值落盘 → 外部变更永久丢失
|
||||
- **修复方向**:UI 页面用局部副本 + 显式保存提交(保存时才合并写回);或写回前基线比对(检测到副本过期则放弃/提示)
|
||||
|
||||
#### E-10 NAND init 单次无重试 + 内部错误吞
|
||||
- **位置**:`App/app_main.c:64-75`(`gd5f2gq5ue_init` 单次调用);`gd5f2gq5ue.c:615-668`(init 内 read_id 恒返 OK、唯一真失败=ID 校验)、`:519-520`(bbt_scan 读失败吞)、`:651/655/659`(`wait_busy(100)` 返回值吞 ×3)、`:653-655`(ECC 使能未校验 → E-54)
|
||||
- **模式**:P4 + P9
|
||||
- **影响**:冷启动瞬态 SPI 毛刺 = 好芯片判 "NAND fault" 整次开机无存储(无重试);反向慢芯片 = 假 OK + 垃圾 BBT(E-42)
|
||||
- **修复方向**:init(或 reset + ID 读)重试 1~2 次;`wait_busy`/`bbt_scan`/ECC 使能 错误向上传递(与 E-42/E-53/E-54 同批改造)
|
||||
|
||||
#### E-33 CH395F DMA 路径 `void` 化:失败不可见,收路径冒充新数据
|
||||
- **位置**:`Drivers/BSP/CH395F/ch395f.c:89-135`(DMA 传输:HAL 错误 → log+return;1s 超时 → log + `HAL_SPI_Abort` + return;DMA 错误完成 → **仅 DBG_INFO 当正常** `:130-134`);`:1117-1147`(`write_send_buf` void → `net_socket.c:972-975` 仍计该块已发);`:1161-1191`(`read_recv_buf` void;`:1190` memcpy **无条件**执行 → 失败时调用方缓冲得到 `s_spi2_dma_rx_buf` 的**陈旧内容**)
|
||||
- **模式**:P4 + P8(与 09-21 生产 bug 同族:状态对调用方不可见)
|
||||
- **影响**:发送侧整块数据静默丢失;接收侧陈旧字节当"新数据" → 静默数据损坏(TCP 可掩盖、UDP 丢包);日志有 ERROR 行但**无程序化错误码**,net 层对调用方返回"成功"
|
||||
- **修复方向**:DMA 传输返回状态(或可查询错误标志 + 序号);失败时 `net_send`/`net_recv` 返回 -1;错误完成分支 INFO→ERROR
|
||||
|
||||
#### E-34 CH395F 字节级路径:`HAL_MAX_DELAY` 无限超时 + 返回值未检查
|
||||
- **位置**:`ch395f.c:181`(write_cmd)、`:195`(write_data)、`:210`(read_data)——均 `HAL_SPI_TransmitReceive(..., HAL_MAX_DELAY)` 且返回值丢弃
|
||||
- **模式**:P9(无限等待)+ P4
|
||||
- **触发**:DMA 错误路径 `HAL_SPI_Abort`(`:124`)后 SPI2 状态机残留卡死 → 下一次字节级命令**永久阻塞**
|
||||
- **影响**:一次 SPI 故障 = netTask 挂死 = 全网瘫痪(系统无看门狗);开机 init/check_exist 路径同样暴露
|
||||
- **修复方向**:改有限超时(100~500ms,与 DMA 路径同量级)并检查返回值,失败向上返回错误
|
||||
|
||||
#### E-35 测试 Phase5 块长 4093B > DMA 缓冲 2052B → 静默丢弃 + 误导签名
|
||||
- **位置**:`test/ch395f_test_task.c:1089`(`PHASE5_CHUNK_SIZE 4093`,注释仍写 "4096B/4KB" `:1214/:1285`)、`:1214`(TX 单次写 4093B)、`:1284-1287`(RX 单次读);驱动守卫 `ch395f.c:1126-1129/:1170-1173`(`len+4 > CH395F_DMA_BUF_SIZE(2052)` → **静默 return 无日志**)
|
||||
- **模式**:P3(契约漂移)× P4(静默丢弃)
|
||||
- **触发**:MSS 2048→1024(DMA 缓冲 4100→2052B)后测试常量未同步;台架跑硬件层套件即触发
|
||||
- **影响**:每个 TX 块被守卫丢弃 → 芯片发送缓冲恒空 → SENE 恒满足 → TX 毫秒"完成"(吞吐行失真)→ PC 端无数据 → RX 5s 后报 "rx stall" → **失败签名指向 RX,真因(TX 静默丢弃)不可见** → 质量门禁失效并误导排障
|
||||
- **修复方向**:测试常量对齐 ≤2048;驱动超尺寸守卫加 DBG 告警并把上限写入头文件契约(现仅文档化 NULL/len>0)
|
||||
|
||||
#### E-36 RS485:ISR 重武装清零 `rx_size` → getter 恒 0
|
||||
- **位置**:`Drivers/BSP/RS485/rs485.c:108`(`receive_start` 内 `rx_size = 0`)、`:151-161`(RxEventCallback:set_size → on_rx 钩子 → `receive_start` 清零)、契约 `rs485.h:100-105`
|
||||
- **模式**:P3(文档化契约结构性不可达——"无数据"误判族)
|
||||
- **影响**:getter 承诺"任务可获取最近一次接收字节数",实际 ISR 内在钩子后几 µs 就清零 → **任何任务时刻调用恒为 0** → 首个按文档 API 轮询"新帧"的消费者将永远"无数据" → 485 帧结构性不可见。当前 `rs485_task` 为空桩而休眠(E-59)
|
||||
- **修复方向**:重武装不清零(双缓冲/帧序号);或契约改为"仅 on_rx 钩子内有效"并在头文件明示
|
||||
|
||||
#### E-37 RS485:`rs485_init` 吞掉 RX 武装失败
|
||||
- **位置**:`rs485.c:52`(`(void)rs485_receive_start(p_handle)`);`rs485_init` 返回 `void`
|
||||
- **模式**:P4(init 路径也漏)
|
||||
- **影响**:开机时 UART4 状态非 READY(MspInit 异常/重复 init)→ `HAL_UARTEx_ReceiveToIdle_IT` 返回 BUSY/ERROR → RX 从未武装 → **485 接收终身静默死亡、零日志**
|
||||
- **修复方向**:检查返回值、失败记 FAIL;init 契约改返回码
|
||||
|
||||
#### E-38 SD2506:负温度解码 ≤-17°C 全错(=既有 P2-5,升级 P1)
|
||||
- **位置**:`Drivers/BSP/SD2506/sd2506.c:300`(`*p_temp = (int8_t)(val | 0xF0U)` 负分支)
|
||||
- **模式**:P6(符号转换)
|
||||
- **机制**:手册 L322 证明芯片用二补码("TEMP=FEH → -2℃");`|0xF0` 技巧仅在 val 高半字节已为 0xF(-16~-1)时成立。-30°C(0xE2) → 0xF2 → 报 -14°C;-20°C → 报 -4°C
|
||||
- **影响**:设备工作温度 -30~+80°C → 低温环境读数恒错且**返回 0=OK 无指示**(现消费者=开机日志)
|
||||
- **修复方向**:统一 `*p_temp = (int8_t)val`
|
||||
|
||||
#### E-39 SD2506:ARST 在 CTR3(11H),驱动从未写过 CTR3 → `clear_alarm` 永久不可达
|
||||
- **位置**:手册 4.1 寄存器表:**ARST = CTR3(11H) D7**(D6=F32K/D5=TDS1/D4=TDS0/D3~D0=FS3~FS0,复位值 0000-0000);`sd2506.h:55` 定义了 `SD2506_REG_CTR3=0x11` 但**全驱动零次写 CTR3**(grep 证实);`clear_alarm` 契约 `sd2506.h:261-267`、实现 `sd2506.c:470-478`(依赖"ARST=1 时读 CTR1 自动清 INTAF",手册 L398)
|
||||
- **模式**:P3(契约不可达)/ P8
|
||||
- **影响**:ARST 恒为复位值 0 → 报警匹配后 INTAF 锁存且读不自动清除 → 首次报警后 INTAF **永久锁存**;`clear_alarm` 成为"返回 0 但什么都没清"的空操作;INT 脚单事件报警模式下锁存低电平不释放
|
||||
- **修复方向**:在写允许窗口置 ARST=1(`write_enable` → 写 CTR3=0x80 → `write_disable`);注意 11H 属 00H~71H 保护段,直接写无效(与 E-60 同一写保护机制);若项目确认不使用 RTC 报警,改在契约中明示"报警功能未启用"并评估移除
|
||||
|
||||
#### E-40 FTL:erase/prog 任何失败(含瞬态)→ 判坏块并持久化
|
||||
- **位置**:`nand_ftl.c:104-113`(`dhara_nand_erase`:`ret != GD5F_OK` → `mark_bad` + `DHARA_E_BAD_BLOCK`——前件 `ret==GD5F_ERASE_FAIL` 被后件完全包含,即**一切失败**都判坏块)、`:121-132`(`program_load/exec` 失败 → mark_bad,同坍缩)、`:141-164`(回读/ECC/数据不符 → mark_bad)
|
||||
- **模式**:P3(瞬态/终态混淆)
|
||||
- **影响**:健康块因 `GD5F_BUSY_TIMEOUT`/`GD5F_ERROR` 被**永久除名**并持久化进 BBT 池 → 容量永久损失(2026-09-21 "一轮破坏性 GD5F 测试容量 95248→95200" 的机制性同根);每次误标记触发 BBT 池整块擦写(任务上下文数秒 SPI 阻塞)
|
||||
- **修复方向**:仅 `GD5F_ERASE_FAIL`/`GD5F_PROGRAM_FAIL`(E_FAIL/P_FAIL 状态位 = 真实单元缺陷)映射坏块;timeout/ERROR 返回 `DHARA_E_ERASE`/`DHARA_E_IO` 由 dhara 重试或上报
|
||||
|
||||
#### E-41 `gd5f_bbt_save` 编程失败仍返回 OK 并推进版本(=既有 P2-3)
|
||||
- **位置**:`gd5f2gq5ue.c:1009-1016`(`write_enable/program_load/program_exec/wait_busy` 返回值全丢弃 → `s_bbt_version=ver`、槽前进、`return GD5F_OK`);上游 `gd5f2gq5ue_mark_block_bad` 为 `void`(`gd5f2gq5ue.h:189`)
|
||||
- **模式**:P4 + P8(void 契约隐藏失败)
|
||||
- **影响**:BBT 池块编程失败 → RAM BBT 说"此块坏"、闪存副本陈旧 → **重启后 dhara 重新分配该坏块** → 写入静默丢失;调用方结构性无法得知持久化失败
|
||||
- **修复方向**:检查编程结果返回错误码;`mark_block_bad` 改返回码;失败重试下一槽(槽轮换框架已存在 `:984-989`)
|
||||
|
||||
#### E-42 `gd5f_bbt_scan` 弃两读返回值 → 基于陈旧缓冲判块
|
||||
- **位置**:`gd5f2gq5ue.c:516-526`(`:519` `gd5f_page_read`、`:520` `gd5f_read_from_cache` 返回值均弃;`:522` 判 `spare[0]`——首次迭代未初始化、之后为上一块残留)
|
||||
- **模式**:P4
|
||||
- **影响**:2048 块出厂扫描中任一读失败 → 该块判定基于垃圾(好块判坏 = 容量损失;坏块判好 = 未来数据丢失);缓解:与持久化副本 OR 合并(`:960-969`)兜住已持久化坏块,风险集中首启
|
||||
- **修复方向**:检查两读返回值,失败按保守方向(标坏)或重读
|
||||
|
||||
#### E-43 `gd5f2gq5ue_erase` 缺容量边界检查(与 Trap 03 文档矛盾)
|
||||
- **位置**:`gd5f2gq5ue.c:783-806`(仅块对齐检查;read/write 均有 `offset+size ≤ GD5F_TOTAL_SIZE`,`:705-707/:748-750`)
|
||||
- **模式**:P3(契约/文档分歧)
|
||||
- **触发**:`offset = GD5F_TOTAL_SIZE` → `block_addr=2048` → 24 位行地址(block×64)被芯片解码回范围内块(2048≡0 mod 2048)→ **可能静默擦掉块 0**(journal/BTT 所在)
|
||||
- **影响**:越界擦除命中活块 = 数据丢失;或芯片忽略 = 静默 no-op。`GD5F_Trap_Records.md` Trap 03 称"三个函数入口统一加边界检查"——代码与文档不符,文档高估了修复范围
|
||||
- **修复方向**:erase 补同一容量检查(对齐检查之后);更正 Trap 文档
|
||||
|
||||
#### E-44 TPAFE:`read_all` 无数据有效性校验(承诺的 FRSTDATA 校验未实现)
|
||||
- **位置**:`Drivers/BSP/TPAFE5160/tpafe5160.c:151-168`(`read_all`:start_conv → wait_busy(10ms) → read_channels → 无条件 `return OK`);`tpafe5160.h:132-133`(承诺"通过 FRSTDATA 验证第一通道");`TPAFE5160_FRSTDATA_READ` 宏(`h:37`)有定义、**代码零引用**(全库 grep 证实)
|
||||
- **模式**:P8 + P9
|
||||
- **触发**:芯片掉电/虚焊/总线冲突 → BUSY 引脚读低/浮空 → wait_busy 立即"通过" → RD 脉冲读回三态总线钳位态(0xFFFF/0x0000 常量)
|
||||
- **影响**:上层把常量垃圾当有效采样 → 设备"测量正常"数值全错("无数据误判"的镜像)
|
||||
- **修复方向**:实现 FRSTDATA 首通道校验(首次 RD 后 FRSTDATA 应为高,否则报错),或至少拒绝全 0/全 1
|
||||
|
||||
#### E-45 TPAFE:`s_ready` 三写者 → 重触发即丢未消费帧
|
||||
- **位置**:`tpafe5160.c:288-291`(`start_conv_irq` 在 CONVST 脉冲前 `s_ready=0`)、`:311-313`(`clear_ready`,任务上下文)、`:354-355`(ISR 置 1)
|
||||
- **模式**:P5(事件标志语义:电平非计数)
|
||||
- **影响**:帧 N 完成置 ready → 消费者未及读取 → 下一圈触发清零 → 帧 N **永久丢失**;高速采样系统性丢样,上层无法区分"无新数据"与"数据丢了"(现 adc_task 空桩休眠,E-59)
|
||||
- **修复方向**:从 `start_conv_irq` 移除 `s_ready=0`(生命周期归消费者)或改帧序号制
|
||||
|
||||
### 3.3 P2(36 项,一行式)
|
||||
|
||||
**配置/日志/事件**
|
||||
|
||||
| 编号 | 位置 | 问题 | 修复方向 |
|
||||
|---|---|---|---|
|
||||
| E-11 | `cfg_file.c:1147-1149` | poll 读失败静默 return 无限重试、无日志(load/save 同场景均有日志+自检) | 加日志 + 连续 N 次转自检 |
|
||||
| E-12 | `ffconf.h:39`(`FF_FS_LOCK=0`)× `sys_log_mcu.c:100-125` | /log 同文件双开:defaultTask 写(容量满 unlink 重建)× uiTask 浏览读 → 孤儿读/乱码/瞬态 FR_INT_ERR(=既有 P2-10,复核一致) | `FF_FS_LOCK` 或队列化读 |
|
||||
| E-13 | `freertos.c:200` | `osEventFlagsNew` 返回值不检查 → NULL 句柄时屏障全失效 + defaultTask 失 500ms 节拍紧密循环刷 NAND,零日志 | 检查 + FAIL + 自检 |
|
||||
| E-14 | `net_task.c:78-80/181-184/205-209` | net init/重建失败仅串口日志,无持久自检记录(对照 PHY 有 comms fault、存储有 storage fault)→ 现场网络故障无痕迹(=既有 P1 级可观测性缺口) | `syslog_post(SYSLOG_SELFTEST,…)` |
|
||||
| E-15 | `ringbuf.c:38-76` | API 陷阱:满/空 len=0 不可区分、短写静默、无溢出计数(现零调用者,休眠) | 修复契约或移除 |
|
||||
| E-16 | `cfg_file.c:463/475/907-926` | `s_gen_document` 不查 snprintf 返回值 + 注册不限键名长度 → 未来长键名可致模板缓冲越界 | 检查返回值 + 注册限长 |
|
||||
| E-17 | `net_task.c:201-203` | RECONFIG `Get`+`Clear` 非原子 TOCTOU(=既有 P2-11)——**本次重评**:命令无载荷(执行读最新 RAM 副本、代次在执行时递增),丢失 = 本次重建跳过、下次保存/重启自愈,无数据丢失 → 维持清单、风险低于原评级 | `osEventFlagsWait` 原子消费 |
|
||||
| E-18 | `freertos.c:422-424` | ftpTask `WaitAll(osWaitForever)` 无超时 → 生产端故障时 FTP 永久静默阻塞 | 超时 + 降级日志 |
|
||||
| E-19 | `freertos.c:430`;`sys_events.h:28-29` | 死事件位:`FTP_READY` 置位在 `lftpd_start`(无限循环)返回后 = **不可达**;`TIMESYNC_READY` 从未置位 → 未来消费者 Will 永久阻塞 | 删除或改在首次监听成功后置位 |
|
||||
| E-20 | `lftpd_inet.c:100-127`、`lftpd.c:555-561` | 控制通道 30s 阻塞 recv 兼存活判定 + 行>511B/EOF/真错误同返 -1 → 慢客户端被断、长命令杀会话、叠加 E-02 | DONTWAIT + 调用方状态机;超长回 501 保会话 |
|
||||
| E-21 | `time_sync.c:128-144/:80-97` | accept 一切失败=无连接(200ms 静默重试、无 errno 分类/日志)+ 30s 阻塞 recv + close-then-rebuild 空窗 | errno 分级(BADF/INVAL→重建+日志);DONTWAIT + 会话超时(=既有 P2-8 扩展) |
|
||||
| E-22 | `lftpd.c:608-626`、`time_sync.c:96/:146-149` | close-then-relisten 每会话端口空窗 ~0.2-1.7s(合规对照 = `ui_net_sink.c:446-457`:EOF 不 close、靠 auto_relisten 常驻) | 向 ui_sink 模式看齐 |
|
||||
| E-23 | `test/net_test_task.c:869-917/:296-403` | 监听主循环与 phase7 无代次检查(网络重建后恒 BADF 空转)+ TC704 8 路 socket 与 3 个并发服务抢 8 槽 fd 池 → 假 FAIL | 代次检查 + 降并发/测试构建抑制服务任务 |
|
||||
| E-24 | `net_socket.c:1971-1975 × :162`;`lftpd.c:398-413` | `s_local_ip` 逐字节拼读 × reconfig 重写不原子 → PASV 227 响应 IP 混拼(低频人工触发) | getsockname 入队列或临界区快照 |
|
||||
| E-25 | `net_socket.c:1609`;`net_select.c:63-65`;`net_socket.c:751-753` | API 一致性杂项:成功不清 errno(纪律文档化,读端须知);net_select 参数错误不设 errno(仅测试用);net_accept 失败也覆写 `*addrlen`。"LISTENING 恒可读"部分 = 既有 P2-7 | 统一 errno 纪律 + 契约注释 |
|
||||
| E-26 | `net_socket.c:1351-1519` | UDP 直调 API(sendto/recvfrom/UDP-bind)在调用方上下文做 SPI——**潜在地雷**:生产零调用(timeSync 2026-08-28 起 TCP 化正是为绕开),首个 UDP 用户即破铁律 | 文档化禁令或并入队列 |
|
||||
| E-27 | `Src/main.c:104-113` | CubeMX `MX_*_Init` 返回值忽略(生成代码)→ 下游驱动级检查兜底(check_exist/gd5f ID/sd2506 init),可接受,记录在案 | 保持(生成区不改) |
|
||||
| E-28 | `ui_port.c:60/:163-168` | 远端按键单格缓存(同任务无竞态):10ms 内连按前键丢失(UX 微瑕) | 小环形或文档化 |
|
||||
| E-29 | `ui_net_sink.c:223-227` | LCDMEM 取帧失败静默丢应答、无日志(现网实际不触发) | 加 DBG 日志 |
|
||||
|
||||
**驱动层**
|
||||
|
||||
| 编号 | 位置 | 问题 | 修复方向 |
|
||||
|---|---|---|---|
|
||||
| E-32 | `sd2506.c:182,191`;`sd2506.h:126` | **(2026-09-21 复核后由 P0 降档)**init 两笔写在写禁止态发出被芯片忽略(L394/537 属实),**但**手册 §4.8"出厂均配置成 82H"+ 18H 非易失 → 充电器上电即已使能,**原"充电器终身未使能"P0 链证伪**;残留问题 = ① `:187-190` 注释与代码矛盾(注释称写 0x20 且"ARST=1",实际写 0x7B 且 ARST 在 CTR3 不在 CTR1)② init 无回读校验(18H 若被长时掉电腐蚀则无人发现)③ `docs/硬件平台/V0.0_网表详细分析报告.md:280`"已核实无缺陷"无回读证据 | 18H 改"回读验证 0x82,≠ 则 write_enable→写→回读→write_disable";注释更正(0x7B = 手册 L396 推荐的写禁止态 CTR1 值;ARST 在 CTR3) |
|
||||
| E-46 | `ch395f.h:151` | `0xECH` 无效 ARMCC 字面量(= SD2506 0x0FH 同族);当前零引用休眠,首个使用者即踩 | 改 `0xECU` + pre-commit 禁 `0xNNH` 检查 |
|
||||
| E-47 | `ch395f.c:606-622` | 轮询超时与芯片忙共用 `0x10` 码 + 跨命令陈旧状态(共享 CMD_STATUS 读即清 → 下条命令可能先读到上条的完成态);现边界统一按可重试处理,影响有限 | 独立超时码或轮次序号 |
|
||||
| E-48 | `rs485.c:173` | ErrorCallback 重武装返回值丢弃(现良性:ORE 路径 HAL 先复位 RxState=READY;非阻塞错误路径原武装存活自愈) | 非 HAL_OK 记 WARN |
|
||||
| E-49 | `rs485.c:70-93` | TX 超时立即拉低 DE → 截断在途尾字节(仅超时路径,本身已失败) | 等 TC/短延时后再拉低 |
|
||||
| E-50 | `rs485.c:37-39` | rx_buf/rx_size 单缓冲无 ISR/任务同步 → 任务拷贝中遇新帧 = 帧撕裂(115200×256B≈22ms 窗口) | 双缓冲+序号或契约限定钩子内 |
|
||||
| E-51 | `sd2506.c:450-451` | set_alarm CTR2 全量覆盖(=0x25)中途清掉 WRTC1 → 芯片中途重新上锁 → 后续 write_disable 全被忽略,停在混合态(下轮 write_enable 自愈) | 读-改-写保 WRTC 位 |
|
||||
| E-52 | `app_main.c:89/96/104/109` | 开机诊断块 4 个 `get_*` 返回值全丢弃 → I2C 瞬断时打印**未初始化栈值**当诊断(误导性证据) | 检查返回值,失败打印"读取失败" |
|
||||
| E-53 | `gd5f2gq5ue.c:456/485/684-685` | 轮询路径 HAL 返回值系统性未检查(DMA 路径有检查)→ 状态字节读垃圾 → 假"芯片空闲" → 后续命令未锁存却报成功 | 统一检查,失败返 GD5F_ERROR |
|
||||
| E-54 | `gd5f2gq5ue.c:653-655` | init 的 ECC 使能步骤未校验 → "NAND init OK" ≠ ECC 已开 → 存储栈在无 ECC 保护下运行(数据完整性基座静默降级) | 使能后回读 B0h 校验 bit4 |
|
||||
| E-55 | `nand_ftl.c:206/:180` | `dhara_nand_read/is_free` 弃 `read_from_cache` 返回值 → 传输失败时以陈旧/垃圾冒充成功(is_free 方向:垃圾 0xFF → 已用页判空闲 → 数据覆盖)(=既有 P2-4) | 检查返回值失败上报 |
|
||||
| E-56 | `gd5f2gq5ue.c:703-737` | 公共 `gd5f2gq5ue_read` 对不可纠错页仍返回 0(契约"0=成功"实为"SPI 传输完成";生产主路径有 dhara glue ECC 检查保护,缺口仅在 raw API) | 加 ECCS 检查或头文件明示 |
|
||||
| E-57 | `gd5f2gq5ue.h:119/140/189/222-253` | 公共 API 返回类型 `int`/`int32_t` 混用(码值均为小负数,无功能影响) | 统一 `int32_t` |
|
||||
| E-58 | `tpafe5160.c:322-324/:343/:354` | 双缓冲角色互换 + 任务抢占 → 撕裂读(get_buf 与拷贝完成之间被抢占超一个帧周期 = 8 通道失去同步性) | 原子快照拷贝 API(关中断+memcpy)或文档化时限 |
|
||||
| E-59 | `adc_task.c:37-43`、`rs485_task.c:37-43` | 边界状态:两任务均为空桩(osDelay 循环),`rs485_on_rx` 无覆盖 → E-36/E-44/E-45/E-58 全部休眠;**真实消费者接入之日即触发** | 消费者实现前先修驱动契约(E-36/E-45/E-44) |
|
||||
| E-60 | `sd2506.c:121-133`(write_enable);手册 L392 vs 实测 | **(2026-09-21 复核新增,待台架确认)**write_enable 实际置 WRTC1=1(CTR2=0x80)、WRTC3=1(CTR1=0x84 D7)、**WRTC2=0**(CTR1=0x84 D2);手册 L392 明写"WRTC1=1、WRTC2=1、WRTC3=1 时写允许"(三者全 1)→ 若手册准确则时间写(00H~06H)应全部无效,但与台架实测(RTC 套件 set==readback PASS,写入确已生效)**矛盾**——二者必有一处不符(手册"三者全 1"可能比实际硬件宽松,或 WRTC2 门控范围与描述不符) | 台架实测:write_enable 后回读 CTR1/CTR2 记录 WRTC 位实际态 + 验证时间写生效;据此订正手册理解或补 write_enable 的 WRTC2 位(暂按"硬件实测为准",不阻断当前功能) |
|
||||
|
||||
---
|
||||
|
||||
## 4. 模式 × 子系统矩阵
|
||||
|
||||
> F=发现编号(E-);✓=检查无问题;—=不适用
|
||||
|
||||
| 模式 | net 层/执行器 | 事件标志 | 配置引擎 | syslog | CH395F | RS485 | SD2506 | GD5F/FTL | TPAFE | lftpd | timeSync | ui_sink | test | sim |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| P1 值拷贝 | ✓(契约修复在位) | — | ✓(输入型条目) | ✓(输入型条目) | — | — | — | — | — | — | — | — | — | — |
|
||||
| P2 共享全局 | **E-03** | ✓ | ✓ | ✓ | — | — | — | — | — | — | ✓(免疫) | **E-03** | E-03 | E-30 |
|
||||
| P3 混同 | E-25/E-26 | — | **E-01** | — | E-35/E-47 | E-36 | E-39/E-51 | **E-31/E-40**/E-43/E-56 | — | E-06/E-20 | E-21 | **E-04** | ✓ | E-30 |
|
||||
| P4 未检查 | ✓(put 全查) | E-13 | E-11 | E-08 | **E-33/E-34**/E-35 | E-37/E-48 | E-32/E-52 | E-41/E-42/E-53/E-54/E-55 | — | ✓ | E-21 | ✓ | E-23 | — |
|
||||
| P5 事件语义 | ✓ | E-17/E-18/E-19 | ✓ | — | — | — | — | — | **E-45** | ✓ | ✓ | ✓ | ✓ | — |
|
||||
| P6 符号/宽度 | — | — | — | — | — | — | E-38 | — | — | — | — | — | — | — |
|
||||
| P7 跨上下文 | **E-05** | — | **E-09** | E-08 | — | E-36/E-50 | — | — | E-58 | E-06 | — | — | E-23 | — |
|
||||
| P8 外设/库 | E-26 | — | **E-07** | E-07 | E-33/E-46 | — | E-32/E-60 | E-43/E-54/E-56 | E-44 | — | — | — | — | — |
|
||||
| P9 超时/错误 | **E-02** | — | — | — | E-34/E-47 | E-49 | — | **E-31/E-40** | E-44 | E-20/E-22 | E-21 | — | E-23 | — |
|
||||
|
||||
---
|
||||
|
||||
## 5. 修复计划(三档;待批准后实施,本次审计不改代码)
|
||||
|
||||
### 第一档:立即(改动小、价值高、风险低,无架构变更)
|
||||
|
||||
| # | 项 | 内容 | 关联 |
|
||||
|---|---|---|---|
|
||||
| 1 | **E-32**(降 P2) | SD2506 init:18H 改"先回读验证 0x82,≠ 则 write_enable→写 18H=0x82→回读→write_disable"(18H 非易失、出厂 0x82,正常路径保留原写);注释更正(0x20/ARST 误注 → 0x7B 为手册 L396 推荐写禁止态值;ARST 在 CTR3 非 CTR1) | P2 |
|
||||
| 2 | **E-31** | `dhara_map_resume` 按 `dhara_error_t` 分类:仅 `DHARA_E_TOO_BAD` 判 fresh,其余返回失败给 mount 层 + 自检上报,禁止静默 clear | P0 |
|
||||
| 3 | **E-01** | `cfg_file_poll` 前置 `st.size > CFG_FILE_MAX_LEN` 守卫(工厂默认+模板+自检留痕);save 加 `out_len ≤ 4096` 守卫 | P0 |
|
||||
| 4 | **E-34** | CH395F 字节级路径:`HAL_MAX_DELAY` → 有限超时(100~500ms)+ 返回值检查 | P1 |
|
||||
| 5 | **E-33** | CH395F DMA 传输加错误通道(返回状态/错误标志);失败 → net_send/recv 返回 -1;错误完成分支 INFO→ERROR | P1 |
|
||||
| 6 | **E-36/E-37** | RS485 契约修复:重武装不清零(或 getter 契约改"钩子内有效"并明示);`rs485_init` 返回码 + 失败日志 | P1 |
|
||||
| 7 | **E-43** | `gd5f2gq5ue_erase` 补容量边界检查;更正 Trap 03 文档 | P1 |
|
||||
| 8 | **E-38** | SD2506 负温度统一 `(int8_t)val`(1 行) | P1 |
|
||||
| 9 | **E-40** | FTL erase/prog:仅 E_FAIL/P_FAIL 判坏块;瞬态 → `DHARA_E_ERASE/DHARA_E_IO` | P1 |
|
||||
| 10 | **E-41/E-42** | `bbt_save` 检查编程结果(返回码+重试下一槽,`mark_block_bad` 去 void);`bbt_scan` 检查读返回值 | P1 |
|
||||
| 11 | **E-44/E-45** | TPAFE:实现 FRSTDATA 首通道校验(或拒全 0/全 1);`start_conv_irq` 移除 `s_ready=0`(或改帧序号) | P1 |
|
||||
| 12 | **E-07** | `f_close` 返回值检查 ×3(store 原子写 / syslog ×2 / 卷探针):close 失败 = 写失败 | P1 |
|
||||
| 13 | **E-04** | `remo_send` 校验 `ret == len`;短发送 → DBG_ERROR + `net_close` 强制重连补帧 | P1 |
|
||||
| 14 | **E-03** | errno 出参化:包装器把通知值里的精确码经出参/返回值直传调用方;netTask 侧 locked 函数改局部/出参结果码(消除 `:419-423` 全局读) | P1 |
|
||||
| 15 | **E-09** | UI 页面局部副本 + 显式保存提交(或写回前基线比对) | P1 |
|
||||
| 16 | **E-08** | preboot 分支临界区(或队列创建提前) | P1 |
|
||||
| 17 | **E-39/E-51** | SD2506:写允许窗口内置 ARST=1(写 CTR3=0x80,注意 11H 属保护段须经 write_enable);set_alarm CTR2 改读-改-写保 WRTC 位(按手册 L396 推荐值 0xFF/0x7B) | P1/P2 |
|
||||
| 18 | **E-35** | 测试 Phase5 常量对齐 ≤2048;驱动超尺寸守卫加告警 + 头文件契约 | P1 |
|
||||
| 19 | **E-46** | `0xECH` → `0xECU`;pre-commit 增加 `0xNNH` 字面量检查 | P2(1 行) |
|
||||
| 20 | **E-52** | 开机诊断块检查 `get_*` 返回值 | P2 |
|
||||
| 21 | **E-17** | RECONFIG 改 `osEventFlagsWait` 原子消费(= P2-11 闭环) | P2 |
|
||||
| 22 | **E-14** | net init/重建失败补 `syslog_post(SYSLOG_SELFTEST,…)` | P2 |
|
||||
|
||||
### 第二档:下一迭代(结构性改造)
|
||||
|
||||
| # | 项 | 内容 |
|
||||
|---|---|---|
|
||||
| 1 | **E-02+E-06+E-20+E-21** | 阻塞模型改造:lftpd 控制通道/timeSync 改 `DONTWAIT` + 调用方状态机(ui_sink 模式推广);`net_process_messages` 长阻塞间隙按 tick 继续排空队列;`accept_timeout` 墙钟计时 + 去跨任务 state 直读 |
|
||||
| 2 | **E-05** | `net_socket/net_bind/net_getsockname` 并入队列(新消息类型);过渡方案:alloc+init 临界区 + `s_dynamic_port` 收归 netTask |
|
||||
| 3 | **E-10/E-42/E-53/E-54** | GD5F 驱动错误通道统一:init 重试 1~2 次;`wait_busy`/轮询路径 HAL 检查;ECC 使能回读校验;错误向上传递 |
|
||||
| 4 | **E-22** | lftpd/timeSync 向 ui_sink close 规则看齐(EOF 不 close、auto_relisten 常驻、仅代次变化/判死时重建) |
|
||||
| 5 | **E-23** | 测试套件加代次检查(accept 失败分支);TC704 降并发或测试构建抑制服务任务 |
|
||||
| 6 | **E-30(部分)** | sim 后端 errno 语义对齐(成功不清)+ 缺陷记录标注 sim 复现盲区 |
|
||||
| 7 | **E-60** | 台架实测:write_enable 后回读 CTR1/CTR2 记录 WRTC 位实际态并验证时间写生效;若 WRTC2 确门控时间写范围 → 补 write_enable;否则(当前时间写已实证生效)→ 文档化实测结论、订正手册理解、驱动契约加注 |
|
||||
|
||||
### 第三档:长期
|
||||
|
||||
- 模拟器保真度全面对齐(阻塞模式 = select+超时 / connect CONNECTING 状态机 / accept fd 模型),或文档化"阻塞路径仅 MCU 可验证"(附录 D 逐项)
|
||||
- E-26 UDP 路径并入队列或移除 + 禁令文档化
|
||||
- E-12 `FF_FS_LOCK`;E-15 ringbuf 修复/移除;E-19 死事件位清理;E-18 ftpTask 等待超时;E-13 句柄检查
|
||||
- 驱动健壮性长尾:E-47/E-48/E-49/E-50/E-55/E-56/E-57/E-58
|
||||
- 消费者任务(adc_task/rs485_task)实现时按 AGENTS.md 注册 + 栈预算,**先修 E-36/E-44/E-45 再接入**(E-59)
|
||||
|
||||
---
|
||||
|
||||
## 6. 与既有《项目审计改进清单》交叉引用
|
||||
|
||||
| 既有编号 | 本次对应 | 处置 |
|
||||
|---|---|---|
|
||||
| P2-2(ch395f DMA 超时未向上传播) | **E-33** | 独立复核一致(DMA 路径 void 无错误通道) |
|
||||
| P2-3(bbt_save 丢返回值) | **E-41** | 独立复核一致;并入修复计划第一档 #10 |
|
||||
| P2-4(dhara_nand_read 弃 read_from_cache) | **E-55** | 独立复核一致 |
|
||||
| P2-5(负温度解码) | **E-38** | 独立复核一致并**升级 P1**(≤-17°C 全错 + 返回 OK 无指示;既有清单 high 优先级,与 P1 定级一致) |
|
||||
| P2-6(WRTC 写时序) | **E-51**(子集) | 复核一致:set_alarm CTR2 全量覆盖中途清 WRTC1 → 写保护态混合(并入第一档 #17) |
|
||||
| P2-7(net_select LISTENING 恒可读) | **E-25**(部分) | 复核一致;本次补充同函数"参数错误不设 errno"子项 |
|
||||
| P2-8(time_sync 30s 卡死) | **E-02/E-21** | **重大扩展**:30s 不只是同步任务卡死,而是**全体消费者队列停摆**(头阻塞)——修复方案相应从"短超时"升级为阻塞模型改造 |
|
||||
| P2-10(/log unlink×读竞态) | **E-12** | 独立复核一致 |
|
||||
| P2-11(RECONFIG 非原子) | **E-17** | 复核一致;**重评为 P2**(命令无载荷、丢失自愈、无数据丢失)——建议从清单 high 调整为 medium |
|
||||
| P1 级"网络故障无痕迹"(清单 :93) | **E-14** | 独立复核一致 |
|
||||
| P2-1、P2-9、P2-12~16、P3-1~13 | — | 本次未覆盖其主题(P2-1 外科手术式保存超长行为相邻独立项;其余为装饰宏/构建脚本/栈表等),维持原跟踪 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 健康结论(审计深度证据:检查过且无问题的关键点)
|
||||
|
||||
1. **syslog 队列条目纯输入型**(id + 静态串指针,消费者不回写、生产者不回读)→ 结构性免疫 09-21 类值拷贝陷阱(对照 `net_msg_t.result` 回传字段 = 事故根因)
|
||||
2. **就绪屏障 5 处等待全部 `osFlagsNoClear`**(`freertos.c:422-424`、`time_sync.c:113-114`、`ui_task.c:48-49`、`net_task.c:177-178`、`ch395f_test_task.c:1387-1388`)——09-21 修复在位;命令位均为单一属主消费
|
||||
3. **CFG_SAVE 协议无损**:可消费唤醒位 + `s_save_pending` 锁存 + `s_save_result` 单字节状态机回传 UI——全时序窗口(含 <500ms 双保存合并 = 无损,保存恒以当前 RAM 副本为准)无丢失、无"错误显示成功"可能
|
||||
4. **通知值通道独占**:全库无 `osSignal/osThreadFlags`/流缓冲调用者;`LV_USE_OS=0`(MCU)/`LV_OS_NONE`(sim)→ LVGL osal 不竞争;陈旧通知值无假唤醒(`xTaskNotifyWait` 以 pending 状态为准)
|
||||
5. **`net_select` 纯 RAM 轮询**(调用方无 SPI);"LISTENING 恒可读"偏乐观(现无实际影响,= P2-7)
|
||||
6. **defaultTask 存储自愈健全**:双确认探针、短写归一 FR_DENIED、`FR_INVALID_NAME` 排除重格式化集、写保护类不重格式化
|
||||
7. **内核前纪律**:`app_main_init` 零 CMSIS 调用(HAL_Delay = tick 轮询,非 osDelay);对象创建次序(队列/任务先于 `osKernelStart`);栈溢出/堆失败钩子 = 裸 UART(临界安全)
|
||||
8. **time_sync 全队列路径、从不读 errno** → 免疫 E-03;**netMsgQueue 深度 8 + `osWaitForever` put**(无丢消息)+ put 返回值全检查
|
||||
9. **lftpd 静态缓冲与单会话模型一致**(ftpTask 栈峰值复测 1184B/4096B,余量 71%);队列 wrapper 同步语义挡住 fd 复用竞态(`net_close` 阻塞至 netTask 完成 memset)
|
||||
10. **lftpd PASV IP 双重字节序转换等价正确**(192.168.1.100 各节验证);recv 0=EOF 在 lftpd/timeSync/ui/phase6/8/10 全部正确区分
|
||||
11. **CH395F 中断读清**在单一 SPI 事务内完成(无事务内竞态);DMA 1s 超时 ≫ 最坏传输 ~7ms(无超时误报);`check_exist` 主动回显校验(死芯片不误判);边界检查 `!= CH395F_ERR_SUCCESS` 一致
|
||||
12. **CH395F/GD5F 共享 DMA 标志** `volatile` + 字对齐(M4 原子);GD5F CCM 守卫(`gd5f_dma_buf_ok` + 阈值)防 DMA 不可达缓冲入 DMA 路径
|
||||
13. **GD5F BBT 加载安全方向**:magic+CRC 双校验,读失败降级出厂扫描;slot 轮换有显式终止(无死循环);FTL 生产主路径读/回读均有 ECC 检查
|
||||
14. **GD5F Trap 01/02/03 修复在位**(页地址擦除、read/write 边界检查)——唯 erase 缺边界(E-43,文档与代码不符)
|
||||
15. **RS485 HAL 错误路径自愈**(F4 gState/RxState 分离:阻塞发送期间重武装可用;ORE 路径 HAL 先复位 RxState=READY)→ E-48 仅健壮性问题
|
||||
16. **SD2506 时间写原子性**(7 字节一次写,满足"不可单独写时间寄存器");24h 位/2099 回绕正确;电池电压寄存器 1AH[7]+1BH 9 位与手册一致("读错寄存器"假设被手册证伪)
|
||||
17. **边界调用方正确**:app_main 对 GD5F 7 码 + TPAFE OK/TIMEOUT 分别映射;sys_clock 全检查 sd2506 返回值
|
||||
18. **TPAFE 10ms BUSY 超时** ≫ t_CONV(µs 级)无误报;`s_ready/s_buf_sel` volatile 在位
|
||||
19. **已证伪假设留档**(防重复劳动):SD2506 电池电压读错寄存器(手册证伪);RS485 错误回调杀死并发发送期 RX(HAL 源证伪);ch395f.c "2s×HAL_Delay(1) 忙等"(当前代码不存在);SD2506"上电 18H 写无效 → 充电器终身未使能"(手册 §4.8"出厂均配置成 82H"+18H 非易失证伪,原 E-32 P0 链不成立,降 P2,见 §3.3)
|
||||
20. **模拟器方向**:`ui_net_sink` 已双模型自适应(单/多连接 accept fd);sim 构建仅编 UI+net_sink+配置日志引擎 → lftpd/timeSync 的 fd 泄漏类缺陷当前未触发(E-30 潜在项)
|
||||
|
||||
---
|
||||
|
||||
## 8. 审计边界与未覆盖项
|
||||
|
||||
- **未逐行审计**:`Lib/FatFs` 移植层内部(已知陷阱已在 AGENTS.md 固化:FA_OPEN_APPEND 无 FA_WRITE、短写 FR_OK、8.3 短名)、`Lib/dhara` 内核(仅 audit glue 映射层与错误分类)、lftpd 的字符串/IO 辅助模块(边界抽查)、`Middlewares/` CMSIS/HAL 内部(错误路径抽查)
|
||||
- **模拟器保真度**:16 项差异全表见附录 D;核心结论 = **09-21 类 errno 缺陷在 sim 结构性不可复现**(单线程 + 成功即清 errno 双重掩盖),该类修复只能硬件闭环验证
|
||||
- **驱动契约缺陷的"休眠"性质**:RS485/TPAFE 侧多项 P1(E-36/E-44/E-45/E-58)因消费者任务为空桩而暂不可触发——**接入消费者之前必须先修**(§5 第三档)
|
||||
- 本审计基于 2026-09-21 代码状态(`v2-on-ui` @ `a9aa8ce`);后续代码变更需增量复核
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:NET_ERR 错误码表(`net_types.h:98-109`)
|
||||
|
||||
| 码 | 值 | 含义 | 通知值语义 |
|
||||
|---|---|---|---|
|
||||
| NET_ERR_OK | 0 | 成功 | 成功:通知值 = 字节数/fd(≥0) |
|
||||
| NET_ERR | -1 | 通用错误 | 失败:通知值 = 负的精确错误码 |
|
||||
| NET_ERR_BADF | -2 | 无效 fd(含代次失效) | |
|
||||
| NET_ERR_INVAL | -3 | 参数无效 | |
|
||||
| NET_ERR_NOMEM | -4 | fd 池/缓冲耗尽 | |
|
||||
| NET_ERR_NOTCONN | -5 | 未连接 | |
|
||||
| NET_ERR_ISCONN | -6 | 已连接 | |
|
||||
| NET_ERR_WOULDBLOCK | -7 | 无数据/不可写(正常轮询态,**非错误**) | |
|
||||
| NET_ERR_CONNRESET | -8 | 对端复位 | |
|
||||
| NET_ERR_TIMEDOUT | -9 | 超时(recv 30s / send chunk 1s / listen 重开) | |
|
||||
| NET_ERR_NOSPACE | -10 | 芯片缓冲满 | |
|
||||
| NET_ERR_BUSY | -11 | 芯片忙(含 listen 端口占用) | |
|
||||
|
||||
超时参数(`net_config.h`):`NET_SEND_TIMEOUT_MS=1000`、`NET_RECV_TIMEOUT_MS=30000`(E-02/E-21 的改造对象)。
|
||||
|
||||
## 附录 B:事件位表(`App/sys_events.h`)
|
||||
|
||||
| 位 | 名称 | 语义 | 置位者 | 消费者(等待方式) |
|
||||
|---|---|---|---|---|
|
||||
| bit0 | NET_READY | 状态位 | netTask(组网成功) | ftp/ui/timeSync(`WaitAny|NoClear` ✓) |
|
||||
| bit1 | STORAGE_READY | 状态位 | defaultTask(挂载+配置加载完成) | 同上 |
|
||||
| bit2 | FTP_READY | **死位**(置位代码不可达,E-19) | (lftpd_start 返回后——永不) | 无 |
|
||||
| bit3 | TIMESYNC_READY | **死位**(从未置位,E-19) | 无 | 无(timeSync 实际等 NET/STORAGE) |
|
||||
| bit4 | NET_RECONFIG | 命令位(可消费) | defaultTask(UI 保存网口配置) | netTask(Get+Clear 非原子,E-17) |
|
||||
| bit5 | CFG_SAVE | 命令位(可消费) | uiTask | defaultTask(`WaitAny` 500ms + pending 锁存 ✓) |
|
||||
| bit28-30 | — | 保留 | — | — |
|
||||
|
||||
## 附录 C:项目队列清单与回传机制
|
||||
|
||||
| 队列 | 定义处 | 深度 | 条目形态 | 回传机制 | 免疫/风险 |
|
||||
|---|---|---|---|---|---|
|
||||
| `netMsgQueue` | `Src/freertos.c:166` | 8 | `net_msg_t` **按值**(fd/长度/输入指针) | 任务通知值(精确错误码/字节数);指针字段回写(net_accept 地址) | 09-21 后契约健全;残留 = 全局 errno 竞态(E-03)、头阻塞(E-02) |
|
||||
| syslog 队列 | `sys_log_mcu.c:218-223`(惰性) | 64 | 输入型(id + 静态串指针) | 无回传(fire-and-forget) | 结构免疫 P1;preboot 窗口(E-08) |
|
||||
| `cfgSaveQueue` | `freertos.c`(defaultTask/uiTask 间) | 8 | 输入型(路径指针) | `s_save_pending` 锁存 + `s_save_result` 状态机 | 协议无损(§7-3) |
|
||||
|
||||
## 附录 D:模拟器保真度差异表(MCU `net_socket.c` vs `simulator/sim/net_socket_sim.c`)
|
||||
|
||||
| # | 项目 | MCU 后端 | sim 后端 | 影响 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | accept fd 模型 | 返回监听 fd 自身(单连接 `g_client==g_listen`) | 返回独立新 fd,监听 fd 常驻(`sim:331-347`) | lftpd 式生命周期在 sim 泄漏 fd(移植即触发);ui_sink 已双模型自适应 |
|
||||
| 2 | EOF 后行为 | auto_relisten:fd 回 LISTEN、in_use 保持 | 无:fd 仍 in_use 但 Winsock 已关,二次 recv 报通用 NET_ERR | 依赖"EOF 后 fd 仍活"的写法在 sim 失真 |
|
||||
| 3 | recv 阻塞模式 | flags=0 阻塞,30s → -1/TIMEDOUT | 恒非阻塞(FIONBIO),`(void)flags`(`sim:447` 已复核),无 TIMEDOUT 路径 | **生产阻塞路径在主开发后端零覆盖**(E-02/E-06/E-20/E-21 类缺陷 sim 不可见) |
|
||||
| 4 | send 停滞契约 | 分块阻塞;1s/chunk 停滞 → 返回已发字节(可 0)+ TIMEDOUT | 单次非阻塞:WSAEWOULDBLOCK → 0 + WOULDBLOCK;部分发送 → 部分值 + **NET_OK(静默截断无告警)** | 同一条件双端 errno 不同 |
|
||||
| 5 | 零长度 send | -1/INVAL | 0/OK | phase10 零长用例 sim 必假 FAIL |
|
||||
| 6 | 零长度 recv | -1/INVAL(`len<=0`) | 0 = 对端关闭(`sim:453` 检查为 `len<0` → recv(0) → EOF 分支) | 边界契约不一致 |
|
||||
| 7 | net_connect 语义 | 异步:0=issued,netTask net_poll 推进 CONNECTING→ESTABLISHED | 进行中 = -1/WOULDBLOCK;state 永不推进(net_poll 空操作) | TC701 假 FAIL / TC703 负向用例**假 PASS** |
|
||||
| 8 | 错误码粒度 | 12 种精确码(附录 A) | 仅 BADF/INVAL/WOULDBLOCK + 通用 NET_ERR | 依赖精确码的分类逻辑在 sim 失真 |
|
||||
| 9 | 成功后 errno | **不清除**(stale 滞留,`net_socket.c:1609`) | **清零 NET_OK**(12 处,`sim:461` 等已复核) | **09-21 类缺陷在 sim 结构性不可复现**(+ 单线程无交错)——主开发后端对该缺陷族零灵敏度 |
|
||||
| 10 | net_listen 前置 | 须 BOUND/CREATED | 仅查 in_use | sim 更宽松 |
|
||||
| 11 | getsockname 数据源 | s_local_ip 缓存(静态 IP;DHCP → 0.0.0.0) | Winsock getsockname(INADDR_ANY → 127.0.0.1) | PASV 响应 IP 双端不同 |
|
||||
| 12 | 代次号 | reconfig 递增 | 恒 0 | 重建/重监听路径 sim 零覆盖 |
|
||||
| 13 | 事件回调 | net_poll 内 fire_event | 只存储不触发 | 回调驱动逻辑 sim 全死(当前无注册者) |
|
||||
| 14 | 并发模型 | 队列 + netTask 串行 SPI | 单主线程直调 Winsock | 跨任务 errno 竞态 sim 不可复现 |
|
||||
| 15 | UDP bind | open_socket + SPI(仅 UDP 分支) | 无对应步骤 | 当前无 UDP 用户 |
|
||||
| 16 | net_init | 芯片全量初始化 | 仅 WSAStartup | 设计对齐 |
|
||||
|
||||
---
|
||||
|
||||
*报告完。60 项发现全部带 file:line 落点;P0/P1 核心机制经父审计组逐行复核(含 `docs/芯片手册/SD2506API-G.md` L392-398/L537/§4.8 原文核对与 `SD2506_Trap_Records.md` 硬件观测交叉验证)。2026-09-21 复核修订:E-32 由 P0 降 P2(充电器 P0 链被"出厂 82H"证伪)、E-39 机制改写(ARST 在 CTR3、驱动从未写过 CTR3)、新增 E-60(write_enable WRTC2 与手册矛盾,待台架确认)、§6 交叉引用补 P2-2/P2-6。未修改任何代码、未执行构建。*
|
||||
Reference in New Issue
Block a user