fix(N34): E14 丢中断复核——芯片不丢事件;listen 排事件挪至 LISTEN 前+全局读收口+全库归因更正
48 轮直读 SINT 矩阵(8 周期×6 变体)复核历史"丢中断"说法:芯片侧事件 丢失 = 0。归类:drain 读清消费 3/48(drain-int 探针现场抓获)、门控 竞态 2/48、主机同步 1/48、幽灵态 0/48 未复现。 根除性修复:net_listen_locked / net_listener_try 的排事件读清从 listen 后挪至 LISTEN 命令之前——新连接 CONNECT 落进该窗即被本方读清 吞掉,即"数据被吞"的生产侧自伤机制(台架 3/48 为同形态放大版)。 全局读收口:全局中断寄存器唯一读者 = net_poll(I7);生产/测试/台架 游离全局读全部删除,ch395f.h 声明处加护栏注释。 归因更正:17 文件约 40 处"丢中断"措辞改为"事件未收到/读清消费竞态" (机制全部保留,仅归因更正);设计文档 §5.2 新增 E14 锚点;Trap Records TRAP-NET-17/-20/B3 归因更正段;配图生成器与 SVG 重生成。 台架:事件未收到复核探针保留(drain-int / SINT 补读×3 / 超时归类), 下次跑矩阵自动归类;实验注册(main.c/uvprojx)已完全还原。 验证:@build 0 错误 0 警告;ci_local L0 32/0 + gitlint + L0.5 953/953;doc-links --all --sections-strict 通过;@test PASS(无 [ERR],net init 619ms);回归 :7003 300ms×10 = 10/10、:21 n30×50 = 0/50 失败。
This commit is contained in:
@@ -28,10 +28,10 @@ CH395F 以太网驱动与 NET 层的实证陷阱:现象、定案根因、修
|
||||
| TRAP-NET-14 | IP/端口字节序 | ✅ | IP 四字节数组=点分顺序;16 位端口=低字节在前,两套口径 |
|
||||
| TRAP-NET-15 | FUN_PARA bit0 迷思 | ✅ | bit0 保留必须为 0(`0x08`);Socket 可用性取决于缓冲显式分配 |
|
||||
| TRAP-NET-16 | 单次写超发送缓冲(写穿) | ✅ | 上限=本仓 2 块发送缓冲 1KB(`NET_SEND_CHUNK_MAX`),门控是 SENBUF_FREE |
|
||||
| TRAP-NET-17 | 快速复连丢中断 | ✅ | 芯片丢 CONNECT/DISCONNECT → 周期对账 `net_reconcile_tick`(reconciler 之源) |
|
||||
| TRAP-NET-17 | 快速复连事件面未收到 → 监听静默死亡 | ✅ | 周期对账 `net_reconcile_tick`(reconciler 之源);E14 复核:非芯片丢事件,是读清消费竞态 |
|
||||
| TRAP-NET-18 | 上传中止后首笔复连被截断 | ✅ | 残渣投递给下一条连接 → 两级沉降/隔离 + 异常释放回 450 删截断文件 |
|
||||
| TRAP-NET-19 | SET_MAC 顺序 | ✅ | §9.2.1:SET_MAC 必须在 `INIT_CH395` **之前**(INIT 从 EEPROM 加载) |
|
||||
| TRAP-NET-20 | FTP 数据连接三连故障 | ✅ | 三重定案:丢中断窗口探针+幽灵甄别 / closed-with-data / 背靠背小段毒化 TX |
|
||||
| TRAP-NET-20 | FTP 数据连接三连故障 | ✅ | 三重定案:状态回读补 accept + 幽灵甄别 / closed-with-data / 背靠背小段毒化 TX("丢中断"归因已被 E14 复核推翻) |
|
||||
| TRAP-NET-21 | IP 显示段序与对端地址 | ✅ | `net_inet_ntoa` 段序 MSB 首;对端地址用 `net_getpeername`,勿用 `getsockname` |
|
||||
|
||||
---
|
||||
@@ -106,12 +106,12 @@ CH395F 以太网驱动与 NET 层的实证陷阱:现象、定案根因、修
|
||||
3. **反核实**:依据中断做结论前用 `GET_SOCKET_STATUS` 验证实际状态一致(负向事件与 ESTABLISHED 矛盾时以状态为准)。
|
||||
- **注意**:B3 修复后预清只准读**本 Socket** SINT,不得再全量读 8 个(见第六章 B3)。
|
||||
|
||||
## TRAP-NET-17 · 快速复连丢 CONNECT/DISCONNECT 中断 → 监听静默死亡
|
||||
## TRAP-NET-17 · 快速复连事件面未收到 → 监听静默死亡(E14 复核:非芯片丢事件)
|
||||
|
||||
- **状态**:✅(周期对账 `net_reconcile_tick` 是 N34 reconciler 的前身;阶段 3 已并入单一 100ms tick)
|
||||
- **现象**:单连接监听 socket 快速复连后**永久失效**(后续 SYN 被拒或超时,10s 内不自恢复),芯片其余部分完全正常(其他监听端口照常工作)。两种表现:芯片完成握手但 CONNECT 中断未上报 → 控制块滞留 LISTENING;或 DISCONNECT 中断丢失 → 本层仍以为有客户端。两种情况芯片侧都占住单连接监听;应用侧连锁 `net_send` 被拒、读不到 EOF。
|
||||
- **状态**:✅(周期对账 `net_reconcile_tick` 是自愈架构 reconciler 的前身;收敛阶段 3 已并入单一 100ms tick)
|
||||
- **现象**:单连接监听 socket 快速复连后**永久失效**(后续 SYN 被拒或超时,10s 内不自恢复),芯片其余部分完全正常(其他监听端口照常工作)。两种表现:芯片完成握手但本层收不到 CONNECT 事件 → 控制块滞留 LISTENING;或 DISCONNECT 事件未收到 → 本层仍以为有客户端。两种情况芯片侧都占住单连接监听;应用侧连锁 `net_send` 被拒、读不到 EOF。
|
||||
- **复现(可信判据,纯客户端;工具 `MDK-ARM/tcp_reconnect_probe.py`)**:待机 60s 后 `:7003` 300ms 快速复连 → 修复前首 2 次成功、之后永久拒连(2/10);FTP `:21` 100ms×50 → 第 29 轮起永久拒连。对照 `:21` 0ms/600ms 与 `:8888` 正常 → 触发条件 = 复连间隔 <~1s + 轻负载。
|
||||
- **机制(设备侧日志实证)**:① 芯片丢 CONNECT 或 DISCONNECT 中断;② 此后本层**收不到任何中断**,纯事件驱动无路可走;③ 故必须**周期回读芯片状态对账**。裸机芯片台架(剥离 OS/net/FTP)独立复现——芯片天性,不是固件 bug。
|
||||
- **机制(设备侧日志实证)**:① 本层收不到对应事件;② 此后本层**收不到任何中断**,纯事件驱动无路可走;③ 故必须**周期回读芯片状态对账**。**归因更正(E14 复核,2026-10-14,48 轮直读 SINT 矩阵)**:旧"芯片丢中断、芯片天性"结论已被推翻——SINT 可靠锁存事件,"丢失"实为**读清消费竞态**(台架 post-listen drain 吞掉即时到达的 CONNECT,drain-int 探针现场抓获 3/48)与**门控观察伪象**(2/48,SINT 30ms 内即捕获)。对账机制保留不变:事件可被其它合法读者消费,且状态面存在事件覆盖不了的幽灵态。
|
||||
- **修复(`net_socket.c`)**:
|
||||
1. **周期对账 `net_reconcile_tick()`**(与 pump 合并为单一 100ms tick):修正失同步形态——①本层 ESTABLISHED 而芯片已无连接→按断开处理;②本层 LISTENING 而芯片已握手→补 accept;③本层 LISTENING 而芯片监听消失→重建;④孤儿监听(B10)→重建。**去抖+冷却**:同一失配连续 2 次才动手,动手后冷却 1s。
|
||||
2. `handle_disconnect_event` LISTENING 分支:回读芯片状态——已不可监听即重建。
|
||||
@@ -123,11 +123,11 @@ CH395F 以太网驱动与 NET 层的实证陷阱:现象、定案根因、修
|
||||
|
||||
- **状态**:✅(回归 4 轮 11/11;全局寄存器读即清见第六章 B3)
|
||||
- **现象**:同一会话内**第 2 次及以后**的数据连接(LIST/STOR/RETR)两类失败,概率随上一条数据连接的负载与间隔变化:① **吞数据**——新连接建立成功、服务端 `net_send` 返回成功,客户端 20s 收不到任何字节;② **425**——新连接被接受后 ~10-20ms 出现 DISCONNECT(`chip tcp=0x00`),accept 失败。
|
||||
- **定案实验(裸机芯片台架,剥离 OS/net/FTP)**:`test/board/ch395f_reuse_bench.c` 内核启动前直接调 `ch395f_*` 受控矩阵 + 主机侧 `test/host/ch395f_reuse_host.py`。**剥离后 bug 依旧复现** → 纯芯片操作时序问题。关键现场:`post-listen st=0x05/0x04`(listen 成功但 TCP 态停在旧连接 ESTABLISHED=幽灵态)、`unexpected int=0x10`(只等来旧连接滞后 DISCONNECT)、`no CONNECT within 10s`(芯片状态已 0x04 但中断永远没来)。
|
||||
- **芯片级规律**:CH395F 拆链收尾窗口**变长且不可观测**(状态显示 CLOSED 持续 1s+ 也不代表拆完,实测可 >2s 且时长不定)。窗口内重新 `open+listen` 三种形态:① 握手完成但 **CONNECT 中断被丢**(TRAP-NET-17 机制的窗口期形态)→ 吞数据;② 状态直接假报 0x04 幽灵态(回读对端端口=旧连接端口);③ 旧连接滞后 DISCONNECT 打到新监听 → relisten 毁掉新连接 → 425。
|
||||
- **定案实验(裸机芯片台架,剥离 OS/net/FTP)**:`test/board/ch395f_reuse_bench.c` 内核启动前直接调 `ch395f_*` 受控矩阵 + 主机侧 `test/host/ch395f_reuse_host.py`。**剥离后 bug 依旧复现** → 纯芯片操作时序问题。关键现场:`post-listen st=0x05/0x04`(listen 成功但 TCP 态停在旧连接 ESTABLISHED=幽灵态)、`unexpected int=0x10`(只等来旧连接滞后 DISCONNECT)、`no CONNECT within 10s`(芯片状态已 0x04 但事件始终未到等待环——E14 复核:事件已被 drain 读清消费,非芯片丢失)。
|
||||
- **芯片级规律**:CH395F 拆链收尾窗口**变长且不可观测**(状态显示 CLOSED 持续 1s+ 也不代表拆完,实测可 >2s 且时长不定)。窗口内重新 `open+listen` 三种形态:① 握手完成但 **CONNECT 事件未收到**(E14 复核:事件被台架 post-listen drain 或读清竞态吞掉,非芯片侧丢失)→ 吞数据;② 状态直接假报 0x04 幽灵态(回读对端端口=旧连接端口);③ 旧连接滞后 DISCONNECT 打到新监听 → relisten 毁掉新连接 → 425。
|
||||
- **已证伪的修复**(受控实验全部无效,列出防重踩):close 后固定沉降 100~3000ms / Socket 轮换(跨 socket 毒化)/ listen 后 close+重试 / 缓冲重分配 / close 前 TX 排空延时 / 禁用对账 tick(对账不是元凶)。
|
||||
- **定案一:双信源原则——中断只是快路径,状态寄存器才是真相**:
|
||||
1. **accept 路径内联探针**(`net_lost_connect_probe`,`net_accept_locked` 每 100ms 挂探针):芯片已 ESTABLISHED 而 CONNECT 中断未到 → 连续 2 轮去抖后补 accept;
|
||||
1. **accept 路径内联探针**(`net_lost_connect_probe`,`net_accept_locked` 每 100ms 挂探针):芯片已 ESTABLISHED 而 CONNECT 事件未到 → 连续 2 轮去抖后补 accept;
|
||||
2. **幽灵态甄别**:补 accept 前回读对端端口(`GET_REMOT_IPP`),**= 上一条连接端口(`last_peer_port`)→ 幽灵,不 accept**;
|
||||
3. 对账②同步加幽灵甄别;`last_peer_port` 登记点:`handle_disconnect_event` / `handle_timeout_event` / `net_relisten_try`(清零 remote_port 前转登)/ `net_close_locked`(跨 memset 保留——幽灵属芯片 Socket 级,与 fd 复用无关)。
|
||||
- **定案二:closed-with-data accept(425 真根因=accept 竞态自毁缓冲)**:`FUN_PARA` bit3 `SOCKET_CLOSE=1`(§5.52 ②)下产生 DISCONNECT 后芯片**不关 Socket、不清缓冲**,RX 缓冲里 STOR 载荷一个不少(探针实证 `recv_len=1027 re-read st=0x05/0x00`);那个 DISCONNECT 是**客户端正常 FIN**(ftplib `storbinary` 发完即关,局域网小文件 ~5ms)。旧 `net_accept_locked` 把 CLOSED 一律当"无连接" → `net_relisten_try`(close+open **清空芯片 RX 缓冲**)→ 数据被**我方亲手销毁** → 425。**修复**:CLOSED + auto_relisten + **芯片 RX 有数据**(`GET_RECV_LEN_SN`>0)→ 接受这条"已关但可读"连接(日志 `closed-with-data accept (drain pending)`),应用快路径排空,排空后 EOF 自然触发 relisten——对齐 §5.52 ② 契约。
|
||||
@@ -153,8 +153,8 @@ CH395F 以太网驱动与 NET 层的实证陷阱:现象、定案根因、修
|
||||
2. **C 模式真因**:`FUN_PARA` bit3 `SOCKET_CLOSE=1`(§5.52 ②)下产生 DISCONNECT 后**芯片不关 Socket、不清缓冲**,长期停在 `05H/00H`;此时直接 `open_socket` 必返 `0x20` FAIL——**把 Socket 拉回 CLOSED 的动作是 `close`**;
|
||||
3. **做法**:直接 `ch395f_close_socket()`;`disconnect` 纯属多余且会**制造** DISCONNECT 事件(bit3=1 下必须被消费/预清,否则污染下一条连接,TRAP-NET-12/23 实证);
|
||||
4. **观测口径**:§5.22 第 1 字节只有 `00H/05H`,第 2 字节(TCP 态)**仅在 byte0=OPEN 时有意义**——关闭后轮询**第 1 字节**等 `00H` 再 open,别拿 byte1 当"卡住"判据。
|
||||
- **边界(E-02 台架)**:`close` 无 RST 的适用条件——对端配合(收到 FIN 后其内核 close):芯片走 `TIME_WAIT` ~150ms → relisten → 新连接接入;**对端内核存活但不 close**:芯片停在 **`FIN_WAIT_2`(byte0=05H/byte1=06H)长尾**——没有 RST 就拆不掉,**监听恢复受限于对端 close**。不配合场景的兜底 = 芯片自身 TIMEOUT/KeepAlive 拆僵尸会话(判死窗 30~90s,`docs/网络/N34-自愈审计.md` §6 E13)——**不是**应用可加速的路径。
|
||||
- **应用侧出口 = `net_drop_conn(fd)`**(N34 阶段 3):netTask 侧走驱动 `sock_reset` QUARANTINE(FIN+排渣+排空事件+等 byte0=00H,400ms 底)+ 本层收口 CLOSED + `auto_relisten` 即时重监听;**保监听不毁**(单连接模型不得 `net_close` 监听 fd)。对端不 close 时复位机等在 `net_poll` 步进里(`ch395f_sock_reset_poll`),对端 FIN 到达即自动走完——**不搁浅**。
|
||||
- **边界(E-02 台架)**:`close` 无 RST 的适用条件——对端配合(收到 FIN 后其内核 close):芯片走 `TIME_WAIT` ~150ms → relisten → 新连接接入;**对端内核存活但不 close**:芯片停在 **`FIN_WAIT_2`(byte0=05H/byte1=06H)长尾**——没有 RST 就拆不掉,**监听恢复受限于对端 close**。不配合场景的兜底 = 芯片自身 TIMEOUT/KeepAlive 拆僵尸会话(判死窗 30~90s,`docs/网络/网络自愈架构设计.md` §5.2 E13)——**不是**应用可加速的路径。
|
||||
- **应用侧出口 = `net_drop_conn(fd)`**(自愈收敛阶段 3):netTask 侧走驱动 `sock_reset` QUARANTINE(FIN+排渣+排空事件+等 byte0=00H,400ms 底)+ 本层收口 CLOSED + `auto_relisten` 即时重监听;**保监听不毁**(单连接模型不得 `net_close` 监听 fd)。对端不 close 时复位机等在 `net_poll` 步进里(`ch395f_sock_reset_poll`),对端 FIN 到达即自动走完——**不搁浅**。
|
||||
|
||||
## TRAP-NET-09 · 沉降窗:PHY link up 后 / close 后不能立即 open
|
||||
|
||||
@@ -226,7 +226,7 @@ CH395F 以太网驱动与 NET 层的实证陷阱:现象、定案根因、修
|
||||
|
||||
## TRAP-NET-21 · IP 显示段序与对端地址(getsockname ≠ 对端)
|
||||
|
||||
- **状态**:✅(N34 阶段 3 收尾修复)
|
||||
- **状态**:✅(自愈收敛阶段 3 收尾修复)
|
||||
- **现象**:FTP 日志把 IP 打成 `100.1.168.192`(字节反转),且打印的是**本机** IP:21 而非客户端地址——排查连接来源时被误导。
|
||||
- **根因(定案)**:① `net_inet_ntoa` 段序反转——`NET_NTOHL` 后主机数字的 **MSB 才是点分首段**,原实现低段在前;② lftpd 打印 `net_getsockname` = 本机监听地址(端口恒 21),对端地址应读 `net_getpeername`。PASV 227 消费点做了 `net_htonl` 再 MSB 拆,**双重反转恰好正确,勿误判**。
|
||||
- **修复**:`net_inet_ntoa` 段序改 MSB 首(MCU+模拟器同步);新增 `net_getpeername`(读 CONNECT 事件缓存,零 SPI);lftpd 改打印**对端** IP:port(台架判据:日志 `connection received from 192.168.1.x:<高位端口>`)。
|
||||
@@ -267,14 +267,14 @@ CH395F 以太网驱动与 NET 层的实证陷阱:现象、定案根因、修
|
||||
|
||||
| # | 严重度 | 位置 | 问题 | 状态 |
|
||||
|---|---|---|---|---|
|
||||
| B2 | 中低 | `net_connect_locked` | `ch395f_tcp_connect()` 返回值曾被丢弃(§9.2.2 ⑧:SUCCESS 仅表示**命令**执行成功)。已修:非 SUCCESS 且**非 BUSY** 才判失败(`ERR_BUSY`=芯片仍在执行,当失败会误杀慢芯片);明确失败 → `[ERR] rejected by chip: 0xNN` + 按 §9.2.2 ⑧ 关 Socket 回滚 + 返 -1 | ⚠ 残余:`net_reconcile_tick` 仍只裁 ESTABLISHED/LISTENING,`CONNECTING` 丢中断无人收口(产品不走出向连接、无法实测,按"不加未证明复杂度"暂不动) |
|
||||
| B3 | 低→**结构性** | `net_listen_locked` + `net_relisten_locked`(后者是每次断连的热路径) | 对**全部 8 个 Socket** `GET_INT_STATUS_SN`(读即清)+ 无条件读清全局。机制:§5.15 全局中断寄存器**整体读即清、无按位清除手段**,而 `net_poll` 只在全局位置位时才读该 Socket 的 SINT(门控)→ 丢弃式读全局把别的在用 Socket 的门一并焊死;SINT 又**粘滞到被读** → 全量读走即**永久销毁**其 CONNECT/DISCONNECT/TIM_OUT/SENBUF_FREE(唯 RECV 是条件位可下轮补,TRAP-NET-08)。官方 SDK 21 个例程一律"全局位图门控 + 单 Socket 读 + 立刻转存",无一例全量读 8 个 SINT | ✅ 已修:两处收窄为**只清本 Socket SINT** + **删除丢弃式全局读清**(全局交 `net_poll` 集中读清=AGENTS 模块约定)。⚠ 残余:`net_connect_locked`/`net_close_locked`/`net_relisten_locked` 三处指定预清仍含无条件全局读清(会**搁浅**而非销毁其它 Socket 挂起事件)——它们是 TRAP-NET-12/23 的两级预清,宜与 B4 一并处置 |
|
||||
| B2 | 中低 | `net_connect_locked` | `ch395f_tcp_connect()` 返回值曾被丢弃(§9.2.2 ⑧:SUCCESS 仅表示**命令**执行成功)。已修:非 SUCCESS 且**非 BUSY** 才判失败(`ERR_BUSY`=芯片仍在执行,当失败会误杀慢芯片);明确失败 → `[ERR] rejected by chip: 0xNN` + 按 §9.2.2 ⑧ 关 Socket 回滚 + 返 -1 | ⚠ 残余:`net_reconcile_tick` 仍只裁 ESTABLISHED/LISTENING,`CONNECTING` 事件未收到无人收口(产品不走出向连接、无法实测,按"不加未证明复杂度"暂不动) |
|
||||
| B3 | 低→**结构性** | `net_listen_locked` + `net_relisten_locked`(后者是每次断连的热路径) | 对**全部 8 个 Socket** `GET_INT_STATUS_SN`(读即清)+ 无条件读清全局。机制:§5.15 全局中断寄存器**整体读即清、无按位清除手段**,而 `net_poll` 只在全局位置位时才读该 Socket 的 SINT(门控)→ 丢弃式读全局把别的在用 Socket 的门一并焊死;SINT 又**粘滞到被读** → 全量读走即**永久销毁**其 CONNECT/DISCONNECT/TIM_OUT/SENBUF_FREE(唯 RECV 是条件位可下轮补,TRAP-NET-08)。官方 SDK 21 个例程一律"全局位图门控 + 单 Socket 读 + 立刻转存",无一例全量读 8 个 SINT | ✅ 已修:两处收窄为**只清本 Socket SINT** + **删除丢弃式全局读清**(全局交 `net_poll` 集中读清=AGENTS 模块约定)。残余的三处指定预清无条件全局读清已由 2026-10-13 全局读收口全部删除(全库唯一读者=`net_poll`)。⚠ 2026-10-14 E14 复核追加:两处 listen 路径的排事件读清原在 LISTEN 命令**之后**——快速复连时新连接 CONNECT 落进该窗即被本方吞掉("数据被吞"的生产侧自伤机制,与 B3 同族),已挪至 LISTEN 命令**之前** |
|
||||
| B4 | 低 | `net_socket.c` 全局中断分发 | 全局中断 bit0-3(`UNREACH`/`IP_CONFLI`/`PHY_CHANGE`/`DHCP`)被读清但从不消费;`ch395f_get_unreach_info()` 全仓零调用 → IP 冲突、ARP 不可达、链路抖动三类事件完全静默 | ⏳ 待修(可观测性缺口) |
|
||||
| B6 | 低 | `net_socket.c` 3 处 | 状态判据只看 `byte1`、未按 §5.22 用 `byte0` 门控;reconcile 的 else-if 链里 `(00H, 陈旧 04H)` 会先命中"补 accept"而非"重建监听" | ⏳ 待修(TRAP-NET-07 实测 `byte0=00H` 时 `byte1` 读回 `0x00`,故为低概率) |
|
||||
| B7 | 低 | `ch395f_test_task.c` Phase 5 | 单次写 4093B 超 1KB 发送缓冲与 2048 DMA 载荷 → 整块被丢;注释仍写旧上限。写路径金丝雀(A6)会打 `[ERR]` | ⏳ 待修(chunk 改 ≤1024 或分块) |
|
||||
| B8 | 低 | `ch395f_test_task.c` Phase 4 | 把 sock6 缓冲重排到块 24-27,与 `net_init` 固定布局中 sock4 的 24-27 **交叠**(§5.45 禁止) | ⏳ 待修 |
|
||||
| B9 | 低 | `ch395f_reuse_bench.c`、`net_socket.c` | bench 用 `get_phy_status()!=0` 判链路(01H=断开也成立,应 `!= CH395F_PHY_DISCONN`);`NET_PHY_CHECK_INTERVAL_MS` 死宏;无芯片版本门控(§5.16 <0x44 不支持 ALL 版、§5.52 bit3/4 需 ≥0x46,实测 0x4A 无影响) | ⏳ 待修(前两项死代码/卫生) |
|
||||
| B10 | **中** | `net_reconcile_tick` 认领范围 + `handle_disconnect_event` 释放分支 | **孤儿监听 → 端口永久拒连**:释放分支把状态置 CLOSED 等"应用重新监听",但 `ui_net_sink` 只在 `net_recv` 返 0(EOF)时才清 `g_client`,而 EOF 已被事件分支消费 → net 层等应用重建 / 应用等 net 层给 EOF / 对账只裁 ESTABLISHED·LISTENING 直接跳过 / 芯片不会自己回 LISTEN,**四方互等**。对账①③都要求本层处于 ESTABLISHED/LISTENING,**孤儿态恰好落在两类之外** | ✅ 已修:对账新增**形态④**认领孤儿(`in_use`+`state==CLOSED`+`auto_relisten`+`type==STREAM`+`local_port!=0`,芯片侧不在 LISTEN)→ `net_relisten_try`。⚠ `auto_relisten` 是决定性门:少了它出向连接会被误判孤儿 relisten 成监听器。⚠ ④ 的副作用:`net_recv` 的 EOF 是**边沿**——④抢先 relisten 会把慢消费者的 EOF 永久吞掉。**根治出口 = `net_drop_conn` + 生命周期设计**(N34 阶段 3 已落):原理与分阶段方案 → `docs/子系统设计/网络Socket监听生命周期设计说明.md` |
|
||||
| B10 | **中** | `net_reconcile_tick` 认领范围 + `handle_disconnect_event` 释放分支 | **孤儿监听 → 端口永久拒连**:释放分支把状态置 CLOSED 等"应用重新监听",但 `ui_net_sink` 只在 `net_recv` 返 0(EOF)时才清 `g_client`,而 EOF 已被事件分支消费 → net 层等应用重建 / 应用等 net 层给 EOF / 对账只裁 ESTABLISHED·LISTENING 直接跳过 / 芯片不会自己回 LISTEN,**四方互等**。对账①③都要求本层处于 ESTABLISHED/LISTENING,**孤儿态恰好落在两类之外** | ✅ 已修:对账新增**形态④**认领孤儿(`in_use`+`state==CLOSED`+`auto_relisten`+`type==STREAM`+`local_port!=0`,芯片侧不在 LISTEN)→ `net_relisten_try`。⚠ `auto_relisten` 是决定性门:少了它出向连接会被误判孤儿 relisten 成监听器。⚠ ④ 的副作用:`net_recv` 的 EOF 是**边沿**——④抢先 relisten 会把慢消费者的 EOF 永久吞掉。**根治出口 = `net_drop_conn` + 生命周期设计**(自愈收敛阶段 3 已落):原理与分阶段方案 → `docs/子系统设计/网络Socket监听生命周期设计说明.md` |
|
||||
|
||||
## 台架注入陷阱(方法论,持续补充)
|
||||
|
||||
@@ -308,9 +308,9 @@ CH395F 以太网驱动与 NET 层的实证陷阱:现象、定案根因、修
|
||||
# 附录 A · 驱动关键点速查(细节以对应条目为准)
|
||||
|
||||
- **初始化顺序**:`SET_MAC → SET_IP/GWIP/MASK → INIT_CH395 → SET_PHY`(TRAP-NET-19;IP 族必须在 INIT 前——INIT 读取并锁定寄存器值;SET_PHY 在 INIT 后重建物理链路);`CMD_PING_ENABLE` 不需显式调用。
|
||||
- **SPI 通信**:每次事务 `ch395f_spi_begin()/end()` 包裹;分频 8(PCB 约束);大数据走 SPI2 DMA(`CH395F_SPI_DMA_BUF_SIZE`=2048+4=2052,`ch395f.h:148`;历史值 1500/4100 已作废)。⚠ **TSL 规格偏离(N34 E12 登记)**:手册 §7.4 要求缓存区命令"长度与数据之间"留 TSL(读 0.4us/写 0.2us),本仓两个缓存区函数把命令头与数据打包进**同一 DMA 突发**(间隔=0)属规格偏离;**实测未发现危害**(460 轮/≈58MB 双向 SHA-256 仅 1 次未复现异常,"两段+TSL"变体与现状无差异)→ 依 TRAP-NET-20 铁律暂不改产品路径,偏离与证据登记于此、变体留作待用补丁(完整 A/B 见 `docs/网络/N34-自愈审计.md` §6 E12)。
|
||||
- **SPI 通信**:每次事务 `ch395f_spi_begin()/end()` 包裹;分频 8(PCB 约束);大数据走 SPI2 DMA(`CH395F_SPI_DMA_BUF_SIZE`=2048+4=2052,`ch395f.h:148`;历史值 1500/4100 已作废)。⚠ **TSL 规格偏离(E12 登记)**:手册 §7.4 要求缓存区命令"长度与数据之间"留 TSL(读 0.4us/写 0.2us),本仓两个缓存区函数把命令头与数据打包进**同一 DMA 突发**(间隔=0)属规格偏离;**实测未发现危害**(460 轮/≈58MB 双向 SHA-256 仅 1 次未复现异常,"两段+TSL"变体与现状无差异)→ 依 TRAP-NET-20 铁律暂不改产品路径,偏离与证据登记于此、变体留作待用补丁(A/B 结论锚点 `docs/网络/网络自愈架构设计.md` §5.2 E12,过程全文见其 §9 归档)。
|
||||
- **TCP 参数**(必须在 INIT_CH395 之前):重传 `SET_RETRAN_COUNT`(默认 12,最大 20)×`SET_RETRAN_PERIOD`(默认 500ms,最大 1000ms);KeepAlive 参数 500ms 倍数、IDLE>INTVL(TRAP-NET-06)。
|
||||
- **KeepAlive 定案链**:默认关闭 → 服务器侧应在 `SINT_STAT_CONNECT` 后开启(§5.56)→ 链路层死亡实测 +2s 即被重传(§5.17 8×200ms)覆盖、与 KeepAlive 无关 → **"链路在、对端静默"场景实测判死 ≈30s**(60s/5s/3,早于手册公式 75s,以实测为准;短参数 6s/2s/2 → ≈5.1s 证明参数生效;静默但存活对端不误杀)→ **已转正**:`handle_connect_event` LISTENING 分支开启 60s/5s/3;消费链路 TIM_OUT → `handle_timeout_event` QUARANTINE → relisten → 端口恢复(方法学与数据见 `docs/网络/N34-自愈审计.md` §6 E11/E13)。
|
||||
- **KeepAlive 定案链**:默认关闭 → 服务器侧应在 `SINT_STAT_CONNECT` 后开启(§5.56)→ 链路层死亡实测 +2s 即被重传(§5.17 8×200ms)覆盖、与 KeepAlive 无关 → **"链路在、对端静默"场景实测判死 ≈30s**(60s/5s/3,早于手册公式 75s,以实测为准;短参数 6s/2s/2 → ≈5.1s 证明参数生效;静默但存活对端不误杀)→ **已转正**:`handle_connect_event` LISTENING 分支开启 60s/5s/3;消费链路 TIM_OUT → `handle_timeout_event` QUARANTINE → relisten → 端口恢复(方法学与数据见 `docs/网络/网络自愈架构设计.md` §4.7 与 §5.2 E11/E13)。
|
||||
- **Socket 缓冲**:默认只给 Socket 0~3;Socket 4~7 必须 `open_socket` 前显式分配(TRAP-NET-02);现行固定布局 6 块/Socket。
|
||||
- **TCP 关闭重连**:直接 `ch395f_close_socket()`,不要先 `disconnect`;轮询 `GET_SOCKET_STATUS` **第 1 字节**等 `00H` 再 open;第 2 字节仅在 byte0=OPEN 时有意义(TRAP-NET-07)。
|
||||
- **SOCK_TIMEOUT**:`SINT_STAT_SOCK_TIMEOUT` 不应视为致命——记录日志后继续操作(消费侧 QUARANTINE 见 TRAP-NET-18/B1 链)。
|
||||
|
||||
Reference in New Issue
Block a user