Files
STM32F4-Base/test/board/ch395f_reuse_bench.c
T
wandering1 83a70721bd docs(N34): E2/E3/E4 实验结果与定案 + bench 实验区复活
E2:状态回读 18.1us(N=1000x2 逐值一致);LISTEN 可见延迟 100%
<=10ms 无长尾(24 轮)-> "每 poll 全量刷新"成立、reconciler 去抖
可减为 1。
E3:GET_CMD_STATUS 对 OPEN/LISTEN/CLOSE 无判别力(残留假阳性),
定案 cmd_done 用状态回读判据;SET 类命令态永不更新。
E4:排渣序列定案=读空 RX -> CLOSE -> 排空事件(quiescent) ->
轮询 byte0=00H;C 变体(不排渣)0/20 且级联自持,Trap 23 残渣
恰等于单 Socket RX 缓冲整容(rxdrain=2048,19/20)。
E2 补充发现(Phase 15 板测):LISTEN 态 CLOSE_SOCKET 二段关闭
(一次只拆监听 byte0 恒 05,再发一次才落 00)——Trap 09 open
重试环的芯片级原因。

bench:ch395f_reuse_bench.c 复活 N30_CHIP_BENCH 门控实验区
(旧 N-30 矩阵 s_bench_legacy=0 停泊),ch395f_reuse_host.py
增 --mode e4。注册(uvprojx/main.c)实验完已还原不入库。
2026-10-13 12:43:56 +08:00

826 lines
32 KiB
C
Raw 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 N-30 芯片级 Socket 复用时序台架(裸机,内核前运行,OS/net/FTP 全剥离)
* @details 直接调用 ch395f_* 命令,循环执行受控时序矩阵(延迟/关闭次序/载荷/Socket 号),
* 每步打印芯片状态与中断寄存器,定位"关→开→监听→连接"复用时序中
* 芯片行为的确切规律。配套主机脚本 test/host/ch395f_reuse_host.py。
* 定案结论与实验矩阵演进 → CH395F_Trap_Records.md Trap 25。
*
* 存档状态:本文件在主分支**不注册编译**(未加入 uvprojx),仅作实验工具存档;
* 运行方法:在实验分支(如 n30-chip-bench)把本 .c 注册进 uvprojx Test 组、
* Src/main.c 的 USER CODE 2 区在 app_main_init() 之前调 ch395f_reuse_bench()
* (内核前裸机循环,永不返回,断电退出),烧录后跑主机脚本对照芯片日志。
* 旧 N-30 定案矩阵(对照 vs "丢 CONNECT 捕获自 accept"修复组)保留在 s_vars[]/
* bench_round(),由 s_bench_legacy=0 停泊不运行。
*
* 2026-10-13 N34 阶段 2 前置实验(task-9):同一固件合并跑 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/网络/N34-自愈审计/实验方案.md E2/E3/E4 条目与实验记录节。
*
* 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 + 丢CONNECT捕获自accept */
{ 3, 300, 100, 0, 4 }, /* 300ms + 丢CONNECT捕获 */
{ 3, 800, 2000, 0, 4 }, /* 2KB + 丢CONNECT捕获 */
};
#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) {
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (sock + 4U))) != 0U) {
uint8_t si = ch395f_get_sock_int_status(sock);
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 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 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;
}
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());
}
/* 清残留中断(与生产 listen 后一致) */
for (int j = 0; j < 8; j++) {
(void)ch395f_get_sock_int_status((uint8_t)j);
}
(void)ch395f_get_glob_int_status_all();
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 中断补发 */
while ((uint32_t)(HAL_GetTick() - tw) < budget) {
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (sock + 4U))) != 0U) {
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);
}
(void)ch395f_get_glob_int_status_all();
}
}
/* 2. 等 CONNECT(10s;幽灵等待期已见 CONNECT 则跳过)
* v6 关键探针:等待期每 500ms 读芯片状态;若已 ESTABLISHED(0x04)
* 说明握手完成但 CONNECT 中断丢失 → 补做 accept(对账②模拟) */
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) {
DBG_INFO("BENCH LOST-CONNECT caught: state ESTABLISHED, self-accept t=%u",
(unsigned)HAL_GetTick());
got = 1;
break;
}
}
}
if (!got) {
DBG_ERROR("BENCH no CONNECT within 10s (host not synced?)");
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 20) */
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 记状态+中断(抓"秒杀") */
for (int k = 0; k < 10; k++) {
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (sock + 4U))) != 0U) {
si = ch395f_get_sock_int_status(sock);
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 14 滞后中断取证) */
for (int k = 0; k < 20; k++) {
uint16_t glob = ch395f_get_glob_int_status_all();
uint8_t late = 0;
if ((glob & (1U << (sock + 4U))) != 0U) {
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);
}
/* =====================================================================
* N34 阶段 2 前置实验区(2026-10-13):E2 / E3 / E4 一次固件合并跑
* ===================================================================== */
/* 旧 N-30 定案矩阵停泊开关: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 版) */
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) {
uint16_t glob = ch395f_get_glob_int_status_all();
uint8_t si = 0U;
if ((glob & (1U << (sock + 4U))) != 0U) {
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 23 形态),复位后立即
* 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) {
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (sock + 4U))) != 0U) {
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);
}
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (E4_SOCK + 4U))) != 0U) {
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 ===== N34 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) {
DBG_INFO("BENCH legacy N-30 matrix cycling %u variants", (unsigned)BENCH_N_VARS);
unsigned idx = 0;
for (;;) {
bench_round(idx);
idx = (idx + 1U) % (unsigned)BENCH_N_VARS;
}
}
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 */