diff --git a/Drivers/BSP/CH395F/ch395f.h b/Drivers/BSP/CH395F/ch395f.h index b0f621d4..03c69cbd 100644 --- a/Drivers/BSP/CH395F/ch395f.h +++ b/Drivers/BSP/CH395F/ch395f.h @@ -585,7 +585,7 @@ uint16_t ch395f_get_recv_len(uint8_t sock); * 任何未配对 SINT 消费的读取都会吞掉其它 Socket 的事件通知。 * **唯一读者 = net_poll(net_socket.c);测试/台架代码同样禁自行读取** * (用户裁定 2026-10-13)——等事件一律直读本 Socket ch395f_get_sock_int_status()。 - * 唯一例外:被测对象就是本 API 的板级用例(寄存器断言 / Phase 12 L1 + * 唯一例外:被测对象就是本 API 的板级用例(寄存器断言 / Phase 12 SPI_CMD * 时延基准,硬件层独占上下文、无并发消费者)。 */ uint16_t ch395f_get_glob_int_status_all(void); diff --git a/test/board/ch395f_test.h b/test/board/ch395f_test.h index 4b82e177..99a1868a 100644 --- a/test/board/ch395f_test.h +++ b/test/board/ch395f_test.h @@ -23,9 +23,9 @@ * Phase 9 | NET 层 | testTask:Select/Poll I/O 多路复用 * Phase 10 | NET 层 | testTask:边界条件测试(零长度、DMA 边界、缓冲溢出) * Phase 11 | NET 层 | testTask:KeepAlive 超时断开测试 - * Phase 12 | 硬件层 | 独立运行:SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC) - * Phase 13 | NET 层 | testTask:芯片 + 队列时延(L3~L7/L10,需 PC peer,独立运行) - * Phase 14 | NET 层 | testTask:生产路径时延(L8~L11 + 模拟器 A/B,需 PC peer)〔M3 规划〕 + * Phase 12 | 硬件层 | 独立运行:SPI 字节命令 / DMA 时延基线(SPI_CMD/SPI_DMA,无需 PC) + * Phase 13 | NET 层 | testTask:芯片 + 队列时延(CHUNK_WAIT~FRAME_RT/LOGTAX,需 PC peer,独立运行) + * Phase 14 | NET 层 | testTask:生产路径时延(PROD_CYCLE~UI_LOOP + 模拟器 A/B,需 PC peer)〔M3 规划〕 * 时延分段定义/判定基线/执行流程:docs/测试说明/链路延迟测试说明.md */ @@ -67,14 +67,14 @@ extern "C" { * ⚠ 说明写在**上一行**而不是挂在 #define 行尾:见本节末尾的"开关行注释规范" */ #define ENABLE_PHASE11_TESTS /* [网] KeepAlive 超时断开(判据见 CH395F_Test_Guide) */ /* ---- 链路时延基线(M1;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */ -//#define ENABLE_PHASE12_TESTS /* [硬] SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC)—— M1 已完成,基线见说明文档 §6.1 */ +//#define ENABLE_PHASE12_TESTS /* [硬] SPI 字节命令 / DMA 时延基线(SPI_CMD/SPI_DMA,无需 PC)—— M1 已完成,基线见说明文档 §6.1 */ //#define ENABLE_PHASE15_TESTS /* [硬] 自愈驱动原语:状态回读一致性/cmd_done/sock_reset 状态机跑通/链路缓存(无需 PC)——TC 详见 docs/测试说明/CH395F_Test_Guide.md 阶段 15;台架 21/21 PASS */ /* ---- 链路时延(M2/M3;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */ -//#define ENABLE_PHASE13_TESTS /* [网] 芯片 + 队列时延(L3~L7/L10,需 PC peer)—— M2 已完成,基线见说明文档 §6.2 */ -/* [网] 生产路径时延(L8~L11 + 模拟器 Δ)—— M3,需 peer --mode remo(:7003); +//#define ENABLE_PHASE13_TESTS /* [网] 芯片 + 队列时延(CHUNK_WAIT~FRAME_RT/LOGTAX,需 PC peer)—— M2 已完成,基线见说明文档 §6.2 */ +/* [网] 生产路径时延(PROD_CYCLE~UI_LOOP + 模拟器 Δ)—— M3,需 peer --mode remo(:7003); * 与 Phase 11 共存安全:peer 缺席时有 LAT_P14_TIMEOUT_MS 整体超时,自动 done, * 不永久挡 accept */ -#define ENABLE_PHASE14_TESTS /* [网] 生产路径时延(L8~L11) */ +#define ENABLE_PHASE14_TESTS /* [网] 生产路径时延(PROD_CYCLE~UI_LOOP) */ /* * 开关行注释规范(2026-10-25 构建矩阵实测踩出来的,别改回去): diff --git a/test/board/ch395f_test_task.c b/test/board/ch395f_test_task.c index 706f3ecd..3e58e763 100644 --- a/test/board/ch395f_test_task.c +++ b/test/board/ch395f_test_task.c @@ -1736,7 +1736,7 @@ void ch395f_test_run(void) phase5_run(); #endif #ifdef ENABLE_PHASE12_TESTS - /* Phase 12(链路时延基线 L1/L2,M1):独立于 Phase 1~5 的 SPI 时延 + /* Phase 12(链路时延基线 SPI_CMD/SPI_DMA,M1):独立于 Phase 1~5 的 SPI 时延 * 采样,放硬件层阶段末尾执行(说明文档:链路延迟测试说明.md) */ net_latency_phase12_run(); #endif diff --git a/test/board/net_latency_test.c b/test/board/net_latency_test.c index 8e8cb2af..6aa38c78 100644 --- a/test/board/net_latency_test.c +++ b/test/board/net_latency_test.c @@ -509,7 +509,7 @@ void net_latency_phase12_run(void) * 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 发操作字节,peer(test/host/latency_peer.py --mode net) * 执行对应流量;段完成后 MCU 打 [LAT] 报告行并推进下一操作。 * * 段定义(说明文档 §1): @@ -534,41 +534,41 @@ void net_latency_phase12_run(void) #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 /* QUEUE_RT flood 块数(2026-09-22 run1 实证:50×100B@5ms +#define LAT_P13_QUEUE_RT_CHUNKS 20U /* QUEUE_RT flood 块数(2026-09-22 run1 实证:50×100B@5ms * 达 20KB/s 饱和芯片接收缓冲 → 窗口关闭重传 → * 重复递交 11884/5000B → 对端 RST;降至 1KB/s * 静默档,见 §6.2 M2 基线) */ -#define LAT_P13_L5_CHUNK_LEN 100U -#define LAT_P13_L3_SENDS 16U /* 每次 4KB = 4 chunk → 16×4 = 64 探针样本 +#define LAT_P13_QUEUE_RT_CHUNK_LEN 100U +#define LAT_P13_CHUNK_WAIT_SENDS 16U /* 每次 4KB = 4 chunk → 16×4 = 64 探针样本 * (run3 后从 32 减半:控制持续 TX 总量) */ -#define LAT_P13_L3_SEND_LEN 4096U -#define LAT_P13_L3_SAMPLES (LAT_P13_L3_SENDS * 4U) -#define LAT_P13_L6_SIZE 10U -#define LAT_P13_L7_REQ_LEN 9U +#define LAT_P13_CHUNK_WAIT_SEND_LEN 4096U +#define LAT_P13_CHUNK_WAIT_SAMPLES (LAT_P13_CHUNK_WAIT_SENDS * 4U) +#define LAT_P13_ECHO_RT_SIZE 10U +#define LAT_P13_FRAME_RT_REQ_LEN 9U #define LAT_P13_FRAME_LEN 3209U /* 4B 头 + 3204B 数据段(偏移u32+位图3200B) + 1B CRC */ #define LAT_P13_RECV_LEN 256U #define LAT_P13_TIMEOUT_MS 240000U /* 整体会话超时(压力档含 30s 冻结) */ #define LAT_P13_SAMPLE_T_MS 35000U /* 单样本等待超时(覆盖一个 30s 冻结) */ /* 初始阈值(us):warn ≈ 2~3×预期,crit 覆盖硬失败(DMA/发送超时 1s 必超) */ -#define LAT_P13_QRT_WARN_US 10000U -#define LAT_P13_QRT_CRIT_US 100000U -#define LAT_P13_R2F_WARN_US 20000U -#define LAT_P13_R2F_CRIT_US 200000U -#define LAT_P13_FF_WARN_US 50000U -#define LAT_P13_FF_CRIT_US 500000U -#define LAT_P13_CW_WARN_US 10000U -#define LAT_P13_CW_CRIT_US 500000U -#define LAT_P13_CW_SPIKE_US 500000U /* ≥500ms = 重传粒度指纹(D1) */ -#define LAT_P13_LT_WARN_US 10000U -#define LAT_P13_LT_CRIT_US 25000U +#define LAT_P13_QUEUE_RT_WARN_US 10000U +#define LAT_P13_QUEUE_RT_CRIT_US 100000U +#define LAT_P13_REQ2FIRST_WARN_US 20000U +#define LAT_P13_REQ2FIRST_CRIT_US 200000U +#define LAT_P13_FULLFRAME_WARN_US 50000U +#define LAT_P13_FULLFRAME_CRIT_US 500000U +#define LAT_P13_CHUNK_WAIT_WARN_US 10000U +#define LAT_P13_CHUNK_WAIT_CRIT_US 500000U +#define LAT_P13_CHUNK_WAIT_SPIKE_US 500000U /* ≥500ms = 重传粒度指纹(D1) */ +#define LAT_P13_LOGTAX_WARN_US 10000U +#define LAT_P13_LOGTAX_CRIT_US 25000U /* 操作字节(MCU → peer) */ -#define LAT_P13_OP_L5_RECV 0x01U -#define LAT_P13_OP_L5_SEND 0x02U -#define LAT_P13_OP_L6 0x03U -#define LAT_P13_OP_L7 0x04U -#define LAT_P13_OP_L3 0x05U +#define LAT_P13_OP_QUEUE_RT_RECV 0x01U +#define LAT_P13_OP_QUEUE_RT_SEND 0x02U +#define LAT_P13_OP_ECHO_RT 0x03U +#define LAT_P13_OP_FRAME_RT 0x04U +#define LAT_P13_OP_CHUNK_WAIT 0x05U #define LAT_P13_OP_DONE 0xFFU /* HELLO 魔术字(peer → MCU,4 字节) */ @@ -576,12 +576,12 @@ static const uint8_t a_p13_hello[4] = { 0x5AU, 0xA5U, 0x58U, 0x01U }; enum { P13_ST_HELLO = 0, - P13_ST_L5_RECV, - P13_ST_L5_SEND, - P13_ST_L6, - P13_ST_L7, - P13_ST_L3, - P13_ST_L10, + P13_ST_QUEUE_RT_RECV, + P13_ST_QUEUE_RT_SEND, + P13_ST_ECHO_RT, + P13_ST_FRAME_RT, + P13_ST_CHUNK_WAIT, + P13_ST_LOGTAX, P13_ST_DONE, P13_ST_ABORT }; @@ -600,10 +600,10 @@ 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]; /* QUEUE_RT/CHUNK_WAIT flood 载荷(模式预填) */ +static uint8_t s_p13_tx[LAT_P13_CHUNK_WAIT_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]; +static uint8_t s_p13_l6rx[LAT_P13_ECHO_RT_SIZE]; +static uint8_t s_p13_l7rx[LAT_P13_FRAME_RT_REQ_LEN]; /* 构造 FRAME_RT 应答帧(严格照生产 RemoDispBus 帧格式,ui_net_sink.c remo_send): * [TAG=0xBB][CMD=0x03][LEN=3204 大端][DATA = 偏移u32(0) + 1bpp 位图 3200B][CRC] @@ -707,8 +707,8 @@ static void p13_on_hello(int32_t sockfd) lat_stat_reset(&s_p13_st); s_p13_stage = 0U; s_p13_rx_bytes = 0U; - s_p13_state = P13_ST_L5_RECV; - (void)p13_send_op(sockfd, LAT_P13_OP_L5_RECV); + s_p13_state = P13_ST_QUEUE_RT_RECV; + (void)p13_send_op(sockfd, LAT_P13_OP_QUEUE_RT_RECV); } /* 会话重启初始化(abort 后 peer 重连再 HELLO 时调用): @@ -791,7 +791,7 @@ void net_latency_phase13_run(int32_t sockfd) s_p13_start_tick = HAL_GetTick(); lat_time_init(); p13_build_frame(); - for (i = 0U; i < LAT_P13_L3_SEND_LEN; i++) { + for (i = 0U; i < LAT_P13_CHUNK_WAIT_SEND_LEN; i++) { s_p13_tx[i] = (uint8_t)(i & 0xFFU); } DBG_INFO("=== CH395F Phase 13 Tests (chip+queue latency, CHUNK_WAIT~FRAME_RT/LOGTAX) ==="); @@ -838,7 +838,7 @@ void net_latency_phase13_run(int32_t sockfd) /* ---- QUEUE_RT 静默基线:peer flood 50×100B,MCU 逐次 DONTWAIT 收取 ---- * 仅对"收到数据"的调用计时(队列往返 + netTask SPI 读);空轮询不采样 */ - case P13_ST_L5_RECV: { + case P13_ST_QUEUE_RT_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); @@ -854,16 +854,16 @@ void net_latency_phase13_run(int32_t sockfd) * 关闭/重开会把重传段重复递交(run1:5000B 送达 11884B,+138%)→ * 超容差即判 FAIL 并落 partial 报告(段内数据仍记录) */ if (s_p13_rx_bytes > - (LAT_P13_L5_CHUNKS * LAT_P13_L5_CHUNK_LEN) + (2U * LAT_P13_RECV_LEN)) { + (LAT_P13_QUEUE_RT_CHUNKS * LAT_P13_QUEUE_RT_CHUNK_LEN) + (2U * LAT_P13_RECV_LEN)) { 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)) { - p13_report_seg("queue_rt_recv", LAT_P13_QRT_WARN_US, LAT_P13_QRT_CRIT_US, 0U); - s_p13_state = P13_ST_L5_SEND; + if (s_p13_rx_bytes >= (LAT_P13_QUEUE_RT_CHUNKS * LAT_P13_QUEUE_RT_CHUNK_LEN)) { + p13_report_seg("queue_rt_recv", LAT_P13_QUEUE_RT_WARN_US, LAT_P13_QUEUE_RT_CRIT_US, 0U); + s_p13_state = P13_ST_QUEUE_RT_SEND; s_p13_stage = 0U; lat_stat_reset(&s_p13_st); - if (p13_send_op(sockfd, LAT_P13_OP_L5_SEND) == 0U) { + if (p13_send_op(sockfd, LAT_P13_OP_QUEUE_RT_SEND) == 0U) { p13_abort("QUEUE_RT->QUEUE_RT_SEND op send failed (peer desync)"); return; } @@ -873,12 +873,12 @@ void net_latency_phase13_run(int32_t sockfd) /* ---- QUEUE_RT 静默基线:MCU 阻塞 net_send 50×100B(调用即含队列往返全程) ---- * 阻塞调用经 xTaskNotifyWait 让出 → netTask(Normal) 自然抢占,无额外延迟 */ - case P13_ST_L5_SEND: { - while (s_p13_stage < LAT_P13_L5_CHUNKS) { + case P13_ST_QUEUE_RT_SEND: { + while (s_p13_stage < LAT_P13_QUEUE_RT_CHUNKS) { c0 = lat_cycles_now(); - n = net_send(sockfd, s_p13_tx, (int32_t)LAT_P13_L5_CHUNK_LEN, 0); + n = net_send(sockfd, s_p13_tx, (int32_t)LAT_P13_QUEUE_RT_CHUNK_LEN, 0); us = lat_cycles_diff_us(c0); - if (n != (int32_t)LAT_P13_L5_CHUNK_LEN) { + if (n != (int32_t)LAT_P13_QUEUE_RT_CHUNK_LEN) { s_p13_seg_force_fail = 1; #if defined(TEST_LATENCY_PROBE) DBG_INFO("[LAT] QUEUE_RT send unexpected n=%d stage=%u", @@ -888,18 +888,18 @@ void net_latency_phase13_run(int32_t sockfd) lat_stat_add(&s_p13_st, us, 0U); s_p13_stage++; } - p13_report_seg("queue_rt_send", LAT_P13_QRT_WARN_US, LAT_P13_QRT_CRIT_US, 0U); + p13_report_seg("queue_rt_send", LAT_P13_QUEUE_RT_WARN_US, LAT_P13_QUEUE_RT_CRIT_US, 0U); /* run3 重排:先 CHUNK_WAIT(纯 TX flood,1s 超时保护下不挂死)+ LOGTAX(本地), * 后 ECHO_RT/FRAME_RT(RX 协议段;run3 实证芯片 TCP 对小帧交换易失步 → 靠后并降样本数) */ - s_p13_state = P13_ST_L3; + s_p13_state = P13_ST_CHUNK_WAIT; s_p13_stage = 0U; break; } /* ---- ECHO_RT:每样本一轮 op→10B 请求→10B 回显(MCU 侧:收齐→发出) ---- */ - case P13_ST_L6: { + case P13_ST_ECHO_RT: { n = net_recv(sockfd, s_p13_l6rx + s_p13_l6rx_n, - (int)(LAT_P13_L6_SIZE - s_p13_l6rx_n), NET_MSG_DONTWAIT); + (int)(LAT_P13_ECHO_RT_SIZE - s_p13_l6rx_n), NET_MSG_DONTWAIT); if (n == 0) { p13_abort("peer connection closed (ECHO_RT)"); return; @@ -907,11 +907,11 @@ void net_latency_phase13_run(int32_t sockfd) if (n > 0) { s_p13_l6rx_n += (uint16_t)n; } - if (s_p13_l6rx_n >= LAT_P13_L6_SIZE) { + if (s_p13_l6rx_n >= LAT_P13_ECHO_RT_SIZE) { c0 = lat_cycles_now(); - n = net_send(sockfd, s_p13_l6rx, (int32_t)LAT_P13_L6_SIZE, 0); /* 回显 */ + n = net_send(sockfd, s_p13_l6rx, (int32_t)LAT_P13_ECHO_RT_SIZE, 0); /* 回显 */ us = lat_cycles_diff_us(c0); - if (n != (int32_t)LAT_P13_L6_SIZE) { + if (n != (int32_t)LAT_P13_ECHO_RT_SIZE) { s_p13_seg_force_fail = 1; #if defined(TEST_LATENCY_PROBE) DBG_INFO("[LAT] ECHO_RT echo unexpected n=%d stage=%u", @@ -921,16 +921,16 @@ void net_latency_phase13_run(int32_t sockfd) lat_stat_add(&s_p13_st, us, 0U); s_p13_stage++; if (s_p13_stage >= LAT_P13_SAMPLES) { - p13_report_seg("req2first_mcu", LAT_P13_R2F_WARN_US, LAT_P13_R2F_CRIT_US, 0U); - s_p13_state = P13_ST_L7; + p13_report_seg("req2first_mcu", LAT_P13_REQ2FIRST_WARN_US, LAT_P13_REQ2FIRST_CRIT_US, 0U); + s_p13_state = P13_ST_FRAME_RT; s_p13_stage = 0U; s_p13_l7rx_n = 0U; s_p13_step_tick = HAL_GetTick(); - (void)p13_send_op(sockfd, LAT_P13_OP_L7); + (void)p13_send_op(sockfd, LAT_P13_OP_FRAME_RT); } else { s_p13_l6rx_n = 0U; s_p13_step_tick = HAL_GetTick(); - (void)p13_send_op(sockfd, LAT_P13_OP_L6); + (void)p13_send_op(sockfd, LAT_P13_OP_ECHO_RT); } } else if ((HAL_GetTick() - s_p13_step_tick) > LAT_P13_SAMPLE_T_MS) { p13_abort("ECHO_RT peer timeout"); @@ -940,9 +940,9 @@ void net_latency_phase13_run(int32_t sockfd) } /* ---- FRAME_RT:9B LCDMEM 请求 → 3209B 全帧(netTask 内 4 分块停止等待) ---- */ - case P13_ST_L7: { + case P13_ST_FRAME_RT: { n = net_recv(sockfd, s_p13_l7rx + s_p13_l7rx_n, - (int)(LAT_P13_L7_REQ_LEN - s_p13_l7rx_n), NET_MSG_DONTWAIT); + (int)(LAT_P13_FRAME_RT_REQ_LEN - s_p13_l7rx_n), NET_MSG_DONTWAIT); if (n == 0) { p13_abort("peer connection closed (FRAME_RT)"); return; @@ -950,7 +950,7 @@ void net_latency_phase13_run(int32_t sockfd) if (n > 0) { s_p13_l7rx_n += (uint16_t)n; } - if (s_p13_l7rx_n >= LAT_P13_L7_REQ_LEN) { + if (s_p13_l7rx_n >= LAT_P13_FRAME_RT_REQ_LEN) { /* 校验请求帧(严格照生产解析器 ui_net_sink.c remo_handle 的语义): * [TAG=0xAA][CMD=0x03][LEN=4 大端][偏移u32 大端=0][CRC=XOR(数据段 4B)] * 注意 LEN 是"数据段长度"(4),非请求的位图字节数;CRC 只覆盖数据段 */ @@ -978,7 +978,7 @@ void net_latency_phase13_run(int32_t sockfd) lat_stat_add(&s_p13_st, us, 0U); 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); + p13_report_seg("fullframe_mcu", LAT_P13_FULLFRAME_WARN_US, LAT_P13_FULLFRAME_CRIT_US, 0U); /* 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 实证) */ @@ -987,7 +987,7 @@ void net_latency_phase13_run(int32_t sockfd) } else { s_p13_l7rx_n = 0U; s_p13_step_tick = HAL_GetTick(); - (void)p13_send_op(sockfd, LAT_P13_OP_L7); + (void)p13_send_op(sockfd, LAT_P13_OP_FRAME_RT); } } else if ((HAL_GetTick() - s_p13_step_tick) > LAT_P13_SAMPLE_T_MS) { p13_abort("FRAME_RT peer timeout"); @@ -1000,14 +1000,14 @@ void net_latency_phase13_run(int32_t sockfd) /* ---- CHUNK_WAIT:探针武装 + 32×4KB 发送 flood(= 128 chunk 样本) ---- * chunk_wait 在 netTask 内测量(写发送缓冲 → SENBUF_FREE), * 调用方侧不可见 → 必须经 net_socket.c 探针(TEST_LATENCY_PROBE) */ - case P13_ST_L3: { + case P13_ST_CHUNK_WAIT: { if (s_p13_stage == 0U) { - (void)p13_send_op(sockfd, LAT_P13_OP_L3); - lat_probe_arm("chunk_wait", (uint32_t)LAT_P13_L3_SAMPLES, LAT_P13_CW_SPIKE_US); + (void)p13_send_op(sockfd, LAT_P13_OP_CHUNK_WAIT); + lat_probe_arm("chunk_wait", (uint32_t)LAT_P13_CHUNK_WAIT_SAMPLES, LAT_P13_CHUNK_WAIT_SPIKE_US); } - while (s_p13_stage < LAT_P13_L3_SENDS) { - n = net_send(sockfd, s_p13_tx, (int32_t)LAT_P13_L3_SEND_LEN, 0); - if (n != (int32_t)LAT_P13_L3_SEND_LEN) { + while (s_p13_stage < LAT_P13_CHUNK_WAIT_SENDS) { + n = net_send(sockfd, s_p13_tx, (int32_t)LAT_P13_CHUNK_WAIT_SEND_LEN, 0); + if (n != (int32_t)LAT_P13_CHUNK_WAIT_SEND_LEN) { s_p13_seg_force_fail = 1; #if defined(TEST_LATENCY_PROBE) DBG_INFO("[LAT] CHUNK_WAIT send unexpected n=%d stage=%u", @@ -1027,8 +1027,8 @@ void net_latency_phase13_run(int32_t sockfd) if (s_p13_seg_force_fail != 0) { v = LAT_VERDICT_FAIL; } - lat_stat_report("chunk_wait", p_ps, LAT_P13_CW_WARN_US, - LAT_P13_CW_CRIT_US, 0U, &v); + lat_stat_report("chunk_wait", p_ps, LAT_P13_CHUNK_WAIT_WARN_US, + LAT_P13_CHUNK_WAIT_CRIT_US, 0U, &v); if (s_p13_seg_force_fail != 0) { DBG_INFO("[LAT] note: chunk_wait has send error/short write -> FAIL"); } @@ -1037,26 +1037,26 @@ void net_latency_phase13_run(int32_t sockfd) } if (s_p13_seg_force_fail != 0) { DBG_INFO("[LAT] note: chunk_wait partial n=%u/%u (chip TX stall suspected)", - (unsigned)p_ps->n, (unsigned)LAT_P13_L3_SAMPLES); + (unsigned)p_ps->n, (unsigned)LAT_P13_CHUNK_WAIT_SAMPLES); } s_p13_seg_force_fail = 0; - s_p13_state = P13_ST_L10; + s_p13_state = P13_ST_LOGTAX; s_p13_stage = 0U; } break; } #else /* TEST_LATENCY_PROBE */ - case P13_ST_L3: { + case P13_ST_CHUNK_WAIT: { /* 探针未编译(未选 Phase 13/14 之外的组合):跳过本段 */ DBG_INFO("[LAT] skip chunk_wait (probe not compiled)"); - s_p13_state = P13_ST_L10; + s_p13_state = P13_ST_LOGTAX; s_p13_stage = 0U; break; } #endif /* TEST_LATENCY_PROBE */ /* ---- LOGTAX:一行 60 字符 printf 的耗时(fputc 逐字节 @115200,D6 量化) ---- */ - case P13_ST_L10: { + case P13_ST_LOGTAX: { static char s_line[64]; if (s_p13_stage == 0U) { snprintf(s_line, sizeof(s_line), "LAT logtax line: 0123456789 0123456789 012345"); @@ -1068,7 +1068,7 @@ void net_latency_phase13_run(int32_t sockfd) lat_stat_add(&s_p13_st, lat_cycles_diff_us(c0), 0U); s_p13_stage++; } - p13_report_seg("logtax_line", LAT_P13_LT_WARN_US, LAT_P13_LT_CRIT_US, 0U); + p13_report_seg("logtax_line", LAT_P13_LOGTAX_WARN_US, LAT_P13_LOGTAX_CRIT_US, 0U); /* ECHO_RT 前清空 socket 残留(重传/重复递交字节,run1 实证 6884B、run3 实证 9B 碎片): * 残留会与 ECHO_RT/FRAME_RT 的请求帧错位混读 → 协议失步。此窗口对端静默 * (对端只在收到操作字节后才发包),DONTWAIT 抽干即安全。 */ @@ -1088,11 +1088,11 @@ void net_latency_phase13_run(int32_t sockfd) (unsigned)drained); } } - s_p13_state = P13_ST_L6; + s_p13_state = P13_ST_ECHO_RT; s_p13_stage = 0U; s_p13_l6rx_n = 0U; s_p13_step_tick = HAL_GetTick(); - (void)p13_send_op(sockfd, LAT_P13_OP_L6); + (void)p13_send_op(sockfd, LAT_P13_OP_ECHO_RT); break; } diff --git a/test/board/net_latency_test.h b/test/board/net_latency_test.h index c5fc2f45..b32413cb 100644 --- a/test/board/net_latency_test.h +++ b/test/board/net_latency_test.h @@ -2,7 +2,7 @@ * 模块名称:CH395F 链路延迟测试 * 模块功能:链路分段时延测量的微秒时基(DWT CYCCNT)、采样统计 * (min/med/p95/max + 尖峰计数)、机器可读 [LAT] 报告输出与判定。 - * Phase 12(硬件层,M1):SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC); + * Phase 12(硬件层,M1):SPI 字节命令 / DMA 时延基线(SPI_CMD/SPI_DMA,无需 PC); * Phase 13/14(NET 层,M2/M3 规划):芯片发送握手 / 队列往返 / * 生产路径时延。分段定义、判定基线与执行流程见 * docs/测试说明/链路延迟测试说明.md。 @@ -10,7 +10,7 @@ * 作者:王建锋 * 创建日期:2026-09-22 * 修改记录: - * 2026-09-22 王建锋 初版:时基 + 统计/报告 + Phase 12(L1/L2 SPI 时延基线) + * 2026-09-22 王建锋 初版:时基 + 统计/报告 + Phase 12(SPI_CMD/SPI_DMA SPI 时延基线) ******************************************************************************/ #ifndef NET_LATENCY_TEST_H @@ -24,18 +24,18 @@ extern "C" { /* * === 链路时延测试阶段一览(见 链路延迟测试说明.md §4.2) === - * Phase 12 [硬件层] SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC) —— M1(已完成) - * Phase 13 [NET 层] 芯片 + 队列时延(L3~L7/L10,需 PC peer) —— M2(已实现) - * Phase 14 [NET 层] 生产路径时延(L8~L11 + 模拟器 A/B,需 PC peer)—— M3(规划) + * Phase 12 [硬件层] SPI 字节命令 / DMA 时延基线(SPI_CMD/SPI_DMA,无需 PC) —— M1(已完成) + * Phase 13 [NET 层] 芯片 + 队列时延(CHUNK_WAIT~FRAME_RT/LOGTAX,需 PC peer) —— M2(已实现) + * Phase 14 [NET 层] 生产路径时延(PROD_CYCLE~UI_LOOP + 模拟器 A/B,需 PC peer)—— M3(规划) * * 时基说明:DWT CYCCNT 为 32 位,168MHz 下回绕周期 25.17ms。 * M1 各段(<5ms)与 M2 调用方侧测量(单次 <25ms)用单次读数差即可; - * 跨 25ms 的段(L4/L8 整帧与重传尖峰)由 PC 端 peer 计时。 + * 跨 25ms 的段(TO_PC/PROD_CYCLE 整帧与重传尖峰)由 PC 端 peer 计时。 * * Phase 13 协议(服务器驱动:MCU 发操作字节,peer 执行;详见说明文档 §4.5): * HELLO = 5A A5 58 01(peer → MCU,4 字节) - * 操作字节(MCU → peer):0x01=L5 recv flood / 0x02=L5 send flood / - * 0x03=L6 单请求 / 0x04=L7 LCDMEM 全帧 / 0x05=L3 send flood / 0xFF=完成 + * 操作字节(MCU → peer):0x01=QUEUE_RT recv flood / 0x02=QUEUE_RT send flood / + * 0x03=ECHO_RT 单请求 / 0x04=FRAME_RT LCDMEM 全帧 / 0x05=CHUNK_WAIT send flood / 0xFF=完成 * Phase 13 需**独立运行**(同 Phase 4 约定):与其他 Phase 共用 8080 端口 * 的 socket,混跑会污染帧测量。 */ @@ -113,7 +113,7 @@ void lat_stat_report(const char *p_seg, const lat_stats_t *p, uint16_t max_spikes, int *p_verdict); /* - * 函数功能:Phase 12 入口(硬件层:SPI 字节命令 / DMA 时延基线,L1/L2,无需 PC) + * 函数功能:Phase 12 入口(硬件层:SPI 字节命令 / DMA 时延基线,SPI_CMD/SPI_DMA,无需 PC) * 入口参数:无 * 返回值:无 * 限定条件:由 ch395f_test_run() 硬件层分支调用(net_stack_bring_up 完成后 @@ -124,11 +124,11 @@ void lat_stat_report(const char *p_seg, const lat_stats_t *p, void net_latency_phase12_run(void); /* - * 函数功能:Phase 13 入口(NET 层:芯片 + 队列时延 L3~L7/L10,需 PC peer) + * 函数功能:Phase 13 入口(NET 层:芯片 + 队列时延 CHUNK_WAIT~FRAME_RT/LOGTAX,需 PC peer) * 入口参数:sockfd - 测试监听 socket(连接后的数据通道 fd,8080 端口) * 返回值:无 * 限定条件:由 net_layer_test_main() 监听循环每 ~10ms 调用一次(事件驱动 - * 状态机);需 test/latency_peer.py --mode net 配合;须独立运行 + * 状态机);需 test/host/latency_peer.py --mode net 配合;须独立运行 * 函数说明:服务器驱动协议(见文件头协议注释);会话完成后 s_done 置位, * 后续调用为空操作;套件未选中本 Phase 时函数体为空 */ @@ -143,11 +143,11 @@ void net_latency_phase13_run(int32_t sockfd); int net_latency_phase13_busy(void); /* - * 函数功能:Phase 14 入口(NET 层:生产路径时延 L8~L11,真实 uiTask + :7003) + * 函数功能:Phase 14 入口(NET 层:生产路径时延 PROD_CYCLE~UI_LOOP,真实 uiTask + :7003) * 入口参数:无(本 Phase 不占用测试 socket,测量生产 socket 的服务端行为) * 返回值:无 * 限定条件:由 net_layer_test_main() 循环每 ~10ms 调用一次;需 - * test/latency_peer.py --mode remo --port 7003 配合;须单独使能 + * test/host/latency_peer.py --mode remo --port 7003 配合;须单独使能 * (与 Phase 13 同启时两者都会跑,M3 台架建议只开 Phase 14) * 函数说明:状态机顺序 = ui_loop(本地节拍窗口)→ prod_cycle_mcu(等 peer * 发满 N 帧 LCDMEM)→ relisten(等 peer 断开重连)→ DONE; diff --git a/test/board/net_test_task.c b/test/board/net_test_task.c index 90828e45..04a15111 100644 --- a/test/board/net_test_task.c +++ b/test/board/net_test_task.c @@ -967,13 +967,13 @@ void net_layer_test_main(void) } #endif #ifdef ENABLE_PHASE13_TESTS - /* Phase 13(链路时延 L3~L7/L10,M2):独立运行,勿与其他 Phase 混选; + /* Phase 13(链路时延 CHUNK_WAIT~FRAME_RT/LOGTAX,M2):独立运行,勿与其他 Phase 混选; * 注意此处**不**重置 phase_active —— 保持 10ms 节拍(时延测量对 * 循环周期敏感,Phase 8 式 accept 重入的 100ms 退避会污染样本) */ net_latency_phase13_run(sockfd); #endif #ifdef ENABLE_PHASE14_TESTS - /* Phase 14(生产路径时延 L8~L11,M3):不占测试 socket —— 测量真实 + /* Phase 14(生产路径时延 PROD_CYCLE~UI_LOOP,M3):不占测试 socket —— 测量真实 * uiTask + ui_net_sink(:7003) 生产路径,由 peer --mode remo 驱动; * 本调用只是探针窗口状态机(arm/disarm/report),同样保持 10ms 节拍 */ net_latency_phase14_run(); diff --git a/test/board/test_config.h b/test/board/test_config.h index 58649008..59c2be1c 100644 --- a/test/board/test_config.h +++ b/test/board/test_config.h @@ -105,7 +105,7 @@ extern "C" { #endif /* - * 链路时延探针(M2/M3):在 net_socket.c 发送分块循环内定义 L3(chunk_wait) + * 链路时延探针(M2/M3):在 net_socket.c 发送分块循环内定义 CHUNK_WAIT * 测量钩子(netTask 上下文);产品构建未定义 → 钩子代码编译消失(零开销)。 * 依赖 ch395f_test.h 的 Phase 宏(上方 TEST_SUITE_CH395F 块内已包含)。 */ diff --git a/test/host/latency_peer.py b/test/host/latency_peer.py index f8c812cf..136bfeb3 100644 --- a/test/host/latency_peer.py +++ b/test/host/latency_peer.py @@ -6,14 +6,14 @@ byte; the peer executes the corresponding traffic pattern. See docs/测试说明/链路延迟测试说明.md §4.5 for the full protocol spec. Ops (MCU -> peer): - 0x01 L5 recv flood : peer sends 50 x 100B (5ms apart) - 0x02 L5 send drain : peer drains 50 x 100B - 0x03 L6 : peer sends 10B request, expects 10B echo (timed) - 0x04 L7 : peer sends 9B LCDMEM request, expects 3209B frame - 0x05 L3 send drain : peer drains 32 x 4096B - 0xFF done : session complete + 0x01 QUEUE_RT recv flood : peer sends 50 x 100B (5ms apart) + 0x02 QUEUE_RT send drain : peer drains 50 x 100B + 0x03 ECHO_RT : peer sends 10B request, expects 10B echo (timed) + 0x04 FRAME_RT : peer sends 9B LCDMEM request, expects 3209B frame + 0x05 CHUNK_WAIT send drain : peer drains 32 x 4096B + 0xFF done : session complete -The peer records host-side (L4-side) timing: first byte / frame total / +The peer records host-side (TO_PC-side) timing: first byte / frame total / per-batch arrival curve / >=500ms spikes (TCP retransmission fingerprint). Both the serial log and this JSON carry HOST timestamps -> direct correlation. @@ -33,22 +33,22 @@ import time # --- Protocol constants (mirror test/board/net_latency_test.c Phase 13 defines) --- HELLO = b"\x5a\xa5\x58\x01" -OP_L5_RECV = 0x01 -OP_L5_SEND = 0x02 -OP_L6 = 0x03 -OP_L7 = 0x04 -OP_L3 = 0x05 +OP_QUEUE_RT_RECV = 0x01 +OP_QUEUE_RT_SEND = 0x02 +OP_ECHO_RT = 0x03 +OP_FRAME_RT = 0x04 +OP_CHUNK_WAIT = 0x05 OP_DONE = 0xFF -L5_CHUNKS = 20 # 2026-09-22 run1: 50 chunks saturated the chip RX buffer +QUEUE_RT_CHUNKS = 20 # 2026-09-22 run1: 50 chunks saturated the chip RX buffer # (window close -> retransmit re-delivery -> RST); 20 @ 50ms # = 1KB/s quiet baseline (see 链路延迟测试说明.md §6.2) -L5_LEN = 100 -L6_SIZE = 10 -L7_REQ_LEN = 9 -L7_FRAME_LEN = 3209 -L3_SENDS = 16 # mirror firmware LAT_P13_L3_SENDS (run3: 32->16, 64 chunk samples) -L3_LEN = 4096 +QUEUE_RT_CHUNK_LEN = 100 +ECHO_RT_SIZE = 10 +FRAME_RT_REQ_LEN = 9 +FRAME_RT_FRAME_LEN = 3209 +CHUNK_WAIT_SENDS = 16 # mirror firmware LAT_P13_CHUNK_WAIT_SENDS (run3: 32->16, 64 chunk samples) +CHUNK_WAIT_LEN = 4096 SPIKE_MS = 500.0 # retransmission granularity fingerprint (D1) @@ -89,7 +89,7 @@ def recv_exact(sock, n, deadline_ns): return bytes(buf) -def l7_request(seq=0): +def frame_rt_request(seq=0): """Production-shaped 9B LCDMEM request (ui_net_sink.c remo_handle): [TAG 0xAA][CMD 0x03][LEN=4 BE][offset u32 BE = 0][CRC = XOR of the 4 offset bytes]. LEN is the DATA-segment length (4), NOT the requested bitmap size; the offset @@ -101,11 +101,11 @@ def l7_request(seq=0): return bytes([0xAA, 0x03, 0x00, 0x04]) + data + bytes([crc]) -def l7_frame_ok(data): +def frame_rt_ok(data): """Validate the 3209B production LCDMEM response (ui_net_sink.c remo_send): [TAG 0xBB][CMD 0x03][LEN=3204 BE][offset u32 = 0][1bpp bitmap 3200B] [CRC = XOR of the 3204 data bytes] -> 4 + 3204 + 1 = 3209B.""" - if len(data) != L7_FRAME_LEN: + if len(data) != FRAME_RT_FRAME_LEN: return False if data[0] != 0xBB or data[1] != 0x03 or data[2] != 0x0C or data[3] != 0x84: return False @@ -239,7 +239,7 @@ def remo_bitmap_nontrivial(payload): class RemoPeer: - """Production-path peer (M3): drives L8/L9 against :7003 (device) or the + """Production-path peer (M3): drives PROD_CYCLE/RELISTEN against :7003 (device) or the simulator, measuring the full client-observed frame cycle.""" def __init__(self, host, port, wait_s, json_path, samples, cadence_ms, @@ -312,7 +312,7 @@ class RemoPeer: """Wait for the stress window to open, then hold an idle FTP login: lftpd read_line -> blocking net_recv(0) inside netTask (old 30s default budget NET_RECV_TIMEOUT_MS, removed in self-heal phase 1.5; today the wait is - an L3 pending recv -> this scenario must NOT freeze the queue anymore) + a CHUNK_WAIT pending recv -> this scenario must NOT freeze the queue anymore) -> every queued message (incl. the LCDMEM response) waits behind it.""" if not start_evt.wait(120.0): sys.stdout.write("[peer] ftp stress: window never opened\n") @@ -405,7 +405,7 @@ class RemoPeer: self.dump_json() - # --- L9: close + reconnect rounds (each close drives the device auto-relisten + # --- RELISTEN: close + reconnect rounds (each close drives the device auto-relisten # probe; each reconnect measures connect + INIT round trip) --- for r in range(self.reconnect_rounds): self.sock.close() @@ -579,7 +579,7 @@ class Peer: if not d: break reply += d - start_evt.set() # L5 window starts under stress + start_evt.set() # QUEUE_RT window starts under stress sys.stdout.write("[peer] ftp stress armed (331: %r)\n" % reply[:40]) stop_evt.wait(35.0) # > 30s: netTask read times out (full freeze) try: @@ -661,19 +661,19 @@ class Peer: self.connect_with_retry() print("[peer] connected at %s" % iso_now()) - l5_start_evt = threading.Event() - l5_stop_evt = threading.Event() + queue_rt_start_evt = threading.Event() + queue_rt_stop_evt = threading.Event() if self.ftp_stress: th = threading.Thread(target=self.ftp_stress_worker, - args=(l5_start_evt, l5_stop_evt), daemon=True) + args=(queue_rt_start_evt, queue_rt_stop_evt), daemon=True) th.start() self.sock.sendall(HELLO) print("[peer] HELLO sent, awaiting MCU ops ...") - l5_window_open = False - l5_t0 = 0 - l3_t0 = 0 + queue_rt_window_open = False + queue_rt_t0 = 0 + chunk_wait_t0 = 0 while True: op = recv_exact(self.sock, 1, t0_ns + 300 * 1000 * 1000000) op = op[0] @@ -682,29 +682,29 @@ class Peer: print("[peer] op=done after %d ops" % self.n_ops) break - if op == OP_L5_RECV: + if op == OP_QUEUE_RT_RECV: if self.ftp_stress: - l5_start_evt.wait(60.0) # start window only under full stress - l5_window_open = True - l5_t0 = now_ns() - for i in range(L5_CHUNKS): - self.sock.sendall(pattern(L5_LEN, seed=i)) + queue_rt_start_evt.wait(60.0) # start window only under full stress + queue_rt_window_open = True + queue_rt_t0 = now_ns() + for i in range(QUEUE_RT_CHUNKS): + self.sock.sendall(pattern(QUEUE_RT_CHUNK_LEN, seed=i)) time.sleep(0.05) # 50ms: keep 1KB/s below MCU drain rate - self.reports["l5_recv"]["window_ms"] = (now_ns() - l5_t0) / 1e6 - self.reports["l5_recv"]["bytes"] = L5_CHUNKS * L5_LEN - l5_stop_evt.set() + self.reports["l5_recv"]["window_ms"] = (now_ns() - queue_rt_t0) / 1e6 + self.reports["l5_recv"]["bytes"] = QUEUE_RT_CHUNKS * QUEUE_RT_CHUNK_LEN + queue_rt_stop_evt.set() - elif op == OP_L5_SEND: + elif op == OP_QUEUE_RT_SEND: t = now_ns() - data = recv_exact(self.sock, L5_CHUNKS * L5_LEN, t + 120 * 1000 * 1000000) + data = recv_exact(self.sock, QUEUE_RT_CHUNKS * QUEUE_RT_CHUNK_LEN, t + 120 * 1000 * 1000000) self.reports["l5_send"]["bytes"] = len(data) self.reports["l5_send"]["window_ms"] = (now_ns() - t) / 1e6 - elif op == OP_L6: + elif op == OP_ECHO_RT: t0 = now_ns() - req = pattern(L6_SIZE, seed=self.n_ops) + req = pattern(ECHO_RT_SIZE, seed=self.n_ops) self.sock.sendall(req) - resp = recv_exact(self.sock, L6_SIZE, t0 + 40 * 1000 * 1000000) + resp = recv_exact(self.sock, ECHO_RT_SIZE, t0 + 40 * 1000 * 1000000) ms = (now_ns() - t0) / 1e6 self.reports["l6"]["samples"].append(ms) if resp == req: @@ -712,20 +712,20 @@ class Peer: else: self.reports["l6"]["echo_fail"] += 1 - elif op == OP_L7: + elif op == OP_FRAME_RT: t_send = now_ns() - self.sock.sendall(l7_request(self.n_ops)) + self.sock.sendall(frame_rt_request(self.n_ops)) got = bytearray() batches = [] first_ns = None - while len(got) < L7_FRAME_LEN: + while len(got) < FRAME_RT_FRAME_LEN: # bounded read: never consume the next op byte (ASCII 0x04) that # follows the frame in the stream (run10: unbounded recv ate it -> # 3210-byte "frame" -> length check failed -> peer closed) - d = self.sock.recv(min(4096, L7_FRAME_LEN - len(got))) + d = self.sock.recv(min(4096, FRAME_RT_FRAME_LEN - len(got))) if not d: - raise ConnectionError("L7 frame truncated (%d/%d)" % - (len(got), L7_FRAME_LEN)) + raise ConnectionError("FRAME_RT frame truncated (%d/%d)" % + (len(got), FRAME_RT_FRAME_LEN)) if first_ns is None: first_ns = now_ns() got.extend(d) @@ -742,7 +742,7 @@ class Peer: self.reports["l7"]["total_ms"].append(total_ms) self.reports["l7"]["spikes500"] += spikes self.reports["l7"]["batches_per_frame"].append(len(batches)) - if l7_frame_ok(bytes(got)): + if frame_rt_ok(bytes(got)): self.reports["l7"]["crc_ok"] += 1 else: self.reports["l7"]["crc_fail"] += 1 @@ -750,11 +750,11 @@ class Peer: {"first_ms": round(first_ms, 3) if first_ms is not None else None, "total_ms": round(total_ms, 3), "batches": batches}) - elif op == OP_L3: - l3_t0 = now_ns() - recv_exact(self.sock, L3_SENDS * L3_LEN, l3_t0 + 120 * 1000 * 1000000) - self.reports["l3"]["bytes"] = L3_SENDS * L3_LEN - self.reports["l3"]["window_ms"] = (now_ns() - l3_t0) / 1e6 + elif op == OP_CHUNK_WAIT: + chunk_wait_t0 = now_ns() + recv_exact(self.sock, CHUNK_WAIT_SENDS * CHUNK_WAIT_LEN, chunk_wait_t0 + 120 * 1000 * 1000000) + self.reports["l3"]["bytes"] = CHUNK_WAIT_SENDS * CHUNK_WAIT_LEN + self.reports["l3"]["window_ms"] = (now_ns() - chunk_wait_t0) / 1e6 else: raise RuntimeError("unknown op byte 0x%02X (protocol desync)" % op)