f754d59218
分层:流程与规范/ 单元测试/ 板级测试/ 专项验证/ 测试工具/ 测试数据/, 顶层只留新增的目录导航 README.md(归层判据 + 硬约束 + 本次处置记录)。 - 搬迁 28 个路径:测试数据三个目录 58 个文件为纯改名(R100,内容逐字节不变) - 全库引用同步 124 个文件:AGENTS / docs 索引 / skills / 门禁注释 / scripts - 门禁面:scripts/check_test_traceability.py 的 glob 改 rglob (分层后不递归扫描=追溯门禁直接瞎掉) - scripts/rename_ref.py:收尾的 ℹ 提示在 GBK 代码页抛 UnicodeEncodeError, 让进程以退出码 1 收场而改写其实已落盘(RC 与真实结果相反);改为把 stdout/stderr reconfigure 成 UTF-8,与 check_doc_links.py 等同法 - docs/文档规范.md §1.1 补「子目录文件过多时再分一层」;§3.6 补 rename_ref 搬整目录的两个实测坑:① 会改写目录内部文件的内容 ② 内部文件的 ../ 出链 不补层级(本次 23 个被误改的证据文件已按 HEAD 原始字节逐字节还原) - docs/README.md §6 改为与六个子目录一一对应,并补登原先漏登记的 「启动日志原子性复验流程」一行 - .cubemx_survival_baseline.json:App/util/task_watchdog.c 的注释路径也随分层更新, 该文件是存活审计的 watchdog-module 漂移锚点,按脚本自己的提示跑 --write 重建基线 (实测重建后仅 watchdog-module 一条哈希变化) - 铁律 4 例外(本次在任务树里改了单写者文件):AGENTS.md 与 .pre-commit-config.yaml 只被 rename_ref 改写路径字面量(后者全在注释行), 任务表块与钩子声明一字未动 验证:check_doc_links.py --sections-strict 覆盖全部 88 个改动 .md 全绿; check_test_traceability.py 输出与主树逐行一致;196 个文本文件无 BOM、无新增 CR、无 U+FFFD、无行尾空白;陈旧引用面由主树 25 处降到 6 处(无新增断链)。
229 lines
10 KiB
C
229 lines
10 KiB
C
/******************************************************************************
|
||
* 模块名称:CH395F 链路延迟测试
|
||
* 模块功能:链路分段时延测量的微秒时基(DWT CYCCNT)、采样统计
|
||
* (min/med/p95/max + 尖峰计数)、机器可读 [LAT] 报告输出与判定。
|
||
* Phase 12(硬件层,M1):SPI 字节命令 / DMA 时延基线(SPI_CMD/SPI_DMA,无需 PC);
|
||
* Phase 13/14(NET 层,M2/M3 规划):芯片发送握手 / 队列往返 /
|
||
* 生产路径时延。分段定义、判定基线与执行流程见
|
||
* docs/测试说明/专项验证/链路延迟测试说明.md。
|
||
* 适用平台:STM32F4 系列(CH395F)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-09-22
|
||
* 修改记录:
|
||
* 2026-09-22 王建锋 初版:时基 + 统计/报告 + Phase 12(SPI_CMD/SPI_DMA SPI 时延基线)
|
||
******************************************************************************/
|
||
|
||
#ifndef NET_LATENCY_TEST_H
|
||
#define NET_LATENCY_TEST_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#include <stdint.h>
|
||
|
||
/*
|
||
* === 链路时延测试阶段一览(见 链路延迟测试说明.md §4.2) ===
|
||
* 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 的段(TO_PC/PROD_CYCLE 整帧与重传尖峰)由 PC 端 peer 计时。
|
||
*
|
||
* Phase 13 协议(服务器驱动:MCU 发操作字节,peer 执行;详见说明文档 §4.5):
|
||
* HELLO = 5A A5 58 01(peer → MCU,4 字节)
|
||
* 操作字节(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,混跑会污染帧测量。
|
||
*/
|
||
|
||
/* verdict 三态(lat_stat_report 输出,调用方据此汇总) */
|
||
#define LAT_VERDICT_OK 0
|
||
#define LAT_VERDICT_WARN 1
|
||
#define LAT_VERDICT_FAIL 2
|
||
|
||
/*
|
||
* 采样保存上限:静态数组容量(自研代码禁用 C 库堆,见 AGENTS.md)。
|
||
* n 可超过该值(min/max/spikes 仍对全量累计;med/p95 按已保存样本计算)。
|
||
*/
|
||
#define LAT_MAX_SAMPLES 128
|
||
|
||
/*
|
||
* 采样统计容器。
|
||
* n - 总样本数(可 > LAT_MAX_SAMPLES)
|
||
* mn/mx - 全量 min/max(us)
|
||
* med - 中位数(us,lat_stat_finish 后有效)
|
||
* p95 - 95 分位(us,lat_stat_finish 后有效)
|
||
* spikes - 达到尖峰阈值的样本数(spike_th_us=0 表示本段不统计)
|
||
*/
|
||
typedef struct {
|
||
uint16_t n;
|
||
uint32_t mn;
|
||
uint32_t med;
|
||
uint32_t p95;
|
||
uint32_t mx;
|
||
uint16_t spikes;
|
||
} lat_stats_t;
|
||
|
||
/*
|
||
* 函数功能:使能 DWT CYCCNT 周期计数器时基(幂等)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:任何测量前调用一次即可;仅测试构建路径会执行(产品构建不调用)
|
||
* 函数说明:CoreDebug->DEMCR(TRCENA) + DWT->CYCCNT 清零 + CYCCNTENA 置位
|
||
*/
|
||
void lat_time_init(void);
|
||
|
||
/*
|
||
* 函数功能:重置统计容器(mn 置 0xFFFFFFFF,空集可辨识)
|
||
* 入口参数:p - 统计容器
|
||
*/
|
||
void lat_stat_reset(lat_stats_t *p);
|
||
|
||
/*
|
||
* 函数功能:追加一个时延样本(微秒)
|
||
* 入口参数:p - 统计容器
|
||
* us - 本样本测量时长(微秒)
|
||
* spike_th_us - 尖峰阈值(us);0 表示本段不统计尖峰
|
||
*/
|
||
void lat_stat_add(lat_stats_t *p, uint32_t us, uint32_t spike_th_us);
|
||
|
||
/*
|
||
* 函数功能:采样结束后排序计算 med/p95(内部静态工作区,零堆)
|
||
* 入口参数:p - 统计容器(lat_stat_add 全部追加完成后调用)
|
||
*/
|
||
void lat_stat_finish(lat_stats_t *p);
|
||
|
||
/*
|
||
* 函数功能:输出 [LAT] 报告行并给出本段 verdict
|
||
* 入口参数:p_seg - 段名(纯 ASCII 标识,如 "spi_dma_1024")
|
||
* p - 统计容器(须先 lat_stat_finish)
|
||
* warn_med_us - WARN 阈值:med 超过 → WARN(重锁阈值提示,计 PASS)
|
||
* fail_med_us - FAIL 阈值:med 超过 → FAIL
|
||
* max_spikes - 尖峰数上限:spikes 超过 → FAIL
|
||
* p_verdict - 输出:本段 verdict(LAT_VERDICT_*)
|
||
* 返回值:无
|
||
* 函数说明:报告行格式见 链路延迟测试说明.md §4.4;字符串字面量纯 ASCII
|
||
*/
|
||
void lat_stat_report(const char *p_seg, const lat_stats_t *p,
|
||
uint32_t warn_med_us, uint32_t fail_med_us,
|
||
uint16_t max_spikes, int *p_verdict);
|
||
|
||
/*
|
||
* 函数功能:Phase 12 入口(硬件层:SPI 字节命令 / DMA 时延基线,SPI_CMD/SPI_DMA,无需 PC)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:由 ch395f_test_run() 硬件层分支调用(net_stack_bring_up 完成后
|
||
* testTask 独占 CH395F,无 SPI 并发);仅台架执行
|
||
* 函数说明:内部先做 check_exist 存活自检,失败即判 FAIL 并跳过;
|
||
* 套件未选中本 Phase 时函数体为空(产品构建可编译、不执行)
|
||
*/
|
||
void net_latency_phase12_run(void);
|
||
|
||
/*
|
||
* 函数功能:Phase 13 入口(NET 层:芯片 + 队列时延 CHUNK_WAIT~FRAME_RT/LOGTAX,需 PC peer)
|
||
* 入口参数:sockfd - 测试监听 socket(连接后的数据通道 fd,8080 端口)
|
||
* 返回值:无
|
||
* 限定条件:由 net_layer_test_main() 监听循环每 ~10ms 调用一次(事件驱动
|
||
* 状态机);需 test/host/latency_peer.py --mode net 配合;须独立运行
|
||
* 函数说明:服务器驱动协议(见文件头协议注释);会话完成后 s_done 置位,
|
||
* 后续调用为空操作;套件未选中本 Phase 时函数体为空
|
||
*/
|
||
void net_latency_phase13_run(int32_t sockfd);
|
||
|
||
/*
|
||
* 函数功能:Phase 13 会话是否活动(HELLO 等待/进行中/abort 重连等待)
|
||
* 返回值:1 - 活动(监听循环应跳过 net_accept,由本模块轮询接管)
|
||
* 0 - 非活动(正常完成/终态,恢复常规 accept 等待)
|
||
* 限定条件:Phase 13 未启用时恒返回 0
|
||
*/
|
||
int net_latency_phase13_busy(void);
|
||
|
||
/*
|
||
* 函数功能:Phase 14 入口(NET 层:生产路径时延 PROD_CYCLE~UI_LOOP,真实 uiTask + :7003)
|
||
* 入口参数:无(本 Phase 不占用测试 socket,测量生产 socket 的服务端行为)
|
||
* 返回值:无
|
||
* 限定条件:由 net_layer_test_main() 循环每 ~10ms 调用一次;需
|
||
* 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;
|
||
* 各窗口通过 TEST_LATENCY_PROBE 采集(uiTask/netTask 侧打点);
|
||
* 未定义 TEST_LATENCY_PROBE 时只打印跳过说明;套件未选中时函数体为空
|
||
*/
|
||
void net_latency_phase14_run(void);
|
||
|
||
/*
|
||
* 函数功能:Phase 14 是否活动(活动期监听循环应让位,保证 10ms 轮询节拍)
|
||
* 返回值:1 - 活动;0 - 非活动/未启用
|
||
*/
|
||
int net_latency_phase14_busy(void);
|
||
|
||
/*
|
||
* === TEST_LATENCY_PROBE 探针接口(M2/M3)===
|
||
*
|
||
* 用途:netTask 上下文(net_socket.c 发送分块循环)向 testTask 报告的
|
||
* 段内打点。单写者(netTask)/单读者(testTask),32 位变量原子
|
||
* 读写,无锁。仅在 TEST_LATENCY_PROBE 定义(测试构建)时编译。
|
||
*/
|
||
|
||
/* 当前是否处于采样窗口(netTask 侧高频查询,须极廉价) */
|
||
uint8_t lat_probe_armed(void);
|
||
|
||
/* testTask 侧开一个采样窗口:seg 段名、max_n 样本上限、spike_th_us 尖峰阈值 */
|
||
void lat_probe_arm(const char *seg, uint32_t max_n, uint32_t spike_th_us);
|
||
|
||
/* netTask 侧追加一个样本(us);未武装或已满 → 无操作 */
|
||
void lat_probe_add(uint32_t us);
|
||
|
||
/*
|
||
* 函数功能:**按段名**追加样本(多打点站点共存时唯一安全的写法)
|
||
* 入口参数:seg - 本打点站点所属段名(须与 lat_probe_arm 的 seg 完全一致)
|
||
* us - 样本值(us)
|
||
* 返回值:无
|
||
* 函数说明:探针是**全局单窗口**:窗口武装成 A 段时,B 段站点的 lat_probe_add
|
||
* 会把 B 的样本灌进 A 的窗口(2026-09-22 run17 实证:Phase 14 三段
|
||
* 全被 uiTask 循环样本填满,prod/relisten 都读出 ~11ms 的 ui_loop 值)。
|
||
* 只有段名匹配才落样本 → 各站点互不污染。多站点构建**一律**用本接口。
|
||
*/
|
||
void lat_probe_add_seg(const char *seg, uint32_t us);
|
||
|
||
/* 已采集样本数(32 位原子读) */
|
||
uint32_t lat_probe_count(void);
|
||
|
||
/* 关闭采样窗口(此后的 lat_probe_add 无操作;在飞样本最多超 1 个) */
|
||
void lat_probe_disarm(void);
|
||
|
||
/* testTask 侧取窗口统计(disarm 后调用;内部 lat_stat_finish 已含 med/p95) */
|
||
const lat_stats_t *lat_probe_stats(void);
|
||
|
||
/*
|
||
* 函数功能:DWT CYCCNT 读数 / 差值换算(供 net_socket.c 探针使用)
|
||
* 入口参数:c_start - 起始读数(lat_cycles_now 返回值)
|
||
* 返回值:当前读数(us 计数域)/ 自 c_start 起的微秒数(<25.17ms 有效)
|
||
*/
|
||
uint32_t lat_cycles_now(void);
|
||
uint32_t lat_cycles_diff_us(uint32_t c_start);
|
||
|
||
/* ---- M3 探针计时标记(可跨 >25ms 区间的安全计时) ----
|
||
* DWT CYCCNT 为 32 位 @168MHz → 单次差值超过 **25.17ms 即回绕**,会把 30ms 读成
|
||
* 4.8ms 这类"静默错误小值"(M1 各段均 <25ms 时无此问题,M3 的压力档/重监听会超)。
|
||
* lat_mark/lat_mark_us 同时记 DWT 与 HAL tick,跨度 ≥20ms 时自动改用 tick 兜底
|
||
* (分辨率降为 1ms,量级正确);<20ms 用 DWT(亚微秒精度)。 */
|
||
typedef struct {
|
||
uint32_t cyc; /* DWT->CYCCNT 起始值 */
|
||
uint32_t tick; /* HAL_GetTick() 起始值 */
|
||
} lat_tmark_t;
|
||
|
||
void lat_mark(lat_tmark_t *p);
|
||
uint32_t lat_mark_us(const lat_tmark_t *p);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* NET_LATENCY_TEST_H */
|