diff --git a/Drivers/BSP/CH395F/ch395f.c b/Drivers/BSP/CH395F/ch395f.c index c1817d10..87599495 100644 --- a/Drivers/BSP/CH395F/ch395f.c +++ b/Drivers/BSP/CH395F/ch395f.c @@ -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; +} diff --git a/Drivers/BSP/CH395F/ch395f.h b/Drivers/BSP/CH395F/ch395f.h index a39aa49d..cb3cd9f0 100644 --- a/Drivers/BSP/CH395F/ch395f.h +++ b/Drivers/BSP/CH395F/ch395f.h @@ -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 diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index 0ecfa972..68821deb 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -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%** | 各区的容量与常量来源: diff --git a/docs/内存管理/RAM预算台账.md b/docs/内存管理/RAM预算台账.md index 1056b566..fe53fb96 100644 --- a/docs/内存管理/RAM预算台账.md +++ b/docs/内存管理/RAM预算台账.md @@ -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 | 堆=配额 | diff --git a/docs/测试说明/CH395F_Test_Guide.md b/docs/测试说明/CH395F_Test_Guide.md index b941f5c6..4be57f86 100644 --- a/docs/测试说明/CH395F_Test_Guide.md +++ b/docs/测试说明/CH395F_Test_Guide.md @@ -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 | 链路缓存/事件护栏 | ✅ | ✅ = 已实现并验证 | 🟡 = 已实现待验证 | 🟠 = 待实现 diff --git a/scripts/ram_baseline.tsv b/scripts/ram_baseline.tsv index cea76a8d..511cd685 100644 --- a/scripts/ram_baseline.tsv +++ b/scripts/ram_baseline.tsv @@ -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 diff --git a/test/board/ch395f_test.h b/test/board/ch395f_test.h index 8feac10a..e93cd37d 100644 --- a/test/board/ch395f_test.h +++ b/test/board/ch395f_test.h @@ -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 diff --git a/test/board/ch395f_test_task.c b/test/board/ch395f_test_task.c index af8af638..514588e1 100644 --- a/test/board/ch395f_test_task.c +++ b/test/board/ch395f_test_task.c @@ -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