feat(N34 阶段2): ch395f 五原语只加不接线 + Phase 15 套件 20/20

实施方案 docs/网络/N34-自愈审计/实施方案.md §4 五原语,形态按
E2/E3/E4 定案(实验方案.md):
1. 类型化状态回读 + 每 Socket 缓存(open=0 时 tcp_state 哨兵
   0xFF;sn>7 边界返 {00,FF} 计 fault);
2. 事件护栏 ch395f_sock_events:只读本 Socket SINT,全局寄存器
   唯一读者=net_poll(F-NET-7 契约);
3. ch395f_sock_reset_begin/poll 非阻塞状态机:读空 RX -> CLOSE
   -> 排空事件(quiescent 2 轮) -> 轮询 byte0=00H;GRACEFUL/
   QUARANTINE 最短等待 100/400ms(Trap 23);WAIT_CLOSE 内置
   LISTEN 态二段 CLOSE 补发(TC1502b 实证 ~180ms 落定);
4. ch395f_cmd_done 状态回读判据(E3:GET_CMD_STATUS 对
   OPEN/LISTEN/CLOSE 无判别力);
5. 链路态缓存 ch395f_glob_event_feed + link_up/变化计数
   (B9 判据 PHY!=DISCONN)。

Phase 15 板测(硬件层模式,Socket 7,无需 PC):20/20 PASS,
含二段关闭语义断言(一段 05/00、二段 byte0=00)。

