test(net/udp): 阶段 16 UDP 数据面真机回归台架(板端 + PC peer),台账补实测数字(#73)
- 板端 net_layer_udp_bench_test_main:TC-NET-1601~1609,逐步覆盖主机测替代不了的 5 条台架必验——形态切换与 bind 后首包、48 B 应答在客户端形态的载荷完整(TRAP-NET-22 的真机对照)、截断包排空、挂起等待期间的 IWDG 余量、DesIP 先于 OPEN_SOCKET 的顺序。 每步都有 25 s 预算,PC 缺席时报 [FAIL] 而不是把 testTask 钉死。 - PC peer test/host/udp_queue_bench.py:板端先 bind 当服务端,对端地址从收到的包里学 (PC 是 DHCP,TEST_PC_PEER_IP 那个固定地址不在本机上);对 >1024 B 的回显额外监听 第二片,把 net_send_locked 分块在 UDP 上的后果量出来。 - ch395f_test.h:阶段 16 宏默认关(与其它阶段一致),RAM 台账补 2026-11-03 实测—— 台架镜像 RW_IRAM1 96,232 B / 73.4%,阶段 16 自身代价 1,600 B;产品镜像 RW+ZI 与改造前逐字节相同。 - 文档:CH395F_Test_Guide §12.6 跑法与判据 + Python 脚本表加一行;覆盖率口径第 4 轮 「台架必验」指向本阶段的可执行形态,并写明尚未在真机上跑过。
This commit is contained in:
@@ -1125,6 +1125,64 @@ WAIT_CLOSE 阶段已内置二段补发(只补一次),生产 net 层则靠
|
||||
|
||||
---
|
||||
|
||||
## 12.6 阶段 16:BSD UDP 数据面真机回归(NET 层,需 PC peer)
|
||||
|
||||
**动机**:PR #74 把 UDP 数据面并入 `netMsgQueue` 串行执行(见
|
||||
`docs/网络/BSD_Socket_API_函数调用关系.md` §4.11),并顺手修掉「DesIP 形态与剥头不一致」
|
||||
这个会让 48 字节应答丢掉前 8 字节的缺陷(`docs/问题记录/CH395F_Trap_Records.md` 的 TRAP-NET-22)。
|
||||
主机单测(`test/host/net_socket/ns_s7_udp.c`,`docs/测试说明/单元测试/主机单测覆盖率口径.md` 第 4 轮)
|
||||
把逻辑面钉死了,但芯片行为——DesIP 生效时机、UDP 的 SINT 触发、读指针上的残包排空——在桩里怎么编都"对"。
|
||||
下面 5 条就是那轮登记簿留下的「台架必验」的可执行形态。
|
||||
|
||||
**架构**:板端一个 UDP socket,先 `net_bind(:6001)` 做服务端,再从收到的包里学对端地址回显。
|
||||
**必须板端先听**:PC 侧是 DHCP(本轮实测 192.168.31.161),`net_test_task.h` 里那个
|
||||
`TEST_PC_PEER_IP` 192.168.31.2 并不在本机上,让板端当客户端就连不上;板端当服务端则从不断言
|
||||
"对端是谁"才收,对端从实测包文学来,PC 换 IP 也不用改固件。PC 侧跑
|
||||
`test/host/udp_queue_bench.py` 逐步激励,用**三个不同源端口的 socket** 分批发("客户端形态会不会
|
||||
把别的源过滤掉"那一步必须有区分度才有意义)。
|
||||
|
||||
| 用例 | 内容 | 通过标准 | 必验 |
|
||||
|------|------|----------|------|
|
||||
| TC-NET-1600 | 载荷样式自检(fill/match 自洽) | 篡改第 9 字节 → match 返回 10;换 tag → 返回 1 | — |
|
||||
| TC-NET-1601 | socket + bind :6001 + bind 后首包 | `net_udp_get_mode` = SERVER;20 B 收到且长度/样式/`addrlen`/`sin_family` 全对、学到 PC 源端口;回显后 mode = CLIENT | ① ⑤ |
|
||||
| TC-NET-1602 | **48 B 应答落在客户端形态**(SNTP 尺寸) | 必须读到 48 B 且 `buf[0]` 就是载荷首字节——判据写错会只剩 40 B 且前 8 字节被当信息头吃掉;回显走 `dest_addr = NULL`(用记账对端那条路) | ① |
|
||||
| TC-NET-1603 | `net_udp_set_peer(fd, NULL)` 回服务器形态 | 返回 0、mode = SERVER,且**不重开**芯片 socket;随后**换新源端口**发来的包必须收到(客户端形态会被 DesIP 过滤),再回显给这个新端口 | ② |
|
||||
| TC-NET-1604 / 1605 | 截断与残包排空 | 200 B 的包用 64 B 缓冲只读到 64;紧接着 30 B 的下一个包必须整包到达(残包没排干净,这里会读到上一条的尾巴) | ③ |
|
||||
| TC-NET-1606 | 超时出口 | 空窗 `net_recvfrom_to(..., 1000)` 返回 **-2**(不是 -9),实耗 1000~4000 ms;下一步照常收发(槽没被毒化) | — |
|
||||
| TC-NET-1607 | 挂起等待期间的 IWDG 余量 | PC 迟到约 12 s 才发;板端 `net_recvfrom_to(..., 30000)` 醒来看见 24 B 正确载荷,实测等待落在 7~25 s,`net_get_generation()` 不变(没重建没复位)。硬证据在串口日志:这段 `[ERR][WD] task stall` 零条、`[PROF]` 心跳连续 | ④ |
|
||||
| TC-NET-1608 | 关旧 socket → 新开 → 重 bind :6002 | 新 socket 的首包必到(DesIP 要在 `OPEN_SOCKET` **之前**写才生效) | ⑤ |
|
||||
| TC-NET-1609 | 分块边界 1 / 20 / 1024 / 1025 / 1472 | 每条往返字节完全一致 | — |
|
||||
|
||||
**运行方法**:
|
||||
|
||||
1. `test/board/test_config.h` 开 `TEST_SUITE_CH395F`;`test/board/ch395f_test.h` **只**开
|
||||
`ENABLE_PHASE16_TESTS`,并把默认的 `ENABLE_PHASE11_TESTS` / `ENABLE_PHASE14_TESTS` 注掉——
|
||||
它们会带进 `TEST_LATENCY_PROBE`,污染要证的 UDP 面。
|
||||
2. `/keil-build` 后烧录(`.dsh/skills/stlink-flash/scripts/stlink_flash.py flash`,`-Rst -Run` 必带)。
|
||||
3. PC 先起 peer:`python test/host/udp_queue_bench.py --board 192.168.31.100`,再给板子上电/复位;
|
||||
peer 首步每 4 s 重发,能盖住板子启动那段时间。
|
||||
4. 判据是**两侧都绿**:板端串口 `=== Phase 16: N/N PASSED (failed=0) ===`,PC 端
|
||||
`=== UDP Bench PC Peer: N/N PASSED (failed=0) ===`。PC 绿板红 = 板端断言没对上激励;
|
||||
板端绿 PC 红 = 回显内容或分片不对。Windows 防火墙静默丢包会让首步一直"无回显"(§14 已记)。
|
||||
5. 跑完把这四行宏恢复成产品态重新构建 + 烧录(`test-suite-switch` 钩子也会拦住带测试宏的提交)。
|
||||
|
||||
**限度与观测点**:
|
||||
|
||||
- `net_send_locked` 的分块上限 `NET_SEND_CHUNK_MAX = 1024` 是为 TCP 流设计的;UDP 上它会把一条
|
||||
超过 1024 B 的 `sendto` 拆成多次 `WRITE_SEND_BUF`,芯片就发成**多个数据报**(UDP 没有可拼的流语义)。
|
||||
TC-NET-1609 的 1025 / 1472 两条会在 PC 侧把这件事直接量出来(多收到一片就记 [FAIL] 并打印各片长度)。
|
||||
产品侧结论:**UDP 单包保持在 1024 B 以内**(#57 的 SNTP 是 48 B,碰不到这条)。这不是本轮引入的——
|
||||
改造前的直调 `net_sendto` 就是同一个分块循环,只是那时 UDP 根本没有多任务调用者。
|
||||
- TC-NET-1607 用 30 s 预算版的挂起等待,不用无限版:同一套 park/唤醒机制,但无人值守的台架不该留一个
|
||||
能把任务钉死的等待。无限版(`timeout_ms = 0` → deadline = 0)的语义由主机单测 UDP-6 钉住。
|
||||
- 本阶段构建实测(2026-11-03,`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`):Code 257,176 /
|
||||
RO 33,900 / RW 2,060 / ZI 151,596;RW_IRAM1 **96,232 B = 73.4%**(距 92% 红线余 24,354 B),
|
||||
RW_CCM 57,376 B / 87.9% 与产品一致。台账数字以 `test/board/ch395f_test.h` 那段为准。
|
||||
- **本阶段尚未在真机上跑过**:本轮台架的板子 19:19 起断电(SWD 报 `No target connected`、
|
||||
ping 不通、串口静默),而 PR #74 的合并前提就是这条回归跑完拿到证据。
|
||||
|
||||
---
|
||||
|
||||
## 13. Python 测试脚本参考
|
||||
|
||||
| 命令 | 模式 | 说明 |
|
||||
@@ -1134,6 +1192,7 @@ WAIT_CLOSE 阶段已内置二段补发(只补一次),生产 net 层则靠
|
||||
| `tcp_file --ip ... --port 8080 --size 20480` | PC 作文件接收 | 测试大文件传输(阶段 7) |
|
||||
| `udp_client --ip ... --port 60000` | PC 作 UDP Client | 测试 UDP Echo(阶段 3) |
|
||||
| `udp_server --port 8082` | PC 作 UDP Server | 接收 CH395F UDP 数据 |
|
||||
| `python test/host/udp_queue_bench.py --board <板 IP>` | PC 作 UDP 对端(独立脚本,不在上面那个脚本的模式里) | 阶段 16 BSD UDP 数据面回归:逐步换源端口、切形态、截断、空窗、迟到包(§12.6) |
|
||||
| `dhcp_server ...` | DHCP Server | 内置 DHCP(阶段 4,需管理员权限) |
|
||||
|
||||
### 通用参数
|
||||
|
||||
Reference in New Issue
Block a user