diff --git a/docs/测试说明/链路延迟测试说明.md b/docs/测试说明/链路延迟测试说明.md index 5877397b..0f69393d 100644 --- a/docs/测试说明/链路延迟测试说明.md +++ b/docs/测试说明/链路延迟测试说明.md @@ -100,7 +100,7 @@ sequenceDiagram | 区制 | 实测 | 主导成本 | 适用 | |---|---|---|---| | **小帧交互**(≈1 chunk) | 3209B → 66ms(客户端关延迟 ACK 后 → 20.7ms) | 固定尾批惩罚 ~52ms(已客户端侧解决,§7.3/§7.4) | LCDMEM 整帧轮询 | -| **批量流式**(多满块) | 4KB/7.8ms、64KB/125ms ≈ 2ms/KB | SPI DMA(L2 = 1.61ms/KB) | 大文件 / FTP 传数据 | +| **批量流式**(多满块) | 4KB/7.8ms、64KB/125ms ≈ 2ms/KB | SPI DMA(SPI DMA = 1.61ms/KB) | 大文件 / FTP 传数据 | 两条独立的优化线:小帧交互的尾批已定性并解决;批量流式仍受 SPI DMA 约束。 @@ -108,20 +108,20 @@ sequenceDiagram ## 3. 分段定义与测量点 -| 段 | 名称 | 测什么 | 测点 / 方法 | 基线(实测,详见 §6) | 超标指向 | -|---|---|---|---|---|---| -| **L1** | SPI 字节命令 | 一条小 SPI 事务(GET_IC_VER / CHECK_EXIST / GLOB_INT,4~7B)端到端 | MCU,DWT µs,Phase 12,N=100/命令 | 10 / 15 / 15µs | SPI 走线/PCB/分频/DMA 配置 | -| **L2** | SPI DMA | `ch395f_read_recv_buf(N)` 完整 DMA 事务(含 CS 切换),N ∈ {4, 64, 1024, 2048} | MCU,DWT µs,Phase 12,N=100/档 | 20 / 114 / **1611** / 3208µs(斜率 1.56µs/B + 固定 14µs) | SPI2/DMA/总线异常 | -| **L3** | 发送分块握手 | `write_send_buf(1KB)` → `SENBUF_FREE` 的等待时长(= `net_send_locked` 每 chunk 的真实等待) | MCU(netTask 上下文),Phase 13 | **219µs**/chunk | 芯片发送路径 / 重传(D1) / 链路(D7) | -| **L4** | 芯片→线上+PC | 数据交入芯片协议栈 → PC 首见字节 / 收齐全帧(peer 逐 `recv()` 批次打时间戳) | PC(peer),Phase 13/14 | 首批 8.6ms;**段间间隙 ≥200ms = 重传**(P0-a 后粒度;旧固件 500ms) | D1/D4/D7("网络延迟"直接证据) | -| **L5** | 队列往返 | 调用方视角 `net_recv(DONTWAIT)` / `net_send` 全程 = 入队 + netTask 调度 + 处理 + 通知 | MCU(调用方计时),Phase 13,**两档**:静默基线 + 压力档(并发 FTP 流量) | 静默:收 1223µs / 发 988µs(2026-09-22 重锁,P0-b 后;L3 对 DONTWAIT 路径逐位不变,重验留待 Phase 13 下次窗口) | D3(头阻塞,已两阶段修复) / D5 调度 | -| **L6** | 请求→首字节 | peer 发 10B 请求 → MCU 收齐并回显(服务端视角);与 PC 侧对照,差值 = 线路 + PC 栈 | MCU + PC 双侧,Phase 13 | MCU 986µs / PC 3.9ms | MCU 收路径/调度 | -| **L7** | 整帧 3209B 往返 | LCDMEM 全帧:PC 发 → 收齐(含 L3+L4) | PC 为主,Phase 13 | 61.0ms(M2);**spike(≥500ms)=0** | D1+D2+D4 综合 | -| **L8** | 生产路径帧周期 | **真实 uiTask + 真实 `ui_net_sink`**(:7003)完整帧周期:请求拾取(10ms 拍)+ 队列 + 发送 + L4 + peer 侧编码 | MCU 探针 + PC,Phase 14 | 真机 61.6ms / 模拟器 6.8ms(含 peer 100ms 节拍) | 全段(用户真实体感) | -| **L9** | 连接/重监听 | connect→ESTABLISHED;close→relisten 成功 | MCU(net_poll 观测点),Phase 14 | relisten **89ms**(D10 修复后) | 握手/队列;**≥500ms = D10 回归** | -| **L10** | 日志税 | 一行 60 字符 `printf`(现 fputc 逐字节 @115200)耗时 | MCU,Phase 13 附带 | **4402µs/行** | D6(per-recv 日志已删 2026-09-22;非阻塞日志仍待办) | -| **L11** | uiTask 节拍 | 生产循环实测周期(`osDelay(10)` 之外多花多少 = LVGL 渲染 + 渲染中排空) | MCU 探针,Phase 14 | **10986µs**(渲染仅 ~1ms/帧) | D5/D9(实测均非瓶颈) | -| **Δ** | CH395F 净贡献 | `L8(真机) − L8(模拟器)`,同一 peer 交替跑 | PC,Phase 14 | **54.8ms**(阈值 ≤80ms) | 直接回答"网络延迟"占多少 | +| 名称 | 测什么 | 测点 / 方法 | 基线(实测,详见 §6) | 超标指向 | +|---|---|---|---|---| +| SPI 字节命令 | 一条小 SPI 事务(GET_IC_VER / CHECK_EXIST / GLOB_INT,4~7B)端到端 | MCU,DWT µs,Phase 12,N=100/命令 | 10 / 15 / 15µs | SPI 走线/PCB/分频/DMA 配置 | +| SPI DMA | `ch395f_read_recv_buf(N)` 完整 DMA 事务(含 CS 切换),N ∈ {4, 64, 1024, 2048} | MCU,DWT µs,Phase 12,N=100/档 | 20 / 114 / **1611** / 3208µs(斜率 1.56µs/B + 固定 14µs) | SPI2/DMA/总线异常 | +| 发送分块握手 | `write_send_buf(1KB)` → `SENBUF_FREE` 的等待时长(= `net_send_locked` 每 chunk 的真实等待) | MCU(netTask 上下文),Phase 13 | **219µs**/chunk | 芯片发送路径 / 重传(D1) / 链路(D7) | +| 芯片→线上+PC | 数据交入芯片协议栈 → PC 首见字节 / 收齐全帧(peer 逐 `recv()` 批次打时间戳) | PC(peer),Phase 13/14 | 首批 8.6ms;**段间间隙 ≥200ms = 重传**(P0-a 后粒度;旧固件 500ms) | D1/D4/D7("网络延迟"直接证据) | +| 队列往返 | 调用方视角 `net_recv(DONTWAIT)` / `net_send` 全程 = 入队 + netTask 调度 + 处理 + 通知 | MCU(调用方计时),Phase 13,**两档**:静默基线 + 压力档(并发 FTP 流量) | 静默:收 1223µs / 发 988µs(2026-09-22 重锁,P0-b 后;发送分块握手对 DONTWAIT 路径逐位不变,重验留待 Phase 13 下次窗口) | D3(头阻塞,已两阶段修复) / D5 调度 | +| 请求→首字节 | peer 发 10B 请求 → MCU 收齐并回显(服务端视角);与 PC 侧对照,差值 = 线路 + PC 栈 | MCU + PC 双侧,Phase 13 | MCU 986µs / PC 3.9ms | MCU 收路径/调度 | +| 整帧 3209B 往返 | LCDMEM 全帧:PC 发 → 收齐(含发送分块握手+芯片→线上+PC) | PC 为主,Phase 13 | 61.0ms(M2);**spike(≥500ms)=0** | D1+D2+D4 综合 | +| 生产路径帧周期 | **真实 uiTask + 真实 `ui_net_sink`**(:7003)完整帧周期:请求拾取(10ms 拍)+ 队列 + 发送 + 芯片→线上+PC + peer 侧编码 | MCU 探针 + PC,Phase 14 | 真机 61.6ms / 模拟器 6.8ms(含 peer 100ms 节拍) | 全段(用户真实体感) | +| 连接/重监听 | connect→ESTABLISHED;close→relisten 成功 | MCU(net_poll 观测点),Phase 14 | relisten **89ms**(D10 修复后) | 握手/队列;**≥500ms = D10 回归** | +| 日志税 | 一行 60 字符 `printf`(现 fputc 逐字节 @115200)耗时 | MCU,Phase 13 附带 | **4402µs/行** | D6(per-recv 日志已删 2026-09-22;非阻塞日志仍待办) | +| uiTask 节拍 | 生产循环实测周期(`osDelay(10)` 之外多花多少 = LVGL 渲染 + 渲染中排空) | MCU 探针,Phase 14 | **10986µs**(渲染仅 ~1ms/帧) | D5/D9(实测均非瓶颈) | +| CH395F 净贡献 | `生产路径帧周期(真机) − 生产路径帧周期(模拟器)`,同一 peer 交替跑 | PC,Phase 14 | **54.8ms**(阈值 ≤80ms) | 直接回答"网络延迟"占多少 | --- @@ -140,9 +140,9 @@ sequenceDiagram ### 4.2 阶段编号与构建配置(沿用 `ch395f_test.h` 互斥体系) ``` -ENABLE_PHASE12_TESTS [硬] SPI 时延基线(L1/L2)—— testTask 独占 CH395F,**无需 PC** -ENABLE_PHASE13_TESTS [网] 芯片+队列时延(L3~L7/L10)—— 需 latency_peer.py --mode net(:8080) -ENABLE_PHASE14_TESTS [网] 生产路径时延(L8~L11 + Δ)—— 真实 uiTask/:7003,peer --mode remo +ENABLE_PHASE12_TESTS [硬] SPI 时延基线(SPI 字节命令/SPI DMA)—— testTask 独占 CH395F,**无需 PC** +ENABLE_PHASE13_TESTS [网] 芯片+队列时延(发送分块握手~整帧往返/日志税)—— 需 latency_peer.py --mode net(:8080) +ENABLE_PHASE14_TESTS [网] 生产路径时延(生产路径帧周期~uiTask 节拍 + Δ)—— 真实 uiTask/:7003,peer --mode remo ``` - Phase 12 归 HW 组,13/14 归 NET 组;13 与 14 可同启(13 用 testTask 私有监听 :8080,14 用生产 :7003,Socket 不冲突); @@ -157,7 +157,7 @@ ENABLE_PHASE14_TESTS [网] 生产路径时延(L8~L11 + Δ)—— 真实 uiT `CoreDebug->DEMCR | TRCENA` + `DWT->CYCCNT=0` + `DWT->CTRL | CYCCNTENA`; - 分辨率 1/168MHz ≈ 5.95ns;换算用 `SystemCoreClock`(不写死频率); - **32 位回绕 = 25.17ms**:M1 各段(<5ms)与 M2 调用方侧测量(单次 <25ms)用单次读数差; - 跨 25ms 的段(L4/L8 整帧与重传尖峰)由 PC 端 peer 计时,或 MCU 侧**累积采样** + 跨 25ms 的段(芯片→线上+PC/生产路径帧周期整帧与重传尖峰)由 PC 端 peer 计时,或 MCU 侧**累积采样** (等待循环中每 ~10ms 采样计数差并累加,`lat_mark_us`); - 每次运行先打一行校准线 `[LAT] cal core_clk=...`,台架可用逻辑分析仪核对时基正确性。 @@ -199,11 +199,11 @@ DTU-RemoteLCD 客户端;输出每帧 `req2first / total / recv 批次到达时 | 操作 | 值 | 语义 | peer 侧动作 | |---|---|---|---| | HELLO | `5A A5 58 01` | 会话起始 | 连接后立即发送;MCU 识别后重置会话状态机 | -| L5_RECV | `0x01` | 对端灌入 | 20×100B,**50ms 间隔**(≈1KB/s,低于 MCU 抽干速率);测非阻塞收的队列往返 | -| L5_SEND | `0x02` | MCU 连发 | 收 2000B(20×100B 阻塞发) | -| L6 | `0x03` | 小请求/回显 | 发 10B 模式请求 → 校验 MCU 原样回显;逐样本重复 | -| L7 | `0x04` | 全帧读取 | 发 9B LCDMEM 请求 → 收 3209B 全帧(4 批);逐样本重复 | -| L3 | `0x05` | 4KB flood | 排空 16×4096B(=64KB);MCU 侧 chunk 等待由探针测 | +| 队列往返_RECV | `0x01` | 对端灌入 | 20×100B,**50ms 间隔**(≈1KB/s,低于 MCU 抽干速率);测非阻塞收的队列往返 | +| 队列往返_SEND | `0x02` | MCU 连发 | 收 2000B(20×100B 阻塞发) | +| 请求→首字节 | `0x03` | 小请求/回显 | 发 10B 模式请求 → 校验 MCU 原样回显;逐样本重复 | +| 整帧 3209B 往返 | `0x04` | 全帧读取 | 发 9B LCDMEM 请求 → 收 3209B 全帧(4 批);逐样本重复 | +| 发送分块握手 | `0x05` | 4KB flood | 排空 16×4096B(=64KB);MCU 侧 chunk 等待由探针测 | | DONE | `0xFF` | 会话结束 | 打印汇总并退出(exit 0 = PASS) | - **帧格式严格照生产 RemoDispBus**(`ui_net_sink.c` `remo_handle`/`remo_send`): @@ -270,8 +270,8 @@ DTU-RemoteLCD 客户端;输出每帧 `req2first / total / recv 批次到达时 # Forbidden: HardFault / assert / [ERR] / [FAIL] / verdict=FAIL ``` -- **会话顺序**:HELLO → L5_RECV → L5_SEND → L3 → L10 → L6 → L7 → DONE。 - 易失步的 RX 协议段(L6/L7)置于末位,避免失步污染前置段;L6/L7 样本数 10,L3 = 16×4KB; +- **会话顺序**:HELLO → 队列往返_RECV → 队列往返_SEND → 发送分块握手 → 日志税 → 请求→首字节 → 整帧 3209B 往返 → DONE。 + 易失步的 RX 协议段(请求→首字节/整帧 3209B 往返)置于末位,避免失步污染前置段;请求→首字节/整帧 3209B 往返样本数 10,发送分块握手 = 16×4KB; - **abort 后自动进"重连等待态"**(120s):DONTWAIT 轮询 scan `5A A5 58 01` → 命中即 `p13_reinit()` + 重启会话;期间周期自愈(10s 无 peer 且状态仍 LISTENING → 重发芯片重监听), **干净完成后停止自愈**(否则每 10s 拆建监听器,日志永不静默、台架无法判空闲); @@ -348,12 +348,12 @@ DMA 斜率实测 1.56µs/B(≈5.25 Mbit/s,与 SPI2 分频 8 @42MHz APB1 设 | 段(seg) | 测量含义 | med(min/max, n) | warn(med >) | crit(med >) | |---|---|---|---|---| -| `queue_rt_recv` | L5 对端灌 100B×20@50ms,MCU 非阻塞收的队列往返 | **1223µs**(1194/1223, 20) | 10ms | 100ms | -| `queue_rt_send` | L5 MCU 阻塞发 100B×20 | **988µs**(840/1216, 20) | 10ms | 100ms | -| `chunk_wait` | L3 16×4KB,芯片 chunk 内 SENBUF 等待(探针) | **219µs**(213/378, 64) | 10ms | 500ms(spike) | +| `queue_rt_recv` | 队列往返对端灌 100B×20@50ms,MCU 非阻塞收的队列往返 | **1223µs**(1194/1223, 20) | 10ms | 100ms | +| `queue_rt_send` | 队列往返 MCU 阻塞发 100B×20 | **988µs**(840/1216, 20) | 10ms | 100ms | +| `chunk_wait` | 发送分块握手 16×4KB,芯片 chunk 内 SENBUF 等待(探针) | **219µs**(213/378, 64) | 10ms | 500ms(spike) | | `logtax_line` | 60 字符 printf @115200(本地) | **4402µs**(4396/8779, 10) | 10ms | 25ms | -| `req2first_mcu` | L6 10B 请求到齐 → 回显发出 | **986µs**(979/1015, 10) | 20ms | 200ms | -| `fullframe_mcu` | L7 9B 请求 → 3209B 全帧发给芯片(netTask 内 4 分块) | **6614µs**(6546/6649, 10) | 50ms | 500ms | +| `req2first_mcu` | 请求→首字节 10B 请求到齐 → 回显发出 | **986µs**(979/1015, 10) | 20ms | 200ms | +| `fullframe_mcu` | 整帧 3209B 往返 9B 请求 → 3209B 全帧发给芯片(netTask 内 4 分块) | **6614µs**(6546/6649, 10) | 50ms | 500ms | (旧 run15 值对照:`queue_rt_recv` 5997 / `req2first_mcu` 319 / `fullframe_mcu` 6028µs——前两项差异均为 per-recv 日志移除的调度效应,非芯片行为变化:`queue_rt_recv` 差值 4.8ms ≈ 一行日志税;`req2first_mcu` @@ -364,18 +364,18 @@ per-recv 日志移除的调度效应,非芯片行为变化:`queue_rt_recv` | 段 | med(min/max) | 说明 | |---|---|---| -| L6 端到端(op→收到回显) | 3.9ms(1.7/12.0) | 比 MCU 侧 `req2first_mcu` 多 ~2.9ms → PC/线路段 | -| L7 首字节 | 10.0ms(4.0/13.8) | 同上量级 | -| L7 全帧 3209B | 57.9ms(53.6/69.9) | 每帧固定 4 个 recv 批次;**尾批 137B 稳定迟到 ~50ms**(§7.3) | -| L7 CRC 校验 | 10/0 fail | 帧格式/CRC 正确(生产格式对齐后) | -| L3 flood 64KB(16×4KB) | 7.5~8.7ms/次 | ≈2ms/KB,满块无尾批惩罚(MCU 发送路径,与 per-recv 日志无关) | +| 请求→首字节端到端(op→收到回显) | 3.9ms(1.7/12.0) | 比 MCU 侧 `req2first_mcu` 多 ~2.9ms → PC/线路段 | +| 整帧 3209B 往返首字节 | 10.0ms(4.0/13.8) | 同上量级 | +| 整帧 3209B 往返全帧 3209B | 57.9ms(53.6/69.9) | 每帧固定 4 个 recv 批次;**尾批 137B 稳定迟到 ~50ms**(§7.3) | +| 整帧 3209B 往返 CRC 校验 | 10/0 fail | 帧格式/CRC 正确(生产格式对齐后) | +| 发送分块握手 flood 64KB(16×4KB) | 7.5~8.7ms/次 | ≈2ms/KB,满块无尾批惩罚(MCU 发送路径,与 per-recv 日志无关) | **M2 三个结论**: 1. **安静档不存在重传台阶**——六段全部 `verdict=OK`、零尖峰、p95≈med;会话级路径的 MCU 净成本 ~1.2ms(`queue_rt_recv`,P0-b 删日志后;旧值 5.997ms 含 per-recv 日志税 4.4ms/行); 2. **芯片 chunk 级不是瓶颈**——`chunk_wait` ≈219µs ≈ 每 chunk SPI 成本(1.61ms)的 1/7, `SENBUF_FREE` 等待远小于 SPI 搬运本身; -3. **L7 全帧的最后 137B 尾巴稳定迟到 ~50ms**(10/10 帧)——尾批主导整帧时长,MCU 侧发完 3209B 仅 6.6ms +3. **整帧 3209B 往返全帧的最后 137B 尾巴稳定迟到 ~50ms**(10/10 帧)——尾批主导整帧时长,MCU 侧发完 3209B 仅 6.6ms → 延迟不在 MCU 发送路径,在芯片 TCP 尾段发送/对端 ACK 交互(M3 定性见 §7.3)。 ### 6.3 M3 段(Phase 14,生产路径 + 模拟器 Δ) @@ -384,9 +384,9 @@ per-recv 日志移除的调度效应,非芯片行为变化:`queue_rt_recv` | 段(seg) | 测量含义 | 实测 med(min/max, n) | warn(med >) | crit(med >) | |---|---|---|---|---| -| `ui_loop`(L11) | uiTask 生产循环实测周期(渲染+轮询+osDelay(10)) | **10986µs**(10951/11022, 120) | 25ms | 50ms | -| `prod_cycle_mcu`(L8) | LCDMEM 取 1bpp + 组帧 + net_send 4 分块(不含 10ms 拾取拍) | **6707µs**(6701/6757, 50) | 20ms | 200ms | -| `relisten`(L9) | 断线后 auto-relisten 全程(**修复后**:单次尝试 + netTask 延迟重试) | **89000µs ×3**(三次同值) | 100ms | 500ms | +| `ui_loop`(uiTask 节拍) | uiTask 生产循环实测周期(渲染+轮询+osDelay(10)) | **10986µs**(10951/11022, 120) | 25ms | 50ms | +| `prod_cycle_mcu`(生产路径帧周期) | LCDMEM 取 1bpp + 组帧 + net_send 4 分块(不含 10ms 拾取拍) | **6707µs**(6701/6757, 50) | 20ms | 200ms | +| `relisten`(连接/重监听) | 断线后 auto-relisten 全程(**修复后**:单次尝试 + netTask 延迟重试) | **89000µs ×3**(三次同值) | 100ms | 500ms | (旧值对照:`prod_cycle_mcu` 7142µs → 6707µs = P0-b 移除请求路径 per-recv 日志的 ~435µs; `ui_loop`/`relisten` 两档同值 = 与日志无关的段。) @@ -405,7 +405,7 @@ D10 修复后实测 89ms(= 30ms 重试间隔 + 约 60ms 芯片沉降),故 | 帧 CRC / 位图非平凡 | 50/0、50/50 | 50/0、50/50 | — | 全通过 | | 连接 / 断开重连(+INIT) | 20.9ms / 22.6ms(3/32.6) | 10.5ms / 32.0ms | — | ≤150ms | -(模拟器列为 2026-09-21 基线,模拟器工程不受 L3 影响(公开 API 不变);真机列 = +(模拟器列为 2026-09-21 基线,模拟器工程不受发送分块握手影响(公开 API 不变);真机列 = **2026-09-22 16:42 挂起 recv 构建重锁**(`m3_data/m3_pending_quiet.json`,50/50 帧全 OK、零尖峰)。 上一档真机值(P0-a/P0-b 分片代码,同日 11:35):帧周期 61.6ms / 首字节 9.6ms / 尾批 52.6ms / 重连 17.1/22.4ms → pending 构建:帧周期 med **58.9ms**(−2.7ms)、尾批 **50.4ms**(−2.2ms)—— @@ -428,29 +428,29 @@ med 65.0ms/max 133.5ms(133.5 = 压力启动瞬态单帧);**挂起 recv 化 | 段 | 含义 | 实测(med) | min/max | n | 阈值 warn/crit | 判定 | |---|---|---|---|---|---|---| -| **L1** `spi_cmd_*` | SPI 字节命令(3 条) | 10 / 15 / 15µs | 10~16µs | 100 | 40/100µs | OK | -| **L2** `spi_dma_*` | SPI DMA 读(4/64/1024/2048B) | 20 / 114 / **1611** / 3208µs | ±2µs | 100 | 见 §6.1 | OK | -| **L3** `chunk_wait` | 写发送缓冲 → `SENBUF_FREE`(每 chunk) | **219µs** | 213/378µs | 64 | 10ms/500ms | OK | -| **L5** `queue_rt_recv` | 调用方 `net_recv(DONTWAIT)` 队列全程 | **1223µs**¹ | 1194/1223µs | 20 | 10ms/100ms | OK | -| **L5** `queue_rt_send` | 调用方 `net_send` 队列全程 | **988µs**¹ | 840/1216µs | 20 | 10ms/100ms | OK | -| **L6** `req2first_mcu` | 10B 请求到齐 → 回显发出(MCU 侧) | **986µs** | 979/1015µs | 10 | 20ms/200ms | OK | -| **L7** `fullframe_mcu` | 9B 请求 → 3209B 全帧交给芯片 | **6614µs** | 6546/6649µs | 10 | 50ms/500ms | OK | -| **L8** `prod_cycle_mcu` | **生产路径**:LCDMEM 取帧+组帧+net_send(MCU 净成本) | **6706µs**(L3 重锁) | 6698/6720µs | 50 | 20ms/200ms | OK | -| **L10** `logtax_line` | 一行 60 字符 `printf` @115200 | **4402µs** | 4396/8779µs | 10 | 10ms/25ms | OK(D6 量化) | -| **L11** `ui_loop` | uiTask 生产循环实测周期 | **10986µs**(L3 重锁,值不变) | 10951/11022µs | 120 | 25ms/50ms | OK | -| **L9** `relisten` | 断线后自动重监听耗时(MCU 侧,修复后) | **90000µs**(L3 重锁) | 90000µs ×3 | 3 | 100ms/500ms | OK | -| **L4/L8** peer cycle | 生产客户端完整帧周期(发→收齐 3209B) | **58.9ms**(L3 重锁) | 47.7/72.1ms | 50 | — | 见 §7 | -| **Δ** | `peer cycle(设备) − peer cycle(模拟器)` | **52.1ms**(L3 重锁) | — | 50/50 | ≤80ms | 见 §7.1 | +| **SPI 字节命令** `spi_cmd_*` | SPI 字节命令(3 条) | 10 / 15 / 15µs | 10~16µs | 100 | 40/100µs | OK | +| **SPI DMA** `spi_dma_*` | SPI DMA 读(4/64/1024/2048B) | 20 / 114 / **1611** / 3208µs | ±2µs | 100 | 见 §6.1 | OK | +| **发送分块握手** `chunk_wait` | 写发送缓冲 → `SENBUF_FREE`(每 chunk) | **219µs** | 213/378µs | 64 | 10ms/500ms | OK | +| **队列往返** `queue_rt_recv` | 调用方 `net_recv(DONTWAIT)` 队列全程 | **1223µs**¹ | 1194/1223µs | 20 | 10ms/100ms | OK | +| **队列往返** `queue_rt_send` | 调用方 `net_send` 队列全程 | **988µs**¹ | 840/1216µs | 20 | 10ms/100ms | OK | +| **请求→首字节** `req2first_mcu` | 10B 请求到齐 → 回显发出(MCU 侧) | **986µs** | 979/1015µs | 10 | 20ms/200ms | OK | +| **整帧 3209B 往返** `fullframe_mcu` | 9B 请求 → 3209B 全帧交给芯片 | **6614µs** | 6546/6649µs | 10 | 50ms/500ms | OK | +| **生产路径帧周期** `prod_cycle_mcu` | **生产路径**:LCDMEM 取帧+组帧+net_send(MCU 净成本) | **6706µs**(发送分块握手重锁) | 6698/6720µs | 50 | 20ms/200ms | OK | +| **日志税** `logtax_line` | 一行 60 字符 `printf` @115200 | **4402µs** | 4396/8779µs | 10 | 10ms/25ms | OK(D6 量化) | +| **uiTask 节拍** `ui_loop` | uiTask 生产循环实测周期 | **10986µs**(发送分块握手重锁,值不变) | 10951/11022µs | 120 | 25ms/50ms | OK | +| **连接/重监听** `relisten` | 断线后自动重监听耗时(MCU 侧,修复后) | **90000µs**(发送分块握手重锁) | 90000µs ×3 | 3 | 100ms/500ms | OK | +| **芯片→线上+PC/生产路径帧周期** peer cycle | 生产客户端完整帧周期(发→收齐 3209B) | **58.9ms**(发送分块握手重锁) | 47.7/72.1ms | 50 | — | 见 §7 | +| **Δ** | `peer cycle(设备) − peer cycle(模拟器)` | **52.1ms**(发送分块握手重锁) | — | 50/50 | ≤80ms | 见 §7.1 | -¹ L5 两行为 2026-09-22 上午分片时代(M2 B2 档)数值:L3 对 DONTWAIT 快速路径**逐位不变**(dispatch +¹ 队列往返两行为 2026-09-22 上午分片时代(M2 B2 档)数值:发送分块握手对 DONTWAIT 快速路径**逐位不变**(dispatch → `net_poll` → `get_recv_len` 读,无 readable 门控),pending tick 无 pending 时为纯指针读零 SPI -(µs 级)——机制上不影响 L5;M2 重验未在本轮执行(低风险,留待 Phase 13 下次窗口,见 `docs/任务规划/README.md` 条目「L5 队列往返基线待重验」)。 +(µs 级)——机制上不影响队列往返;M2 重验未在本轮执行(低风险,留待 Phase 13 下次窗口,见 `docs/任务规划/README.md` 条目「队列往返队列往返基线待重验」)。 -数据来源:L1/L2 = §6.1;L3/L5/L6/L7 = M2 B2 档(200ms、per-recv 日志已删)串口 `[LAT]` 行 -(`m2_data/m2_quiet_r200.json`);L8/L9/L11 = **挂起 recv 构建 2026-09-22 16:42/16:43 重锁** -(`m3_data/m3_pending_quiet.json` / `m3_pending_stress.json` 之 MCU 侧 `[LAT]` 行);L10 = M3 静默档 -(`m3_data/m3_dev_quiet_r200.json`,与 L3 无关的串口税段);A/B 对照档 = `m2_quiet_r500.json`(旧条件); -peer 侧 = `m3_data/m3_pending_*.json`(L3 重锁)与 `m3_data/m3_dev_*_r200.json`(上一档,历史对照)。 +数据来源:SPI 字节命令/SPI DMA = §6.1;发送分块握手/队列往返/请求→首字节/整帧 3209B 往返 = M2 B2 档(200ms、per-recv 日志已删)串口 `[LAT]` 行 +(`m2_data/m2_quiet_r200.json`);生产路径帧周期/连接/重监听/uiTask 节拍 = **挂起 recv 构建 2026-09-22 16:42/16:43 重锁** +(`m3_data/m3_pending_quiet.json` / `m3_pending_stress.json` 之 MCU 侧 `[LAT]` 行);日志税 = M3 静默档 +(`m3_data/m3_dev_quiet_r200.json`,与发送分块握手无关的串口税段);A/B 对照档 = `m2_quiet_r500.json`(旧条件); +peer 侧 = `m3_data/m3_pending_*.json`(发送分块握手重锁)与 `m3_data/m3_dev_*_r200.json`(上一档,历史对照)。 旧 run15 摘录(`m2_serial_excerpt_run15.txt`)保留作 A/B 历史对照。 ### 6.5 重锁规则 @@ -496,8 +496,8 @@ peer 侧 = `m3_data/m3_pending_*.json`(L3 重锁)与 `m3_data/m3_dev_*_r200. | 对照 | 观测 | 说明 | |---|---|---| | ① 同一客户端 → 模拟器 | tail = **0.0ms**,cycle 6.8ms | 客户端栈、Windows、请求格式全部相同 → 尾批**不是 PC 侧**造成 | -| ② M2 `L3` 批量排空(16×4096B,全满块) | 65536B / **125.4ms**(≈2ms/KB) | 16 个满块若各带 50ms 尾巴应 ≥800ms → **满块无此惩罚** | -| ③ 压力档(头阻塞)下的 L7/生产帧 | tail 仍 43~62ms | 尾批**不受 netTask 拥塞影响** → 属芯片 TX 行为 | +| ② M2 `发送分块握手` 批量排空(16×4096B,全满块) | 65536B / **125.4ms**(≈2ms/KB) | 16 个满块若各带 50ms 尾巴应 ≥800ms → **满块无此惩罚** | +| ③ 压力档(头阻塞)下的整帧 3209B 往返/生产帧 | tail 仍 43~62ms | 尾批**不受 netTask 拥塞影响** → 属芯片 TX 行为 | ### 7.3 尾批 ~52ms:机理与带内验证(全部不需要抓包) @@ -513,7 +513,7 @@ peer 侧 = `m3_data/m3_pending_*.json`(L3 重锁)与 `m3_data/m3_dev_*_r200. | 1 | **带内 ACK 探针** `--ack-probe` | 帧未收齐时立刻发 **1 个解析器会忽略的字节**(0x00,非 TAG 0xAA)→ 该 TCP 段**捎带**对已收数据的 ACK | **3.9ms** | **13.7ms** | 扣住**可被对端 ACK 解除** ⇒ 等 ACK(Nagle 式),非芯片固定定时器 | | 2 | **对端立即 ACK** `--ack-fast` | Windows 每 socket 关延迟 ACK:`SIO_TCP_SET_ACK_FREQUENCY=1`(ctypes 调 `WSAIoctl`,**不发任何探测字节**) | **3.9ms** | **20.7ms** | **根因 = 对端延迟 ACK**(去掉它,尾批自行消失) | | 3 | 对照:模拟器(同 peer/同 Windows) | — | 0.0ms | 6.8ms | 芯片侧才有该扣住行为 | -| 4 | 对照:满块批量(M2 L3,16×4096B) | — | 无累积惩罚(64KB/125ms) | — | **满 MSS 段不受影响** | +| 4 | 对照:满块批量(M2 发送分块握手,16×4096B) | — | 无累积惩罚(64KB/125ms) | — | **满 MSS 段不受影响** | > 实验 1/2 已固化为 peer 选项:`--ack-probe`(可移植:任何 OS/客户端只要在帧未收齐时发一个字节即可)、 > `--ack-fast`(仅 Windows,无需改协议)。原始数据:`m3_data/m3_tail_ackprobe.json`、`m3_data/m3_tail_ackfast.json`。 @@ -549,11 +549,11 @@ peer 侧 = `m3_data/m3_pending_*.json`(L3 重锁)与 `m3_data/m3_dev_*_r200. ### 7.5 队列与调度:静默档不存在台阶 -- **L5 队列往返**:收 1223µs / 发 988µs(P0-b 后;旧值 5997/987µs)——收段 ≈ 1 个 netTask 循环周期量级, +- **队列往返队列往返**:收 1223µs / 发 988µs(P0-b 后;旧值 5997/987µs)——收段 ≈ 1 个 netTask 循环周期量级, 属**调度周期**而非排队拥塞(旧收值 5997µs 含 per-recv 日志税 4.4ms/行,已移除); `queue_rt_send` 988µs ≈ 1 个循环周期,发送路径没有额外排队; -- **L3 chunk 级**:219µs ≈ 每 chunk SPI 成本的 1/7 → **芯片 TX 缓冲不是瓶颈**,SPI 搬运才是; -- **L11 uiTask 周期**:10986µs(= osDelay(10) + 渲染开销 ≈ 1ms)→ **LVGL 渲染只占约 1ms/帧**, +- **发送分块握手 chunk 级**:219µs ≈ 每 chunk SPI 成本的 1/7 → **芯片 TX 缓冲不是瓶颈**,SPI 搬运才是; +- **uiTask 节拍 uiTask 周期**:10986µs(= osDelay(10) + 渲染开销 ≈ 1ms)→ **LVGL 渲染只占约 1ms/帧**, uiTask 不是延迟来源; - **结论:调优先级/栈不会改善体感**——预算应花在尾批解法(已做)与 D20(§8.2)。 @@ -650,17 +650,17 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复 | 现象(段超标) | 首要怀疑 | 动作 | |---|---|---| -| L1/L2 超 crit | SPI 走线/PCB/SPI2 分频或 DMA 配置 | 对照 `docs/问题记录/CH395F_Trap_Records.md`;逻辑分析仪抓 SCK/CS | -| L3 spike ≥200ms(连续台阶;旧固件 500ms) | **D1 重传 / D7 链路** | Wireshark `tcp.analysis.retransmission`;查 PHY 协商/网线/Wi-Fi | -| L3 基线(非 spike)抬高 | 芯片发送路径 / 中断响应 | 对照 `docs/芯片手册/CH395F.md` §5.17/5.18(注意:状态寄存器**不被 SET 类命令更新**,`GET_CMD_STATUS` 恒读到 RESET 的 0x10 陈旧残留,2026-09-22 台架实证——不可用作 SET 值验收) | -| L5 压力档爆表 | **头阻塞(已修)→ 回归** | 定位触发源(阻塞 recv 的 FTP/对时);核对 挂起 recv 在位(`net_recv_locked` 阻塞分支 = 登记 pending 而非等待;`net_pending_tick` 在主循环) | -| L5/L6 静默档抬高 | D5 调度(任务抢占/节拍) | 查任务优先级与 osDelay;`stack_report.py` 核对栈水位 | -| L6/L7 整体抬高且 L1~L5 正常 | 线路+PC 段(L4) | Wireshark 段间间隙;`Δ` 对照定位 MCU 侧净贡献 | -| **L7/L8 尾批固定迟到 ~50ms** | 已定性(§7.3):次 MSS 尾段被 Nagle 扣住 + Windows 延迟 ACK | 客户端侧解法已实测(§7.4):`--ack-probe` / `--ack-fast` → tail 52→3.9ms;生产客户端已实装;**设备侧无解**,不必再投入 | -| **L5 出现重复投递**(收到 > 发送) | **D11**:芯片 TCP RX 饱和重复递交 | 降低灌入速率/加抽干(§8 D11) | +| SPI 字节命令/SPI DMA 超 crit | SPI 走线/PCB/SPI2 分频或 DMA 配置 | 对照 `docs/问题记录/CH395F_Trap_Records.md`;逻辑分析仪抓 SCK/CS | +| 发送分块握手 spike ≥200ms(连续台阶;旧固件 500ms) | **D1 重传 / D7 链路** | Wireshark `tcp.analysis.retransmission`;查 PHY 协商/网线/Wi-Fi | +| 发送分块握手基线(非 spike)抬高 | 芯片发送路径 / 中断响应 | 对照 `docs/芯片手册/CH395F.md` §5.17/5.18(注意:状态寄存器**不被 SET 类命令更新**,`GET_CMD_STATUS` 恒读到 RESET 的 0x10 陈旧残留,2026-09-22 台架实证——不可用作 SET 值验收) | +| 队列往返压力档爆表 | **头阻塞(已修)→ 回归** | 定位触发源(阻塞 recv 的 FTP/对时);核对挂起 recv 在位(`net_recv_locked` 阻塞分支 = 登记 pending 而非等待;`net_pending_tick` 在主循环) | +| 队列往返/请求→首字节静默档抬高 | D5 调度(任务抢占/节拍) | 查任务优先级与 osDelay;`stack_report.py` 核对栈水位 | +| 请求→首字节/整帧 3209B 往返整体抬高且 SPI 字节命令~队列往返正常 | 线路+PC 段(芯片→线上+PC) | Wireshark 段间间隙;`Δ` 对照定位 MCU 侧净贡献 | +| **整帧 3209B 往返/生产路径帧周期尾批固定迟到 ~50ms** | 已定性(§7.3):次 MSS 尾段被 Nagle 扣住 + Windows 延迟 ACK | 客户端侧解法已实测(§7.4):`--ack-probe` / `--ack-fast` → tail 52→3.9ms;生产客户端已实装;**设备侧无解**,不必再投入 | +| **队列往返出现重复投递**(收到 > 发送) | **D11**:芯片 TCP RX 饱和重复递交 | 降低灌入速率/加抽干(§8 D11) | | **会话无征兆失步**(结果错位/挂死) | **D12**:net 层任务通知值语义 | 核对 `net_wait_result` 清除掩码全 1 + 按返回值判定(§8 D12/D12-a) | -| **L7 一直超时 / peer 报 `l7_crc.fail`** | **D15**:帧格式不一致 / peer 超读 | 对照 `ui_net_sink.c` 的 `remo_send`/`remo_handle`(§8 D15) | -| **会话跑完一段后无限循环、peer 空等 300s** | **D16**:Phase 13 状态机迁移目标错 | 核对顺序 L5→L3→L10→L6→L7→DONE(§8 D16) | +| **整帧 3209B 往返一直超时 / peer 报 `l7_crc.fail`** | **D15**:帧格式不一致 / peer 超读 | 对照 `ui_net_sink.c` 的 `remo_send`/`remo_handle`(§8 D15) | +| **会话跑完一段后无限循环、peer 空等 300s** | **D16**:Phase 13 状态机迁移目标错 | 核对顺序队列往返→发送分块握手→日志税→请求→首字节→整帧 3209B 往返→DONE(§8 D16) | | **`[LAT]` 段行缺失/截断、wait-idle 判不出空闲** | **D12-b**:热路径日志洪水 | 删除 net 热路径逐调用日志(§8 D12-b) | | **开机日志连续丢行**(含必检标记 `net init: OK` 等)但系统健康 | **日志并发竞态**:多任务并发 `printf` → 非重入 `fputc`/`HAL_UART_Transmit` 丢整行、UART 状态短暂卡住(2026-09-22 台架实证:boot 时 testTask 启动行与 netTask 日志并发 → 连续 4 行丢失、~2s 后自恢复) | 必现证据行采用"静默采集 + 延迟到安静窗口打印"模式(范例:P0-a 验收行曾按此模式实装,验收完成后探针已移除);根治 = 日志非阻塞/串行化(§10.2) | | **`relisten` 段 FAIL、断线固定 ~588ms** | **D10** 回归(退回 `osDelay(500)`) | 核对 `net_listener_request`/`net_listener_tick` 非阻塞重试在位(§8 D10) | @@ -670,8 +670,8 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复 | **`[LAT]` 多段数值雷同 / 全 0 却 verdict=OK** | **D18/D19**(测试工装) | 核对 `lat_probe_add_seg` 段名过滤 + `lat_time_init` 调用 + `med==0` 判无效 | | **连接建立后 15~30s 无流量即被 RST** | **D13**:芯片空闲超时(芯片行为) | 客户端须容忍并重连(peer `--wait` 重试是正常路径) | | **peer 重连后 MCU 状态滞留、新连接字节被吞** | **D14**:芯片静默回收 Socket | 对账⑤自动认领(手动 `net_resync_listen`/`net_relisten` 已退役,Phase 13 abort 期自愈亦交 net 层) | -| L8 超而 L7 正常 | 生产路径附加成本(uiTask 节拍/LVGL/日志) | 对照 L10/L11 与 §10.1(渲染已排除;per-recv 日志已删,非阻塞日志仍待办) | -| Δ 显著 >80ms | CH395F 栈净开销异常 | 回到 L1~L9 逐段排查 | +| 生产路径帧周期超而整帧 3209B 往返正常 | 生产路径附加成本(uiTask 节拍/LVGL/日志) | 对照日志税/uiTask 节拍与 §10.1(渲染已排除;per-recv 日志已删,非阻塞日志仍待办) | +| Δ 显著 >80ms | CH395F 栈净开销异常 | 回到 SPI 字节命令~连接/重监听逐段排查 | --- @@ -747,7 +747,7 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复 | 数据文件 | 内容 | |---|---| -| `m2_data/m2_quiet.json` | M2 安静档 peer 侧(run15 最终代码):L3~L7 端到端 + 逐帧尾批明细 | +| `m2_data/m2_quiet.json` | M2 安静档 peer 侧(run15 最终代码):发送分块握手~整帧 3209B 往返端到端 + 逐帧尾批明细 | | `m2_data/m2_quiet_run13.json` | M2 run13(独立 PASS 对照) | | `m2_data/m2_quiet_run14.json` | M2 run14(仅 peer 侧有效;MCU 侧因热路径日志洪水不作基线,D12-b) | | `m2_data/m2_serial_excerpt_run13/15.txt` | M2 串口 `[LAT]`/`[NET]` 证据片段(旧基线,A/B 历史对照) | @@ -759,9 +759,9 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复 | `m3_data/m3_dev_stress.json` | 头阻塞压力档(修复前:17.7s 尖峰) | | `m3_data/m3_dev_stress_fixed.json` | 头阻塞压力档(修复后:med 74.7ms / max 106.8ms,零尖峰) | | `m3_data/m3_dev_quiet_r200.json` | **2026-09-22 M3 静默档重锁基线**(50 帧,P0-a/P0-b 代码:帧周期 61.6ms) | -| `m3_data/m3_dev_stress_r200.json` | **2026-09-22 M3 压力档重锁基线**(45 帧,第 15 帧起 FTP stress:med 65.0ms / max 133.5ms,零尖峰)——分片时代数值,**已被 L3 档取代**(历史对照) | -| `m3_data/m3_pending_quiet.json` | **2026-09-22 M3 静默档 L3 重锁基线(当前)**(50 帧,pending 构建 16:42:帧周期 58.9ms / 首字节 9.2ms / 尾批 50.4ms,零尖峰,CRC 50/0) | -| `m3_data/m3_pending_stress.json` | **2026-09-22 M3 压力档 L3 重锁基线(当前)**(50 帧,第 25 帧起 FTP 空闲登录保持:stress 段 med 60.1ms / p95 66.4ms / max 69.3ms,零尖峰,CRC 50/0;MCU 侧 prod_cycle_mcu 6706µs 压力下平坦) | +| `m3_data/m3_dev_stress_r200.json` | **2026-09-22 M3 压力档重锁基线**(45 帧,第 15 帧起 FTP stress:med 65.0ms / max 133.5ms,零尖峰)——分片时代数值,**已被发送分块握手档取代**(历史对照) | +| `m3_data/m3_pending_quiet.json` | **2026-09-22 M3 静默档发送分块握手重锁基线(当前)**(50 帧,pending 构建 16:42:帧周期 58.9ms / 首字节 9.2ms / 尾批 50.4ms,零尖峰,CRC 50/0) | +| `m3_data/m3_pending_stress.json` | **2026-09-22 M3 压力档发送分块握手重锁基线(当前)**(50 帧,第 25 帧起 FTP 空闲登录保持:stress 段 med 60.1ms / p95 66.4ms / max 69.3ms,零尖峰,CRC 50/0;MCU 侧 prod_cycle_mcu 6706µs 压力下平坦) | | `m3_data/m3_sim_quiet.json` | 模拟器基线 50 帧(Δ 对照,tail=0) | | `m3_data/m3_tail_ackprobe.json` | 尾批实验 ①:帧未收齐即回 1 字节(捎带 ACK)→ tail 3.9ms | | `m3_data/m3_tail_ackfast.json` | 尾批实验 ②:Windows 立即 ACK(不发探测字节)→ tail 3.9ms | diff --git a/test/board/net_latency_test.c b/test/board/net_latency_test.c index 75e16dae..8e8cb2af 100644 --- a/test/board/net_latency_test.c +++ b/test/board/net_latency_test.c @@ -2,13 +2,13 @@ * 模块名称:CH395F 链路延迟测试(实现) * 模块功能:DWT CYCCNT 微秒时基 + 采样统计(min/med/p95/max/尖峰)+ [LAT] * 机器可读报告与 verdict;Phase 12(M1):SPI 字节命令 / DMA 时延 - * 基线(L1/L2,硬件层模式,无需 PC)。分段定义与判定基线见 + * 基线(SPI_CMD/SPI_DMA,硬件层模式,无需 PC)。分段定义与判定基线见 * docs/测试说明/链路延迟测试说明.md。 * 适用平台:STM32F4 系列(CH395F) * 作者:王建锋 * 创建日期:2026-09-22 * 修改记录: - * 2026-09-22 王建锋 初版:时基 + 统计/报告 + Phase 12(L1/L2) + * 2026-09-22 王建锋 初版:时基 + 统计/报告 + Phase 12(SPI_CMD/SPI_DMA) * * 构建形态: * - 产品构建:三个 Phase 宏均未定义 → 本文件除 include 外全部不参与编译; @@ -20,7 +20,7 @@ #include "net_latency_test.h" #include "ch395f_test.h" /* TEST_CHECK / TEST_REPORT / g_test_stats / Phase 宏 */ -#include "ch395f.h" /* 驱动公开 API(L1 字节命令 / L2 DMA) */ +#include "ch395f.h" /* 驱动公开 API(SPI_CMD 字节命令 / SPI_DMA DMA) */ #include "net_socket.h" /* net_send / net_recv / NET_MSG_DONTWAIT(Phase 13) */ #include "test_config.h" /* TEST_LATENCY_PROBE(探针编译开关) */ #include "main.h" /* CoreDebug / DWT / SystemCoreClock / HAL_GetTick */ @@ -38,7 +38,7 @@ * 工程内首个 DWT 用户(sys_clock 为纯 HAL tick,无 us 能力)。 * 32 位计数 168MHz 下回绕周期 25.17ms:M1 各段(<5ms)与 M2 * 调用方侧测量(单次 <25ms)用单次读数差即可;跨 25ms 的段 - * (L4/L8 整帧与重传尖峰)由 PC 端 peer 计时(说明文档 §2.3)。 + * (TO_PC/PROD_CYCLE 整帧与重传尖峰)由 PC 端 peer 计时(说明文档 §2.3)。 * 读取 DWT->CYCCNT 无共享状态,任意任务上下文安全。 * ============================================================ */ @@ -77,7 +77,7 @@ static uint32_t lat_elapsed_us(uint32_t c_start) } #endif /* ENABLE_PHASE12_TESTS */ -/* 公开读数接口(net_socket.c 的 L3 探针经此访问 DWT,避免直接碰寄存器) */ +/* 公开读数接口(net_socket.c 的 CHUNK_WAIT 探针经此访问 DWT,避免直接碰寄存器) */ uint32_t lat_cycles_now(void) { return DWT->CYCCNT; @@ -205,7 +205,7 @@ void lat_stat_report(const char *p_seg, const lat_stats_t *p, * TEST_LATENCY_PROBE 探针(M2/M3) * * netTask 上下文(net_socket.c 发送分块循环)向 testTask 报告段内 - * 打点(L3 chunk_wait 等调用方侧无法测量的段)。 + * 打点(CHUNK_WAIT chunk_wait 等调用方侧无法测量的段)。 * 并发模型:单写者(netTask)/单读者(testTask); * - s_probe_n 为 32 位(Cortex-M4 单字读写原子,无锁) * - 样本写入 → n++ 的顺序保证读者看到一致的 (n, samples[0..n)) @@ -352,12 +352,12 @@ const lat_stats_t *lat_probe_stats(void) #endif /* TEST_LATENCY_PROBE */ /* ============================================================ - * Phase 12 — SPI 时延基线(L1/L2,硬件层模式,无需 PC) + * Phase 12 — SPI 时延基线(SPI_CMD/SPI_DMA,硬件层模式,无需 PC) * * 测量对象(见 链路延迟测试说明.md §3): - * L1 SPI 字节命令 —— 3 个代表性命令的完整 SPI 事务时长 + * SPI_CMD SPI 字节命令 —— 3 个代表性命令的完整 SPI 事务时长 * (公开函数内部含 spi_begin/spi_end,即一次完整事务) - * L2 SPI DMA —— ch395f_read_recv_buf(N) 完整 DMA 事务 + * SPI_DMA SPI DMA —— ch395f_read_recv_buf(N) 完整 DMA 事务 * (读方向:MCU 主发时钟,芯片侧输出内容不影响传输时长; * 硬件层模式 socket 未打开,读方向不改芯片任何状态, * 保持非侵入。写方向 DMA 机制相同(s_spi2_dma_tx_buf), @@ -369,11 +369,11 @@ const lat_stats_t *lat_probe_stats(void) * ============================================================ */ #ifdef ENABLE_PHASE12_TESTS -#define LAT_P12_CMD_N 100U /* L1 每命令样本数 */ -#define LAT_P12_DMA_N 100U /* L2 每长度档样本数 */ -#define LAT_P12_DMA_LEN_MAX 2048U /* L2 单发上限 = CH395F_SPI_DMA_MAX_PAYLOAD */ +#define LAT_P12_CMD_N 100U /* SPI_CMD 每命令样本数 */ +#define LAT_P12_DMA_N 100U /* SPI_DMA 每长度档样本数 */ +#define LAT_P12_DMA_LEN_MAX 2048U /* SPI_DMA 单发上限 = CH395F_SPI_DMA_MAX_PAYLOAD */ -/* L1 阈值(us)—— 2026-09-22 台架首跑后重锁(实测 med 10~15us; +/* SPI_CMD 阈值(us)—— 2026-09-22 台架首跑后重锁(实测 med 10~15us; * warn ≈ 2×med,crit ≈ 2×warn,见 链路延迟测试说明.md §6.1) */ #define LAT_P12_CMD_WARN_US 40U #define LAT_P12_CMD_FAIL_US 100U @@ -409,7 +409,7 @@ void net_latency_phase12_run(void) * 前序阶段已打印的 TEST_REPORT,使 Phase 12 计数独立清晰 */ memset(&g_test_stats, 0U, sizeof(g_test_stats)); - DBG_INFO("=== CH395F Phase 12 Tests (link latency baseline, L1/L2) ==="); + DBG_INFO("=== CH395F Phase 12 Tests (link latency baseline, SPI_CMD/SPI_DMA) ==="); lat_time_init(); DBG_INFO("[LAT] run_id=%lu phase=12", (unsigned long)HAL_GetTick()); @@ -434,7 +434,7 @@ void net_latency_phase12_run(void) memset(s_dma_buf, 0U, sizeof(s_dma_buf)); - /* ---- L1:SPI 字节命令时延(3 个代表性命令 × 100) ---- */ + /* ---- SPI_CMD:SPI 字节命令时延(3 个代表性命令 × 100) ---- */ for (i = 0U; i < 3U; i++) { lat_stat_reset(&st); for (k = 0U; k < LAT_P12_CMD_N; k++) { @@ -463,7 +463,7 @@ void net_latency_phase12_run(void) } } - /* ---- L2:SPI DMA 时延(4 档长度 × 100) ---- */ + /* ---- SPI_DMA:SPI DMA 时延(4 档长度 × 100) ---- */ for (i = 0U; i < 4U; i++) { lat_stat_reset(&st); for (k = 0U; k < LAT_P12_DMA_N; k++) { @@ -506,35 +506,35 @@ void net_latency_phase12_run(void) #ifdef ENABLE_PHASE13_TESTS /* ==================================================================== - * Phase 13 — 芯片 + 队列时延(L3~L7/L10,需 PC peer,M2) + * Phase 13 — 芯片 + 队列时延(CHUNK_WAIT~FRAME_RT/LOGTAX,需 PC peer,M2) * * 由 net_layer_test_main() 监听循环(8080 端口,~10ms 节拍)事件驱动。 * 服务器驱动协议:MCU 发操作字节,peer(test/latency_peer.py --mode net) * 执行对应流量;段完成后 MCU 打 [LAT] 报告行并推进下一操作。 * * 段定义(说明文档 §1): - * L5 queue_rt_recv / queue_rt_send —— 调用方视角 net_recv/net_send + * QUEUE_RT queue_rt_recv / queue_rt_send —— 调用方视角 net_recv/net_send * 全程(入队 + netTask 调度 + 处理 + 通知),静默基线档; * 压力档 = peer --ftp-stress(lftpd 阻塞读占住 netTask → 头阻塞复现) - * L6 req2first_mcu —— 请求收齐 → 应答发送完成(MCU 侧,含队列+1 chunk) - * L7 fullframe_mcu —— 9B LCDMEM 请求 → 3209B 全帧发送完成 + * ECHO_RT req2first_mcu —— 请求收齐 → 应答发送完成(MCU 侧,含队列+1 chunk) + * FRAME_RT fullframe_mcu —— 9B LCDMEM 请求 → 3209B 全帧发送完成 * (netTask 内 4 次分块停止等待,= 生产路径发送段) - * L3 chunk_wait —— netTask 内"写发送缓冲 → SENBUF_FREE"等待 + * CHUNK_WAIT chunk_wait —— netTask 内"写发送缓冲 → SENBUF_FREE"等待 * (调用方不可见,经 TEST_LATENCY_PROBE 探针,见 net_socket.c) - * L10 logtax_line —— 一行 60 字符 printf(fputc 逐字节 @115200) + * LOGTAX logtax_line —— 一行 60 字符 printf(fputc 逐字节 @115200) * - * 执行顺序(2026-09-22 run3 后重排):L5 → L3 → L10 → L6 → L7 —— - * 失步敏感的小帧 RX 协议段(L6/L7)放最后,先锁住抗失步段的基线。 - * 对端侧(L4 线上传输/整帧到达/尖峰)由 peer JSON 报告,按主机时间戳关联。 + * 执行顺序(2026-09-22 run3 后重排):QUEUE_RT → CHUNK_WAIT → LOGTAX → ECHO_RT → FRAME_RT —— + * 失步敏感的小帧 RX 协议段(ECHO_RT/FRAME_RT)放最后,先锁住抗失步段的基线。 + * 对端侧(TO_PC 线上传输/整帧到达/尖峰)由 peer JSON 报告,按主机时间戳关联。 * * 阈值:初始值,台架首跑后重锁(说明文档 §4.2)。 * 独立运行:与其他 Phase 共用 8080 socket,混跑会污染帧测量。 * ==================================================================== */ -#define LAT_P13_SAMPLES 10U /* L6/L7 每段样本数(2026-09-22 run3 实证:L6 50 轮 +#define LAT_P13_SAMPLES 10U /* ECHO_RT/FRAME_RT 每段样本数(2026-09-22 run3 实证:ECHO_RT 50 轮 * 小交换后芯片 TCP 失步 → TX 停滞 + 重复递交 9B 碎片 * → 降至 10,先取干净基线再量化失步边界) */ -#define LAT_P13_L5_CHUNKS 20U /* L5 flood 块数(2026-09-22 run1 实证:50×100B@5ms +#define LAT_P13_L5_CHUNKS 20U /* QUEUE_RT flood 块数(2026-09-22 run1 实证:50×100B@5ms * 达 20KB/s 饱和芯片接收缓冲 → 窗口关闭重传 → * 重复递交 11884/5000B → 对端 RST;降至 1KB/s * 静默档,见 §6.2 M2 基线) */ @@ -592,7 +592,7 @@ static uint8_t s_p13_hello_n = 0U; static int s_p13_verdict_all = LAT_VERDICT_OK; static int s_p13_seg_force_fail = 0; /* 当前段出现短写/错误 → 强制 FAIL */ static uint32_t s_p13_start_tick; -static uint32_t s_p13_step_tick; /* L6/L7 单样本等待计时 */ +static uint32_t s_p13_step_tick; /* ECHO_RT/FRAME_RT 单样本等待计时 */ static uint32_t s_p13_stage; static uint32_t s_p13_rx_bytes; static uint16_t s_p13_l6rx_n; @@ -600,12 +600,12 @@ static uint16_t s_p13_l7rx_n; static lat_stats_t s_p13_st; static uint8_t s_p13_rx[LAT_P13_RECV_LEN]; -static uint8_t s_p13_tx[LAT_P13_L3_SEND_LEN]; /* L5/L3 flood 载荷(模式预填) */ -static uint8_t s_p13_frame[LAT_P13_FRAME_LEN]; /* L7 应答帧(建一次) */ +static uint8_t s_p13_tx[LAT_P13_L3_SEND_LEN]; /* QUEUE_RT/CHUNK_WAIT flood 载荷(模式预填) */ +static uint8_t s_p13_frame[LAT_P13_FRAME_LEN]; /* FRAME_RT 应答帧(建一次) */ static uint8_t s_p13_l6rx[LAT_P13_L6_SIZE]; static uint8_t s_p13_l7rx[LAT_P13_L7_REQ_LEN]; -/* 构造 L7 应答帧(严格照生产 RemoDispBus 帧格式,ui_net_sink.c remo_send): +/* 构造 FRAME_RT 应答帧(严格照生产 RemoDispBus 帧格式,ui_net_sink.c remo_send): * [TAG=0xBB][CMD=0x03][LEN=3204 大端][DATA = 偏移u32(0) + 1bpp 位图 3200B][CRC] * CRC = 仅对 DATA 段(3204B)逐字节异或;整帧 = 4 + 3204 + 1 = 3209B */ static void p13_build_frame(void) @@ -699,7 +699,7 @@ static void p13_abort(const char *p_why) s_p13_state = P13_ST_ABORT; } -/* HELLO 确认后的公共入口(首连与重启共用):复位会话进度 → 发 op 0x01 进 L5 */ +/* HELLO 确认后的公共入口(首连与重启共用):复位会话进度 → 发 op 0x01 进 QUEUE_RT */ static void p13_on_hello(int32_t sockfd) { s_p13_hello_n = 0U; @@ -722,7 +722,7 @@ static void p13_reinit(void) lat_probe_disarm(); } #endif - s_p13_state = P13_ST_HELLO; /* 由 p13_on_hello 覆盖为 L5_RECV */ + s_p13_state = P13_ST_HELLO; /* 由 p13_on_hello 覆盖为 QUEUE_RT_RECV */ s_p13_stage = 0U; s_p13_rx_bytes = 0U; s_p13_l6rx_n = 0U; @@ -772,7 +772,7 @@ void net_latency_phase13_run(int32_t sockfd) s_p13_rx[i + 2U] == 0x58U && s_p13_rx[i + 3U] == 0x01U) { p13_reinit(); DBG_INFO("[LAT] peer re-HELLO -> session restart"); - p13_on_hello(sockfd); /* HELLO 已消耗 → 直接进 L5 并回 op */ + p13_on_hello(sockfd); /* HELLO 已消耗 → 直接进 QUEUE_RT 并回 op */ break; } } @@ -794,7 +794,7 @@ void net_latency_phase13_run(int32_t sockfd) for (i = 0U; i < LAT_P13_L3_SEND_LEN; i++) { s_p13_tx[i] = (uint8_t)(i & 0xFFU); } - DBG_INFO("=== CH395F Phase 13 Tests (chip+queue latency, L3~L7/L10) ==="); + DBG_INFO("=== CH395F Phase 13 Tests (chip+queue latency, CHUNK_WAIT~FRAME_RT/LOGTAX) ==="); DBG_INFO("[LAT] run_id=%lu phase=13 cal core_clk=%lu us_per_1024cyc=%lu", (unsigned long)HAL_GetTick(), (unsigned long)SystemCoreClock, @@ -836,14 +836,14 @@ void net_latency_phase13_run(int32_t sockfd) break; } - /* ---- L5 静默基线:peer flood 50×100B,MCU 逐次 DONTWAIT 收取 ---- + /* ---- QUEUE_RT 静默基线:peer flood 50×100B,MCU 逐次 DONTWAIT 收取 ---- * 仅对"收到数据"的调用计时(队列往返 + netTask SPI 读);空轮询不采样 */ case P13_ST_L5_RECV: { c0 = lat_cycles_now(); n = net_recv(sockfd, s_p13_rx, (int)sizeof(s_p13_rx), NET_MSG_DONTWAIT); us = lat_cycles_diff_us(c0); if (n == 0) { - p13_abort("peer connection closed (L5 recv)"); + p13_abort("peer connection closed (QUEUE_RT recv)"); return; } if (n > 0) { @@ -855,7 +855,7 @@ void net_latency_phase13_run(int32_t sockfd) * 超容差即判 FAIL 并落 partial 报告(段内数据仍记录) */ if (s_p13_rx_bytes > (LAT_P13_L5_CHUNKS * LAT_P13_L5_CHUNK_LEN) + (2U * LAT_P13_RECV_LEN)) { - p13_abort("L5 duplicate delivery (CH395F TCP retransmit re-delivery)"); + p13_abort("QUEUE_RT duplicate delivery (CH395F TCP retransmit re-delivery)"); return; } if (s_p13_rx_bytes >= (LAT_P13_L5_CHUNKS * LAT_P13_L5_CHUNK_LEN)) { @@ -864,14 +864,14 @@ void net_latency_phase13_run(int32_t sockfd) s_p13_stage = 0U; lat_stat_reset(&s_p13_st); if (p13_send_op(sockfd, LAT_P13_OP_L5_SEND) == 0U) { - p13_abort("L5->L5SEND op send failed (peer desync)"); + p13_abort("QUEUE_RT->QUEUE_RT_SEND op send failed (peer desync)"); return; } } break; } - /* ---- L5 静默基线:MCU 阻塞 net_send 50×100B(调用即含队列往返全程) ---- + /* ---- QUEUE_RT 静默基线:MCU 阻塞 net_send 50×100B(调用即含队列往返全程) ---- * 阻塞调用经 xTaskNotifyWait 让出 → netTask(Normal) 自然抢占,无额外延迟 */ case P13_ST_L5_SEND: { while (s_p13_stage < LAT_P13_L5_CHUNKS) { @@ -881,7 +881,7 @@ void net_latency_phase13_run(int32_t sockfd) if (n != (int32_t)LAT_P13_L5_CHUNK_LEN) { s_p13_seg_force_fail = 1; #if defined(TEST_LATENCY_PROBE) - DBG_INFO("[LAT] L5 send unexpected n=%d stage=%u", + DBG_INFO("[LAT] QUEUE_RT send unexpected n=%d stage=%u", (int)n, (unsigned)s_p13_stage); #endif } @@ -889,19 +889,19 @@ void net_latency_phase13_run(int32_t sockfd) s_p13_stage++; } p13_report_seg("queue_rt_send", LAT_P13_QRT_WARN_US, LAT_P13_QRT_CRIT_US, 0U); - /* run3 重排:先 L3(纯 TX flood,1s 超时保护下不挂死)+ L10(本地), - * 后 L6/L7(RX 协议段;run3 实证芯片 TCP 对小帧交换易失步 → 靠后并降样本数) */ + /* run3 重排:先 CHUNK_WAIT(纯 TX flood,1s 超时保护下不挂死)+ LOGTAX(本地), + * 后 ECHO_RT/FRAME_RT(RX 协议段;run3 实证芯片 TCP 对小帧交换易失步 → 靠后并降样本数) */ s_p13_state = P13_ST_L3; s_p13_stage = 0U; break; } - /* ---- L6:每样本一轮 op→10B 请求→10B 回显(MCU 侧:收齐→发出) ---- */ + /* ---- ECHO_RT:每样本一轮 op→10B 请求→10B 回显(MCU 侧:收齐→发出) ---- */ case P13_ST_L6: { n = net_recv(sockfd, s_p13_l6rx + s_p13_l6rx_n, (int)(LAT_P13_L6_SIZE - s_p13_l6rx_n), NET_MSG_DONTWAIT); if (n == 0) { - p13_abort("peer connection closed (L6)"); + p13_abort("peer connection closed (ECHO_RT)"); return; } if (n > 0) { @@ -914,7 +914,7 @@ void net_latency_phase13_run(int32_t sockfd) if (n != (int32_t)LAT_P13_L6_SIZE) { s_p13_seg_force_fail = 1; #if defined(TEST_LATENCY_PROBE) - DBG_INFO("[LAT] L6 echo unexpected n=%d stage=%u", + DBG_INFO("[LAT] ECHO_RT echo unexpected n=%d stage=%u", (int)n, (unsigned)s_p13_stage); #endif } @@ -933,18 +933,18 @@ void net_latency_phase13_run(int32_t sockfd) (void)p13_send_op(sockfd, LAT_P13_OP_L6); } } else if ((HAL_GetTick() - s_p13_step_tick) > LAT_P13_SAMPLE_T_MS) { - p13_abort("L6 peer timeout"); + p13_abort("ECHO_RT peer timeout"); return; } break; } - /* ---- L7:9B LCDMEM 请求 → 3209B 全帧(netTask 内 4 分块停止等待) ---- */ + /* ---- FRAME_RT:9B LCDMEM 请求 → 3209B 全帧(netTask 内 4 分块停止等待) ---- */ case P13_ST_L7: { n = net_recv(sockfd, s_p13_l7rx + s_p13_l7rx_n, (int)(LAT_P13_L7_REQ_LEN - s_p13_l7rx_n), NET_MSG_DONTWAIT); if (n == 0) { - p13_abort("peer connection closed (L7)"); + p13_abort("peer connection closed (FRAME_RT)"); return; } if (n > 0) { @@ -965,7 +965,7 @@ void net_latency_phase13_run(int32_t sockfd) len16 != 4U || crc != s_p13_l7rx[8U] || s_p13_l7rx[4U] != 0U || s_p13_l7rx[5U] != 0U || s_p13_l7rx[6U] != 0U || s_p13_l7rx[7U] != 0U) { - p13_abort("L7 bad request frame"); + p13_abort("FRAME_RT bad request frame"); return; } } @@ -979,8 +979,8 @@ void net_latency_phase13_run(int32_t sockfd) s_p13_stage++; if (s_p13_stage >= LAT_P13_SAMPLES) { p13_report_seg("fullframe_mcu", LAT_P13_FF_WARN_US, LAT_P13_FF_CRIT_US, 0U); - /* L7 为末段(run3 重排后的最终顺序:L5→L3→L10→L6→L7→DONE); - * 此处若回跳 L3 会形成 L3→L10→L6→L7 无限循环、永不下发 0xFF, + /* FRAME_RT 为末段(run3 重排后的最终顺序:QUEUE_RT→CHUNK_WAIT→LOGTAX→ECHO_RT→FRAME_RT→DONE); + * 此处若回跳 CHUNK_WAIT 会形成 CHUNK_WAIT→LOGTAX→ECHO_RT→FRAME_RT 无限循环、永不下发 0xFF, * peer 只能等 300s 超时(run11 实证) */ s_p13_state = P13_ST_DONE; s_p13_stage = 0U; @@ -990,14 +990,14 @@ void net_latency_phase13_run(int32_t sockfd) (void)p13_send_op(sockfd, LAT_P13_OP_L7); } } else if ((HAL_GetTick() - s_p13_step_tick) > LAT_P13_SAMPLE_T_MS) { - p13_abort("L7 peer timeout"); + p13_abort("FRAME_RT peer timeout"); return; } break; } #if defined(TEST_LATENCY_PROBE) - /* ---- L3:探针武装 + 32×4KB 发送 flood(= 128 chunk 样本) ---- + /* ---- CHUNK_WAIT:探针武装 + 32×4KB 发送 flood(= 128 chunk 样本) ---- * chunk_wait 在 netTask 内测量(写发送缓冲 → SENBUF_FREE), * 调用方侧不可见 → 必须经 net_socket.c 探针(TEST_LATENCY_PROBE) */ case P13_ST_L3: { @@ -1010,7 +1010,7 @@ void net_latency_phase13_run(int32_t sockfd) if (n != (int32_t)LAT_P13_L3_SEND_LEN) { s_p13_seg_force_fail = 1; #if defined(TEST_LATENCY_PROBE) - DBG_INFO("[LAT] L3 send unexpected n=%d stage=%u", + DBG_INFO("[LAT] CHUNK_WAIT send unexpected n=%d stage=%u", (int)n, (unsigned)s_p13_stage); #endif } @@ -1055,7 +1055,7 @@ void net_latency_phase13_run(int32_t sockfd) } #endif /* TEST_LATENCY_PROBE */ - /* ---- L10:一行 60 字符 printf 的耗时(fputc 逐字节 @115200,D6 量化) ---- */ + /* ---- LOGTAX:一行 60 字符 printf 的耗时(fputc 逐字节 @115200,D6 量化) ---- */ case P13_ST_L10: { static char s_line[64]; if (s_p13_stage == 0U) { @@ -1069,8 +1069,8 @@ void net_latency_phase13_run(int32_t sockfd) s_p13_stage++; } p13_report_seg("logtax_line", LAT_P13_LT_WARN_US, LAT_P13_LT_CRIT_US, 0U); - /* L6 前清空 socket 残留(重传/重复递交字节,run1 实证 6884B、run3 实证 9B 碎片): - * 残留会与 L6/L7 的请求帧错位混读 → 协议失步。此窗口对端静默 + /* ECHO_RT 前清空 socket 残留(重传/重复递交字节,run1 实证 6884B、run3 实证 9B 碎片): + * 残留会与 ECHO_RT/FRAME_RT 的请求帧错位混读 → 协议失步。此窗口对端静默 * (对端只在收到操作字节后才发包),DONTWAIT 抽干即安全。 */ { int32_t d; @@ -1084,7 +1084,7 @@ void net_latency_phase13_run(int32_t sockfd) drained += (uint32_t)d; } if (drained > 0U) { - DBG_INFO("[LAT] note: drained %u residual bytes before L6 (retransmit/duplicate)", + DBG_INFO("[LAT] note: drained %u residual bytes before ECHO_RT (retransmit/duplicate)", (unsigned)drained); } } @@ -1125,7 +1125,7 @@ void net_latency_phase13_run(int32_t sockfd) #ifdef ENABLE_PHASE14_TESTS /* ==================================================================== - * Phase 14 — 生产路径时延(L8~L11,M3) + * Phase 14 — 生产路径时延(PROD_CYCLE~UI_LOOP,M3) * * 与 Phase 13 的关键区别:**不占用测试 socket**,测的是真实生产路径 * (uiTask + ui_net_sink :7003 + 真实 LVGL 取帧),由 PC 端 @@ -1222,7 +1222,7 @@ void net_latency_phase14_run(void) s_p14_banner = 1U; s_p14_start_tick = HAL_GetTick(); lat_time_init(); /* DWT CYCCNT 使能(漏调则所有样本恒 0) */ - DBG_INFO("=== CH395F Phase 14 Tests (prod-path latency, L8~L11) ==="); + DBG_INFO("=== CH395F Phase 14 Tests (prod-path latency, PROD_CYCLE~UI_LOOP) ==="); DBG_INFO("[LAT] run_id=%lu phase=14 cal core_clk=%lu us_per_1024cyc=%lu", (unsigned long)s_p14_start_tick, (unsigned long)SystemCoreClock,