RAM +56B < 64B(g_sock_state 16B + g_rst 32B + 链路/fault 8B);
产品 Code 与阶段 2 前逐字节同 209780(无调用者被链接器裁剪)。
@test PASS;relisten_stress 20/20、n30 3/3 零回归。
RAM 台账同步:RW_IRAM1 97952B 74.7% / RW_CCM 57376B 87.9%。
This commit is contained in:
2026-10-13 12:43:17 +08:00
parent 33405a57b5
commit f7f17931c9
8 changed files with 634 additions and 6 deletions
+367
View File
@@ -1408,3 +1408,370 @@ void ch395f_get_unreach_info(uint8_t *p_buf) {
}
ch395f_spi_end();
}
/* ============================================================================
* N34 阶段 2 驱动原语(实施方案 docs/网络/N34-自愈审计/实施方案.md §4;
* 形态定案 docs/网络/N34-自愈审计/实验方案.md E2/E3/E4,2026-10-13 台架实证)
* 只加不接线:net_poll 全量刷新 / 两级预清改调用 / 链路 feed 接线 = 阶段 3。
* ========================================================================== */
/* 每 Socket 状态缓存(刷新点:主动回读 ch395f_get_sock_state;
* 阶段 3 起 net_poll 每轮对在用 Socket 各刷一次——E2 实测 18.1us/次,
* 4 监听 ~72us 远小于 1ms 轮询周期) */
static ch395f_sock_state_t g_sock_state[8];
/* sock_reset 非阻塞状态机槽位(每 Socket 一个;t0 用 16 位 tick 差值,
* 各阶段超时 <=2s,远小于 65535ms 回绕窗) */
#define CH395F_RST_ST_IDLE 0U
#define CH395F_RST_ST_RX_DRAIN 1U
#define CH395F_RST_ST_CLOSE 2U
#define CH395F_RST_ST_MIN_WAIT 3U
#define CH395F_RST_ST_EVT_DRAIN 4U
#define CH395F_RST_ST_WAIT_CLOSE 5U
#define CH395F_RST_RX_MAX_ROUNDS 4U /* RX 缓冲 2KB,单读即空;4 为安全上限 */
#define CH395F_RST_QUIESCENT 2U /* 排空事件判据:连续 2 轮无新位(E8 M5 口径) */
#define CH395F_RST_RX_TMO_MS 500U
#define CH395F_RST_EVT_TMO_MS 500U
#define CH395F_RST_CLS_TMO_MS 2000U
typedef struct {
uint16_t t0; /* 当前阶段起始 tick */
uint8_t state; /* CH395F_RST_ST_* */
uint8_t flags; /* bit7=mode(1=QUARANTINE) bit6..4=quiescent 轮数 bit3..0=drain 轮数(饱和) */
} ch395f_rst_slot_t;
static ch395f_rst_slot_t g_rst[8];
/* 链路态缓存(F-DRV-13) */
static uint8_t g_link_up;
static uint8_t g_link_valid;
static uint16_t g_link_changes;
/* 边界 fault 计数(sn 越界等;日志只留痕一次,其余只计数) */
static uint16_t g_p2_faults;
static uint8_t g_p2_fault_logged;
/*
* 函数功能:阶段 2 原语边界异常留痕(首次 DBG_ERROR,之后只计数)
* 入口参数:tag - 出错点短标签 const char*
*/
static void ch395f_p2_fault_note(const char *tag) {
g_p2_faults++;
if (g_p2_fault_logged == 0U) {
g_p2_fault_logged = 1U;
DBG_ERROR("ch395f P2 boundary fault @%s (further ones counted only)", tag);
}
}
/*
* 函数功能:读空并丢弃 RX(排空契约形态=整缓冲单读,与 ch395f_read_recv_buf
* 同形但不拷出——复位残渣是垃圾,零新增 RAM)
* 入口参数:sn - Socket 索引 uint8_t
* len - 本次读出字节数(<=CH395F_SPI_DMA_MAX_PAYLOAD) uint16_t
*/
static void ch395f_rst_rx_discard(uint8_t sn, uint16_t len) {
s_spi2_dma_tx_buf[0] = CH395F_CMD_READ_RECV_BUF_SN;
s_spi2_dma_tx_buf[1] = sn;
s_spi2_dma_tx_buf[2] = len & 0xFFU;
s_spi2_dma_tx_buf[3] = (len >> 8) & 0xFFU;
memset(&s_spi2_dma_tx_buf[4], 0xFFU, len);
ch395f_spi_begin();
(void)ch395f_spi_dma_transfer(4U + len);
ch395f_spi_end();
}
/*
* 函数功能:主动回读本 Socket 状态并刷新驱动缓存(缓存刷新点之一,另一个
* 是阶段 3 的 net_poll 每轮全量刷新)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:类型化状态;sn>7 返回 {CLOSED, 哨兵} 并计一次边界 fault
* 限定条件:芯片已初始化;netTask 上下文(与其它 CH395F 事务串行)
*/
ch395f_sock_state_t ch395f_get_sock_state(uint8_t sn) {
ch395f_sock_state_t st;
uint8_t raw[2];
if (sn >= 8U) {
ch395f_p2_fault_note("get_state sn");
st.open = CH395F_SOCKET_CLOSED;
st.tcp_state = CH395F_SOCK_STATE_NONE;
return st;
}
ch395f_get_socket_status(sn, raw);
st.open = raw[0];
st.tcp_state = (raw[0] == CH395F_SOCKET_OPEN) ? raw[1] : CH395F_SOCK_STATE_NONE;
g_sock_state[sn] = st;
return st;
}
/*
* 函数功能:读驱动缓存(零 SPI 成本;新鲜度取决于刷新点,见上)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:最近一次刷新的状态;sn>7 返回 {CLOSED, 哨兵}
* 限定条件:芯片已初始化
*/
ch395f_sock_state_t ch395f_sock_state_cached(uint8_t sn) {
if (sn >= 8U) {
ch395f_sock_state_t st;
ch395f_p2_fault_note("cached sn");
st.open = CH395F_SOCKET_CLOSED;
st.tcp_state = CH395F_SOCK_STATE_NONE;
return st;
}
return g_sock_state[sn];
}
/*
* 函数功能:判"本 Socket 正在监听"(读缓存:open=OPEN 且 TCP=LISTEN)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:1=在听 0=不在听 int
* 限定条件:缓存新鲜度同 ch395f_sock_state_cached
*/
int ch395f_sock_is_listening(uint8_t sn) {
ch395f_sock_state_t c = ch395f_sock_state_cached(sn);
return (c.open == CH395F_SOCKET_OPEN && c.tcp_state == CH395F_TCP_LISTEN) ? 1 : 0;
}
/*
* 函数功能:事件护栏——只读本 Socket SINT(读即清),永不触碰全局寄存器
* (F-NET-7 收窄:两级预清会清掉其它 Socket 的事件位)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:SINT 位图;sn>7 返回 0 uint8_t
* 限定条件:芯片已初始化;全局寄存器唯一读者=net_poll(驱动侧契约)
*/
uint8_t ch395f_sock_events(uint8_t sn) {
if (sn >= 8U) {
ch395f_p2_fault_note("events sn");
return 0U;
}
/* 契约:只读本 Socket SINT(0x30 读即清),永不触碰全局寄存器 0x19——
* 全局寄存器读即清且含全部 Socket 事件位,唯一读者=net_poll(F-NET-7) */
return ch395f_get_sock_int_status(sn);
}
/*
* 函数功能:启动 Socket 复合复位(非阻塞状态机,每 net_poll 步进;E4 定案序列
* 读空 RX -> CLOSE -> 排空事件(quiescent) -> 轮询 byte0=00H)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* mode - 复位模式(GRACEFUL 100ms / QUARANTINE 400ms 最短等待)
* 返回值:0=已启动 -1=参数错或已在运行(调用方逻辑错误) int
* 限定条件:netTask 上下文;调用前本层已放弃该会话(复位丢弃 RX 残渣)
*/
int ch395f_sock_reset_begin(uint8_t sn, ch395f_reset_mode_t mode) {
if (sn >= 8U) {
ch395f_p2_fault_note("reset_begin sn");
return -1;
}
if (g_rst[sn].state != CH395F_RST_ST_IDLE) {
/* 已在运行:调用方逻辑错误(同一 Socket 不允许并发复位) */
return -1;
}
g_rst[sn].state = CH395F_RST_ST_RX_DRAIN;
g_rst[sn].flags = (mode == CH395F_RESET_QUARANTINE) ? 0x80U : 0x00U;
g_rst[sn].t0 = (uint16_t)HAL_GetTick();
return 0;
}
/*
* 函数功能:复位状态机步进(net_poll 或测试循环周期调用,禁复制忙等)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:0=完成(含未启动,幂等) 1=进行中 -1=失败(超时/参数,槽位已复位可重开) int
* 限定条件:netTask 上下文
*/
int ch395f_sock_reset_poll(uint8_t sn) {
ch395f_rst_slot_t *p;
uint16_t now;
uint16_t el;
if (sn >= 8U) {
ch395f_p2_fault_note("reset_poll sn");
return -1;
}
p = &g_rst[sn];
if (p->state == CH395F_RST_ST_IDLE) {
return 0; /* 未启动/已完成:幂等 */
}
now = (uint16_t)HAL_GetTick();
el = (uint16_t)(now - p->t0);
switch (p->state) {
case CH395F_RST_ST_RX_DRAIN: {
/* E4 定案第 1 步:close 前 RX 必须为空(带脏缓冲关闭 -> 拆链停滞 ->
* 毒化下一会话且级联自持,C 变体 0/20 实证) */
uint16_t len = ch395f_get_recv_len(sn);
uint8_t rounds = (uint8_t)(p->flags & 0x0FU);
if (len == 0U) {
p->state = CH395F_RST_ST_CLOSE;
p->t0 = now;
break;
}
if (rounds < CH395F_RST_RX_MAX_ROUNDS) {
ch395f_rst_rx_discard(sn, (len > (uint16_t)CH395F_SPI_DMA_MAX_PAYLOAD)
? (uint16_t)CH395F_SPI_DMA_MAX_PAYLOAD : len);
p->flags = (uint8_t)((p->flags & 0xF0U) | (rounds + 1U));
} else {
/* 排不空(异常形态):不再恋战,继续 CLOSE,失败由 WAIT_CLOSE 超时兜底 */
ch395f_p2_fault_note("rx drain stall");
p->state = CH395F_RST_ST_CLOSE;
p->t0 = now;
}
if ((uint16_t)(now - p->t0) > CH395F_RST_RX_TMO_MS) {
p->state = CH395F_RST_ST_IDLE;
ch395f_p2_fault_note("rx drain tmo");
return -1;
}
break;
}
case CH395F_RST_ST_CLOSE:
/* E4 定案第 2 步:CLOSE(裸命令,不复制 poll_cmd_status 忙等——
* E3 实证其对 CLOSE 无判别力;完成判据=状态回读,E3 定案) */
ch395f_spi_begin();
ch395f_write_cmd(CH395F_CMD_CLOSE_SOCKET_SN);
ch395f_write_data(sn);
ch395f_spi_end();
p->state = CH395F_RST_ST_MIN_WAIT;
p->t0 = now;
break;
case CH395F_RST_ST_MIN_WAIT: {
uint16_t need = ((p->flags & 0x80U) != 0U) ? 400U : 100U;
if (el >= need) {
p->state = CH395F_RST_ST_EVT_DRAIN;
p->t0 = now;
p->flags = (uint8_t)(p->flags & 0x8FU); /* quiescent 计数清零 */
}
break;
}
case CH395F_RST_ST_EVT_DRAIN: {
/* E4 定案第 3 步:排空事件至 quiescent(陈旧粘滞位会误导事件驱动
* accept——E4 C 变体签名 0x04 陈旧 RECV;读即清,连续 2 轮无新位即静) */
uint8_t bits = ch395f_sock_events(sn);
if (bits == 0U) {
uint8_t q = (uint8_t)((p->flags >> 4) & 0x07U);
if ((q + 1U) >= CH395F_RST_QUIESCENT) {
p->state = CH395F_RST_ST_WAIT_CLOSE;
p->t0 = now;
} else {
p->flags = (uint8_t)((p->flags & 0x8FU) | ((q + 1U) << 4));
}
} else {
p->flags = (uint8_t)(p->flags & 0x8FU); /* 有新位:quiescent 计数清零 */
}
if (el > CH395F_RST_EVT_TMO_MS) {
p->state = CH395F_RST_ST_IDLE;
ch395f_p2_fault_note("evt drain tmo");
return -1;
}
break;
}
case CH395F_RST_ST_WAIT_CLOSE:
/* E4 定案第 4 步:轮询 byte0=00H(E4 实测 closed_ms=0——命令落定即空,
* 但为契约必需步) */
if (ch395f_cmd_done(sn, CH395F_DONE_CLOSE) == 1) {
p->state = CH395F_RST_ST_IDLE;
return 0;
}
/* 二段关闭实证(TC1501 台架 2026-10-13):LISTEN 态 Socket 的 CLOSE 返
* SUCCESS 但只拆监听(TCP 01->00),byte0 恒 05;需再发一次 CLOSE 才落
* 00(生产 net 层靠 Trap 09 open 重试吸收此行为)。复位状态机自行补发,
* 补发标记=drain 字段置 0xF(本阶段已用尽,复用安全)。 */
{
ch395f_sock_state_t c = ch395f_sock_state_cached(sn);
if (c.open == CH395F_SOCKET_OPEN && c.tcp_state == CH395F_TCP_CLOSED
&& (p->flags & 0x0FU) != 0x0FU) {
p->flags = (uint8_t)((p->flags & 0xF0U) | 0x0FU);
ch395f_spi_begin();
ch395f_write_cmd(CH395F_CMD_CLOSE_SOCKET_SN);
ch395f_write_data(sn);
ch395f_spi_end();
}
}
if (el > CH395F_RST_CLS_TMO_MS) {
p->state = CH395F_RST_ST_IDLE;
ch395f_p2_fault_note("wait closed tmo");
return -1;
}
break;
default:
p->state = CH395F_RST_ST_IDLE;
ch395f_p2_fault_note("bad rst state");
return -1;
}
return 1;
}
/*
* 函数功能:非阻塞命令完成查询(E3 定案=状态回读判据;单次回读 18.1us,E2 背书)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* what - 完成判据(OPEN=byte0 05H / LISTEN=+TCP 01H / CLOSE=byte0 00H)
* 返回值:1=已完成 0=未完成 -1=参数错 int
* 限定条件:netTask 上下文;勿复制 poll_cmd_status 忙等(E3 实证无判别力)
*/
int ch395f_cmd_done(uint8_t sn, ch395f_done_what_t what) {
ch395f_sock_state_t st;
if (sn >= 8U) {
return -1;
}
st = ch395f_get_sock_state(sn); /* 主动回读,单次 18.1us(E2) */
switch (what) {
case CH395F_DONE_OPEN:
return (st.open == CH395F_SOCKET_OPEN) ? 1 : 0;
case CH395F_DONE_LISTEN:
return (st.open == CH395F_SOCKET_OPEN && st.tcp_state == CH395F_TCP_LISTEN) ? 1 : 0;
case CH395F_DONE_CLOSE:
return (st.open == CH395F_SOCKET_CLOSED) ? 1 : 0;
default:
return -1;
}
}
/*
* 函数功能:喂全局中断位(阶段 3 接线:net_poll 读全局后喂 bit0~3;
* bit2 PHY_CHANGE 置位时重读 PHY 更新 link_up 缓存 + 变化计数)
* 入口参数:glob_bits - 全局中断状态低字节语义位图 uint16_t
* 返回值:无
* 限定条件:netTask 上下文(会发 SPI 读 PHY)
*/
void ch395f_glob_event_feed(uint16_t glob_bits) {
uint8_t up;
if ((glob_bits & CH395F_GINT_STAT_PHY_CHANGE) == 0U) {
return;
}
/* B9 判据(Trap 09 修正):PHY != DISCONN 即 link up */
up = (ch395f_get_phy_status() != CH395F_PHY_DISCONN) ? 1U : 0U;
if (g_link_valid == 0U) {
g_link_valid = 1U;
g_link_up = up;
} else if (up != g_link_up) {
g_link_up = up;
g_link_changes++;
}
}
/*
* 函数功能:读链路态缓存(首次查询惰性读 PHY 建档,不依赖 feed 已发生)
* 入口参数:无
* 返回值:1=link up 0=down uint8_t(B9 判据:PHY != DISCONN 即 up)
* 限定条件:芯片已初始化
*/
uint8_t ch395f_link_up_cached(void) {
if (g_link_valid == 0U) {
/* 惰性建档(首次查询):不依赖 feed 已发生过 */
g_link_up = (ch395f_get_phy_status() != CH395F_PHY_DISCONN) ? 1U : 0U;
g_link_valid = 1U;
}
return g_link_up;
}
/*
* 函数功能:读链路变化计数(net 层订阅:计数变化 -> 日志/可选重配)
* 入口参数:无
* 返回值:自建档以来的 PHY 连接状态翻转次数 uint16_t
* 限定条件:无
*/
uint16_t ch395f_link_change_count(void) {
return g_link_changes;
}
+107
View File
@@ -747,6 +747,113 @@ void ch395f_set_ttl(uint8_t sock, uint8_t ttl);
*/
void ch395f_get_unreach_info(uint8_t *p_buf);
/*
* 函数声明区 - N34 阶段 2 驱动原语(实施方案 docs/网络/N34-自愈审计/实施方案.md §4;
* 形态定案 docs/网络/N34-自愈审计/实验方案.md E2/E3/E4)
* ⚠ 阶段 2 = 只加不接线:net_poll 全量刷新、三处两级预清改调用、链路 feed 接线
* 均在阶段 3 收敛时进行。E2 实测:单次状态回读 18.1us;LISTEN 可见延迟 100% <=10ms。
*/
/* 类型化状态回读 + 每 Socket 缓存(F-DRV-1/3) */
#define CH395F_SOCK_STATE_NONE 0xFFU /* open=CLOSED 时 tcp_state 哨兵("无意义") */
typedef struct {
uint8_t open; /* byte0:CH395F_SOCKET_CLOSED / CH395F_SOCKET_OPEN */
uint8_t tcp_state; /* byte1:CH395F_TCP_*;open=CLOSED 时为 CH395F_SOCK_STATE_NONE */
} ch395f_sock_state_t;
/*
* 函数功能:主动回读本 Socket 状态并刷新驱动缓存(缓存刷新点之一,另一个
* 是阶段 3 的 net_poll 每轮全量刷新)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:类型化状态;sn>7 返回 {CLOSED, 哨兵} 并计一次边界 fault uint8_t×2
* 限定条件:芯片已初始化;netTask 上下文(与其它 CH395F 事务串行)
*/
ch395f_sock_state_t ch395f_get_sock_state(uint8_t sn);
/*
* 函数功能:读驱动缓存(零 SPI 成本;数据新鲜度取决于刷新点,见上)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:最近一次刷新的状态;sn>7 返回 {CLOSED, 哨兵}
*/
ch395f_sock_state_t ch395f_sock_state_cached(uint8_t sn);
/*
* 函数功能:判"本 Socket 正在监听"(读缓存:open=OPEN 且 TCP=LISTEN)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:1=在听 0=不在 int
*/
int ch395f_sock_is_listening(uint8_t sn);
/*
* 函数功能:事件护栏——只读本 Socket SINT(0x30,读即清),永不触碰全局寄存器
* (F-NET-7 收窄:两级预清"先读全局再读本 socket"会清掉其它 Socket 的
* 事件位;全局寄存器唯一读者=net_poll 写成驱动侧契约)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:SINT 位图;sn>7 返回 0 uint8_t
*/
uint8_t ch395f_sock_events(uint8_t sn);
/* 复合复位模式(F-DRV-4;两模式都必须排 RX——E4 定案,差异只剩最短等待) */
typedef enum {
CH395F_RESET_GRACEFUL = 0, /* 最短等待 100ms(Trap 23 二阶段实测值) */
CH395F_RESET_QUARANTINE /* 最短等待 400ms */
} ch395f_reset_mode_t;
/*
* 函数功能:启动 Socket 复合复位(非阻塞状态机,每 net_poll 步进;内部序列
* E4 定案:读空 RX -> CLOSE -> 排空事件(quiescent) -> 轮询 byte0=00H)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* mode - 复位模式 ch395f_reset_mode_t
* 返回值:0=已启动 -1=参数错或已在运行(调用方逻辑错误) int
* 限定条件:netTask 上下文;调用前本层已放弃该会话(复位会丢弃 RX 残渣)
*/
int ch395f_sock_reset_begin(uint8_t sn, ch395f_reset_mode_t mode);
/*
* 函数功能:复位状态机步进(在 net_poll 或测试循环里周期调用,禁忙等复制)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* 返回值:0=完成(含未启动,幂等) 1=进行中 -1=失败(超时/参数,槽位已复位可重开) int
*/
int ch395f_sock_reset_poll(uint8_t sn);
/* 命令完成判据(F-DRV-8;E3 定案=状态回读,GET_CMD_STATUS 对这三条无判别力) */
typedef enum {
CH395F_DONE_OPEN = 0, /* open 完成 = byte0 变 05H */
CH395F_DONE_LISTEN, /* listen 完成 = byte0=05H 且 TCP=01H(E2:<=10ms 可见) */
CH395F_DONE_CLOSE /* close 完成 = byte0=00H */
} ch395f_done_what_t;
/*
* 函数功能:非阻塞命令完成查询(单次主动回读 18.1us,E2 背书;勿复制
* poll_cmd_status 式忙等——E3 实证其对 OPEN/LISTEN/CLOSE 恒白等且无判别力)
* 入口参数:sn - Socket 索引 uint8_t 0 - 7
* what - 完成判据 ch395f_done_what_t
* 返回值:1=已完成 0=未完成 -1=参数错 int
*/
int ch395f_cmd_done(uint8_t sn, ch395f_done_what_t what);
/* 链路态缓存(F-DRV-13;接线在阶段 3:net_poll 读全局后喂 bit0~3) */
/*
* 函数功能:喂全局中断位(net_poll 读 GET_GLOB_INT_STATUS_ALL 后调用;
* bit2 PHY_CHANGE 置位时驱动重读 PHY 更新 link_up 缓存 + 变化计数)
* 入口参数:glob_bits - 全局中断状态低字节语义位图 uint16_t
*/
void ch395f_glob_event_feed(uint16_t glob_bits);
/*
* 函数功能:读链路态缓存(首次查询惰性读 PHY 建档)
* 返回值:1=link up 0=down uint8_t(判据 B9:PHY != DISCONN 即 up)
*/
uint8_t ch395f_link_up_cached(void);
/*
* 函数功能:读链路变化计数(net 层订阅用:计数变化 -> 日志/可选重配)
* 返回值:自建档以来的 PHY 连接状态翻转次数 uint16_t
*/
uint16_t ch395f_link_change_count(void);
#ifdef __cplusplus
}
#endif
@@ -37,9 +37,9 @@
| 区域 | 用量 | 容量 | 占比 |
|---|---|---|---|
| ER_IROM1 | 228,372 B (223.02 KB) | 1,048,576 B (1024 KB) | 21.8% |
| RW_IRAM1 | 97,896 B (95.60 KB) | 131,072 B (128 KB) | 74.7% |
| RW_IRAM1 | 97,952 B (95.66 KB) | 131,072 B (128 KB) | 74.7% |
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
| **Flash (ROM 合计)** | **229,476 B (224.1 KB)** | 1,048,576 B (1024 KB) | **21.9%** |
| **Flash (ROM 合计)** | **229,484 B (224.1 KB)** | 1,048,576 B (1024 KB) | **21.9%** |
<!-- MEM_SNAPSHOT:END -->
各区的容量与常量来源:
+2 -2
View File
@@ -18,11 +18,11 @@
| ER_IROM1 (Flash) | 228,372 B | 1,048,576 B | 21.8% | 参考 |
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
| RW_IRAM1 | 97,896 B | 131,072 B | 74.7% | PASS |
| RW_IRAM1 | 97,952 B | 131,072 B | 74.7% | PASS |
| 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 |
|---|---|---|---|
| 网络 | 13,442 B | 12 KB | ⚠ 超 1.1 KB |
| 网络 | 13,498 B | 12 KB | ⚠ 超 1.2 KB |
| 存储 | 7,056 B | 12 KB | OK |
| UI | 54,988 B | 16 KB | ⚠ 超 37.7 KB |
| RTOS 堆 | 57,376 B | 56 KB | 堆=配额 |
+29 -1
View File
@@ -84,7 +84,7 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
| 构建模式 | 启用宏 | 固件形态 |
|----------|--------|----------|
| **硬件层模式** | `ENABLE_PHASE1~5_TESTS` | netTask 自挂起(基础线程创建但不运行),CH395F 协议栈初始化由 testTask 完成后独占芯片执行阶段 1~5(裸调 `ch395f_*` 命令) |
| **硬件层模式** | `ENABLE_PHASE1~5_TESTS`(另含 12/15) | netTask 自挂起(基础线程创建但不运行),CH395F 协议栈初始化由 testTask 完成后独占芯片执行阶段 1~5(裸调 `ch395f_*` 命令) |
| **NET 层模式** | `ENABLE_PHASE6~11_TESTS` | 完整网络栈:netTask 运行 `net_poll()` 轮询,testTask 经 `net_socket` API 执行阶段 6~11(`net_layer_test_main()` 回显、`net_layer_client_test_main()` Client 等) |
**执行顺序严格递增**:先烧录硬件层固件完成阶段 1→2→3→4→5(全部通过),再切换为 NET 层固件执行阶段 6→7→…→11。硬件层测试期间 netTask 轮询不存在,从根上排除中断偷取与 SPI 并发问题。
@@ -1101,6 +1101,30 @@ IDLE → ACCEPT → [SEND: 分块发 → DONE] / [RECV: 分块收+校验 → DON
## 12.5 阶段 15:N34 阶段 2 驱动原语(硬件层,无需 PC)
验证 `ch395f` 新增自愈原语(`docs/网络/N34-自愈审计/实施方案.md` §4;形态定案
`docs/网络/N34-自愈审计/实验方案.md` E2/E3/E4)。硬件层模式独占芯片,用 Socket 7
(产品监听 0~4 不涉),无需 PC 配合。污染会话下的复位载荷完整性不在本 Phase
(已由 E4 bench 60 轮实证),本 Phase 只验状态机跑通与判据语义。
| 用例 | 内容 | 通过标准 |
|------|------|----------|
| TC-NET-1501 | 状态回读一致性 + `ch395f_cmd_done` 语义 + 二段关闭 + 哨兵/边界 | open done ≤50ms;listen done ≤20ms(E2:100% ≤10ms);缓存 05/01;**LISTEN 态 close 二段**:一段后 05/00(监听已拆、byte0 未落)、二段后 byte0=00 ≤500ms;closed 时 tcp_state=0xFF 哨兵;sn=8 越界返 {00,FF} |
| TC-NET-1502 | `sock_reset_begin/poll` 非阻塞状态机(OPEN 态,QUARANTINE) | begin 受理;运行中二次 begin 返 -1(busy reject);≤3000ms 落定 rc=0;复位后芯片 CLOSED、poll 幂等返 0、可重开 |
| TC-NET-1502b | LISTEN 态 Socket 复位(GRACEFUL,走 WAIT_CLOSE 二段补发路径) | ≤3000ms 落定 rc=0(实测 ~180ms);复位后 CLOSED |
| TC-NET-1503 | 链路态缓存 + 事件护栏边界 | 稳定链路 feed 两次不虚增变化计数;link_up 缓存=1;`sock_events(8)` 越界返 0 |
**二段关闭语义**(本 Phase 首跑实证,详见实验方案 E2 补充发现):LISTEN 态
`CLOSE_SOCKET` 返 SUCCESS 但只拆监听(TCP 01→00),byte0 恒 05;需再发一次
CLOSE 才落 00。与 Trap 07 的 `0x05/0x00` 驻留签名同源;`sock_reset_poll` 的
WAIT_CLOSE 阶段已内置二段补发(只补一次),生产 net 层则靠 Trap 09 open 重试吸收。
**运行方法**:`ch395f_test.h` 启用 `ENABLE_PHASE15_TESTS`(自动进入硬件层模式,
勿与 NET 层 Phase 混选)→ `@build` → 烧录 → 串口看 `=== Phase 15: 20/20 PASSED ===`。
---
## 13. Python 测试脚本参考
| 命令 | 模式 | 说明 |
@@ -1180,6 +1204,10 @@ IDLE → ACCEPT → [SEND: 分块发 → DONE] / [RECV: 分块收+校验 → DON
| TM-PHY-10 | TC-NET-1005 | P1 | 压力测试 | 🟡 |
| TM-PHY-10 | TC-NET-1006 | P1 | 稳定性测试 | 🟡 |
| TM-PHY-11 | TC-NET-1101 | P1 | 功能测试 | 🟡 |
| TM-PHY-15 | TC-NET-1501 | P0 | 状态回读/二段关闭 | ✅ |
| TM-PHY-15 | TC-NET-1502 | P0 | 复位状态机 | ✅ |
| TM-PHY-15 | TC-NET-1502b | P1 | LISTEN 态复位 | ✅ |
| TM-PHY-15 | TC-NET-1503 | P1 | 链路缓存/事件护栏 | ✅ |
✅ = 已实现并验证 | 🟡 = 已实现待验证 | 🟠 = 待实现
+1
View File
@@ -1,2 +1,3 @@
date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
2026-10-12 209492 18332 1104 154184 227824 21.7 97864 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;console_log.o=4120;ch395f.o=4106;default_task.o=2736;sys_log_mcu.o=1924
2026-10-13 209780 18592 1112 154264 228372 21.8 97952 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
1 date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
2 2026-10-12 209492 18332 1104 154184 227824 21.7 97864 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;console_log.o=4120;ch395f.o=4106;default_task.o=2736;sys_log_mcu.o=1924
3 2026-10-13 209780 18592 1112 154264 228372 21.8 97952 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
+3 -1
View File
@@ -67,6 +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 */
/* ---- 链路时延(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);
@@ -75,7 +76,8 @@ extern "C" {
#if defined(ENABLE_PHASE1_TESTS) || defined(ENABLE_PHASE2_TESTS) || \
defined(ENABLE_PHASE3_TESTS) || defined(ENABLE_PHASE4_TESTS) || \
defined(ENABLE_PHASE5_TESTS) || defined(ENABLE_PHASE12_TESTS)
defined(ENABLE_PHASE5_TESTS) || defined(ENABLE_PHASE12_TESTS) || \
defined(ENABLE_PHASE15_TESTS)
#define ENABLE_HW_LAYER_TESTS 1
#endif
+123
View File
@@ -1653,6 +1653,125 @@ static void phase5_run(void) {
TEST_REPORT("Phase 5");
}
#endif
/* ===================================================================
* Phase 15 — N34 阶段 2 驱动原语(无需 PC)
*
* 测试目的:验证 ch395f 新增原语(实施方案 docs/网络/N34-自愈审计/实施方案.md §4)
* 语义正确:类型化状态回读一致性 / cmd_done(E3 定案=状态回读)/
* sock_reset 非阻塞状态机跑通(E4 定案序列)/ 链路态缓存 / 事件护栏。
* 污染会话下的复位载荷完整性已由 E4 bench 60 轮实证(实验方案 E4),
* 本 Phase 只验"状态机跑通 + 判据语义",不依赖 PC。
* 上下文:硬件层模式(testTask 独占 CH395F),用 Socket 7(产品监听 0~4 不涉)。
* =================================================================== */
#ifdef ENABLE_PHASE15_TESTS
static void phase15_run(void) {
const uint8_t sn = 7U;
ch395f_sock_state_t st;
uint32_t t0;
int done;
int rc;
DBG_INFO("=== CH395F Phase 15 Tests (N34 phase-2 primitives) ===");
/* ---- TC-1501: 状态回读一致性 + cmd_done 语义(E2/E3 定案形态) ---- */
ch395f_set_proto_type(sn, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(sn, 5107U);
(void)ch395f_open_socket(sn);
t0 = HAL_GetTick();
done = 0;
while ((uint32_t)(HAL_GetTick() - t0) < 50U) {
if (ch395f_cmd_done(sn, CH395F_DONE_OPEN) == 1) { done = 1; break; }
HAL_Delay(1);
}
TEST_CHECK(done == 1, "TC1501 open done within 50ms");
(void)ch395f_tcp_listen(sn);
t0 = HAL_GetTick();
done = 0;
while ((uint32_t)(HAL_GetTick() - t0) < 20U) {
if (ch395f_cmd_done(sn, CH395F_DONE_LISTEN) == 1) { done = 1; break; }
HAL_Delay(1);
}
/* E2 矩阵:LISTEN 可见延迟 100% <=10ms(24 轮无长尾) */
TEST_CHECK(done == 1, "TC1501 listen done within 20ms (E2: 100 pct <=10ms)");
TEST_CHECK(ch395f_sock_is_listening(sn) == 1, "TC1501 cached is_listening");
st = ch395f_sock_state_cached(sn);
TEST_CHECK(st.open == CH395F_SOCKET_OPEN && st.tcp_state == CH395F_TCP_LISTEN,
"TC1501 cached state 05/01 (got %02X/%02X)", st.open, st.tcp_state);
/* 二段关闭实证(2026-10-13 本 TC 二跑定案):LISTEN 态 Socket 的
* CLOSE_SOCKET 返 SUCCESS 但只拆监听(TCP 01->00),byte0 恒 05;
* 再发一次 CLOSE 才落 00。生产 net 层靠 Trap 09 open 重试吸收此行为,
* sock_reset 状态机在 WAIT_CLOSE 阶段自行补发(见 ch395f.c)。 */
uint8_t close_st = ch395f_close_socket(sn);
TEST_CHECK(close_st == CH395F_ERR_SUCCESS,
"TC1501 close cmd st=0x%02X (expect 00)", close_st);
st = ch395f_get_sock_state(sn);
TEST_CHECK(st.open == CH395F_SOCKET_OPEN && st.tcp_state == CH395F_TCP_CLOSED,
"TC1501 listen-close stage1: 05/00 listen torn down (got %02X/%02X)",
st.open, st.tcp_state);
(void)ch395f_close_socket(sn);
t0 = HAL_GetTick();
done = -1;
while ((uint32_t)(HAL_GetTick() - t0) < 500U) {
if (ch395f_cmd_done(sn, CH395F_DONE_CLOSE) == 1) { done = (int)(HAL_GetTick() - t0); break; }
HAL_Delay(1);
}
TEST_CHECK(done >= 0, "TC1501 listen-close stage2: byte0=00 in %dms (win 500)", done);
st = ch395f_get_sock_state(sn);
TEST_CHECK(st.tcp_state == CH395F_SOCK_STATE_NONE,
"TC1501 tcp_state sentinel 0xFF when closed (got %02X)", st.tcp_state);
st = ch395f_get_sock_state(8U);
TEST_CHECK(st.open == CH395F_SOCKET_CLOSED && st.tcp_state == CH395F_SOCK_STATE_NONE,
"TC1501 sn=8 boundary -> CLOSED+sentinel");
/* ---- TC-1502: sock_reset 非阻塞状态机跑通(E4 定案序列) ---- */
(void)ch395f_open_socket(sn);
rc = ch395f_sock_reset_begin(sn, CH395F_RESET_QUARANTINE);
TEST_CHECK(rc == 0, "TC1502 begin accepted (rc=%d)", rc);
TEST_CHECK(ch395f_sock_reset_begin(sn, CH395F_RESET_QUARANTINE) == -1,
"TC1502 busy reject on second begin");
t0 = HAL_GetTick();
rc = 1;
while (rc == 1 && (uint32_t)(HAL_GetTick() - t0) < 3000U) {
rc = ch395f_sock_reset_poll(sn);
HAL_Delay(1);
}
/* QUARANTINE 最短等待 400ms + 各阶段,3s 窗内必落定 */
TEST_CHECK(rc == 0, "TC1502 reset done within 3000ms (rc=%d)", rc);
TEST_CHECK(ch395f_cmd_done(sn, CH395F_DONE_CLOSE) == 1, "TC1502 chip CLOSED after reset");
TEST_CHECK(ch395f_sock_reset_poll(sn) == 0, "TC1502 poll idempotent after done");
/* 复位后可重开(复位语义 = 回到可用态) */
(void)ch395f_open_socket(sn);
TEST_CHECK(ch395f_cmd_done(sn, CH395F_DONE_OPEN) == 1, "TC1502 reopen after reset OK");
(void)ch395f_close_socket(sn);
/* TC-1502b: LISTEN 态 Socket 复位(走 WAIT_CLOSE 二段补发路径,GRACEFUL) */
(void)ch395f_open_socket(sn);
(void)ch395f_tcp_listen(sn);
rc = ch395f_sock_reset_begin(sn, CH395F_RESET_GRACEFUL);
t0 = HAL_GetTick();
rc = 1;
while (rc == 1 && (uint32_t)(HAL_GetTick() - t0) < 3000U) {
rc = ch395f_sock_reset_poll(sn);
HAL_Delay(1);
}
TEST_CHECK(rc == 0, "TC1502b reset on LISTEN socket done (rc=%d, two-stage absorbed)", rc);
TEST_CHECK(ch395f_cmd_done(sn, CH395F_DONE_CLOSE) == 1, "TC1502b chip CLOSED after listen-reset");
/* ---- TC-1503: 链路态缓存 + 事件护栏边界 ---- */
uint16_t c0 = ch395f_link_change_count();
ch395f_glob_event_feed(CH395F_GINT_STAT_PHY_CHANGE); /* 首次 feed 建档 */
ch395f_glob_event_feed(CH395F_GINT_STAT_PHY_CHANGE); /* 链路稳定:不应虚增计数 */
TEST_CHECK(ch395f_link_change_count() == c0, "TC1503 no spurious link change on stable link");
TEST_CHECK(ch395f_link_up_cached() == 1U, "TC1503 link_up cached (bench link present)");
TEST_CHECK(ch395f_sock_events(8U) == 0U, "TC1503 sock_events sn=8 boundary -> 0");
(void)ch395f_sock_events(sn); /* 读即清护栏:合法调用不崩 */
TEST_REPORT("Phase 15");
}
#endif
/*
* CH395F 测试套件入口(由 test_config.c 的 start_test_task 统一调度)。
* 此处只负责 CH395F 自身的硬件层 / NET 层逻辑,套件选择已上移到 test_config.c。
@@ -1688,6 +1807,10 @@ void ch395f_test_run(void)
/* Phase 12(链路时延基线 L1/L2,M1):独立于 Phase 1~5 的 SPI 时延
* 采样,放硬件层阶段末尾执行(说明文档:链路延迟测试说明.md) */
net_latency_phase12_run();
#endif
#ifdef ENABLE_PHASE15_TESTS
/* Phase 15(N34 阶段 2 驱动原语):独占态下直调驱动,放 HW 层末尾 */
phase15_run();
#endif
}
#else