Files
STM32F4-Base/test/board/ch395f_reuse_bench.c
wandering1 b7c0889cc7 refactor(编号·批次二): 代码与测试侧 215 处保护功能号改成功能名,零行为改动
用户裁定「不要用字母加数字的编号,语义标识是最好的」后的批次二代码半(道 ui-param 执行,逐文件站点数与
自证见其交付)。唯一口径 = 映射表 ci-artifacts/lprot_map.tsv 的功能名列原文,**零自造同义词**;L2.5 与
L-TEST 经实测各 0 处(不是本族),TC 族一字未动。

固件 22 文件 128 处(adc_task.c 21、phasor.c 22、phasor.h 13…),主机测试 13 文件 87 处(pha
sor_test.c
57…);设计文档指针新增 19 行,另 16 只文件本来就写着规划文档 ⇒ 不重复加。

**两条只有真改过 215 处才会踩到的坑,都写进注释规范实践**:
· 指针**不能接在原句尾部**——那会替原文一个未闭合的半括号封口,实测咬坏 App/algo/sample_ring.h 这类
  跨行句 ⇒ 指针一律**单独成行**;
· 号删掉会留下**叠词**(「自检失败闭锁 闭锁判定」「定值校验和 定值巡检」)⇒ 复扫并收口 4 处,措辞只用
  映射名本身;具体对象需要留住时写成「自检失败闭锁(采样故障闭锁)」。

自证(不靠嘴说):改前把 35 只文件原文备份到仓外 temp,逐文件比 `/*`、`*/` 计数、三类括号计数、行数增量
恰等于指针行数、全角括号增删相等、残留号 0 ⇒ **35/35 通过**;Lead 复核后跑权威 @build **0 错 0 警**,
Program Size `Code=233900 RO=26104 RW=1536 ZI=136808` —— **与三条道各自改前改后测的四数逐
字节同数**,
这就是「只动注释与文本」的机器证据;八套主机单测 exit 0,cppcheck 按钩子参数零发现,coding-standard 0。

文件头「修改记录」里的号**一并换名、不还原**(Lead 裁定):这些号是全仓退役的,留一个查无此号的引用比
留一条改过名的条目更坏——「冻结历史不回改」保护的是**别替历史改口**,不是保护一个再也查不到的字符串;
id-check 对变更记录行本就豁免,两种选择都不影响门禁。
2026-10-27 08:59:26 +08:00

