fix(N34): E14 丢中断复核——芯片不丢事件;listen 排事件挪至 LISTEN 前+全局读收口+全库归因更正
48 轮直读 SINT 矩阵(8 周期×6 变体)复核历史"丢中断"说法:芯片侧事件 丢失 = 0。归类:drain 读清消费 3/48(drain-int 探针现场抓获)、门控 竞态 2/48、主机同步 1/48、幽灵态 0/48 未复现。 根除性修复:net_listen_locked / net_listener_try 的排事件读清从 listen 后挪至 LISTEN 命令之前——新连接 CONNECT 落进该窗即被本方读清 吞掉,即"数据被吞"的生产侧自伤机制(台架 3/48 为同形态放大版)。 全局读收口:全局中断寄存器唯一读者 = net_poll(I7);生产/测试/台架 游离全局读全部删除,ch395f.h 声明处加护栏注释。 归因更正:17 文件约 40 处"丢中断"措辞改为"事件未收到/读清消费竞态" (机制全部保留,仅归因更正);设计文档 §5.2 新增 E14 锚点;Trap Records TRAP-NET-17/-20/B3 归因更正段;配图生成器与 SVG 重生成。 台架:事件未收到复核探针保留(drain-int / SINT 补读×3 / 超时归类), 下次跑矩阵自动归类;实验注册(main.c/uvprojx)已完全还原。 验证:@build 0 错误 0 警告;ci_local L0 32/0 + gitlint + L0.5 953/953;doc-links --all --sections-strict 通过;@test PASS(无 [ERR],net init 619ms);回归 :7003 300ms×10 = 10/10、:21 n30×50 = 0/50 失败。
This commit is contained in:
@@ -10,16 +10,16 @@
|
||||
* 运行方法:在实验分支(如 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[]/
|
||||
* 旧 N-30 定案矩阵(对照 vs "CONNECT 事件未收到捕获自 accept"修复组)保留在 s_vars[]/
|
||||
* bench_round(),由 s_bench_legacy=0 停泊不运行。
|
||||
*
|
||||
* 2026-10-13 N34 阶段 2 前置实验(task-9):同一固件合并跑 E2/E3/E4——
|
||||
* 2026-10-13 自愈收敛阶段 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 §6 E2/E3/E4 条目与实验记录节。
|
||||
* 判据与结果锚点 → docs/网络/网络自愈架构设计.md §5.2 E2/E3/E4(过程全文已归档,见该文档 §9)。
|
||||
*
|
||||
* close_mode:0=主机先关(芯片等 DISCONNECT);1=芯片先关;2=无数据连上即关
|
||||
* verify:0=无修复;1=listen 后确认 LISTEN 重试;2=缓冲重分配;
|
||||
@@ -48,9 +48,9 @@ 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捕获 */
|
||||
{ 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]))
|
||||
@@ -71,9 +71,11 @@ static uint8_t s_rx_buf[2048];
|
||||
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);
|
||||
/* 直读本 Socket SINT(读即清)。不用全局寄存器门控:它读即清,未配对
|
||||
* 读取会吞事件;且门控观察分不清"芯片未锁存"与"全局位被读清"
|
||||
* (TRAP-NET-20 观察混淆源)。唯一读者 = net_poll(F-NET-7)。 */
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si != 0U) {
|
||||
if (out_bits != 0) {
|
||||
*out_bits = si;
|
||||
}
|
||||
@@ -168,6 +170,17 @@ static void bench_round(unsigned idx) {
|
||||
DBG_ERROR("BENCH open fail 0x%02X", ost);
|
||||
return;
|
||||
}
|
||||
/* 排事件在 LISTEN **之前**(对齐生产 listen 路径 E14 改法:post-listen 排事件
|
||||
* 会吞掉快速到达的 CONNECT——上一版矩阵 3/48 被本探针现场抓获,现已根除该
|
||||
* 自伤形态;本探针保留,drain 若仍见 CONNECT 即回归信号)。
|
||||
* 逐 Socket SINT 读(读即清,不读全局——唯一读者 = net_poll,F-NET-7) */
|
||||
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);
|
||||
@@ -182,11 +195,6 @@ static void bench_round(unsigned idx) {
|
||||
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);
|
||||
@@ -199,17 +207,14 @@ static void bench_round(unsigned idx) {
|
||||
uint8_t st[2] = { 0, 0 };
|
||||
uint32_t tw = HAL_GetTick();
|
||||
int connect_fired = 0;
|
||||
/* 等 TCP 进 LISTEN(幽灵消退)或 CONNECT 中断补发 */
|
||||
/* 等 TCP 进 LISTEN(幽灵消退)或 CONNECT 中断补发(直读本 Socket SINT) */
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
@@ -235,13 +240,14 @@ static void bench_round(unsigned idx) {
|
||||
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(对账②模拟) */
|
||||
* 说明握手完成但 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) {
|
||||
@@ -260,15 +266,40 @@ static void bench_round(unsigned idx) {
|
||||
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());
|
||||
/* 丢中断复核实验:宣布"未收到"之前补读 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) {
|
||||
DBG_ERROR("BENCH no CONNECT within 10s (host not synced?)");
|
||||
/* 丢中断复核实验:对照变体 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;
|
||||
@@ -311,11 +342,10 @@ static void bench_round(unsigned idx) {
|
||||
}
|
||||
}
|
||||
DBG_INFO("BENCH tx %u written t=%u", (unsigned)v->tx_len, (unsigned)HAL_GetTick());
|
||||
/* 连接存活观察窗:500ms 内每 50ms 记状态+中断(抓"秒杀") */
|
||||
/* 连接存活观察窗:500ms 内每 50ms 记状态+中断(抓"秒杀";直读 SINT) */
|
||||
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);
|
||||
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);
|
||||
@@ -345,13 +375,9 @@ static void bench_round(unsigned idx) {
|
||||
ch395f_close_socket(sock);
|
||||
}
|
||||
|
||||
/* 5. 关闭后拆链观察:1s 内每 50ms 记状态 + 中断(TRAP-NET-12 滞后中断取证) */
|
||||
/* 5. 关闭后拆链观察:1s 内每 50ms 记状态 + 中断(TRAP-NET-12 滞后中断取证;直读 SINT) */
|
||||
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);
|
||||
}
|
||||
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());
|
||||
}
|
||||
@@ -366,7 +392,7 @@ static void bench_round(unsigned idx) {
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
* N34 阶段 2 前置实验区(2026-10-13):E2 / E3 / E4 一次固件合并跑
|
||||
* 自愈收敛阶段 2 前置实验区(2026-10-13):E2 / E3 / E4 一次固件合并跑
|
||||
* ===================================================================== */
|
||||
|
||||
/* 旧 N-30 定案矩阵停泊开关:0 = 不运行(保留代码备查,勿删) */
|
||||
@@ -428,18 +454,15 @@ static int bench_wait_closed(uint8_t sock, uint32_t timeout_ms) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 排空本 Socket 事件:全局位图门控 + SINT 读(读即清),连续 2 轮(20ms 节奏)无新位即停;
|
||||
* 返回排空期间见过的 SINT 位 OR(E8 M5 drain-until-quiescent 的 bench 版) */
|
||||
/* 排空本 Socket 事件:直读 SINT(读即清),连续 2 轮(20ms 节奏)无新位即停;
|
||||
* 返回排空期间见过的 SINT 位 OR(E8 M5 drain-until-quiescent 的 bench 版)。
|
||||
* 不读全局寄存器——读即清,唯一读者 = net_poll(F-NET-7) */
|
||||
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);
|
||||
}
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si != 0U) {
|
||||
seen |= si;
|
||||
quiet = 0;
|
||||
@@ -635,15 +658,12 @@ static uint8_t e4_seed(unsigned rg) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
@@ -717,7 +737,7 @@ static void bench_e4_round(unsigned rg, uint8_t variant) {
|
||||
HAL_Delay(200);
|
||||
return;
|
||||
}
|
||||
/* 消费循环:电平式 GET_RECV_LEN 收集(对丢 RECV 免疫),DISCONNECT/TIM_OUT 即止
|
||||
/* 消费循环:电平式 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;
|
||||
@@ -734,12 +754,9 @@ static void bench_e4_round(unsigned rg, uint8_t variant) {
|
||||
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;
|
||||
}
|
||||
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);
|
||||
@@ -796,7 +813,7 @@ static void bench_e4(void) {
|
||||
}
|
||||
|
||||
void ch395f_reuse_bench(void) {
|
||||
DBG_INFO("BENCH ===== N34 E2/E3/E4 chip bench START (legacy matrix parked) =====");
|
||||
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;
|
||||
@@ -806,12 +823,18 @@ void ch395f_reuse_bench(void) {
|
||||
}
|
||||
bench_dwt_enable();
|
||||
if (s_bench_legacy != 0) {
|
||||
DBG_INFO("BENCH legacy N-30 matrix cycling %u variants", (unsigned)BENCH_N_VARS);
|
||||
/* 丢中断复核实验(2026-10-14):固定 8 周期 × 6 变体后返回正常启动
|
||||
* (主机脚本 ch395f_reuse_host.py --mode legacy --cycles 8 同步收工) */
|
||||
DBG_INFO("BENCH legacy N-30 matrix: 8 cycles x %u variants", (unsigned)BENCH_N_VARS);
|
||||
unsigned idx = 0;
|
||||
for (;;) {
|
||||
bench_round(idx);
|
||||
idx = (idx + 1U) % (unsigned)BENCH_N_VARS;
|
||||
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();
|
||||
|
||||
@@ -67,7 +67,7 @@ extern "C" {
|
||||
* E11 实证);勿与 Phase 6/9/10/13 混选(recv 互抢数据) */
|
||||
/* ---- 链路时延基线(M1;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */
|
||||
//#define ENABLE_PHASE12_TESTS /* [硬] SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC)—— M1 已完成,基线见说明文档 §6.1 */
|
||||
//#define ENABLE_PHASE15_TESTS /* [硬] N34 阶段 2 驱动原语:状态回读一致性/cmd_done/sock_reset 状态机跑通/链路缓存(无需 PC)——TC 详见 docs/测试说明/CH395F_Test_Guide.md 阶段 15;2026-10-13 台架 20/20 PASS */
|
||||
//#define ENABLE_PHASE15_TESTS /* [硬] 自愈收敛阶段 2 驱动原语:状态回读一致性/cmd_done/sock_reset 状态机跑通/链路缓存(无需 PC)——TC 详见 docs/测试说明/CH395F_Test_Guide.md 阶段 15;2026-10-13 台架 20/20 PASS */
|
||||
/* ---- 链路时延(M2/M3;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */
|
||||
//#define ENABLE_PHASE13_TESTS /* [网] 芯片 + 队列时延(L3~L7/L10,需 PC peer)—— M2 已完成,基线见说明文档 §6.2 */
|
||||
#define ENABLE_PHASE14_TESTS /* [网] 生产路径时延(L8~L11 + 模拟器 Δ)—— M3,需 peer --mode remo(:7003);
|
||||
|
||||
+77
-152
@@ -77,12 +77,9 @@ static void test_parse_ip(const char *str, uint8_t *ip_arr)
|
||||
static void phase1_run(void) {
|
||||
uint8_t ip_info[20];
|
||||
|
||||
/* ---- 前置清全局中断(读即清除) ----
|
||||
* net_stack_bring_up() 在 set_phy(100M_FULL) 强制重协商 + 等待链路建立
|
||||
* 期间,芯片会锁存 GINT_STAT_PHY_CHANGE(0x0004)标志;硬件层模式下无
|
||||
* netTask 轮询消费它,若不清除将导致 P1-06 的"原始状态 0x0000"检查误报。
|
||||
* 此处读取一次丢弃,保证 P1-06 反映的是测试期间的纯净状态。 */
|
||||
(void)ch395f_get_glob_int_status_all();
|
||||
/* 全局中断寄存器读即清,唯一读者 = net_poll(F-NET-7;用户裁定 2026-10-13:
|
||||
* 测试代码同样禁自行读取)。net_stack_bring_up() 的 set_phy 强制重协商会
|
||||
* 锁存 GINT_STAT_PHY_CHANGE(0x0004)——由 P1-06 内部双读自清,不在此预清。 */
|
||||
|
||||
DBG_INFO("=== CH395F Phase 1 Tests ===");
|
||||
DBG_INFO("--- 9 test items (P1-01 ~ P1-09) ---");
|
||||
@@ -180,13 +177,15 @@ static void phase1_run(void) {
|
||||
}
|
||||
|
||||
/* ---- P1-06: ch395f_get_glob_int_status_all()(16 位版) ----
|
||||
* 原理:读取全局中断状态寄存器(2 字节版本,含所有 Socket 中断位)。
|
||||
* 在无任何网络事件的空闲状态下,应为 0x0000。
|
||||
* 通过:返回 0x0000。
|
||||
* 失败:返回非 0x0000(有残留中断,如前序操作异常触发但未清除)。
|
||||
* 注意:此寄存器为读后清零,故先读 16 位版再读 1 字节版。
|
||||
* 全库唯一允许 net_poll 之外读全局寄存器的位置——被测对象就是该 API 本身
|
||||
* (硬件层模式独占上下文,无并发消费者)。读即清语义 → 先读一次自清
|
||||
* (吸收 bring_up 锁存的 PHY_CHANGE),短暂延时后第二次读为断言值。
|
||||
* 通过:第二次读返回 0x0000。
|
||||
* 失败:第二次读非 0x0000(测试期间出现新事件)。
|
||||
*/
|
||||
{
|
||||
(void)ch395f_get_glob_int_status_all(); /* 自清读(被测 API 自身,见上) */
|
||||
HAL_Delay(10);
|
||||
uint16_t gint_all = ch395f_get_glob_int_status_all();
|
||||
DBG_INFO(" [P1-06] ch395f_get_glob_int_status_all()");
|
||||
DBG_INFO(" expect: 0x0000");
|
||||
@@ -330,54 +329,24 @@ static void phase2_tc201(void)
|
||||
|
||||
/* 步4:等待 CONNECT 中断
|
||||
*
|
||||
* CH395F 的中断上报是【两级】结构:
|
||||
*
|
||||
* 第 1 级 —— 全局中断状态 GET_GLOB_INT_STATUS_ALL(16 位,读即清除)
|
||||
* 只回答"哪个 Socket 发生了事件",不回答具体是什么事件。
|
||||
* 16 位布局(见 ch395f.h GINT_STAT 宏定义):
|
||||
* bit0 UNREACH (0x0001) 目标不可达
|
||||
* bit1 IP_CONFLI (0x0002) IP 冲突
|
||||
* bit2 PHY_CHANGE (0x0004) 链路状态变化
|
||||
* bit3 DHCP (0x0008) DHCP 事件
|
||||
* bit4~7 Socket0~3 (0x0010/0x20/0x40/0x80)
|
||||
* bit8~11 Socket4~7 (0x0100~0x0800)
|
||||
* bit15 DHCPV6 (0x8000)
|
||||
*
|
||||
* 第 2 级 —— Socket 中断状态 CMD_GET_INT_STATUS_SN(8 位,读即清除)
|
||||
* 才包含具体事件类型:SENBUF_FREE / SEND_OK / RECV_OK /
|
||||
* CONNECT / DISCONNECT / DISCARD / SOCK_TIMEOUT。
|
||||
* 必须在第 1 级提示"本 Socket 有事件"后再读,否则读了也是空。
|
||||
*
|
||||
* 【掩码 (1U << (sock + 4)) 的由来】
|
||||
* 全局状态的 bit0~3 被 4 个全局事件占用,因此 Socket N 的事件标志
|
||||
* 固定从 bit4 开始排列 —— Socket N 对应第 (N + 4) 位:
|
||||
* sock=0 → 1<<4 = 0x0010(Socket0 事件位,宏已删 N34-0-5)
|
||||
* sock=1 → 1<<5 = 0x0020(Socket1 事件位,宏已删 N34-0-5)
|
||||
* ...
|
||||
* sock=7 → 1<<11 = 0x0800(CH395F_GINT_STAT_SOCK7)
|
||||
* `int_st & (1U << (sock + 4))` 非 0
|
||||
* ⇔ 芯片报告"Socket [sock] 有待处理事件",
|
||||
* 此时才值得再花一次 SPI 事务去读第 2 级确认具体事件。
|
||||
* 直接读本 Socket 中断状态 CMD_GET_INT_STATUS_SN(8 位,读即清)——事件位:
|
||||
* SENBUF_FREE / SEND_OK / RECV_OK / CONNECT / DISCONNECT / DISCARD / SOCK_TIMEOUT。
|
||||
* 不用全局寄存器 GET_GLOB_INT_STATUS_ALL 做门控:它读即清,任何未配对 SINT
|
||||
* 消费的读取都会吞掉事件通知;唯一读者 = net_poll(F-NET-7),测试代码同样
|
||||
* 禁自行读取(用户裁定 2026-10-13)。测试知道自己操作哪个 Socket,直读
|
||||
* SINT 即可——每轮一次 SPI 事务,成本与旧两级方案相同,且观察无门控混淆。
|
||||
*/
|
||||
tick = HAL_GetTick();
|
||||
ok = 0;
|
||||
while (HAL_GetTick() - tick < PHASE2_TIMEOUT_MS) {
|
||||
/* 第 1 级:读全局状态。注意本次读取本身会清掉全部全局标志
|
||||
* 并拉高 INT#(读即清除),所以每轮循环都在重新采样。 */
|
||||
uint16_t int_st = ch395f_get_glob_int_status_all();
|
||||
|
||||
/* 本 Socket 在全局寄存器中有事件挂起? */
|
||||
if (int_st & (1U << (sock + 4))) {
|
||||
/* 第 2 级:读本 Socket 的具体事件类型(同样读即清除) */
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & CH395F_SINT_STAT_CONNECT) {
|
||||
/* bit3 = 0x08:TCP 三次握手完成,进入 ESTABLISHED */
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
/* 是别的事件(理论上此阶段不应出现),已被读清、直接忽略,
|
||||
* 继续下一轮轮询等 CONNECT */
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & CH395F_SINT_STAT_CONNECT) {
|
||||
/* bit3 = 0x08:TCP 三次握手完成,进入 ESTABLISHED */
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
/* 是别的事件(理论上此阶段不应出现),已被读清、直接忽略,
|
||||
* 继续下一轮轮询等 CONNECT */
|
||||
HAL_Delay(10); /* 10ms 轮询周期,直至 CONNECT 或超时退出 */
|
||||
}
|
||||
TEST_CHECK(ok, "TC201 step4 connected");
|
||||
@@ -392,13 +361,9 @@ static void phase2_tc201(void)
|
||||
tick = HAL_GetTick();
|
||||
ok = 0;
|
||||
while (HAL_GetTick() - tick < 2000) {
|
||||
uint16_t int_st = ch395f_get_glob_int_status_all();
|
||||
if (int_st & (1U << (sock + 4))) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & CH395F_SINT_STAT_SENBUF_FREE) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
HAL_Delay(10);
|
||||
}
|
||||
@@ -461,16 +426,12 @@ static void phase2_tc201(void)
|
||||
}
|
||||
|
||||
/* ---- ② 连接异常检查(还没等到数据才走到这里)----
|
||||
* 两级中断:全局状态确认本 Socket 有事件 → 再读具体事件类型。
|
||||
* 注意先查数据再查断开:断开事件可能和最后一批数据同时到达,
|
||||
* 数据优先保证"临终回显"不被漏掉。 */
|
||||
uint16_t int_st = ch395f_get_glob_int_status_all();
|
||||
if (int_st & (1U << (sock + 4))) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) {
|
||||
break; /* 对端断开 / 超时:继续等也不会有回显了。
|
||||
ok 保持 0,跳出后由下方统一判 FAIL */
|
||||
}
|
||||
* 直读本 Socket SINT(读即清)。先查数据再查断开:断开事件可能和
|
||||
* 最后一批数据同时到达,数据优先保证"临终回显"不被漏掉。 */
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) {
|
||||
break; /* 对端断开 / 超时:继续等也不会有回显了。
|
||||
ok 保持 0,跳出后由下方统一判 FAIL */
|
||||
}
|
||||
|
||||
/* 每 50ms 一轮。若本轮只读了部分数据(rlen > 300 的极端情况),
|
||||
@@ -534,11 +495,7 @@ static void phase2_tc202_once(uint8_t sock, uint16_t local_port)
|
||||
tick = HAL_GetTick();
|
||||
ok = 0;
|
||||
while (HAL_GetTick() - tick < PHASE2_TIMEOUT_MS) {
|
||||
uint16_t int_st = ch395f_get_glob_int_status_all();
|
||||
if (int_st & (1U << (sock + 4))) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & CH395F_SINT_STAT_CONNECT) { ok = 1; break; }
|
||||
}
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_CONNECT) { ok = 1; break; }
|
||||
HAL_Delay(10);
|
||||
}
|
||||
TEST_CHECK(ok, "TC202 step3 connected");
|
||||
@@ -550,11 +507,7 @@ static void phase2_tc202_once(uint8_t sock, uint16_t local_port)
|
||||
tick = HAL_GetTick();
|
||||
ok = 0;
|
||||
while (HAL_GetTick() - tick < 2000) {
|
||||
uint16_t int_st = ch395f_get_glob_int_status_all();
|
||||
if (int_st & (1U << (sock + 4))) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & CH395F_SINT_STAT_SENBUF_FREE) { ok = 1; break; }
|
||||
}
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE) { ok = 1; break; }
|
||||
HAL_Delay(10);
|
||||
}
|
||||
TEST_CHECK(ok, "TC202 step4 sendbuf_free");
|
||||
@@ -578,11 +531,8 @@ static void phase2_tc202_once(uint8_t sock, uint16_t local_port)
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
uint16_t int_st = ch395f_get_glob_int_status_all();
|
||||
if (int_st & (1U << (sock + 4))) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) break;
|
||||
}
|
||||
if (ch395f_get_sock_int_status(sock) &
|
||||
(CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) break;
|
||||
HAL_Delay(50);
|
||||
}
|
||||
if (!ok) TEST_CHECK(0, "TC202 step5 echo rcvd");
|
||||
@@ -689,21 +639,18 @@ static uint8_t tc204_session(uint8_t sock, uint16_t lport, uint8_t mode, uint8_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 等 CONNECT 中断(期间其它中断现场打印 = 残留事件取证点) */
|
||||
/* 等 CONNECT 中断(期间其它中断现场打印 = 残留事件取证点;直读本 Socket SINT) */
|
||||
tick = HAL_GetTick();
|
||||
while (HAL_GetTick() - tick < PHASE2_TIMEOUT_MS) {
|
||||
uint16_t gl = ch395f_get_glob_int_status_all();
|
||||
if ((gl & (1U << (sock + 4))) != 0U) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if ((si & CH395F_SINT_STAT_CONNECT) != 0U) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
if (si != 0U) {
|
||||
DBG_INFO("TC204 m%u r%u stale-int=0x%02X at +%ums",
|
||||
(unsigned)mode, (unsigned)round, si,
|
||||
(unsigned)(HAL_GetTick() - tick));
|
||||
}
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if ((si & CH395F_SINT_STAT_CONNECT) != 0U) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
if (si != 0U) {
|
||||
DBG_INFO("TC204 m%u r%u stale-int=0x%02X at +%ums",
|
||||
(unsigned)mode, (unsigned)round, si,
|
||||
(unsigned)(HAL_GetTick() - tick));
|
||||
}
|
||||
HAL_Delay(10);
|
||||
}
|
||||
@@ -720,13 +667,9 @@ static uint8_t tc204_session(uint8_t sock, uint16_t lport, uint8_t mode, uint8_t
|
||||
tick = HAL_GetTick();
|
||||
ok = 0;
|
||||
while (HAL_GetTick() - tick < 2000) {
|
||||
uint16_t gl = ch395f_get_glob_int_status_all();
|
||||
if ((gl & (1U << (sock + 4))) != 0U) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if ((si & CH395F_SINT_STAT_SENBUF_FREE) != 0U) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
HAL_Delay(10);
|
||||
}
|
||||
@@ -917,7 +860,7 @@ static void phase2_tc204(void)
|
||||
*
|
||||
* 已知陷阱(TRAP-NET-12):上一会话 close 的 DISCONNECT 会滞后锁存在
|
||||
* SINT_SN(wait-CLOSED 的 STATUS 查询不清中断)。防御双保险:
|
||||
* ① 步 2 前预清两级中断;
|
||||
* ① 步 2 前预清本 Socket SINT;
|
||||
* ② 判负向后用 GET_SOCKET_STATUS 反核实,ESTABLISHED 即改判环境错误。
|
||||
*/
|
||||
static void phase2_tc203(void)
|
||||
@@ -946,8 +889,8 @@ static void phase2_tc203(void)
|
||||
|
||||
/* 预清陈旧事件(TRAP-NET-12):上一会话 close 的 DISCONNECT 可能滞后锁存
|
||||
* 在 SINT_SN 中(wait-CLOSED 轮询的 STATUS 是纯查询、不清中断),
|
||||
* 若不清除,本用例首次读中断会误吃旧事件导致负向误判。 */
|
||||
(void)ch395f_get_glob_int_status_all();
|
||||
* 若不清除,本用例首次读中断会误吃旧事件导致负向误判。
|
||||
* 只清本 Socket SINT——全局寄存器读即清,唯一读者 = net_poll(F-NET-7)。 */
|
||||
(void)ch395f_get_sock_int_status(sock);
|
||||
|
||||
/* 步2:OPEN + CONNECT。命令为异步下发,此时均应立即返回成功,
|
||||
@@ -960,8 +903,9 @@ static void phase2_tc203(void)
|
||||
TEST_CHECK(r == CH395F_ERR_SUCCESS, "TC203 step2 connect");
|
||||
if (r != CH395F_ERR_SUCCESS) { ch395f_close_socket(sock); return; }
|
||||
|
||||
/* 步3:30s 两级中断轮询,等"连不上"的证据。
|
||||
* 先扫全局 UNREACH 位作备注,再查本 socket 位取具体事件。
|
||||
/* 步3:30s 直读本 Socket SINT 轮询(读即清),等"连不上"的证据。
|
||||
* (旧版先扫全局 UNREACH 位作备注——已删:全局寄存器唯一读者 = net_poll,
|
||||
* 该备注本就不影响判定。)
|
||||
*
|
||||
* 实测行为(2026-08 上板):PC 端口关闭时 SYN 被 RST 拒绝,
|
||||
* 芯片以【毫秒级 SOCK_TIMEOUT】上报,并非等待 30s 静默重传耗尽;
|
||||
@@ -973,23 +917,15 @@ static void phase2_tc203(void)
|
||||
ev = 0;
|
||||
neg_si = 0;
|
||||
while (HAL_GetTick() - tick < PHASE2_TC203_TIMEOUT_MS) {
|
||||
uint16_t int_st = ch395f_get_glob_int_status_all();
|
||||
|
||||
if (int_st & CH395F_GINT_STAT_UNREACH) {
|
||||
DBG_INFO(" global UNREACH: ARP fail (peer offline)");
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & (CH395F_SINT_STAT_SOCK_TIMEOUT | CH395F_SINT_STAT_DISCONNECT)) {
|
||||
neg_si = si; /* 记下事件位,跳出后再细分原因 */
|
||||
ev = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (int_st & (1U << (sock + 4))) {
|
||||
uint8_t si = ch395f_get_sock_int_status(sock);
|
||||
if (si & (CH395F_SINT_STAT_SOCK_TIMEOUT | CH395F_SINT_STAT_DISCONNECT)) {
|
||||
neg_si = si; /* 记下事件位,跳出后再细分原因 */
|
||||
ev = 1;
|
||||
break;
|
||||
}
|
||||
if (si & CH395F_SINT_STAT_CONNECT) {
|
||||
ev = 2; /* 连上了 = PC 服务没关,属环境错误而非芯片故障 */
|
||||
break;
|
||||
}
|
||||
if (si & CH395F_SINT_STAT_CONNECT) {
|
||||
ev = 2; /* 连上了 = PC 服务没关,属环境错误而非芯片故障 */
|
||||
break;
|
||||
}
|
||||
HAL_Delay(10);
|
||||
}
|
||||
@@ -1173,11 +1109,8 @@ static void phase3_run(void) {
|
||||
|
||||
uint32_t sw = HAL_GetTick();
|
||||
while (HAL_GetTick() - sw < 2000) {
|
||||
uint16_t gs = ch395f_get_glob_int_status_all();
|
||||
if (gs & (1U << (sock + 4))) {
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE)
|
||||
break;
|
||||
}
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE)
|
||||
break;
|
||||
HAL_Delay(10);
|
||||
}
|
||||
echo_ok = 1;
|
||||
@@ -1247,7 +1180,8 @@ static void phase4_run(void) {
|
||||
uint32_t tick = HAL_GetTick();
|
||||
uint8_t dhcp_ok = 0;
|
||||
while (HAL_GetTick() - tick < 30000) {
|
||||
ch395f_get_glob_int_status_all();
|
||||
/* DHCP 状态寄存器是真相(纯查询);不读全局寄存器——读即清,
|
||||
* 唯一读者 = net_poll(F-NET-7),测试代码同样禁自行读取 */
|
||||
if (ch395f_get_dhcp_status() == 0x00) { dhcp_ok = 1; break; }
|
||||
HAL_Delay(500);
|
||||
}
|
||||
@@ -1310,10 +1244,7 @@ static void phase4_run(void) {
|
||||
ch395f_write_send_buf(anno_sock, ip_info, 4);
|
||||
uint32_t sw = HAL_GetTick();
|
||||
while (HAL_GetTick() - sw < 2000) {
|
||||
uint16_t gs = ch395f_get_glob_int_status_all();
|
||||
if (gs & (1U << (anno_sock + 4))) {
|
||||
if (ch395f_get_sock_int_status(anno_sock) & CH395F_SINT_STAT_SENBUF_FREE) break;
|
||||
}
|
||||
if (ch395f_get_sock_int_status(anno_sock) & CH395F_SINT_STAT_SENBUF_FREE) break;
|
||||
HAL_Delay(10);
|
||||
}
|
||||
TEST_CHECK(1, "DHCP announcement sent");
|
||||
@@ -1324,7 +1255,6 @@ static void phase4_run(void) {
|
||||
int32_t hello_ok = 0;
|
||||
uint32_t wait = HAL_GetTick();
|
||||
while (HAL_GetTick() - wait < PHASE4_HELLO_WAIT_MS) {
|
||||
ch395f_get_glob_int_status_all();
|
||||
uint16_t rlen = ch395f_get_recv_len(anno_sock);
|
||||
/* ⚠ 同 Phase 3:夹到本地缓冲容量,防栈溢出(2026-10-11 审计修正) */
|
||||
if (rlen > (uint16_t)sizeof(rx_buf)) {
|
||||
@@ -1466,9 +1396,7 @@ static void phase5_run(void) {
|
||||
|
||||
t0 = HAL_GetTick();
|
||||
while (HAL_GetTick() - t0 < PHASE5_CONN_TIMEOUT_MS) {
|
||||
uint16_t gs = ch395f_get_glob_int_status_all();
|
||||
if ((gs & (1U << (sock + 4))) &&
|
||||
(ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_CONNECT)) {
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_CONNECT) {
|
||||
conn_ok = 1;
|
||||
break;
|
||||
}
|
||||
@@ -1523,13 +1451,10 @@ static void phase5_run(void) {
|
||||
tx_sw = HAL_GetTick(); /* 记录等待起点(供超时判定) */
|
||||
} else {
|
||||
/* ---- 等待上一次写入被芯片搬走(SENBUF_FREE) ----
|
||||
* 两级中断轮询(DP-02):先读全局中断状态确认本 socket 有事件,
|
||||
* 再读该 socket 的中断状态确认具体为 SENBUF_FREE。
|
||||
* 读全局中断状态即清除中断标志(读即清)。
|
||||
* 直读本 Socket SINT(读即清);不读全局寄存器——读即清且
|
||||
* 唯一读者 = net_poll(F-NET-7)。
|
||||
*/
|
||||
uint16_t gs = ch395f_get_glob_int_status_all();
|
||||
if ((gs & (1U << (sock + 4))) && /* socket 中断位(socket 0 → bit 4) */
|
||||
(ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE)) {
|
||||
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE) {
|
||||
can_write = 1; /* 缓冲已腾空,下一轮写入下一块 */
|
||||
} else if (HAL_GetTick() - tx_sw >= PHASE5_TX_TIMEOUT_MS) {
|
||||
/* 超时:芯片迟迟未腾出缓冲 —— 对端未收/链路异常/芯片堵死 */
|
||||
@@ -1655,12 +1580,12 @@ static void phase5_run(void) {
|
||||
#endif
|
||||
|
||||
/* ===================================================================
|
||||
* Phase 15 — N34 阶段 2 驱动原语(无需 PC)
|
||||
* Phase 15 — 自愈收敛阶段 2 驱动原语(无需 PC)
|
||||
*
|
||||
* 测试目的:验证 ch395f 新增原语(实施方案 docs/网络/N34-自愈审计.md §5.4)
|
||||
* 测试目的:验证 ch395f 新增原语(设计 docs/网络/网络自愈架构设计.md §4.4)
|
||||
* 语义正确:类型化状态回读一致性 / cmd_done(E3 定案=状态回读)/
|
||||
* sock_reset 非阻塞状态机跑通(E4 定案序列)/ 链路态缓存 / 事件护栏。
|
||||
* 污染会话下的复位载荷完整性已由 E4 bench 60 轮实证(实验方案 E4),
|
||||
* 污染会话下的复位载荷完整性已由 E4 bench 60 轮实证(《网络自愈架构设计》§5.2 E4),
|
||||
* 本 Phase 只验"状态机跑通 + 判据语义",不依赖 PC。
|
||||
* 上下文:硬件层模式(testTask 独占 CH395F),用 Socket 7(产品监听 0~4 不涉)。
|
||||
* =================================================================== */
|
||||
@@ -1672,7 +1597,7 @@ static void phase15_run(void) {
|
||||
int done;
|
||||
int rc;
|
||||
|
||||
DBG_INFO("=== CH395F Phase 15 Tests (N34 phase-2 primitives) ===");
|
||||
DBG_INFO("=== CH395F Phase 15 Tests (self-heal phase-2 primitives) ===");
|
||||
|
||||
/* ---- TC-1501: 状态回读一致性 + cmd_done 语义(E2/E3 定案形态) ---- */
|
||||
ch395f_set_proto_type(sn, CH395F_PROTO_TYPE_TCP);
|
||||
@@ -1809,7 +1734,7 @@ void ch395f_test_run(void)
|
||||
net_latency_phase12_run();
|
||||
#endif
|
||||
#ifdef ENABLE_PHASE15_TESTS
|
||||
/* Phase 15(N34 阶段 2 驱动原语):独占态下直调驱动,放 HW 层末尾 */
|
||||
/* Phase 15(自愈收敛阶段 2 驱动原语):独占态下直调驱动,放 HW 层末尾 */
|
||||
phase15_run();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -447,6 +447,9 @@ void net_latency_phase12_run(void)
|
||||
(void)ch395f_check_exist();
|
||||
break;
|
||||
default:
|
||||
/* 例外(同 P1-06):被测对象就是本 API 的 SPI 时延——硬件层
|
||||
* 独占上下文、无并发消费者;除此之外测试代码禁读全局寄存器
|
||||
* (读即清,唯一读者 = net_poll,F-NET-7) */
|
||||
(void)ch395f_get_glob_int_status_all();
|
||||
break;
|
||||
}
|
||||
@@ -757,7 +760,7 @@ void net_latency_phase13_run(int32_t sockfd)
|
||||
if (s_p13_state != P13_ST_ABORT) {
|
||||
return; /* 正常完成/DONE:会话终态,不再处理 */
|
||||
}
|
||||
/* N34 阶段1(F-NET-3):原"进 abort 分状态手动重同步 + 10s 周期 relisten"
|
||||
/* 自愈收敛阶段1(F-NET-3):原"进 abort 分状态手动重同步 + 10s 周期 relisten"
|
||||
* 已删——三种去向全部由 net 层自动机制认领(run8 静默回收滞留 ESTABLISHED
|
||||
* → 对账⑤ RESYNC;CLOSED 失位 → pump 100ms 认领重建;LISTENING 静默丢
|
||||
* 监听 → pump"芯片不在听"判据),手动 API net_resync_listen/net_relisten
|
||||
|
||||
@@ -803,7 +803,7 @@ static void phase10_run(int32_t sockfd) {
|
||||
* 一直等待;防火墙规则对已建立连接无效,E11 实证)。
|
||||
* 通过:从连接建立到 recv 返回 0 的间隔落在 10s~120s 有界窗
|
||||
* (芯片定时语义与手册公式 75s 不符,实测判死 18~31s——
|
||||
* 见实验方案 E11 与 phase11_run 内判据注释)。
|
||||
* 见《网络自愈架构设计》§5.2 E11 与 phase11_run 内判据注释)。
|
||||
* =================================================================== */
|
||||
|
||||
#ifdef ENABLE_PHASE11_TESTS
|
||||
|
||||
+3
-3
@@ -25,8 +25,8 @@
|
||||
| `ftp_abort_probe.py` | FTP 中止场景探针(TRAP-NET-18;RST→450 / FIN→226,`--check-abort`;2026-10-13 自 `MDK-ARM/` 迁入) |
|
||||
| `ftp_integrity_test.py` | FTP 上传 SHA-256 完整性多轮回归 |
|
||||
| `n30_repro.py` | 背靠背 STOR 复现器(TRAP-NET-20 常备回归腿) |
|
||||
| `relisten_stress.py` | 监听快速复连压力(TRAP-NET-17 / N34 E1:`--port --n --gap-ms [--hold-ms]`) |
|
||||
| `e11_keepalive_probe.py` | keepalive 黑洞判死探针(N34 E11) |
|
||||
| `relisten_stress.py` | 监听快速复连压力(TRAP-NET-17 / E1:`--port --n --gap-ms [--hold-ms]`) |
|
||||
| `e11_keepalive_probe.py` | keepalive 黑洞判死探针(E11) |
|
||||
| `e6_soak.py` | E6 24h 挂机驱动(常态 + 每小时/每 4h E1 压测注入;判据读串口 `U15 probe` 计数) |
|
||||
| `pasv_probe.py` | PASV 数据通道时序探针 |
|
||||
| `ch395f_reuse_host.py` | Socket 复用/中止形态 PC 对端(bench 配套) |
|
||||
@@ -115,7 +115,7 @@
|
||||
- **PYTHONPATH/路径敏感**:脚本按仓库根相对路径读写,**从仓库根执行**
|
||||
- 本目录**被 pre-commit 门禁的 cppcheck / 代码规范排除**(`test/` 豁免);
|
||||
但各模块的 `run_unit_test.py` 自身**作为独立钩子**参与门禁(见上,共 7 个)
|
||||
- 台架探针族分工(2026-10-13 起):**N34/Trap 常备回归腿在本目录**(`relisten_stress` /
|
||||
- 台架探针族分工(2026-10-13 起):**自愈/Trap 常备回归腿在本目录**(`relisten_stress` /
|
||||
`ftp_abort_probe` / `n30_repro` / `e02_probe` / `e6_soak` 等);`MDK-ARM/*.py` 只余
|
||||
历史一次性探针(`ftp_freeze_probe` / `tcp_reconnect_probe` / `ftp_retr_abort_probe` /
|
||||
`bench_verdict`)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# N-30 芯片级复用时序台架:主机端(与 test/board/ch395f_reuse_bench.c 共用变体表)
|
||||
# 用法: python ch395f_reuse_host.py [--host IP] [--cycles N] [--mode legacy|e4]
|
||||
# legacy: 按 idx 轮转连接 5000+idx 端口,记录收发结果与时刻。
|
||||
# e4: N34 E4 sock_reset A/B/C 矩阵配套(2026-10-13):每轮两次连接——
|
||||
# e4: E4 sock_reset A/B/C 矩阵配套(2026-10-13):每轮两次连接——
|
||||
# ①污染:连上发 3000B 后 RST(SO_LINGER 0,TRAP-NET-18 中止形态);
|
||||
# ②载荷:立即重连发 4096B 图案(pat[i]=(seed+i)&0xFF, seed=(rg*97+13)&0xFF)后 FIN。
|
||||
# 端口 5100+rg,rg=0..(3*rounds-1),变体顺序 A/B/C(与固件 bench 表一致)。
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
全体消费者的队列消息停摆最长 30s;
|
||||
2. 2s 后从 PC 发起 FTP(:21) 连接,测 banner 到达时延 = 队列响应性:
|
||||
修复前 ≈ 25~30s(队头冻结),修复后 ≈ 数百 ms 内;
|
||||
3. 34s 时 30s 总预算应已耗尽:设备拆掉该会话(N34 阶段 3 net_drop_conn:
|
||||
3. 34s 时 30s 总预算应已耗尽:设备拆掉该会话(自愈收敛阶段 3 net_drop_conn:
|
||||
FIN 关闭 + 本层收口 + 重监听),随后**本探针模拟守规矩对端**:
|
||||
close 自己的 socket;
|
||||
4. 芯片回 CLOSED(TC-NET-204 模式 A:TIME_WAIT 过渡 ~150ms)→ relisten,
|
||||
:8888 应能再接受新连接(3s 重试窗内);新连接发完整 8 字节 TIME 包 →
|
||||
设备应落 "wall clock updated"。
|
||||
|
||||
⚠ 定案口径(TRAP-NET-07 定案更正 2026-10-11 + 实验方案 E13 §5):CH395F close
|
||||
⚠ 定案口径(TRAP-NET-07 定案更正 2026-10-11 + 《网络自愈架构设计》§5.2 E13):CH395F close
|
||||
**无 RST 语义**。若对端内核存活(app 挂起但 socket 不关,内核自动 ACK FIN),
|
||||
芯片停在 FIN_WAIT_2、监听恢复受限于对端 close——探针第 3 步的 close 正是
|
||||
模拟"对端配合"这一可恢复边界;不配合场景的兜底是芯片 TIMEOUT/keepalive
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""E11:CH395F 服务器侧 KeepAlive —— "链路在、对端静默"判死验证(PC 侧注入)。
|
||||
|
||||
背景(N34 E11):手册 §5.56 规定 TCP 服务器应在 SINT_STAT_CONNECT 后开启
|
||||
背景(E11):手册 §5.56 规定 TCP 服务器应在 SINT_STAT_CONNECT 后开启
|
||||
KeepAlive;本仓该调用 2026-10-12 被撤回,全仓仅剩客户端分支启用,而
|
||||
App/ 无任何 net_connect() → 产品上 keepalive 实际关闭。本脚本用于验证
|
||||
"开启后能否在有界时间内判死静默对端,且不误杀正常空闲会话"。
|
||||
@@ -32,7 +32,7 @@ import time
|
||||
|
||||
TAG_C, TAG_D = 0xAA, 0xBB
|
||||
CMD_KEEPLIVE = 0
|
||||
RULE_NAME = "N34-E11-DROP"
|
||||
RULE_NAME = "SELFHEAL-E11-DROP"
|
||||
|
||||
|
||||
def _crc(data: bytes) -> int:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""E6 24h soak driver (U15 mismatch frequency, N34 experiment plan E6).
|
||||
"""E6 24h soak driver (U15 mismatch frequency, self-heal design anchor E6).
|
||||
|
||||
Protocol (docs/网络/N34-自愈审计.md §6 E6):
|
||||
Protocol (docs/网络/网络自愈架构设计.md §4.8, anchor §5.2 E6):
|
||||
24h normal operation + periodic E1 stress injection:
|
||||
- every hour : relisten_stress :7003 n=10 gap=100 (UI-like churn)
|
||||
- every 4th hour : full E1 (:7003 n=50 gap=0 + :21 n=50 gap=100)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""E12:CH395F SPI 缓存区 TSL 时序 A/B 的字节级校验工作负载。
|
||||
|
||||
背景(N34 E12):手册 §7.4 规定读写收发缓存区命令需在"长度与数据之间"留
|
||||
背景(E12):手册 §7.4 规定读写收发缓存区命令需在"长度与数据之间"留
|
||||
TSL(读 0.4us / 写 0.2us)。本仓 ch395f_write_send_buf / ch395f_read_recv_buf
|
||||
把命令头与数据打包进**同一 DMA 突发**(零间隔)。本脚本用 SHA-256 逐轮校验
|
||||
"上传→回读"是否字节一致,为 A/B 提供灵敏判据:
|
||||
|
||||
@@ -311,7 +311,7 @@ class RemoPeer:
|
||||
def ftp_stress_worker(self, start_evt, stop_evt, hold_s):
|
||||
"""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 N34 phase 1.5; today the wait is
|
||||
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)
|
||||
-> every queued message (incl. the LCDMEM response) waits behind it (E-02)."""
|
||||
if not start_evt.wait(120.0):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pasv_probe.py — decisive probe for the "storm" signature (N34).
|
||||
pasv_probe.py — decisive probe for the "storm" signature (self-heal audit E8).
|
||||
|
||||
Per FTP round prints: device PASV port, PC data-socket SOURCE port, round
|
||||
result, and timing. Hypothesis under test: the CH395F reuses the same
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""relisten_stress.py - listener reconnect stress (N34 E1/E5 bench leg).
|
||||
"""relisten_stress.py - listener reconnect stress (E1/E5 bench leg).
|
||||
|
||||
Connect -> optional hold -> immediate close, repeated at a fixed gap.
|
||||
Counts refusals and connect latency distribution; non-ok rounds listed.
|
||||
|
||||
@@ -235,7 +235,7 @@ int net_close(int sockfd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- net_drop_conn(N34 阶段 3 E-02 收口):计数 + 记录 fd ---- */
|
||||
/* ---- net_drop_conn(自愈收敛阶段 3 E-02 收口):计数 + 记录 fd ---- */
|
||||
static int s_drop_calls;
|
||||
static int s_drop_socks[32];
|
||||
|
||||
@@ -511,7 +511,7 @@ static void tc_open_listener(void) {
|
||||
}
|
||||
|
||||
/* ===================== TC-TS-020..029:handle_conn =====================
|
||||
* ⚠ N34 阶段1(2026-10-13)契约变更:handle_conn **不再** net_close
|
||||
* ⚠ 自愈收敛阶段1(2026-10-13)契约变更:handle_conn **不再** net_close
|
||||
* (单连接模式下 conn==listen,close 会连监听一起毁);会话收尾只返回外层,
|
||||
* 由 time_sync_task 在 `conn != ls`(模拟器多连接后端)时才关数据 fd。
|
||||
* 故本组断言一律为 s_close_calls == 0;任务级关闭见 TC-TS-040e。 */
|
||||
@@ -530,7 +530,7 @@ static void tc_handle_conn(void) {
|
||||
CHECK(s_set_unix_last == 0x12345678u, "TC-TS-020b epoch 按大端解析");
|
||||
CHECK((s_recv_calls == 1) && (s_recv_len_log[0] == 16), "TC-TS-020c 首次请求 16 字节");
|
||||
CHECK(s_recv_to_log[0] == 50U, "TC-TS-020d 单次等待片长 = 50ms");
|
||||
CHECK(s_close_calls == 0, "TC-TS-020e 处理完**不**关闭连接(N34 阶段1:close 交外层 conn!=ls 分支)");
|
||||
CHECK(s_close_calls == 0, "TC-TS-020e 处理完**不**关闭连接(自愈收敛阶段1:close 交外层 conn!=ls 分支)");
|
||||
CHECK(s_drop_calls == 0, "TC-TS-020g 成功会话不拆(drop 只用于放弃路径)");
|
||||
CHECK(g_tstest_info_count == 1, "TC-TS-020f 留痕 wall clock updated");
|
||||
|
||||
@@ -605,7 +605,7 @@ static void tc_handle_conn(void) {
|
||||
CHECK(s_recv_calls == 1, "TC-TS-027 预算耗尽后不再读(只读 1 次)");
|
||||
CHECK(g_tstest_info_count == 1, "TC-TS-027b 留痕 no TIME packet in 30s, drop conn");
|
||||
CHECK(s_close_calls == 0, "TC-TS-027c 预算耗尽也不关闭连接");
|
||||
CHECK(s_drop_calls == 1, "TC-TS-027d 预算耗尽主动拆会话(N34 阶段 3 E-02 收口:net_drop_conn 拆连接保监听)");
|
||||
CHECK(s_drop_calls == 1, "TC-TS-027d 预算耗尽主动拆会话(自愈收敛阶段 3 E-02 收口:net_drop_conn 拆连接保监听)");
|
||||
|
||||
/* 9) 剩余预算不足一片 */
|
||||
fakes_reset();
|
||||
|
||||
@@ -57,7 +57,7 @@ def main():
|
||||
# 关键:发送后保持连接打开,给板子留出读完 8 字节的窗口。
|
||||
# 若立即 close,CH395F 收到 FIN 会丢弃接收缓冲里尚未被板子读出的
|
||||
# 8 字节时间包,导致板子 net_recv 看到 CLOSED 而无数据、时间同步失败。
|
||||
# N34 阶段1-5 起板子会话收尾**不再 close**(单连接监听跨会话常驻,
|
||||
# 自愈收敛阶段1-5 起板子会话收尾**不再 close**(单连接监听跨会话常驻,
|
||||
# EOF 时 net_close 会连监听一起毁掉)——这里 recv 等 2s 宽限后由
|
||||
# 本端关闭;板子 net 层 auto_relisten 随即恢复监听,下一轮照常连接。
|
||||
sock.settimeout(2.0)
|
||||
|
||||
Reference in New Issue
Block a user