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:
2026-10-14 02:48:50 +08:00
parent 1a35b3684c
commit a12367981d
55 changed files with 852 additions and 2721 deletions
+90 -67
View File
@@ -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();
+1 -1
View File
@@ -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
View File
@@ -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
}
+4 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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 表一致)。
+2 -2
View File
@@ -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 -2
View File
@@ -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:
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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 提供灵敏判据:
+1 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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.
+4 -4
View File
@@ -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();
+1 -1
View File
@@ -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)