849 lines
34 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file ch395f_reuse_bench.c
* @brief relisten 沉降段原语化受阻的芯片级 Socket 复用时序台架(裸机,内核前运行,OS/net/FTP 全剥离)
* @details 直接调用 ch395f_* 命令,循环执行受控时序矩阵(延迟/关闭次序/载荷/Socket 号),
* 每步打印芯片状态与中断寄存器,定位"关→开→监听→连接"复用时序中
* 芯片行为的确切规律。配套主机脚本 test/host/ch395f_reuse_host.py。
* 定案结论与实验矩阵演进 → CH395F_Trap_Records.md TRAP-NET-20。
*
* 存档状态:本文件在主分支**不注册编译**(未加入 uvprojx),仅作实验工具存档;
* 运行方法:在实验分支(如 n30-chip-bench)把本 .c 注册进 uvprojx Test 组、
* Src/main.c 的 USER CODE 2 区在 app_main_init() 之前调 ch395f_reuse_bench()
* (内核前裸机循环,永不返回,断电退出),烧录后跑主机脚本对照芯片日志。
* 早先那轮定案矩阵(对照 vs "CONNECT 事件未收到捕获自 accept"修复组)保留在 s_vars[]/
* bench_round(),由 s_bench_legacy=0 停泊不运行。
*
* 驱动原语前置实验:同一固件合并跑 E2/E3/E4——
* E2 = GET_SOCKET_STATUS 单次 SPI 成本(DWT 周期计数)+ TCP_LISTEN 可见延迟矩阵;
* E3 = GET_CMD_STATUS 判别力矩阵(OPEN/LISTEN/CLOSE/SET_SOUR_PORT/SET_PROTO_TYPE,
* 含"命令未发时"对照读,复核 Trap 记录的 SET 类无判别力 + 陈旧 BUSY 残留);
* E4 = sock_reset 内部顺序 A/B/C 变体 ×10 轮(轮间换端口)+ 立即复连载荷完整性自证
* (配套 test/host/ch395f_reuse_host.py --mode e4)。
* 判据与结果锚点 → docs/网络/网络自愈架构设计.md §5.2 E2/E3/E4(过程全文已归档,见该文档 §12)。
*
* close_mode:0=主机先关(芯片等 DISCONNECT);1=芯片先关;2=无数据连上即关
* verify:0=无修复;1=listen 后确认 LISTEN 重试;2=缓冲重分配;
* 3=幽灵耐心等;4=等 CONNECT 期轮询状态+自 accept(定案修复)
*/
#include "ch395f_reuse_bench.h"
#if N30_CHIP_BENCH
#include "ch395f.h"
#include "dbg_log.h"
#include "main.h"
#include "stm32f4xx_hal.h"
/* 变体表:{sock, close 后复开延迟 ms, TX 字节数, close_mode, verify_listen} */
typedef struct {
uint8_t sock;
uint16_t delay_ms;
uint16_t tx_len;
uint8_t close_mode;
uint8_t verify; /* 1 = listen 后确认 TCP==0x01,否则 close 重试(修复假设) */
} bench_var_t;
static const bench_var_t s_vars[] = {
{ 3, 0, 100, 0, 0 }, /* 0ms 对照 */
{ 3, 300, 100, 0, 0 }, /* 300ms 对照 */
{ 3, 800, 2000, 0, 0 }, /* 2KB 对照 */
{ 3, 0, 100, 0, 4 }, /* 0ms + 事件未收捕获自accept */
{ 3, 300, 100, 0, 4 }, /* 300ms + 事件未收捕获 */
{ 3, 800, 2000, 0, 4 }, /* 2KB + 事件未收捕获 */
};
#define BENCH_N_VARS (sizeof(s_vars) / sizeof(s_vars[0]))
#define BENCH_PORT(i) (5000U + (uint16_t)(i))
/* TX 图案缓冲(静态,SRAM;2KB = 最大变体载荷) */
static uint8_t s_tx_pat[2048];
/* RX 排空缓冲 */
static uint8_t s_rx_buf[2048];
/*
* 函数功能:轮询本 Socket 中断(读全局→读本 Socket,读即清)
* 入口参数:sock - Socket 号 uint8_t
* timeout - 超时 ms uint32_t
* out_bits - 输出最后一次读到的 SINT 位 uint8_t *
* 返回值:1=拿到中断 0=超时 int
*/
static int bench_wait_sock_int(uint8_t sock, uint32_t timeout, uint8_t *out_bits) {
uint32_t t0 = HAL_GetTick();
while ((uint32_t)(HAL_GetTick() - t0) < timeout) {
/* 直读本 Socket SINT(读即清)。不用全局寄存器门控:它读即清,未配对
* 读取会吞事件;且门控观察分不清"芯片未锁存"与"全局位被读清"
* (TRAP-NET-20 观察混淆源)。唯一读者 = net_poll(唯一读者红线(docs/网络/网络自愈架构设计.md §7 I7))。 */
uint8_t si = ch395f_get_sock_int_status(sock);
if (si != 0U) {
if (out_bits != 0) {
*out_bits = si;
}
return 1;
}
HAL_Delay(10);
}
return 0;
}
/* 打印芯片两字节状态(Socket 态 / TCP 态) */
static void bench_log_state(const char *tag, uint8_t sock) {
uint8_t st[2] = { 0, 0 };
ch395f_get_socket_status(sock, st);
DBG_INFO("BENCH %s sock%u st=0x%02X/0x%02X t=%u", tag, (unsigned)sock,
st[0], st[1], (unsigned)HAL_GetTick());
}
/* 芯片级初始化(复刻 net_init 序列,剥离全部内核 API) */
static int bench_chip_init(void) {
if (ch395f_check_exist() != CH395F_STATUS_OK) {
DBG_ERROR("BENCH chip not found");
return -1;
}
ch395f_reset();
/* 等 PHY link(TRAP-NET-09:link up 前 open 必失败;B9 修正:判据是 != DISCONN,
* 旧写法 !=0 对 01H=DISCONN 也成立) */
for (int i = 0; i < 100; i++) {
if (ch395f_get_phy_status() != CH395F_PHY_DISCONN) {
break;
}
HAL_Delay(100);
}
/* bit3=1 SOCKET_CLOSE;bit0 保留必须为 0(§5.52)→ 与生产 net_init 同构 */
ch395f_set_fun_para(0x08);
ch395f_set_tcp_mss(CH395F_TCP_MSS);
ch395f_set_retrans_count(3);
ch395f_set_retrans_period(200);
ch395f_set_keepalive_idle(60000);
ch395f_set_keepalive_intvl(5000);
ch395f_set_keepalive_cnt(3);
/* 静态 IP(与生产一致) */
{
uint8_t ip[4] = { 192, 168, 1, 100 };
uint8_t gw[4] = { 192, 168, 1, 1 };
uint8_t msk[4] = { 255, 255, 255, 0 };
ch395f_set_ip_addr(ip);
ch395f_set_gwip_addr(gw);
ch395f_set_mask_addr(msk);
}
if (ch395f_init() != CH395F_STATUS_OK) {
DBG_ERROR("BENCH ch395f_init failed");
return -1;
}
/* 8 Socket 缓冲布局(与生产一致) */
for (int i = 7; i >= 0; i--) {
uint8_t recv_start = (uint8_t)(i * 6);
ch395f_set_send_buf((uint8_t)i, (uint8_t)(recv_start + 4), 2);
ch395f_set_recv_buf((uint8_t)i, recv_start, 4);
}
ch395f_ping_enable(1);
return 0;
}
/* 单轮实验 */
static void bench_round(unsigned idx) {
const bench_var_t *v = &s_vars[idx];
uint8_t sock = v->sock;
uint16_t port = BENCH_PORT(idx);
uint8_t si = 0;
/* 幽灵等待期已见 CONNECT 的标志(跳过后续 CONNECT 等待) */
int pre_connected = 0;
DBG_INFO("BENCH === r%u sock%u port%u delay%u tx%u mode%u t=%u",
idx, (unsigned)sock, (unsigned)port, (unsigned)v->delay_ms,
(unsigned)v->tx_len, (unsigned)v->close_mode, (unsigned)HAL_GetTick());
/* 1. open + listen(TRAP-NET-09:close 后需延时重开,失败重试 ×3) */
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(sock, port);
uint8_t ost = CH395F_ERR_SUCCESS;
for (int open_try = 0; open_try < 3; open_try++) {
ost = ch395f_open_socket(sock);
if (ost == CH395F_ERR_SUCCESS) {
break;
}
DBG_INFO("BENCH open retry%d err=0x%02X t=%u", open_try, ost, (unsigned)HAL_GetTick());
ch395f_close_socket(sock);
HAL_Delay(500);
}
if (ost != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH open fail 0x%02X", ost);
return;
}
/* 排事件在 LISTEN **之前**(对齐生产 listen 路径 E14 改法:post-listen 排事件
* 会吞掉快速到达的 CONNECT——上一版矩阵 3/48 被本探针现场抓获,现已根除该
* 自伤形态;本探针保留,drain 若仍见 CONNECT 即回归信号)。
* 逐 Socket SINT 读(读即清,不读全局——唯一读者 = net_poll,唯一读者红线) */
for (int j = 0; j < 8; j++) {
uint8_t dj = ch395f_get_sock_int_status((uint8_t)j);
if (dj != 0U) {
DBG_INFO("BENCH drain-int sock%u=0x%02X t=%u", (unsigned)j,
dj, (unsigned)HAL_GetTick());
}
}
uint8_t lst = ch395f_tcp_listen(sock);
if (lst != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH listen fail 0x%02X", lst);
ch395f_close_socket(sock);
return;
}
/* 缓冲重分配变体:listen 后重设收发缓冲(隔离"缓冲管理残留"假设) */
if (v->verify == 2U) {
uint8_t recv_start = (uint8_t)(sock * 6);
ch395f_clear_recv_buf(sock);
ch395f_set_send_buf(sock, (uint8_t)(recv_start + 4), 2);
ch395f_set_recv_buf(sock, recv_start, 4);
DBG_INFO("BENCH realloc done sock%u t=%u", (unsigned)sock, (unsigned)HAL_GetTick());
}
bench_log_state("post-listen", sock);
/* 修复假设验证:listen 后 TCP 态必须真正 == 0x01(LISTEN);
* mode1:close 快速重试;mode3:耐心等幽灵消退(5s,每 100ms 复查),
* 期间幽灵→LISTEN 转变会补发 CONNECT 中断则直接放行 */
if (v->verify == 1U || v->verify == 3U) {
uint32_t budget = (v->verify == 3U) ? 5000U : 1000U;
int att_max = (v->verify == 3U) ? 2 : 4;
for (int att = 0; att < att_max; att++) {
uint8_t st[2] = { 0, 0 };
uint32_t tw = HAL_GetTick();
int connect_fired = 0;
/* 等 TCP 进 LISTEN(幽灵消退)或 CONNECT 中断补发(直读本 Socket SINT) */
while ((uint32_t)(HAL_GetTick() - tw) < budget) {
uint8_t si = ch395f_get_sock_int_status(sock);
if ((si & CH395F_SINT_STAT_CONNECT) != 0U) {
connect_fired = 1;
DBG_INFO("BENCH verify CONNECT during ghost-wait att%d t=%u",
att, (unsigned)HAL_GetTick());
break;
}
ch395f_get_socket_status(sock, st);
if (st[1] == 0x01U) {
break;
}
HAL_Delay(100);
}
DBG_INFO("BENCH verify att%d st=0x%02X/0x%02X cf=%d t=%u",
att, st[0], st[1], connect_fired, (unsigned)HAL_GetTick());
if (connect_fired) {
/* 幽灵期主机已连上:跳过 CONNECT 等待直接进流程 */
pre_connected = 1;
break;
}
if (st[1] == 0x01U) {
break;
}
/* 幽灵未消:close → 50ms → 重新 open+listen(mode3 只试一次) */
ch395f_close_socket(sock);
HAL_Delay(50);
ch395f_open_socket(sock);
ch395f_tcp_listen(sock);
for (int j = 0; j < 8; j++) {
(void)ch395f_get_sock_int_status((uint8_t)j);
}
}
}
/* 2. 等 CONNECT(10s;幽灵等待期已见 CONNECT 则跳过)
* v6 关键探针:等待期每 500ms 读芯片状态;若已 ESTABLISHED(0x04)
* 说明握手完成但 CONNECT 事件未收到 → 补做 accept(对账②模拟)。
* E14 复核(2026-10-14,48 轮):事件从未被芯片丢失——"未收到"全部
* 是 drain 读清消费(见 drain-int)或竞态(见 CONNECT-in-SINT) */
int got = pre_connected;
uint32_t t0 = HAL_GetTick();
while (!got && (uint32_t)(HAL_GetTick() - t0) < 10000U) {
if (bench_wait_sock_int(sock, 100, &si)) {
if ((si & CH395F_SINT_STAT_CONNECT) != 0U) {
got = 1;
break;
}
DBG_INFO("BENCH unexpected int=0x%02X while wait", si);
}
if (v->verify == 4U) {
uint8_t st[2] = { 0, 0 };
ch395f_get_socket_status(sock, st);
if ((uint32_t)(HAL_GetTick() - t0) % 1000U < 110U) {
DBG_INFO("BENCH wait-state st=0x%02X/0x%02X t=%u",
st[0], st[1], (unsigned)HAL_GetTick());
}
if (st[1] == 0x04U) {
/* 丢中断复核实验:宣布"未收到"之前补读 SINT ×3(10ms 间隔)——
* 若 CONNECT 在锁存,这是竞态不是丢失 */
int sint_seen = 0;
for (int rr = 0; rr < 3; rr++) {
if ((ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_CONNECT) != 0U) {
sint_seen = 1;
break;
}
HAL_Delay(10);
}
if (sint_seen != 0) {
DBG_INFO("BENCH CONNECT-in-SINT at state-04 (race, NOT lost) t=%u",
(unsigned)HAL_GetTick());
} else {
/* E14 定案:本形态若上方有 drain-int 0x08,事件是被 drain 消费
* (非芯片丢失);无 drain-int 才是真·未锁存(48 轮矩阵未出现) */
DBG_INFO("BENCH NO-CONNECT-EVENT at state-04, SINT empty x3 (check drain-int above) t=%u",
(unsigned)HAL_GetTick());
}
got = 1;
break;
}
}
}
if (!got) {
/* 丢中断复核实验:对照变体 10s 内 SINT 每 10ms 读一次从未见 CONNECT——
* 此刻读状态+对端端口归类:0x04=芯片未锁存(实锤);0x01=主机未连(同步问题) */
uint8_t st[2] = { 0, 0 };
uint8_t rem[6] = { 0 };
ch395f_get_socket_status(sock, st);
ch395f_get_remot_ipp(sock, rem);
DBG_ERROR("BENCH no CONNECT within 10s: st=0x%02X/0x%02X rport=%u t=%u",
st[0], st[1], (unsigned)(rem[4] | (rem[5] << 8)),
(unsigned)HAL_GetTick());
ch395f_close_socket(sock);
HAL_Delay(v->delay_ms);
return;
}
bench_log_state("post-accept", sock);
/* 对端信息 + RX 残渣检查(旧连接残留的直接证据) */
{
uint8_t rem[6] = { 0 };
ch395f_get_remot_ipp(sock, rem);
uint16_t rl = ch395f_get_recv_len(sock);
DBG_INFO("BENCH remote port=%u recv_len=%u",
(unsigned)(rem[4] | (rem[5] << 8)), (unsigned)rl);
if (rl > 0U) {
uint16_t n = (rl > 2048U) ? 2048U : rl;
ch395f_read_recv_buf(sock, s_rx_buf, n);
DBG_INFO("BENCH drained %u residue bytes", (unsigned)n);
}
}
/* 3. TX 探针(1KB 分块,TRAP-NET-16) */
if (v->tx_len > 0U) {
uint16_t sent = 0;
while (sent < v->tx_len) {
uint16_t chunk = (uint16_t)(v->tx_len - sent);
if (chunk > 1024U) {
chunk = 1024U;
}
ch395f_write_send_buf(sock, &s_tx_pat[sent], chunk);
sent += chunk;
/* 等 SENBUF_FREE(500ms) */
if (sent < v->tx_len) {
uint32_t tw = HAL_GetTick();
while ((uint32_t)(HAL_GetTick() - tw) < 500U) {
if (bench_wait_sock_int(sock, 50, &si)) {
if ((si & CH395F_SINT_STAT_SENBUF_FREE) != 0U) {
break;
}
}
}
}
}
DBG_INFO("BENCH tx %u written t=%u", (unsigned)v->tx_len, (unsigned)HAL_GetTick());
/* 连接存活观察窗:500ms 内每 50ms 记状态+中断(抓"秒杀";直读 SINT) */
for (int k = 0; k < 10; k++) {
si = ch395f_get_sock_int_status(sock);
if (si != 0U) {
DBG_INFO("BENCH int during window=0x%02X t=%u", si, (unsigned)HAL_GetTick());
}
HAL_Delay(50);
bench_log_state("window", sock);
}
}
/* 4. 关闭次序 */
if (v->close_mode == 1U) {
/* 芯片先关 */
HAL_Delay(300);
ch395f_close_socket(sock);
DBG_INFO("BENCH chip closed first t=%u", (unsigned)HAL_GetTick());
} else {
/* 等主机关(DISCONNECT 事件,5s) */
uint32_t tw = HAL_GetTick();
int disc = 0;
while ((uint32_t)(HAL_GetTick() - tw) < 5000U) {
if (bench_wait_sock_int(sock, 100, &si)) {
if ((si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) != 0U) {
disc = 1;
break;
}
}
}
DBG_INFO("BENCH host-close seen=%d t=%u", disc, (unsigned)HAL_GetTick());
ch395f_close_socket(sock);
}
/* 5. 关闭后拆链观察:1s 内每 50ms 记状态 + 中断(TRAP-NET-12 滞后中断取证;直读 SINT) */
for (int k = 0; k < 20; k++) {
uint8_t late = ch395f_get_sock_int_status(sock);
if (late != 0U) {
DBG_INFO("BENCH late-int=0x%02X t=%u", late, (unsigned)HAL_GetTick());
}
if ((k % 4) == 0) {
bench_log_state("post-close", sock);
}
HAL_Delay(50);
}
/* 6. 受控延迟后进入下一轮 */
HAL_Delay(v->delay_ms);
}
/* =====================================================================
* 驱动原语前置实验区:E2 / E3 / E4 一次固件合并跑
* ===================================================================== */
/* 早先那轮定案矩阵的停泊开关:0 = 不运行(保留代码备查,勿删) */
static const int s_bench_legacy = 0;
/* --- DWT 周期计数器:168 MHz,1 cycle ~= 5.95 ns(不依赖 HAL 时基/中断) --- */
static void bench_dwt_enable(void) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0U;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
static uint32_t bench_cyc(void) {
return DWT->CYCCNT;
}
/* cycles -> us(1 位小数,纯整数运算:MicroLIB 不支持 %f) */
static void bench_log_cyc(const char *tag, uint32_t cyc) {
DBG_INFO("BENCH %s=%lu.%luus (%lucyc)", tag,
(unsigned long)(cyc / 168U),
(unsigned long)(((cyc % 168U) * 10U) / 168U),
(unsigned long)cyc);
}
/* --- 裸命令下发:绕过驱动 poll_cmd_status(E2/E3 要观察命令自身状态轨迹)。
* CS 包裹与驱动内部 ch395f_spi_begin/end 完全同构(该二函数为 static 未导出) --- */
static void bench_spi_begin(void) {
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_RESET);
}
static void bench_spi_end(void) {
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
}
static void bench_raw_cmd(uint8_t cmd, uint8_t sock, const uint8_t *args, uint8_t nargs) {
bench_spi_begin();
(void)ch395f_write_cmd(cmd);
(void)ch395f_write_data(sock);
for (uint8_t i = 0U; i < nargs; i++) {
(void)ch395f_write_data(args[i]);
}
bench_spi_end();
}
/* 等 Socket byte0 回 00H(SOCKET_CLOSED);返回等待耗时 ms,-1=超时 */
static int bench_wait_closed(uint8_t sock, uint32_t timeout_ms) {
uint32_t t0 = HAL_GetTick();
uint8_t st[2] = { 0, 0 };
for (;;) {
ch395f_get_socket_status(sock, st);
if (st[0] == CH395F_SOCKET_CLOSED) {
return (int)(HAL_GetTick() - t0);
}
if ((uint32_t)(HAL_GetTick() - t0) >= timeout_ms) {
return -1;
}
HAL_Delay(5);
}
}
/* 排空本 Socket 事件:直读 SINT(读即清),连续 2 轮(20ms 节奏)无新位即停;
* 返回排空期间见过的 SINT 位 OR(E8 M5 drain-until-quiescent 的 bench 版)。
* 不读全局寄存器——读即清,唯一读者 = net_poll(唯一读者红线(docs/网络/网络自愈架构设计.md §7 I7)) */
static uint8_t bench_drain_events(uint8_t sock, uint32_t max_ms) {
uint32_t t0 = HAL_GetTick();
uint8_t seen = 0U;
int quiet = 0;
while ((uint32_t)(HAL_GetTick() - t0) < max_ms) {
uint8_t si = ch395f_get_sock_int_status(sock);
if (si != 0U) {
seen |= si;
quiet = 0;
} else if (++quiet >= 2) {
break;
}
HAL_Delay(20);
}
return seen;
}
/* 读空 RX(GET_RECV_LEN -> read_recv_buf 直到长度 0),返回总字节数 */
static uint32_t bench_drain_rx(uint8_t sock) {
uint32_t total = 0U;
for (;;) {
uint16_t rl = ch395f_get_recv_len(sock);
if (rl == 0U) {
break;
}
uint16_t n = (rl > 2048U) ? 2048U : rl;
ch395f_read_recv_buf(sock, s_rx_buf, n);
total += n;
if (total > 65535U) {
break;
}
}
return total;
}
/* 让命令落定:>=2ms 节奏轮询 GET_CMD_STATUS 直到非 BUSY;返回终值 */
static uint8_t bench_cmd_settle(uint32_t max_ms) {
uint32_t t0 = HAL_GetTick();
uint8_t s;
do {
HAL_Delay(2);
s = ch395f_get_cmd_status();
} while (s == CH395F_ERR_BUSY && (uint32_t)(HAL_GetTick() - t0) < max_ms);
return s;
}
/* ------------------------- E2:状态回读成本 + LISTEN 可见延迟 ------------------------- */
static void bench_e2(void) {
static const uint16_t grid_ms[4] = { 0U, 10U, 50U, 100U };
uint8_t st[2] = { 0, 0 };
uint32_t min = 0xFFFFFFFFUL, max = 0U, sum = 0U;
DBG_INFO("BENCH E2 ===== GET_SOCKET_STATUS cost + LISTEN visibility =====");
/* 1) 单次回读成本 N=1000(socket 空闲态,与生产 net_poll 刷新同形) */
for (uint32_t i = 0U; i < 1000U; i++) {
uint32_t c0 = bench_cyc();
ch395f_get_socket_status(3U, st);
uint32_t d = bench_cyc() - c0;
if (d < min) {
min = d;
}
if (d > max) {
max = d;
}
sum += d;
}
bench_log_cyc("E2 gs_min", min);
bench_log_cyc("E2 gs_avg", sum / 1000U);
bench_log_cyc("E2 gs_max", max);
/* 2) LISTEN 可见性矩阵:裸 TCP_LISTEN(不 poll 命令态)后 +0/10/50/100ms 回读;
* 100ms 仍不可见则 10ms 节奏续查到 1000ms,记录首次观察到 TCP=01H 的延迟 */
for (unsigned r = 0U; r < 12U; r++) {
uint16_t port = (uint16_t)(5200U + r);
int grid[4] = { 0, 0, 0, 0 };
int first_ms = -1;
(void)ch395f_close_socket(3U);
(void)bench_wait_closed(3U, 2000U);
ch395f_set_proto_type(3U, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(3U, port);
if (ch395f_open_socket(3U) != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH E2 v2 r%u open fail", r);
continue;
}
uint32_t t0 = HAL_GetTick();
bench_raw_cmd(CH395F_CMD_TCP_LISTEN_SN, 3U, 0, 0U);
for (int g = 0; g < 4; g++) {
while ((uint32_t)(HAL_GetTick() - t0) < grid_ms[g]) { }
ch395f_get_socket_status(3U, st);
if (st[1] == CH395F_TCP_LISTEN) {
grid[g] = 1;
if (first_ms < 0) {
first_ms = (int)(HAL_GetTick() - t0);
}
}
}
while (first_ms < 0 && (uint32_t)(HAL_GetTick() - t0) < 1000U) {
HAL_Delay(10);
ch395f_get_socket_status(3U, st);
if (st[1] == CH395F_TCP_LISTEN) {
first_ms = (int)(HAL_GetTick() - t0);
}
}
DBG_INFO("BENCH E2 listen r%u port%u grid=%d%d%d%d first=%dms st=0x%02X/0x%02X",
r, (unsigned)port, grid[0], grid[1], grid[2], grid[3],
first_ms, st[0], st[1]);
(void)bench_cmd_settle(2000U);
(void)ch395f_close_socket(3U);
(void)bench_wait_closed(3U, 2000U);
}
}
/* ------------------------- E3:GET_CMD_STATUS 判别力矩阵 ------------------------- */
static void bench_e3_one(const char *name, uint8_t cmd, uint8_t sock,
uint16_t port0, int pre_open, int pre_listen,
const uint8_t *args, uint8_t nargs) {
for (int r = 0; r < 5; r++) {
uint16_t port = (uint16_t)(port0 + (uint16_t)r);
uint8_t largs[2];
const uint8_t *pa = args;
uint8_t n = nargs;
(void)ch395f_close_socket(sock);
(void)bench_wait_closed(sock, 2000U);
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(sock, port);
if (pre_open) {
if (ch395f_open_socket(sock) != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH E3 %s r%d pre-open fail", name, r);
continue;
}
if (pre_listen) {
if (ch395f_tcp_listen(sock) != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH E3 %s r%d pre-listen fail", name, r);
continue;
}
}
}
/* 对照读:命令未发时的残留状态(Trap 记录:陈旧 BUSY 残留即在此现形) */
uint8_t pre = ch395f_get_cmd_status();
if (cmd == CH395F_CMD_SET_SOUR_PORT_SN) {
largs[0] = (uint8_t)(port & 0xFFU);
largs[1] = (uint8_t)(port >> 8);
pa = largs;
n = 2U;
}
uint32_t t0 = HAL_GetTick();
bench_raw_cmd(cmd, sock, pa, n);
int busy_seen = 0, polls = 0, done_ms = -1;
uint8_t firstv = 0xFFU;
HAL_Delay(2); /* 手册 §9.2.1:发命令后 >=2ms 才发首个 GET_CMD_STATUS */
while ((uint32_t)(HAL_GetTick() - t0) < 2000U) {
uint8_t s = ch395f_get_cmd_status();
polls++;
if (s == CH395F_ERR_BUSY) {
busy_seen = 1;
HAL_Delay(1);
continue;
}
firstv = s;
done_ms = (int)(HAL_GetTick() - t0);
break;
}
DBG_INFO("BENCH E3 %s r%d pre=0x%02X busy=%d first=0x%02X done=%dms polls=%d",
name, r, pre, busy_seen, firstv, done_ms, polls);
(void)bench_cmd_settle(1000U);
(void)ch395f_close_socket(sock);
(void)bench_wait_closed(sock, 2000U);
}
}
static void bench_e3(void) {
static const uint8_t proto_tcp = CH395F_PROTO_TYPE_TCP;
DBG_INFO("BENCH E3 ===== GET_CMD_STATUS discriminative matrix =====");
bench_e3_one("OPEN", CH395F_CMD_OPEN_SOCKET_SN, 3U, 5300U, 0, 0, 0, 0U);
bench_e3_one("LISTEN", CH395F_CMD_TCP_LISTEN_SN, 3U, 5310U, 1, 0, 0, 0U);
bench_e3_one("CLOSE", CH395F_CMD_CLOSE_SOCKET_SN, 3U, 5320U, 1, 1, 0, 0U);
bench_e3_one("SET_SOUR_PORT", CH395F_CMD_SET_SOUR_PORT_SN, 3U, 5330U, 0, 0, 0, 0U);
bench_e3_one("SET_PROTO_TYPE", CH395F_CMD_SET_PROTO_TYPE_SN, 3U, 5340U, 0, 0, &proto_tcp, 1U);
}
/* ------------------------- E4:sock_reset 内部顺序 A/B/C -------------------------
* A:读空 RX -> CLOSE -> 排空本 socket 事件 -> 轮询等 byte0=00H
* B:CLEAR_RECV_BUF -> CLOSE -> 排空事件 -> 等 CLOSED
* C:什么都不做 -> CLOSE -> 等 CLOSED
* 每轮先"污染会话"(主机连上发 3000B 后 RST,TRAP-NET-18 形态),复位后立即
* open+listen,主机立即上传 4096B 图案载荷;芯片侧按图案自证完整性
* (PASS 口径 = 消费者在 DISCONNECT/TIM_OUT 前实际读到的字节数与图案)。 */
#define E4_SOCK 3U
#define E4_BASE_PORT 5100U
#define E4_ROUNDS 10U
#define E4_PAYLOAD 4096U
static uint8_t s_e4_acc[8192];
static uint8_t e4_seed(unsigned rg) {
return (uint8_t)(rg * 97U + 13U);
}
/* 污染阶段收尾:等芯片 DISCONNECT/TIM_OUT(RST 中止的死亡事件),1=见到 */
static int bench_e4_wait_death(uint8_t sock, uint32_t timeout_ms, uint8_t *out_bits) {
uint32_t t0 = HAL_GetTick();
while ((uint32_t)(HAL_GetTick() - t0) < timeout_ms) {
uint8_t si = ch395f_get_sock_int_status(sock);
if ((si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) != 0U) {
if (out_bits != 0) {
*out_bits = si;
}
return 1;
}
HAL_Delay(10);
}
return 0;
}
static void bench_e4_round(unsigned rg, uint8_t variant) {
uint16_t port = (uint16_t)(E4_BASE_PORT + rg);
uint8_t si = 0U;
uint32_t rx_drained = 0U;
int closed_ms;
DBG_INFO("BENCH E4 rg%u var=%c port%u t=%u", rg, (char)('A' + variant),
(unsigned)port, (unsigned)HAL_GetTick());
/* 轮前统一清洁(所有变体同起点) */
(void)ch395f_close_socket(E4_SOCK);
(void)bench_wait_closed(E4_SOCK, 2000U);
(void)bench_drain_events(E4_SOCK, 300U);
ch395f_set_proto_type(E4_SOCK, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(E4_SOCK, port);
if (ch395f_open_socket(E4_SOCK) != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH E4 rg%u open fail", rg);
return;
}
if (ch395f_tcp_listen(E4_SOCK) != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH E4 rg%u listen fail", rg);
(void)ch395f_close_socket(E4_SOCK);
return;
}
/* 污染会话:主机 connect -> 发 3000B -> RST;芯片侧只等死亡事件(不读 RX,
* 残渣留给变体处置——A 的排空 / B 的 CLEAR / C 的什么都不做 正是被测对象) */
if (!bench_e4_wait_death(E4_SOCK, 15000U, &si)) {
DBG_ERROR("BENCH E4 rg%u contam: no death event (host not synced?)", rg);
(void)ch395f_close_socket(E4_SOCK);
(void)bench_wait_closed(E4_SOCK, 2000U);
HAL_Delay(500);
return;
}
/* 被测复位序列 */
if (variant == 0U) {
rx_drained = bench_drain_rx(E4_SOCK);
} else if (variant == 1U) {
ch395f_clear_recv_buf(E4_SOCK);
}
(void)ch395f_close_socket(E4_SOCK);
if (variant != 2U) {
(void)bench_drain_events(E4_SOCK, 500U);
}
closed_ms = bench_wait_closed(E4_SOCK, 2000U);
DBG_INFO("BENCH E4 rg%u var=%c reset done rxdrain=%lu closed_ms=%d t=%u",
rg, (char)('A' + variant), (unsigned long)rx_drained, closed_ms,
(unsigned)HAL_GetTick());
/* 立即重开 + LISTEN(主机立即上传全载荷) */
ch395f_set_sour_port(E4_SOCK, port);
if (ch395f_open_socket(E4_SOCK) != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH E4 rg%u reopen fail", rg);
return;
}
if (ch395f_tcp_listen(E4_SOCK) != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH E4 rg%u relisten fail", rg);
(void)ch395f_close_socket(E4_SOCK);
return;
}
/* 等载荷连接 CONNECT(C 变体的陈旧 DISCONNECT 若在此同读出现 = 新连接被杀,如实计 FAIL) */
if (!bench_wait_sock_int(E4_SOCK, 15000U, &si) || ((si & CH395F_SINT_STAT_CONNECT) == 0U)) {
DBG_ERROR("BENCH E4 rg%u var=%c VERIFY FAIL: no CONNECT (si=0x%02X)", rg,
(char)('A' + variant), si);
(void)ch395f_close_socket(E4_SOCK);
(void)bench_wait_closed(E4_SOCK, 2000U);
(void)bench_drain_events(E4_SOCK, 300U);
HAL_Delay(200);
return;
}
/* 消费循环:电平式 GET_RECV_LEN 收集(对 RECV 事件未收到免疫),DISCONNECT/TIM_OUT 即止
* (= 生产 net 层释放语义;closed-with-data 救回不在本判据内,事后读空仅作取证) */
int death = ((si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) != 0U) ? 1 : 0;
uint32_t total = 0U;
uint32_t tr = HAL_GetTick();
while (!death && (uint32_t)(HAL_GetTick() - tr) < 6000U) {
uint16_t rl = ch395f_get_recv_len(E4_SOCK);
while (rl > 0U && total < (uint32_t)sizeof(s_e4_acc)) {
uint16_t n = (rl > 2048U) ? 2048U : rl;
uint32_t room = (uint32_t)sizeof(s_e4_acc) - total;
if ((uint32_t)n > room) {
n = (uint16_t)room;
}
ch395f_read_recv_buf(E4_SOCK, &s_e4_acc[total], n);
total += (uint32_t)n;
rl = ch395f_get_recv_len(E4_SOCK);
}
si = ch395f_get_sock_int_status(E4_SOCK);
if ((si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) != 0U) {
death = 1;
}
if (!death) {
HAL_Delay(5);
}
}
uint32_t loop_total = total;
/* 事后读空(取证:数据是否其实还在芯片缓冲里) */
for (int k = 0; k < 20; k++) {
uint16_t rl = ch395f_get_recv_len(E4_SOCK);
if (rl == 0U) {
break;
}
uint32_t room = (uint32_t)sizeof(s_e4_acc) - total;
if (room == 0U) {
break;
}
uint16_t n = (rl > 2048U) ? 2048U : rl;
if ((uint32_t)n > room) {
n = (uint16_t)room;
}
ch395f_read_recv_buf(E4_SOCK, &s_e4_acc[total], n);
total += (uint32_t)n;
}
/* 载荷自证:图案种子随全局轮次变,上一轮残渣必失配 */
uint8_t seed = e4_seed(rg);
int mm = -1;
for (uint32_t i = 0U; i < loop_total; i++) {
if (s_e4_acc[i] != (uint8_t)(seed + i)) {
mm = (int)i;
break;
}
}
int ok = (loop_total == E4_PAYLOAD) && (mm < 0);
DBG_INFO("BENCH E4 rg%u var=%c VERIFY loop=%lu extra=%lu mm=%d death=%d head=%02X %02X %02X %02X => %s",
rg, (char)('A' + variant), (unsigned long)loop_total,
(unsigned long)(total - loop_total), mm, death,
s_e4_acc[0], s_e4_acc[1], s_e4_acc[2], s_e4_acc[3], ok ? "OK" : "FAIL");
/* 轮后清洁 */
(void)ch395f_close_socket(E4_SOCK);
(void)bench_wait_closed(E4_SOCK, 2000U);
(void)bench_drain_events(E4_SOCK, 300U);
HAL_Delay(200);
}
static void bench_e4(void) {
DBG_INFO("BENCH E4 ===== sock_reset A/B/C x%u (host: ch395f_reuse_host.py --mode e4) =====",
(unsigned)E4_ROUNDS);
unsigned rg = 0U;
for (uint8_t v = 0U; v < 3U; v++) {
for (unsigned r = 0U; r < E4_ROUNDS; r++, rg++) {
bench_e4_round(rg, v);
}
}
}
void ch395f_reuse_bench(void) {
DBG_INFO("BENCH ===== E2/E3/E4 chip bench START (legacy matrix parked) =====");
if (bench_chip_init() != 0) {
DBG_ERROR("BENCH init failed, giving up (normal boot continues)");
return;
}
for (unsigned i = 0; i < 2048U; i++) {
s_tx_pat[i] = (uint8_t)(0xA5U + (i % 251U));
}
bench_dwt_enable();
if (s_bench_legacy != 0) {
/* 丢中断复核实验(2026-10-14):固定 8 周期 × 6 变体后返回正常启动
* (主机脚本 ch395f_reuse_host.py --mode legacy --cycles 8 同步收工) */
DBG_INFO("BENCH legacy matrix: 8 cycles x %u variants", (unsigned)BENCH_N_VARS);
unsigned idx = 0;
for (unsigned cyc = 0U; cyc < 8U; cyc++) {
for (unsigned iv = 0U; iv < (unsigned)BENCH_N_VARS; iv++) {
bench_round(idx);
idx = (idx + 1U) % (unsigned)BENCH_N_VARS;
}
}
DBG_INFO("BENCH legacy matrix DONE, normal boot continues");
return;
}
bench_e2();
bench_e3();
bench_e4();
DBG_INFO("BENCH ===== E2/E3/E4 ALL DONE (reflash or power-cycle to exit) =====");
for (;;) {
HAL_Delay(1000);
}
}
#endif /* N30_CHIP_BENCH */