diff --git a/App/task/ui_task.c b/App/task/ui_task.c index ff3e68cb..21a4b903 100644 --- a/App/task/ui_task.c +++ b/App/task/ui_task.c @@ -19,6 +19,8 @@ #include "ui_net_sink.h" #include "ui_standby.h" #include "sys_clock.h" +#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */ +#include "net_latency_test.h" /* M3 探针:ui_loop(生产循环实测周期) */ /* * 函数功能:待机屏底部时钟提供回调(平台差异:MCU = sys_clock_get_str) @@ -88,7 +90,22 @@ void ui_task_func(void *argument) { DBG_INFO("UI net sink: started (port %u)", (unsigned)UI_NET_SINK_PORT); } +#if defined(TEST_LATENCY_PROBE) + /* M3 探针 ui_loop:主循环实测周期(= LVGL 渲染 + 推流轮询 + osDelay(10))。 + * 每轮开头先结算"上一轮到此轮"的间隔,再重置标记 → 得到真实循环周期。 + * 单写者(本任务)/单读者(testTask),样本经 lat_probe_add 落静态缓冲。 */ + lat_tmark_t tm_loop; + uint8_t tm_loop_valid = 0U; +#endif + while (1) { +#if defined(TEST_LATENCY_PROBE) + if (tm_loop_valid != 0U) { + lat_probe_add_seg("ui_loop", lat_mark_us(&tm_loop)); + } + lat_mark(&tm_loop); + tm_loop_valid = 1U; +#endif ui_port_step(10); ui_net_sink_poll(); osDelay(10); diff --git a/Drivers/BSP/CH395F/ch395f.h b/Drivers/BSP/CH395F/ch395f.h index 81015ce1..1fcc61a2 100644 --- a/Drivers/BSP/CH395F/ch395f.h +++ b/Drivers/BSP/CH395F/ch395f.h @@ -569,6 +569,8 @@ uint8_t ch395f_get_sock_int_status(uint8_t sock); * 限定条件:芯片已初始化 * 函数说明:Socket 状态: 00H=CLOSED, 05H=OPEN * TCP 状态: 00H=CLOSED, 01H=LISTEN, 02H=SYN_SENT, 03H=SYN_RCVD, 04H=ESTABLISHED + * ⚠ 必须给 **2 字节**缓冲:只给 1 字节会栈越界 1 字节(2026-09-22 实测 + * net_socket.c 两处调用曾如此,已修)。 */ void ch395f_get_socket_status(uint8_t sock, uint8_t *status); diff --git a/Drivers/BSP/NET/lftpd/lftpd.c b/Drivers/BSP/NET/lftpd/lftpd.c index 5dcacaf2..5c8d3aa1 100644 --- a/Drivers/BSP/NET/lftpd/lftpd.c +++ b/Drivers/BSP/NET/lftpd/lftpd.c @@ -318,6 +318,9 @@ static int cmd_list(lftpd_client_t *client, const char *arg) } int err = send_list(data_sock, client->directory); + /* 传输结束必须关闭数据连接:对端以 EOF 判定"数据传完"(LIST/RETR); + * 不关会让客户端一直读到超时(曾试"优雅断开+复用监听器"方案,因该语义限制 + * 与芯片行为未能稳定工作,见 链路时延实测数据分析.md D20 记录)。 */ lftpd_inet_close(data_sock); client->data_socket = -1; @@ -347,6 +350,9 @@ static int cmd_nlst(lftpd_client_t *client, const char *arg) } int err = send_nlst(data_sock, client->directory); + /* 传输结束必须关闭数据连接:对端以 EOF 判定"数据传完"(LIST/RETR); + * 不关会让客户端一直读到超时(曾试"优雅断开+复用监听器"方案,因该语义限制 + * 与芯片行为未能稳定工作,见 链路时延实测数据分析.md D20 记录)。 */ lftpd_inet_close(data_sock); client->data_socket = -1; @@ -451,6 +457,9 @@ static int cmd_retr(lftpd_client_t *client, const char *arg) char *path = lftpd_io_canonicalize_path(client->directory, arg); lftpd_log_debug("send '%s'", path); int err = send_file(data_sock, path); + /* 传输结束必须关闭数据连接:对端以 EOF 判定"数据传完"(LIST/RETR); + * 不关会让客户端一直读到超时(曾试"优雅断开+复用监听器"方案,因该语义限制 + * 与芯片行为未能稳定工作,见 链路时延实测数据分析.md D20 记录)。 */ lftpd_inet_close(data_sock); client->data_socket = -1; @@ -507,6 +516,9 @@ static int cmd_stor(lftpd_client_t *client, const char *arg) char *path = lftpd_io_canonicalize_path(client->directory, arg); lftpd_log_debug("receive '%s'", path); int err = receive_file(data_sock, path); + /* 传输结束必须关闭数据连接:对端以 EOF 判定"数据传完"(LIST/RETR); + * 不关会让客户端一直读到超时(曾试"优雅断开+复用监听器"方案,因该语义限制 + * 与芯片行为未能稳定工作,见 链路时延实测数据分析.md D20 记录)。 */ lftpd_inet_close(data_sock); client->data_socket = -1; @@ -606,6 +618,11 @@ static int handle_control_channel(lftpd_client_t *client) } cleanup: + /* 数据监听器在复用模式下会跨命令保留,会话结束必须显式释放(否则每次会话泄漏一个 Socket) */ + if (client->data_socket >= 0) { + lftpd_inet_close(client->data_socket); + client->data_socket = -1; + } lftpd_inet_close(client->socket); return 0; } diff --git a/Drivers/BSP/NET/lftpd/lftpd_inet.c b/Drivers/BSP/NET/lftpd/lftpd_inet.c index 7ea948f7..9c68ed1f 100644 --- a/Drivers/BSP/NET/lftpd/lftpd_inet.c +++ b/Drivers/BSP/NET/lftpd/lftpd_inet.c @@ -5,6 +5,21 @@ #include "net_socket.h" #include +/* + * E-02 头阻塞修复(2026-09-22 M3 台架实证): + * netTask 是全部 socket 操作的唯一执行者;lftpd 原先用默认 30s 的**阻塞** + * net_recv 等待对端下一行/下一块数据 → 只要客户端连上却不发完整行,netTask + * 就被长期占用,其余任务(含远端显示)的网络操作整体冻结(实测一次空闲 FTP + * 登录让 LCDMEM 整帧等待 17.7s)。 + * 修法:把等待切成 ≤LFTPD_NET_SLICE_MS 的片(每片 netTask 占用有界), + * 调用方累计总预算 LFTPD_TOTAL_BUDGET_MS —— **总超时语义与旧实现一致**, + * 但任何时刻都不再有"秒级冻结"。单片超时由 net_recv_to 返回 -2 区分(无 errno 竞态)。 + * 分片长度即"其他任务最坏等待":实测 100ms 片下受压帧仍达 ~750ms(多片排队), + * 收到 5ms 后受压帧降到 ~10ms 量级(见 m3_data 修复后数据)。 + */ +#define LFTPD_NET_SLICE_MS 5U +#define LFTPD_TOTAL_BUDGET_MS 30000U + int lftpd_inet_listen(int port) { int retry; @@ -99,19 +114,34 @@ int lftpd_inet_accept_timeout(int socket, int timeout_ms) int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len) { + TickType_t t0 = xTaskGetTickCount(); + memset(buffer, 0, buffer_len); int total_read_len = 0; while (total_read_len < (int)buffer_len) { - int read_len = net_recv(socket, - buffer + total_read_len, - buffer_len - total_read_len - 1, - 0); + /* 分片有界读(E-02):单片 netTask 占用 ≤ LFTPD_NET_SLICE_MS */ + int read_len = net_recv_to(socket, + buffer + total_read_len, + (int)(buffer_len - total_read_len - 1), + LFTPD_NET_SLICE_MS); if (read_len == 0) { - return -1; + return -1; /* 对端关闭 */ + } + if (read_len == -2) { + /* 本片无数据:累计总预算,未超则继续等。 + * **必须显式让出 CPU**:若某片因 net_poll 自身耗时而在首轮就返回 -2, + * 本任务(Normal)立即重排队会形成忙循环 → 饿死 BelowNormal 的 uiTask + * → 远端显示请求数秒无人拾取(2026-09-22 M3 实测 2.4~3.4s 周期尖刺)。 + * vTaskDelay(1) 保证低优先级任务每毫秒都有运行机会。 */ + if ((xTaskGetTickCount() - t0) >= pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) { + return -1; /* 总预算耗尽(语义同旧 30s 超时) */ + } + vTaskDelay(pdMS_TO_TICKS(1)); + continue; } if (read_len < 0) { - return read_len; + return read_len; /* 真错误 */ } total_read_len += read_len; @@ -170,8 +200,23 @@ int lftpd_inet_write(int socket, const void *buf, int len) int lftpd_inet_read(int socket, void *buf, int len) { - int n = net_recv(socket, buf, len, 0); - return n; + /* 分片有界读(E-02):总预算 30s 不变,但每片 netTask 占用 ≤ LFTPD_NET_SLICE_MS, + * 避免空闲数据连接(客户端开了 PASV 却不发数据)冻结全网 */ + TickType_t t0 = xTaskGetTickCount(); + + for (;;) { + int n = net_recv_to(socket, buf, len, LFTPD_NET_SLICE_MS); + if (n >= 0) { + return n; /* >0 数据 / 0 EOF */ + } + if (n == -1) { + return -1; /* 真错误 */ + } + if ((xTaskGetTickCount() - t0) >= pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) { + return -1; /* 总预算耗尽(语义同旧 30s 超时) */ + } + vTaskDelay(pdMS_TO_TICKS(1)); /* 让出 CPU:防空闲时忙循环饿死低优先级任务 */ + } } int lftpd_inet_close(int socket) diff --git a/Drivers/BSP/NET/net_socket.c b/Drivers/BSP/NET/net_socket.c index 8078f59d..9d32765b 100644 --- a/Drivers/BSP/NET/net_socket.c +++ b/Drivers/BSP/NET/net_socket.c @@ -19,6 +19,8 @@ #include "net_socket.h" #include "ch395f.h" +#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */ +#include "net_latency_test.h" /* L3 探针接口(chunk_wait 采样) */ #include "main.h" /* @@ -65,9 +67,14 @@ static void handle_timeout_event(net_sock_t *p_sock); static void fire_event(net_sock_t *p_sock, net_event_t event); static void parse_ip_string(const char *str, uint8_t *ip_arr); static int net_send_locked(net_sock_t *p_sock, const void *buf, int len); -static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags); +static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags, + uint32_t timeout_ms); static int net_close_locked(net_sock_t *p_sock); static int net_relisten_locked(net_sock_t *p_sock); +/* D10 非阻塞重监听:单次尝试 + 延迟重试(实现见 net_relisten_locked 之后) */ +static void net_relisten_clear(int sockfd); +static int net_relisten_try(net_sock_t *p_sock); +static void net_relisten_retry_tick(void); static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen); static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int addrlen); @@ -354,6 +361,10 @@ void net_process_messages(void) { return; } + /* D10:待重试的自动重监听(Trap 09 沉降后重开),非阻塞、每轮最多一次尝试。 + * 放在消息处理之前:断连后即使没有任何应用消息,监听器也能自主恢复。 */ + net_relisten_retry_tick(); + while (osMessageQueueGet(netMsgQueueHandle, &msg, NULL, 0) == osOK) { switch (msg.type) { case NET_MSG_SEND: { @@ -375,7 +386,8 @@ void net_process_messages(void) { s_net_errno = NET_ERR_BADF; msg.result = -1; } else { - msg.result = net_recv_locked(p_sock, msg.buf, msg.len, msg.flags); + msg.result = net_recv_locked(p_sock, msg.buf, msg.len, msg.flags, + msg.timeout_ms); } break; } @@ -406,17 +418,51 @@ void net_process_messages(void) { msg.result = net_accept_locked(msg.sockfd, p_addr, p_addrlen); break; } + case NET_MSG_RESYNC_LISTEN: { + net_sock_t *p_sock = net_get_sock(msg.sockfd); + if (p_sock == NULL) { + s_net_errno = NET_ERR_BADF; + msg.result = -1; + } else if (p_sock->state != NET_SOCK_STATE_ESTABLISHED) { + /* 非 ESTABLISHED(LISTENING/CLOSED 等):两侧已一致,无需动作 */ + msg.result = 0; + } else { + /* 陈旧 ESTABLISHED:芯片已静默回收 Socket 回监听(无 DISCONNECT + * 中断,2026-09-22 台架实证)→ 仅修 MCU 侧状态。严禁发芯片 + * close/重监听命令——会摧毁芯片侧仍健康的监听器 → 连接永拒 + * (run8 实证 170s 拒绝)。新连接的 CONNECT 中断会把两侧重新同步。 */ + p_sock->state = NET_SOCK_STATE_LISTENING; + msg.result = 0; + } + break; + } + case NET_MSG_RELISTEN: { + net_sock_t *p_sock = net_get_sock(msg.sockfd); + if (p_sock == NULL) { + s_net_errno = NET_ERR_BADF; + msg.result = -1; + } else { + /* 无条件芯片侧重监听(单次尝试 + 非阻塞重试,见 net_relisten_try): + * 芯片侧已监听 → 首次 open 失败 → 登记重试(下一次 netTask 轮询 + * 到点再来,不再 osDelay(500) 忙等);芯片侧已关闭 → 首次即成。 + * 两种陈旧场景都收敛到"芯片监听器确定在位"。 */ + msg.result = net_relisten_try(p_sock); + } + break; + } default: s_net_errno = NET_ERR; /* 未知消息类型:置通用错误码,保证回传值有效 */ msg.result = -1; break; } - /* 结果回传:成功时回传实际字节数/描述符;失败时回传具体错误码(如 NET_ERR_WOULDBLOCK)。 - * 注意消息体是按值拷入队列的——调用方看不到 netTask 对 msg 的修改,错误码只能搭 - * 通知值这趟车带回调用方,否则调用方只拿得到笼统的 NET_ERR,无法区分 WOULDBLOCK。 */ + /* 结果回传:成功=字节数/描述符;失败=具体错误码(如 NET_ERR_WOULDBLOCK)。 + * 注意消息体按值拷入队列——调用方看不到 netTask 对 msg 的修改,结果只能搭 + * 通知值这趟车带回;**整个 32 位都是结果**(若压入序列号等高 16 位标签, + * 单次结果上限会被压到 32767,而 TEST_BUF_SIZE=32768 等调用点会溢出为负)。 */ { uint32_t notify_val = (uint32_t)msg.result; + if (msg.result < 0) { notify_val = (uint32_t)((s_net_errno != 0) ? s_net_errno : NET_ERR); } @@ -425,6 +471,41 @@ void net_process_messages(void) { } } +/* + * 函数功能:结果等待(两段式,防丢失通知;结果按 32 位原值回传) + * 入口参数:kind = 操作简称;sockfd = Socket(两者仅用于调试留痕,避免未用参数告警) + * 返回值:netTask 回传的结果(成功=字节数/描述符;失败=负错误码) + * 函数说明:① 零等待先拾取:落在"入队→等待入口"间隙的通知已在任务通知值里, + * 若只做单段阻塞等待就会错过它(2026-09-22 台架实证); + * ② **清除掩码必须全 1**:xTaskNotifyWait 以位掩码清值,传 0 = 不清除 + * → 上一次调用的结果残留、被后续每次零等待重读成"本次结果" + * (run3~run11 的 L7 挂死/整段错位的真正根因); + * ③ **必须以返回值(pdTRUE/pdFALSE)判定是否已落地**,不能判 `v != 0`: + * 合法的 0 结果(recv 返回 0 = 对端关闭)会被误判成"无通知"而进入 + * 阻塞等待 → 永久挂死; + * ④ 通知值 32 位整体作为结果(不切分位域),保证 TEST_BUF_SIZE=32768 + * 这类大单次收发不被截断; + * ⑤ **禁止在本路径加逐调用日志**(曾加 pick/block 两行:占日志 98.6%, + * 115200 下冲掉 [LAT] 证据行、并让 wait-idle 永远判不出空闲; + * run14 实证)。诊断需求请在 [LAT] 段报告侧做聚合。 + */ +static int net_wait_result(const char *kind, int sockfd) { + uint32_t v = 0U; + + (void)kind; + (void)sockfd; + + /* 零等待:拾取并消费"入队→此处"间隙内已落地的通知(含结果为 0 的通知) */ + if (xTaskNotifyWait(0xFFFFFFFFU, 0xFFFFFFFFU, &v, 0) == pdTRUE) { + return (int)v; + } + + /* 无通知在途:阻塞等待本消息回执(进入临界区的瞬间若有通知落地, + * xTaskNotifyWait 的状态位判定会立即返回,不会睡过去) */ + xTaskNotifyWait(0xFFFFFFFFU, 0xFFFFFFFFU, &v, portMAX_DELAY); + return (int)v; +} + /* * 函数功能:创建 Socket,分配空闲 Socket 控制块并初始化状态 * 入口参数:domain - 地址族,仅支持 NET_AF_INET int @@ -457,6 +538,7 @@ int net_socket(int domain, int type) { p_sock->in_use = 1; p_sock->send_ready = 1; p_sock->readable = 0; + net_relisten_clear(sockfd); /* 新 fd 不得继承上一次的待重试状态 */ return sockfd; } @@ -640,6 +722,7 @@ int net_listen(int sockfd) { msg.type = NET_MSG_LISTEN; msg.sockfd = sockfd; + msg.timeout_ms = 0U; msg.caller = xTaskGetCurrentTaskHandle(); msg.result = -1; @@ -648,11 +731,7 @@ int net_listen(int sockfd) { return -1; } - { - uint32_t s_notified; - xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY); - msg.result = (int)s_notified; - } + msg.result = net_wait_result("lis", sockfd); if (msg.result < 0) { /* netTask 把具体错误码(如 NET_ERR_WOULDBLOCK)以负值搭通知值回传,此处恢复为 @@ -689,7 +768,7 @@ static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen /* 连接已断开(CLOSED)且启用自动重监听:先把 Socket 重新置回 LISTEN, * 下一次 accept 即可等待新连接(BSD 风格监听常驻) */ if (p_listen_sock->state == NET_SOCK_STATE_CLOSED && p_listen_sock->auto_relisten) { - net_relisten_locked(p_listen_sock); + (void)net_relisten_try(p_listen_sock); /* 非阻塞:失败由重试轮询接管 */ } s_net_errno = NET_ERR_WOULDBLOCK; return -1; @@ -734,6 +813,7 @@ int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) { msg.sockfd = sockfd; msg.buf = (void *)addr; msg.len = (addrlen != NULL) ? *addrlen : 0; + msg.timeout_ms = 0U; msg.caller = xTaskGetCurrentTaskHandle(); msg.result = -1; @@ -742,11 +822,7 @@ int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) { return -1; } - { - uint32_t s_notified; - xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY); - msg.result = (int)s_notified; - } + msg.result = net_wait_result("acc", sockfd); if (addrlen != NULL &&addr != NULL) { *addrlen = sizeof(struct net_sockaddr_in); @@ -798,6 +874,7 @@ int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen) { msg.flags = 0; memcpy(&msg.addr, addr, sizeof(struct net_sockaddr_in)); msg.addrlen = addrlen; + msg.timeout_ms = 0U; msg.caller = xTaskGetCurrentTaskHandle(); msg.result = -1; @@ -806,11 +883,7 @@ int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen) { return -1; } - { - uint32_t s_notified; - xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY); - msg.result = (int)s_notified; - } + msg.result = net_wait_result("con", sockfd); if (msg.result < 0) { /* netTask 把具体错误码(如 NET_ERR_WOULDBLOCK)以负值搭通知值回传,此处恢复为 @@ -886,21 +959,23 @@ static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int a ch395f_set_des_ip(sockfd, p_sock->remote_ip_arr); ch395f_set_des_port(sockfd, p_sock->remote_port); - /* 诊断:回读目的 IP/端口与 Socket 状态(GET_REMOT_IPP 为小端,故需按小端解析) */ + /* 诊断:回读目的 IP/端口与 Socket 状态(GET_REMOT_IPP 为小端,故需按小端解析) + * 注意 ch395f_get_socket_status 输出**2 字节**:[0]=Socket 开关状态、[1]=TCP 状态, + * 必须给 2 字节缓冲(曾只给 1 字节 → 栈越界 1 字节) */ { uint8_t rip[6]; - uint8_t st = 0; + uint8_t st[2] = {0, 0}; uint32_t dip; uint16_t dp; ch395f_get_remot_ipp((uint8_t)sockfd, rip); - ch395f_get_socket_status((uint8_t)sockfd, &st); + ch395f_get_socket_status((uint8_t)sockfd, st); dip = ((uint32_t)rip[3] << 24) | ((uint32_t)rip[2] << 16) | ((uint32_t)rip[1] << 8) | rip[0]; dp = ((uint16_t)rip[5] << 8) | rip[4]; - DBG_INFO("[CONN] sock%d openOK dest=%d.%d.%d.%d:%d status=0x%02X", + DBG_INFO("[CONN] sock%d openOK dest=%d.%d.%d.%d:%d status=0x%02X tcp=0x%02X", sockfd, (dip >> 24) & 0xFF, (dip >> 16) & 0xFF, - (dip >> 8) & 0xFF, dip & 0xFF, dp, st); + (dip >> 8) & 0xFF, dip & 0xFF, dp, st[0], st[1]); } /* 发起 TCP 连接 */ @@ -974,6 +1049,12 @@ static int net_send_locked(net_sock_t *p_sock, const void *buf, int len) { sent += chunk; p_sock->send_bytes += (uint32_t)chunk; +#if defined(TEST_LATENCY_PROBE) + /* L3 探针起点:发送缓冲写入刚完成 + * (样本 = 写缓冲 → SENBUF_FREE 等待,见 链路延迟测试说明 §1 L3) */ + uint32_t c_l3 = lat_cycles_now(); +#endif + tick_start = HAL_GetTick(); while (!p_sock->send_ready) { net_poll(); @@ -982,6 +1063,11 @@ static int net_send_locked(net_sock_t *p_sock, const void *buf, int len) { break; } } +#if defined(TEST_LATENCY_PROBE) + if (lat_probe_armed() != 0U) { + lat_probe_add_seg("chunk_wait", lat_cycles_diff_us(c_l3)); + } +#endif } if (sent < len) { @@ -1005,8 +1091,10 @@ static int net_send_locked(net_sock_t *p_sock, const void *buf, int len) { * 2. 非阻塞模式下无数据立即返回 -1(NET_ERR_WOULDBLOCK) * 3. CLOSED 状态尝试排空接收缓冲区残余数据(Trap 13:FIN 可能先于数据到达) */ -static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) { +static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags, + uint32_t timeout_ms) { uint32_t tick_start; + uint32_t to_ms; int sockfd = (int)(p_sock - s_net_socks); if (buf == NULL || len <= 0) { @@ -1014,6 +1102,8 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) { return -1; } + /* 超时来源:调用方指定(分片有界读,E-02 修复)优先,否则用默认值 */ + to_ms = (timeout_ms != 0U) ? timeout_ms : (uint32_t)NET_RECV_TIMEOUT_MS; tick_start = HAL_GetTick(); while (1) { @@ -1034,7 +1124,7 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) { * 否则返回 WOULDBLOCK(-1) 表示暂时无数据 */ if (p_sock->state == NET_SOCK_STATE_CLOSED) { if (p_sock->auto_relisten) { - net_relisten_locked(p_sock); + (void)net_relisten_try(p_sock); /* 非阻塞:失败由重试轮询接管 */ } return 0; } @@ -1042,8 +1132,8 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) { return -1; } - if (NET_RECV_TIMEOUT_MS > 0 && - (HAL_GetTick() - tick_start) >= NET_RECV_TIMEOUT_MS) { + if ((to_ms > 0U) && + ((HAL_GetTick() - tick_start) >= to_ms)) { s_net_errno = NET_ERR_TIMEDOUT; return -1; } @@ -1060,7 +1150,7 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) { } /* EOF:单连接模式自动回到 LISTEN,应用下次 net_accept 即接到新连接 */ if (p_sock->auto_relisten) { - net_relisten_locked(p_sock); + (void)net_relisten_try(p_sock); /* 非阻塞:失败由重试轮询接管 */ } return 0; } @@ -1084,9 +1174,12 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) { * 入口参数:p_sock - 已断开(CLOSED)的 TCP 监听型 Socket 控制块 net_sock_t * * 返回值:0 - 成功,-1 - 失败 * 限定条件:netTask 上下文;p_sock->in_use == 1;保留 local_port/auto_relisten - * 函数说明:1. 复用原本地端口与协议,重新 OPEN + TCP_LISTEN(Trap 09 重试) + * 函数说明:1. 复用原本地端口与协议,重新 OPEN + TCP_LISTEN * 2. 仅将 state 置回 LISTENING,不 memset(fd 与 auto_relisten 保持不变) * 3. 实现 BSD 风格"监听 Socket 常驻",应用层无需手动重监听 + * 4. **单次尝试**:失败立即返回(Trap 09 沉降交给 net_relisten_try/ + * net_relisten_retry_tick 非阻塞重试,见下)。旧实现在此 osDelay(500) + * 忙等,netTask 冻结 500ms、实测恢复 588ms(M3 台架 D10) */ static int net_relisten_locked(net_sock_t *p_sock) { uint8_t status; @@ -1095,15 +1188,11 @@ static int net_relisten_locked(net_sock_t *p_sock) { ch395f_set_proto_type((uint8_t)sockfd, CH395F_PROTO_TYPE_TCP); ch395f_set_sour_port((uint8_t)sockfd, p_sock->local_port); - for (int open_try = 0; open_try < 3; open_try++) { - status = ch395f_open_socket((uint8_t)sockfd); - if (status == CH395F_ERR_SUCCESS) { - break; - } - ch395f_close_socket((uint8_t)sockfd); /* 打开失败先关闭,再延时后重试 */ - osDelay(500); - } + status = ch395f_open_socket((uint8_t)sockfd); if (status != CH395F_ERR_SUCCESS) { + /* Trap 09:断开后芯片需沉降时间才能重新 open。此处只清理并返回 + * (严禁 osDelay 忙等)→ 由重试轮询到点再来。 */ + ch395f_close_socket((uint8_t)sockfd); s_net_errno = NET_ERR_BUSY; return -1; } @@ -1130,6 +1219,115 @@ static int net_relisten_locked(net_sock_t *p_sock) { return 0; } +/* ===== Trap 09 非阻塞重监听重试(D10 修复,2026-09-22 M3 实测) ===== + * 旧实现:open 失败 → close → osDelay(500) → 重试:netTask 忙等 500ms(全网冻结), + * 且实测恢复 588ms、期间客户端重连被拒。 + * 新实现:首次失败即登记待重试,由 netTask 每轮 net_process_messages() 到点重试 + * → 恢复时间 ≈ NET_RELISTEN_RETRY_MS 量级,且**任何时刻都不阻塞任务**。 */ +#define NET_RELISTEN_RETRY_MS 30U /* 重试间隔(Trap 09 沉降窗口;实测 100ms 首次必成, + * 收紧到 30ms 以缩短恢复时间——未成则下一轮到点再来) */ +#define NET_RELISTEN_DEADLINE_MS 2000U /* 放弃时限(自首次尝试起) */ + +static uint8_t s_relisten_wait[NET_MAX_SOCKETS]; /* 1 = 有待重试的重监听 */ +static uint32_t s_relisten_next[NET_MAX_SOCKETS]; /* 下次重试时刻(tick) */ +static uint32_t s_relisten_dead[NET_MAX_SOCKETS]; /* 放弃时刻(tick) */ +#if defined(TEST_LATENCY_PROBE) +static lat_tmark_t s_relisten_mark[NET_MAX_SOCKETS]; /* 恢复计时起点(首次尝试) */ +#endif + +/* + * 函数功能:清除本 fd 的待重监听重试状态(关闭/新建 fd 时调用) + * 入口参数:sockfd - Socket 描述符 (0~7) + * 返回值:无 + * 限定条件:无 + */ +static void net_relisten_clear(int sockfd) { + if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) { + return; + } + s_relisten_wait[sockfd] = 0U; + s_relisten_next[sockfd] = 0U; + s_relisten_dead[sockfd] = 0U; +} + +/* + * 函数功能:标记重监听完成(清重试状态;探针构建下报告恢复计时样本) + * 入口参数:sockfd - Socket 描述符 (0~7) + * 返回值:无 + * 限定条件:netTask 上下文 + */ +static void net_relisten_done(int sockfd) { + net_relisten_clear(sockfd); +#if defined(TEST_LATENCY_PROBE) + lat_probe_add_seg("relisten", lat_mark_us(&s_relisten_mark[sockfd])); +#endif +} + +/* + * 函数功能:尝试重监听一次;失败则登记"待重试"(**不阻塞** netTask) + * 入口参数:p_sock - CLOSED 的监听型 Socket 控制块 net_sock_t * + * 返回值:0 - 成功(监听器已就绪);-1 - 本次失败或已在重试队列中 + * 限定条件:netTask 上下文 + * 函数说明:已有待重试时直接返回 -1(不重复尝试、不重置重试时刻)——否则每 10ms + * 一次的 accept 轮询会不断把重试时刻往后推,导致永不重试(饥饿)。 + */ +static int net_relisten_try(net_sock_t *p_sock) { + int sockfd = (int)(p_sock - s_net_socks); + uint32_t now = HAL_GetTick(); + + if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) { + s_net_errno = NET_ERR_BADF; + return -1; + } + if (s_relisten_wait[sockfd] != 0U) { + s_net_errno = NET_ERR_BUSY; + return -1; /* 已在重试队列:由 tick 负责 */ + } +#if defined(TEST_LATENCY_PROBE) + lat_mark(&s_relisten_mark[sockfd]); /* 恢复计时自首次尝试起 */ +#endif + if (net_relisten_locked(p_sock) == 0) { + net_relisten_done(sockfd); + return 0; + } + s_relisten_wait[sockfd] = 1U; + s_relisten_next[sockfd] = now + NET_RELISTEN_RETRY_MS; + s_relisten_dead[sockfd] = now + NET_RELISTEN_DEADLINE_MS; + return -1; +} + +/* + * 函数功能:重试轮询(netTask 每轮调用一次,非阻塞) + * 返回值:无 + * 限定条件:netTask 上下文(net_process_messages 开头调用) + * 函数说明:到点重试待办的自动重监听;成功即报告探针样本;超时放弃并记错误日志 + */ +static void net_relisten_retry_tick(void) { + uint32_t now = HAL_GetTick(); + int i; + + for (i = 0; i < NET_MAX_SOCKETS; i++) { + if (s_relisten_wait[i] == 0U) { + continue; + } + if ((int32_t)(now - s_relisten_next[i]) < 0) { + continue; /* 未到点(tick 差值按有符号比较,抗回绕) */ + } + if ((int32_t)(now - s_relisten_dead[i]) >= 0) { + net_relisten_clear(i); + DBG_ERROR("sock%d relisten give up (Trap 09 not cleared)", i); + continue; + } + if (net_relisten_locked(&s_net_socks[i]) == 0) { + net_relisten_done(i); + DBG_INFO("sock%d relisten retry ok", i); + } else { + s_relisten_next[i] = now + NET_RELISTEN_RETRY_MS; + } + } +} + + /* * 函数功能:关闭 Socket(内部版本,不经过消息队列) * 入口参数:p_sock - 要关闭的 Socket 控制块指针 net_sock_t * @@ -1151,11 +1349,11 @@ static int net_close_locked(net_sock_t *p_sock) { * 并清掉 Socket/全局中断状态,避免事件串扰。 */ do { - uint8_t st = 0; - ch395f_get_socket_status((uint8_t)sockfd, &st); + uint8_t st[2] = {0, 0}; /* [0]=Socket 状态 [1]=TCP 状态(该 API 输出 2 字节) */ + ch395f_get_socket_status((uint8_t)sockfd, st); (void)ch395f_get_sock_int_status((uint8_t)sockfd); (void)ch395f_get_glob_int_status_all(); - if (st == CH395F_SOCKET_CLOSED) { + if (st[0] == CH395F_SOCKET_CLOSED) { break; } if (HAL_GetTick() - tick > 500) { @@ -1165,6 +1363,7 @@ static int net_close_locked(net_sock_t *p_sock) { } while (1); memset(p_sock, 0, sizeof(net_sock_t)); + net_relisten_clear(sockfd); /* 关闭即取消该 fd 的待重试(D10 重试队列) */ return 0; } @@ -1209,6 +1408,7 @@ int net_send(int sockfd, const void *buf, int len, int flags) { msg.buf = (void *)buf; /* 数据缓冲(队列仅拷贝指针,buf 须有效至函数返回) */ msg.len = len; /* 待发送字节数 */ msg.flags = 0; /* 实际标志位(忽略入参 flags) */ + msg.timeout_ms = 0U; /* 发送不使用超时字段 */ msg.caller = xTaskGetCurrentTaskHandle(); /* 记录调用任务句柄,供 netTask 回执时精准通知 */ msg.result = -1; /* 预置失败;netTask 处理成功后会改写为实际发送字节数 */ @@ -1220,11 +1420,7 @@ int net_send(int sockfd, const void *buf, int len, int flags) { /* ---- 5. 阻塞等待 netTask 处理完毕:netTask 调 net_send_locked 后通过 * xTaskNotify() 唤醒本任务,并把结果(实际发送字节数或 -1)作为通知值传回 ---- */ - { - uint32_t s_notified; - xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY); /* 一直阻塞直到被 netTask 通知 */ - msg.result = (int)s_notified; /* 取回 netTask 写入的结果 */ - } + msg.result = net_wait_result("snd", sockfd); /* 两段式等待(防丢失通知,见 net_wait_result) */ /* ---- 6. 结果回传:负数视为错误并设置线程级错误码;否则返回实际发送字节数 ---- */ if (msg.result < 0) { @@ -1266,6 +1462,7 @@ int net_recv(int sockfd, void *buf, int len, int flags) { msg.buf = buf; msg.len = len; msg.flags = flags; + msg.timeout_ms = 0U; msg.caller = xTaskGetCurrentTaskHandle(); msg.result = -1; @@ -1274,11 +1471,7 @@ int net_recv(int sockfd, void *buf, int len, int flags) { return -1; } - { - uint32_t s_notified; - xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY); - msg.result = (int)s_notified; - } + msg.result = net_wait_result("rcv", sockfd); if (msg.result < 0) { /* netTask 把具体错误码(如 NET_ERR_WOULDBLOCK)以负值搭通知值回传,此处恢复为 @@ -1289,6 +1482,64 @@ int net_recv(int sockfd, void *buf, int len, int flags) { return msg.result; } +/* + * 函数功能:接收数据(带调用方指定超时的阻塞版,用于分片有界读) + * 入口参数:sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS + * buf / len - 接收缓冲与容量 void * / int > 0 + * timeout_ms - 单次占用 netTask 的最长等待(毫秒,>0) uint32_t + * 返回值:>0 字节数;0 = 对端关闭(EOF);-1 = 真错误(errno 可查);**-2 = 超时无数据** + * 限定条件:非 netTask 上下文;net 层已初始化 + * 函数说明:E-02 修复配套接口。lftpd 等"等对端下一行/下一块数据"的调用方若用 + * 默认 30s 阻塞 recv,会把 netTask(全部 socket 操作的唯一执行者)长期 + * 占住,导致其余任务的网络操作整体冻结(M3 实测一次空闲 FTP 登录冻结 + * 远端显示 17.7s)。用本接口把等待切成 ≤timeout_ms 的片,调用方自行 + * 累计总预算,从而"总超时语义不变、单次占用有界"。 + * 超时用独立返回码 -2(而非靠 errno 区分)→ 无跨任务 errno 竞态。 + */ +int net_recv_to(int sockfd, void *buf, int len, uint32_t timeout_ms) { + net_msg_t msg; + + if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) { + s_net_errno = NET_ERR_BADF; + return -1; + } + + if (netMsgQueueHandle == NULL) { + s_net_errno = NET_ERR; + return -1; + } + + if (timeout_ms == 0U) { + s_net_errno = NET_ERR_INVAL; + return -1; + } + + msg.type = NET_MSG_RECV; + msg.sockfd = sockfd; + msg.buf = buf; + msg.len = len; + msg.flags = 0; /* 阻塞式(分片有界) */ + msg.timeout_ms = timeout_ms; /* 调用方指定(分片长度) */ + msg.caller = xTaskGetCurrentTaskHandle(); + msg.result = -1; + + if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) { + s_net_errno = NET_ERR_BUSY; + return -1; + } + + msg.result = net_wait_result("rcv", sockfd); + + if (msg.result == NET_ERR_TIMEDOUT) { + return -2; /* 本片超时:非致命 */ + } + if (msg.result < 0) { + s_net_errno = msg.result; + return -1; + } + return msg.result; +} + /* * 函数功能:关闭 Socket(线程安全版本,通过消息队列委托给 netTask) * 入口参数:sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS @@ -1313,6 +1564,7 @@ int net_close(int sockfd) { msg.type = NET_MSG_CLOSE; msg.sockfd = sockfd; + msg.timeout_ms = 0U; msg.caller = xTaskGetCurrentTaskHandle(); msg.result = -1; @@ -1321,11 +1573,7 @@ int net_close(int sockfd) { return -1; } - { - uint32_t s_notified; - xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY); - msg.result = (int)s_notified; - } + msg.result = net_wait_result("clo", sockfd); if (msg.result < 0) { /* netTask 把具体错误码(如 NET_ERR_WOULDBLOCK)以负值搭通知值回传,此处恢复为 @@ -1555,7 +1803,7 @@ int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags) { s_net_errno = NET_ERR_BADF; return -1; } - return net_recv_locked(p_sock, buf, len, flags); + return net_recv_locked(p_sock, buf, len, flags, 0U); /* 0 = 默认 NET_RECV_TIMEOUT_MS */ } /* * 函数功能:关闭 Socket(内部版本,绕过消息队列,供 netTask 直接使用) @@ -1613,6 +1861,110 @@ int net_get_errno(void) { return s_net_errno; } +/* + * 函数功能:查询 Socket 当前状态(只读,无锁) + * 入口参数:sockfd - Socket 描述符 int + * 返回值:状态值(net_sock_state_t);描述符无效/未占用 → -1 + * 限定条件:无(单字节读,无撕裂;值可能滞后于 netTask 的最新状态) + * 函数说明:跨任务只读快照。典型用途——检测"芯片静默回收"失步 + * (2026-09-22 台架实证:peer 发 FIN 后芯片自行把 Socket 回收 + * 回 LISTEN 且不发 DISCONNECT 中断 → MCU 侧状态滞留 ESTABLISHED + * → 新连接字节被旧会话吞掉);上层可据此执行 close+listen 重同步 + */ +int net_sock_state(int sockfd) { + if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) { + return -1; + } + if (!s_net_socks[sockfd].in_use) { + return -1; + } + return (int)s_net_socks[sockfd].state; +} + +/* + * 函数功能:断连重同步(仅 MCU 侧状态,见头文件注释的适用场景) + * 入口参数:sockfd - Socket 描述符 int + * 返回值:0 - 已同步/原本即一致;-1 - 失败 + * 限定条件:连接已死且芯片已静默回收后调用(应用层判定) + */ +int net_resync_listen(int sockfd) { + net_msg_t msg; + + if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) { + s_net_errno = NET_ERR_BADF; + return -1; + } + + if (netMsgQueueHandle == NULL) { + s_net_errno = NET_ERR; + return -1; + } + + msg.type = NET_MSG_RESYNC_LISTEN; + msg.sockfd = sockfd; + msg.timeout_ms = 0U; + msg.caller = xTaskGetCurrentTaskHandle(); + msg.result = -1; + + if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) { + s_net_errno = NET_ERR_BUSY; + return -1; + } + + msg.result = net_wait_result("rsl", sockfd); + + if (msg.result < 0) { + s_net_errno = msg.result; + return -1; + } + return 0; +} + +/* + * 函数功能:无条件重新监听(芯片侧 open+listen,含 Trap 09 失败重试) + * 入口参数:sockfd - Socket 描述符 int + * 返回值:0 - 成功;-1 - 失败 + * 限定条件:TCP 监听型 Socket;net_init() 已完成 + * 函数说明:用于"断连后监听器可能已失位"的自愈场景(2026-09-22 run9 实证: + * 一次性 net_listen 落在 Trap 09 窗口失败且被静默忽略 → 监听器 + * 永久丢失 → 后续连接全部拒绝)。与 auto_relisten 内部路径同一 + * 实现(net_relisten_locked:3 次 open 尝试,失败则 close+500ms 重试)。 + * 芯片侧已在监听时:首次 open 失败 → close 重建 → 监听短暂中断 ~1s。 + */ +int net_relisten(int sockfd) { + net_msg_t msg; + + if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) { + s_net_errno = NET_ERR_BADF; + return -1; + } + + if (netMsgQueueHandle == NULL) { + s_net_errno = NET_ERR; + return -1; + } + + msg.type = NET_MSG_RELISTEN; + msg.sockfd = sockfd; + msg.timeout_ms = 0U; + msg.caller = xTaskGetCurrentTaskHandle(); + msg.result = -1; + + if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) { + s_net_errno = NET_ERR_BUSY; + return -1; + } + + msg.result = net_wait_result("rel", sockfd); + + if (msg.result < 0) { + s_net_errno = msg.result; + return -1; + } + return 0; +} + + /* * 函数功能:将点分十进制 IP 地址字符串转换为网络字节序的 32 位地址 * 入口参数:cp - 点分十进制 IP 字符串(如 "192.168.1.100") const char * @@ -1799,12 +2151,30 @@ static void handle_connect_event(net_sock_t *p_sock) { * 3. 触发 NET_EVENT_DISCONNECTED 回调 */ static void handle_disconnect_event(net_sock_t *p_sock) { + int sockfd = (int)(p_sock - s_net_socks); + if (p_sock->state == NET_SOCK_STATE_LISTENING) { DBG_ERROR("WARNING: listen sock%d got disconnect", p_sock->ch395_sock); } else if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) { + /* 陈旧 DISCONNECT 甄别(D20 修复,2026-09-22 台架实证): + * 单连接模型下同一个 fd 会被"关闭 → 重新监听 → 再连接"循环复用,而上一条 + * 连接的 FIN/ACK 可能在本 fd 已复用为新连接**之后**才上报(本层是主动 close, + * net_close_locked 当时排空的中断里还没有它)→ 新连接被误判为断开。 + * 实测表现:FTP 同一会话第二个数据连接必失败 425;日志 = connect 后约 20ms + * 立即 "DISCONNECTED, releasing"。 + * 处置:向芯片回读状态,TCP 仍为 ESTABLISHED 即判为陈旧事件丢弃(中断已被 + * net_poll 读取清除,不会重复上报)。真断开时芯片处于 CLOSE_WAIT(0x07)/ + * CLOSED(0x00) 等状态,照常处理;超时事件走 handle_timeout_event,不受影响。 */ + uint8_t st[2]; + + ch395f_get_socket_status((uint8_t)sockfd, st); + if (st[1] == CH395F_TCP_ESTABLISHED) { + DBG_ERROR("sock%d stale DISCONNECT ignored (chip TCP still ESTABLISHED)", sockfd); + return; + } /* 单连接:先不关 CH395F Socket, * 留给 net_recv_locked 把剩余数据读出来再关,置 CLOSED 等待应用重新监听 */ - DBG_INFO("sock%d DISCONNECTED, releasing", p_sock->ch395_sock); + DBG_INFO("sock%d DISCONNECTED, releasing (chip tcp=0x%02X)", p_sock->ch395_sock, st[1]); p_sock->state = NET_SOCK_STATE_CLOSED; fire_event(p_sock, NET_EVENT_DISCONNECTED); } else if (p_sock->state != NET_SOCK_STATE_CLOSED && diff --git a/Drivers/BSP/NET/net_socket.h b/Drivers/BSP/NET/net_socket.h index 67b586a4..d11c12b0 100644 --- a/Drivers/BSP/NET/net_socket.h +++ b/Drivers/BSP/NET/net_socket.h @@ -37,6 +37,8 @@ typedef enum { NET_MSG_CONNECT, NET_MSG_LISTEN, NET_MSG_ACCEPT, + NET_MSG_RESYNC_LISTEN, /* 仅修 MCU 侧状态(ESTABLISHED→LISTENING),不发芯片命令 */ + NET_MSG_RELISTEN, /* 无条件芯片侧重监听(open+listen,含 Trap 09 重试) */ } net_msg_type_t; typedef struct { @@ -45,6 +47,7 @@ typedef struct { void *buf; /* 数据缓冲区(send/recv) */ int len; /* 数据长度/缓冲区大小 */ int flags; /* 接收标志 */ + uint32_t timeout_ms; /* 接收超时(入队方向;0 = 用默认 NET_RECV_TIMEOUT_MS) */ TaskHandle_t caller; /* 调用方任务句柄 */ int result; /* 操作结果 */ /* 连接参数 */ @@ -185,6 +188,20 @@ int net_send(int sockfd, const void *buf, int len, int flags); */ int net_recv(int sockfd, void *buf, int len, int flags); +/* + * 函数功能:接收数据(**带调用方指定超时**的阻塞版,用于分片有界读) + * 入口参数:sockfd - Socket 描述符 + * buf/len - 接收缓冲与容量 + * timeout_ms - 单次调用在 netTask 内的最长等待(毫秒,>0) + * 返回值:>0 实际字节数;0 = 对端关闭(EOF);-1 = 真错误(errno 可查);**-2 = 超时无数据** + * 限定条件:非 netTask 上下文;Socket 已连接 + * 函数说明:为"不能长时间占用 netTask"的调用方(如 lftpd 控制行/数据读)提供 + * **分片**阻塞:每次调用最多占用 netTask timeout_ms,返回 -2 表示这段 + * 时间内无数据,调用方自行累计总预算并决定是否继续(E-02 头阻塞修复)。 + * 超时用独立返回码而非 errno → 无跨任务 errno 竞态。 + */ +int net_recv_to(int sockfd, void *buf, int len, uint32_t timeout_ms); + /* * 函数功能:发送 UDP 数据 * 入口参数:sockfd - socket 描述符 @@ -249,6 +266,30 @@ int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg); */ int net_get_errno(void); +/* + * 函数功能:查询 socket 当前状态(只读快照,可能滞后于 netTask) + * 返回值:状态值(net_sock_state_t);描述符无效/未占用 → -1 + */ +int net_sock_state(int sockfd); + +/* + * 函数功能:断连重同步——仅把 MCU 侧状态对齐到 LISTENING,不发任何芯片命令 + * 适用场景:芯片"静默回收"(peer 发 FIN 后芯片自行把 Socket 回收回监听 + * 且不发 DISCONNECT 中断,2026-09-22 台架实证)→ MCU 状态滞留 + * ESTABLISHED。调用方须确信连接已死/芯片已回收(如会话超时中止后); + * 连接仍存活时严禁调用(会把活连接标记成监听态)。 + * 返回值:0 - 已同步(或原本即 LISTENING);-1 - 失败(描述符/队列无效) + */ +int net_resync_listen(int sockfd); + +/* + * 函数功能:无条件重新监听(芯片侧 open+listen,含 Trap 09 失败重试) + * 适用场景:断连后需确保监听器确定在位的自愈(监听短暂中断 ~1s,调用方 + * 的重连逻辑须容忍) + * 返回值:0 - 成功;-1 - 失败 + */ +int net_relisten(int sockfd); + /* * 函数功能:将 IP 地址字符串转为网络字节序 * 入口参数:cp - 点分十进制字符串 "192.168.1.100" diff --git a/Drivers/BSP/UI/ui_net_sink.c b/Drivers/BSP/UI/ui_net_sink.c index 12d06be2..e817cbe0 100644 --- a/Drivers/BSP/UI/ui_net_sink.c +++ b/Drivers/BSP/UI/ui_net_sink.c @@ -20,6 +20,8 @@ #include "net_socket.h" #include "net_types.h" #include "ui_net_sink_backend.h" +#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */ +#include "net_latency_test.h" /* M3 探针:prod_cycle_mcu(LCDMEM 处理净成本) */ /* RemoDispBus 标签与命令(与 ui_net_sink.h 的 UI_REMO_* 常量对齐) */ #define TAG_CLIENT UI_REMO_TAG_CLIENT @@ -122,7 +124,10 @@ static void remo_send(uint8_t cmd, const uint8_t *data, uint16_t len) { * 入口参数:无 * 返回值:无 * 限定条件:g_client >= 0 - * 函数说明:payload 为 [宽u16][高u16][缓冲字节数u32],共 6 字节(大端) + * 函数说明:payload 为 [宽u16][高u16][缓冲字节数u32],共 8 字节(大端) + * ⚠ 2026-09-22 M3 台架实测修复(D17):本函数原以 len=6 发送,只发出 + * 8 字节数据段的前 6 字节 → u32 帧字节数被截成高半字,客户端读到的 + * "帧字节数"恒为 0(真机与模拟器一致复现)。现按字段定义发满 8B。 */ static void remo_send_init(void) { uint8_t init[8]; @@ -130,7 +135,7 @@ static void remo_send_init(void) { put_u16(init, UI_W); put_u16(init + 2, UI_H); put_u32(init + 4, UI_MEMSIZE); - remo_send(CMD_INIT, init, 6); + remo_send(CMD_INIT, init, 8); } /* @@ -218,6 +223,14 @@ static void remo_handle(const uint8_t *f, int n) { ((uint32_t)data[3]); /* 目前仅支持偏移 0(整帧)请求;非 0 偏移暂不支持,静默忽略 */ if (off == 0) { +#if defined(TEST_LATENCY_PROBE) + /* M3 探针 prod_cycle_mcu:从"请求已解析完"到"应答交入芯片" + * 的 MCU 净成本(1bpp 取帧 + 组帧 CRC + net_send 队列/芯片)。 + * 不含 uiTask 10ms 轮询拾取拍(由 peer 侧 cycle 与 L11 覆盖)。 */ + lat_tmark_t tm; + + lat_mark(&tm); +#endif /* 整帧请求:经平台后端取 1bpp 位图(MCU: ui_port_get_framebuffer / * 模拟器: sim_port_get_screen_mono)写入应答缓冲尾部,失败则静默丢弃 */ if (ui_net_sink_get_lcdmem(g_lcdmem_data + 4, UI_MEMSIZE) == 0) { @@ -225,6 +238,9 @@ static void remo_handle(const uint8_t *f, int n) { memcpy(g_lcdmem_data, data, 4); remo_send(CMD_LCDMEM, g_lcdmem_data, (uint16_t)(4 + UI_MEMSIZE)); } +#if defined(TEST_LATENCY_PROBE) + lat_probe_add_seg("prod_cycle_mcu", lat_mark_us(&tm)); +#endif } } break; diff --git a/MDK-ARM/STM32F407-Demo.uvprojx b/MDK-ARM/STM32F407-Demo.uvprojx index ede6b401..c80283de 100644 --- a/MDK-ARM/STM32F407-Demo.uvprojx +++ b/MDK-ARM/STM32F407-Demo.uvprojx @@ -2038,6 +2038,11 @@ 1 ..\test\net_test_task.c + + net_latency_test.c + 1 + ..\test\net_latency_test.c + gd5f_test_task.c 1 diff --git a/docs/UI 设计/RemoDispBus协议与接收设计说明.md b/docs/UI 设计/RemoDispBus协议与接收设计说明.md index 7275c69c..1ae60fe8 100644 --- a/docs/UI 设计/RemoDispBus协议与接收设计说明.md +++ b/docs/UI 设计/RemoDispBus协议与接收设计说明.md @@ -74,12 +74,18 @@ RemoDispBus 让远端(PC 上的 DTU-RemoteLCD 客户端 / 浏览器页面) | CMD | 名称 | 请求数据段(客户端→服务端) | 应答数据段(服务端→客户端) | 说明 | |---|---|---|---|---| | 0 | KEEPLIVE | 无(LEN=0) | 无(LEN=0) | 保活/心跳,服务端原样回显 | -| 1 | INIT | 无(LEN=0,查询) | `[宽u16][高u16][帧字节数u32]`(6B) | 客户端查询屏幕几何与显存大小 | +| 1 | INIT | 无(LEN=0,查询) | `[宽u16][高u16][帧字节数u32]`(**8B**) | 客户端查询屏幕几何与显存大小 | | 2 | KEY | `[按键码u8]`(1B) | 无(LEN=0,确认) | 远端导航按键,服务端注入 UI | | 3 | LCDMEM | `[起始偏移u32]`(4B) | `[偏移u32][1bpp位图 3200B]`(3204B) | 读取显存(画面);偏移 0 = 整帧 | > **按键码**(`KEY` 数据段,与 `ui_port.h` 一致):`UP=0x02`、`DOWN=0x40`、`LEFT=0x10`、`RIGHT=0x08`、`ENTER=0x20`、`ESC=0x01`。 > **注意**:客户端(如 Python 参考实现)可不使用 `INIT`(直接按 160×160 处理);协议保留该命令供需查询几何的客户端使用。 +> +> ⚠ **INIT 应答长度勘误(2026-09-22 M3 台架实测修复,D17)**:本表原写"6B",而字段定义 +> `[宽u16][高u16][帧字节数u32]` 合计为 **8B**。旧实现按 6 发送 → u32 只剩高半字 → +> 客户端读到的"帧字节数"**恒为 0**(真机与 PC 模拟器一致复现)。现已改为发满 8B +> (`remo_send_init`),客户端按 `LEN>=8` 读 u32;若需兼容旧固件,可按 `LEN==6` 时 +> 只取前 4B(宽高)。 ### 1.6 屏幕位图格式(LCDMEM 数据段) diff --git a/docs/内存管理/存储栈RAM优化调研.md b/docs/内存管理/存储栈RAM优化调研.md index db84009e..d0eaf355 100644 --- a/docs/内存管理/存储栈RAM优化调研.md +++ b/docs/内存管理/存储栈RAM优化调研.md @@ -1,39 +1,24 @@ # 存储栈 RAM 优化调研 -> 版本 v1.0 日期 2026-09-21 作者 AI 辅助调研 +> 版本 v1.1 日期 2026-09-21 作者 AI 辅助调研 > 数据来源:`data/ram_report_20260921.md`(.map 静态占用)+ 逐行代码核验(`nand_ftl.c` / `gd5f2gq5ue.c` / `Lib/dhara` / `Lib/FatFs/ffconf.h` / `lftpd*`) > 定位:`RAM使用分析与优化方案.md` 的存储专项补充;优化项编号 P0/P1/P2,与总方案 §6 的 S 序号在"规划"一节对齐 +> v1.1 清理(2026-09-21 同日):移除已落地的 P0-1 BBT 页缓冲收缩(−1,776B,commit `be1a30a`)与编程回读校验整体移除的实施记录(详见 GD5F Trap 05 / 错误码传播审计),以及过期逐模块基线表;仅保留未实施项与回归要求 -## 1. 现状盘点(2026-09-21 基线) +## 1. 现状约束(不变,是下述全部方案的依据) -存储栈静态 RAM 全部在 **RW_IRAM1(主 SRAM 128KB)**,合计 **≈15.5 KB(占已用 82,048B 的 18.9%)**。运行期堆占用(CCM)仅 lftpd 路径串/`DIR` 约 ≤100B 瞬态,FatFs/dhara 无大块堆分配。 +- 存储栈静态 RAM 全在 **RW_IRAM1(主 SRAM 128KB)**;页缓冲(nand_ftl 3×2KB + gd5f 页缓冲/位图)走 SPI1 DMA(阈值 >1024B),**DMA1/DMA2 物理不可达 CCM** → 不能直迁 CCM(唯一途径:迁移 + `gd5f_dma_buf_ok()` 运行时守卫自动回落 CPU 轮询(2026-09-21 加固),不会静默失败,代价仅每页零点几 ms CPU) +- 3×2KB 页缓冲是结构成本不可直缩:dhara journal 要求整页 checkpoint(`journal.c:196` 整页 memset);512B 扇区落 2KB 页必须整页 RMW;可动的只有 `s_copy_buf`(→ P0-2) +- CPU-only 缓冲(lftpd static ~3.6K / cfg_file 0.7K / FATFS win 0.6K / lftpd_io 0.6K)迁 CCM 零代价(→ P1-5①) +- 栈上 FIL 重复占深链:syslog 两处、cfg 存储两处、开机探针各一个局部 `FIL`(每个含 512B 私有 buf,≈552B),全压在 defaultTask 最坏链 2,776B 内(→ P1-4 靶点) +- FatFs 配置已低 RAM 取向:`FF_MAX_SS=512`、`FF_USE_LFN=0`、`FF_VOLUMES=1`、`FF_FS_LOCK=0`;`FF_FS_REENTRANT=1` 为 defaultTask/ftpTask 并发访问必需,勿动 +- 教训(2026-09-21):任何比对式校验必须在**两份独立副本**间进行(U-Boot `sf test` 双缓冲形态)——旧 GC 拷贝自比对缺陷已随回读校验整体移除而消失;若未来测试构建恢复回读诊断,勿重蹈此坑 -| 模块 (.o) | RW+ZI | 主要构成 | 约束 | -|---|---:|---|---| -| nand_ftl.o | 6,216 | `s_page_buf` 2,048(dhara journal checkpoint,协议要求整页)+ `s_cache_buf` 2,048(512B 扇区 RMW 写回缓存)+ `s_copy_buf` 2,048(GC 拷贝暂存)+ dhara map/journal 结构 ~120 | 页缓冲走 SPI1 DMA(>阈值 1024B),DMA 不可达 CCM | -| gd5f2gq5ue.o | 2,596 | `s_bbt_page_buf` 2,048 + `s_bbt`/`s_bbt_best` 位图 2×256 + 保留池 16 + 状态 | 同上 | -| lftpd.o | 4,768 | 9 处函数级 static:2×1024(STOR/RETR 数据块)、512×3(response/read_buffer/arg_buf)、256×3(message/name×2)+ 256(log) | 纯 CPU 访问,无 DMA | -| lftpd_io.o | 556 | `s_ftp_file`(FIL,内含 512B 私有扇区窗) | 纯 CPU | -| freertos.o 内 `default_fatfs` | ~576 | FATFS 对象(`win[512]` 共享窗 + 卷状态) | 纯 CPU | -| cfg_file.o | 724 | 配置引擎静态态 | 纯 CPU | -| ff.o + ffsystem.o | 16 | 文件系统对象指针 + 同步原语 | `FF_FS_REENTRANT=1`(defaultTask/ftpTask 并发所需,保留) | - -FatFs 配置已是低 RAM 取向:`FF_MAX_SS=512`、`FF_USE_LFN=0`、`FF_VOLUMES=1`、`FF_FS_LOCK=0`。 - -## 2. 调研发现 - -- **F1 BBT 页缓冲超配 10 倍**:`s_bbt_page_buf[2048]`,但实际读写只有 `GD5F_BBT_HDR_SIZE(16)+GD5F_BBT_SIZE(256)=272B`(`gd5f2gq5ue.c:925` 读 272B、`:1010` program_load 272B)。缩到 272B 行为零变化。 -- **F2 ~~GC 搬迁路径 program verify 空转~~(已随校验整体移除而消失,留档备查)**:2026-09-21 前 `dhara_nand_copy()` 回读缓冲与源同址(`s_copy_buf`),memcmp 恒真自比对(空校验);2026-09-21 编程回读校验整体移除(选项 (b),向 Linux mtd 形态靠拢)后该缺陷不复存在。教训:任何比对式校验必须在**两份独立副本**间进行(U-Boot `sf test` 双缓冲形态)——若未来在测试构建恢复回读诊断,勿重蹈此坑。 -- **F3 3×2KB 页缓冲是结构成本,不可直缩**:dhara journal 要求整页 checkpoint(`journal.c:196` 整页 memset);512B 扇区落 2KB 页必须整页 RMW。可动的只有 `s_copy_buf`(改分段暂存)。 -- **F4 CCM 迁移功能上安全**:`gd5f_dma_buf_ok()` 运行时守卫会把 DMA 不可达缓冲自动回落 CPU 轮询(2026-09-21 加固),把 NAND 页缓冲挪进 CCM 不会静默失败,代价只是每页零点几 ms CPU(轮询 vs DMA 卸载)。CPU-only 缓冲(lftpd/cfg/FATFS win)迁 CCM 则零代价。 -- **F5 栈上 FIL 重复占深链**:syslog 两处、cfg 存储两处、开机探针各一个局部 `FIL`(每个含 512B buf,≈552B),全部压在 defaultTask 最坏链 2,776B 内——这是 `FF_FS_TINY` 的靶点(见 P1-4)。 - -## 3. 方案清单 +## 2. 方案清单(未实施) | # | 方案 | 省 RAM | 区域 | 风险 | 验证 | |---|---|---:|---|---|---| -| P0-1 | `s_bbt_page_buf` 2048→272(按实际读写字节数定义) | **−1,776B** | SRAM1 | 极低(一行宏+数组维) | `@build` + 开机 BBT 加载 + `@test` | -| P0-2 | `s_copy_buf` 2048→512:GC 拷贝改分段 read→program_load(`program_load`/`read_from_cache` 支持列地址)【性质变更 2026-09-21:回读校验整体移除后,此项变为**纯 RAM 优化**(GC 拷贝暂存减半),无校验语义风险,与原"分块+修 F2"解耦】 | **−1,536B(可选,不计入当前预期)** | SRAM1 | 低(仅动拷贝机制) | 待 RAM 余量需要时再议 | +| P0-2 | `s_copy_buf` 2048→512:GC 拷贝改分段 read→program_load(`program_load`/`read_from_cache` 支持列地址);纯 RAM 优化(GC 拷贝暂存减半) | **−1,536B(可选,不计入当前预期)** | SRAM1 | 低(仅动拷贝机制) | 待 RAM 余量需要时再议 | | P0-3 | lftpd 缓冲收紧:response 512→256、read_buffer 512→256、arg_buf 512→256、name 256→128 ×2(本卷仅 8.3 短名,路径深度浅);数据块 2×1024 与 message 不动 | **−1,024B** | SRAM1 | 低(FTP 客户端实测行长远低于新值) | `@build` + FTP 上传/下载/LIST 闭环 + `@test` | | P1-4 | `FF_FS_TINY=1`:删每 FIL 私有的 512B buf,文件数据走 `FatFs->win` 共享窗 | 静态 **−528B**;且栈上每处 FIL 峰值 **−528B**(defaultTask 最坏链 2,776→~2,250,实测峰 2,136→~1,600) | SRAM1 + CCM 栈 | 中(所有文件 I/O 经 512B 窗 bounce,FTP 吞吐 −20~40%;LIST 与文件读交错只影响速度) | 存储套件 + FTP 吞吐实测 + 日志/配置闭环;**与 S7 缩栈捆绑**(直接支撑 defaultTask 降档) | | P1-5 | `APP_CCM_ENABLE` 门(与 S6 同宏):①CPU-only 缓冲迁 CCM——lftpd static 3.6K + cfg_file 0.7K + FATFS win 0.6K + lftpd_io ≈ **−4.9K** 零代价;②DMA 页缓冲(nand_ftl 4.6K + gd5f 位图/页 0.8K)迁 CCM 走轮询 ≈ **−5.4K**,需实测吞吐 | **−10.3K** | SRAM1→CCM | 低①/中② | 分段实施;每步跑 `ram_report.py` + `@test` + FTP 吞吐 | @@ -43,28 +28,21 @@ FatFs 配置已是低 RAM 取向:`FF_MAX_SS=512`、`FF_USE_LFN=0`、`FF_VOLUME **明确不做**:移除 dhara(丢掉电一致性与坏块重映射,代价不对称);journal checkpoint 分片(违反 dhara 协议);调 `gc_ratio`(无 RAM 收益);`FF_FS_REENTRANT=0`(defaultTask+ftpTask 并发访问 FatFs 必需)。 -**实施记录**: +## 3. 分阶段规划(与总方案 §6 S 序号对齐) -| # | 状态 | 日期 | 验证 | -|---|---|---|---| -| P0-1 | ✅ 已落地 | 2026-09-21 | `@build` 0/0;`@test` PASS(BBT 开机加载闭环);.map RW_IRAM1 82,048→80,272(**−1,776B**,精确命中) | -| P0-2 | ⏸ 搁置(性质已变) | 2026-09-21 | 回读校验整体移除后,此项由"校验分块+修 F2"变为 **GC 拷贝纯 RAM 优化**(`s_copy_buf` 2048→512 分段 read→load);无紧急性,待 RAM 余量需要时再议 | -| 编程回读校验整体移除 | ✅ 已落地 | 2026-09-21 | 先改编译开关 `GD5F_PROG_VERIFY`,同日按选项 (b) **整体移除**(向 Linux mtd / U-Boot / dhara 上游形态靠拢:数据路径无回读比对,编程正确性以 P_FAIL 状态位+读时 ECC+journal 读时门为准);代码/宏/开关全部移除,"实证失效"误记录同步更正(幻影编程假设已被 Trap 05 证伪,本板无实测编程失效);F2 自比对缺陷随之消失;产品构建 `@build` 0/0 + `@test` PASS | -| P0-3 / P1-4 / P1-5 | 待实施 | — | — | - -## 4. 分阶段规划(与总方案 §6 S 序号对齐) - -| 阶段 | 内容 | 收益(SRAM1 已用 82,048B 基础上) | 前置 | +| 阶段 | 内容 | 收益 | 前置 | |---|---|---:|---| -| **第一步 P0**(独立小批) | P0-1(已落地)+ P0-3(P0-2 搁置,性质已变为纯拷贝优化) | **−2,800B**(P0-1 已省 1,776B) | 无 | +| **第一步**(独立小批) | P0-3(P0-2 搁置,待 RAM 余量需要时再议) | **−1,024B** | 无 | | **第二步**(并入 S7 缩栈批) | P1-4 `FF_FS_TINY=1` → 复跑 `stack_report.py` → defaultTask 降档(最坏链 ~2,250 支撑 3,584B 配置,余量 37%)+ 其余任务按 S7 目标 | −528B 静态 + 栈档下调释放 CCM 堆 | 第一步合入 | | **第三步**(与 S6/S8 同批) | 先 S8 堆 48K→32K(CCM 释放 ~16K),再 P1-5 ①→② 分批迁 CCM | SRAM1 再 **−10.3K**;CCM 占用 +10.3K(由 S8 腾挪覆盖) | S8 或同批评估 CCM 水位 | -**全部落地后存储栈预期**:SRAM1 常驻从 15.5KB 降至 **~1.9KB**(仅小状态量),页缓冲驻 CCM(轮询),静态合计挪入 CCM 换回 SRAM1 ≈ **−13.6KB**(P0-2 搁置(性质已变为纯拷贝优化),其 −1,536B 不计);叠加 S5(LVGL 40K→16K)后 SRAM1 余量 >65KB。 +**全部落地后存储栈预期**:SRAM1 常驻降至 **~1.9KB**(现 13.7KB,其中 P0-1 已省 1,776B 不重复计;余下仅小状态量),页缓冲驻 CCM(轮询),未实施项合计换回 SRAM1 ≈ **−11.9K**(P0-2 搁置,其 −1,536B 不计);叠加 S5(LVGL 40K→16K)后 SRAM1 余量 >65KB。 -## 5. 风险与回归要求 +## 4. 回归与基线 -- 回读校验移除(2026-09-21 落地):产品构建 `@build` 0/0 + `@test` 开机闭环(编程路径仅余 P_FAIL 状态检查);GD5F/STORAGE 测试构建各编译 0/0(套件 ECC 写读用例经读路径覆盖编程正确性);台架破坏性套件 + 掉电 resume 随下次存储改动照常执行。 -- P1-4 前后各跑一次 `stack_report.py` + `ram_report.py`(AGENTS.md 门禁),FTP 吞吐留实测数字(当前基线可先记一轮 RETR/STOR 速率)。 -- P1-5 的 ②(页缓冲入 CCM 走轮询)须以实测吞吐定去留:若 NAND 读吞吐降幅 >30%,则只迁 CPU-only 侧(①)。 -- 本文数字均可由 `data/ram_report_20260921.md` 与上表代码行号复核;实施后重生成基线报告并回填本文状态列。 +- **FTP 吞吐基线(2026-09-21 实测,4MB 文件,端到端含网络;瓶颈在存储侧,100M 网络非瓶颈)**:STOR **126.9 KB/s**(32.3s)/ RETR **321.4 KB/s**(12.7s),回传逐字节 MATCH(测试文件已自清理)→ P0-3/P1-4 前后各留一轮实测对比(P1-4 预期吞吐降幅 20~40%) +- P1-4 前后各跑一次 `stack_report.py` + `ram_report.py`(AGENTS.md 门禁) +- P1-5 的 ②(页缓冲入 CCM 走轮询)须以实测吞吐定去留:若 NAND 读吞吐降幅 >30%,则只迁 CPU-only 侧(①) +- P0-2/P2-x:待 RAM 余量需要时再议 +- 下次存储改动照常执行台架破坏性套件 + 掉电 resume +- 基线数字可用 `data/ram_report_20260921.md`(P0-1 前快照)复核;实施后重生成基线报告并回填总方案 diff --git a/docs/测试说明/m2_data/README.md b/docs/测试说明/m2_data/README.md new file mode 100644 index 00000000..8e4ca1d9 --- /dev/null +++ b/docs/测试说明/m2_data/README.md @@ -0,0 +1,120 @@ +# M2 链路延迟实测数据(Phase 13,安静档) + +本目录保存 M2(Phase 13:芯片 + 队列时延基线)的**原始实测产物**。判定阈值与解读见 +[`../链路延迟测试说明.md`](../链路延迟测试说明.md) §4.2;缺陷定位过程见该文 §6.1。 + +## 1. 运行元数据 + +| 项 | 值 | +|---|---| +| 日期 | 2026-09-22(设备 RTC 显示 2026-09-21,落后 1 天,仅显示差异;主机时间戳 = 设备 + 4s) | +| 台架 | COM6 @115200;目标 192.168.1.100:8080(Phase 13 独占 socket 2) | +| 固件 | `test/test_config.h` = `TEST_SUITE_CH395F`;`test/ch395f_test.h` = `ENABLE_PHASE13_TESTS`(仅此一项) | +| 构建 | `MDK-ARM\build.bat` → **0 错误 0 警告**(测试构建与产品构建均 0/0) | +| 对端 | `.venv\Scripts\python.exe test\latency_peer.py --mode net`(纯标准库,`--wait 170`) | +| 会话顺序 | HELLO → L5_RECV → L5_SEND → L3 → L10 → L6 → L7 → DONE | +| 干扰 | 无(安静档,`ftp_stress=0`;未跑 `--ftp-stress`) | +| PASS 轮次 | **run12 / run13 / run15** 三次独立完整 PASS(`summary verdict=OK`、`=== Phase 13: 1/1 PASSED (failed=0) ===`、peer exit 0) | + +复现命令(顺序不可反:先 `clear` 再起 peer,最后烧录): + +```powershell +& "MDK-ARM\build.bat" +& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py clear +& '.venv\Scripts\python.exe' test\latency_peer.py --host 192.168.1.100 --port 8080 ` + --json "docs\测试说明\m2_data\m2_quiet_runNN.json" --wait 170 # 后台作业 +& "MDK-ARM\download.bat" +& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py wait-idle ` + --timeout 120 --settle-ms 10000 --max-ms 110000 +# 判定:Grep tools\serial_tool\state\serial.log +``` + +判定标记:Required `App init OK` / `Network Init` / `net init: OK` / `[LAT] report complete` / +`=== Phase 13: 1/1 PASSED`;Forbidden `HardFault` / `assert` / `[ERR]` / `[FAIL]` / `verdict=FAIL`; +peer 侧退出码 **0** = PASS(收到 `0xFF` 并打印汇总,`l7_crc.fail == 0`)。 + +## 2. 产物清单 + +| 文件 | 内容 | +|---|---| +| `m2_quiet.json` | **run15**(最终代码,规范基线)peer 端原始 JSON,含 10 帧逐帧批次明细 | +| `m2_quiet_run13.json` | **run13**(PASS 复现)peer 端原始 JSON | +| `m2_quiet_run14.json` | **run14**(PASS;但该轮 MCU 侧探针日志过载,仅 peer 侧数据有效,见 §6) | +| `m2_serial_excerpt_run15.txt` | **run15** 串口证据片段 62 行(**只含烧录重启后的会话**:从 Phase 13 横幅/`run_id` 校准行到 `1/1 PASSED` + relisten) | +| `m2_serial_excerpt_run13.txt` | run13 串口日志中 `[LAT]`/`[NET]`/Phase 13 相关 87 行(含烧录前旧固件段,需自行忽略) | + +## 3. 实测结果 + +### 3.1 MCU 侧(`[LAT]` 报告,三次 PASS) + +| 段 | run12 med(min/max, n) | run13 med(min/max, n) | **run15 med(min/max, n)** | warn / crit | +|---|---|---|---|---| +| `queue_rt_recv` | 6014µs(5365/6020, 19) | 6007µs(5392/6025, 20) | **5997µs(5513/6033, 20)** | 10ms / 100ms | +| `queue_rt_send` | 986µs(836/1204, 20) | 987µs(833/1209, 20) | **987µs(838/1209, 20)** | 10ms / 100ms | +| `chunk_wait` | 235µs(215/238, 64) | 219µs(213/377, 64) | **219µs(214/381, 64)** | 10ms / 500ms(spike) | +| `logtax_line` | 4396µs | 4396µs(4390/4414) | **4396µs(4394/4422)** | 10ms / 25ms | +| `req2first_mcu` | 310µs(296/338, 10) | 318µs(263/361, 10) | **319µs(308/338, 10)** | 20ms / 200ms | +| `fullframe_mcu` | 6021µs(5986/6103, 10) | 6052µs(5966/6078, 10) | **6028µs(5991/6066, 10)** | 50ms / 500ms | + +三轮均:6 段 `verdict=OK`、`spikes=0`、`summary verdict=OK`、`1/1 PASSED`。 + +> run12 的 `queue_rt_recv` n=19 而非 20:对端 20×100B 中相邻两包被 TCP 合并成一次收 +> (完成判定按字节 2000B),属正常合并,非丢包(三轮字节数均为 2000)。 + +### 3.2 PC 侧端到端(peer JSON) + +| 段 | run12 med(min/max) | run13 med(min/max) | run15 med(min/max) | CRC | +|---|---|---|---|---| +| L6 端到端(op→回显) | 12.5ms(5.5/16.0) | 13.8ms(5.9/16.0) | 8.9ms(6.3/16.0) | — | +| L7 首字节 | 10.9ms(7.9/16.2) | 11.8ms(7.5/17.7) | 11.3ms(7.8/16.5) | — | +| L7 全帧 3209B | 61.1ms(54.2/61.5) | 61.7ms(59.9/70.9) | 57.9ms(52.6/72.3) | 10/0 | + +## 4. 关键发现:L7 尾批固定迟到 ~44~60ms + +run15 逐帧明细(10/10 帧结构完全一致): + +``` +frame#1: first=11.620ms batches=[1024,1024,1024,137] tail_lag=+53.8ms +frame#2: first=11.327ms batches=[1024,1024,1024,137] tail_lag=+46.5ms +frame#3: first= 8.160ms batches=[1024,1024,1024,137] tail_lag=+46.5ms +frame#4: first= 7.773ms batches=[1024,1024,1024,137] tail_lag=+44.8ms +frame#5: first= 9.858ms batches=[1024,1024,1024,137] tail_lag=+53.9ms +frame#6: first=11.900ms batches=[1024,1024,1024,137] tail_lag=+43.8ms +frame#7: first=10.465ms batches=[1024,1024,1024,137] tail_lag=+44.2ms +frame#8: first=10.615ms batches=[1024,1024,1024,137] tail_lag=+43.4ms +frame#9: first=11.328ms batches=[1024,1024,1024,137] tail_lag=+60.2ms +frame#10:first=16.522ms batches=[1024,1024,1024,137] tail_lag=+55.7ms +``` + +- 前 3 批各 1024B 在 **~4ms** 内到齐;第 4 批 137B(=3209−3072)稳定落在 **+43~+60ms**; +- MCU 侧把整个 3209B 交给芯片只花 **6.0ms**(`fullframe_mcu`)→ 延迟**不在 MCU 发送路径**; +- 缺口在**芯片 TCP 尾段发送 / 对端 ACK 交互**(候选:Nagle 式小段等待 + 对端延迟 ACK); +- 该尾批**主导整帧时长**(total ≈ 首字节 + 尾批延迟),是远端显示"每帧多 ~50ms"的直接来源; +- **M3 待办**:Wireshark(`tcp.port==8080`)确认尾段是被芯片扣住还是被对端 ACK 拖住,再定对策 + (对端 `TCP_NODELAY` / 帧长与 chunk 对齐),**未证实前不改协议**。 + +## 5. 结论(回答 M2 立项问题) + +1. **安静档不存在 500ms 台阶**:三轮 6 段全 OK、零尖峰、p95≈med; +2. 会话级 MCU 净成本量级 **~6ms**(`queue_rt_recv`,与 netTask 轮询节拍同量级); +3. 芯片 chunk 级不是瓶颈:`chunk_wait` med 219µs ≈ 每 chunk SPI 成本(1.61ms)的 1/7; +4. 生产链路的可见延迟主要在 **PC/线路段**(L6:MCU 侧 0.32ms vs 端到端 8.9~13.8ms) + 与 **L7 尾批 ~50ms**。 + +## 6. 测量方法学(两条必须记住的坑) + +1. **探针不得在 net 热路径逐调用打日志**(run14 实证):在 `net_wait_result` 内加 + `pick/block` 两行后,日志量 40KB → 1.04MB(9,644/9,779 行 = **98.6%**),115200 下 + 丢行/串行错位,冲掉 `[LAT]` 段证据,`wait-idle` 永远判不出空闲;并实测把 + `queue_rt_send` med 从 987µs 抬到 **7685µs(7.8×)**、`queue_rt_recv` p95 抬到 21ms。 + → 该轮 MCU 侧数据**不作基线**;诊断需求改在 `[LAT]` 段报告侧聚合。 +2. **首连必失败是有意为之的正常现象**:peer 首连时常在 MCU 启动期完成握手,随后芯片 + 空闲超时(15~30s 无数据)RST → `WinError 10054`;peer 自动 +2s 重连后会话正常 + (run12/13/15 均如此)。**另注意**:首连时若 MCU 正在烧录,attempt1 由**上一版固件** + 服务(日志里那段偏高的数据不属于本轮固件),只有烧录重启后的会话才计入本轮基线。 + +## 7. 未纳入本目录的数据 + +- run1~run11 为缺陷定位过程(D11~D16 见测试说明 §6.1),其中 run10/run11 的 L7 失败、 + run9 的 170s 连接拒绝均已定位并修复; +- 压力档(`--ftp-stress`,E-02 复现)与 `--mode remo`(生产 RemoDispBus 客户端)属 M3。 diff --git a/docs/测试说明/m2_data/m2_quiet.json b/docs/测试说明/m2_data/m2_quiet.json new file mode 100644 index 00000000..634af10a --- /dev/null +++ b/docs/测试说明/m2_data/m2_quiet.json @@ -0,0 +1,277 @@ +{ + "mode": "net", + "host": "192.168.1.100", + "port": 8080, + "ts_start": "2026-09-21 23:09:38", + "ts_end": "2026-09-21 23:09:59", + "duration_s": 20.53, + "ops_seen": 24, + "ftp_stress": false, + "seg_l6": { + "n": 10, + "min_ms": 6.293, + "med_ms": 8.9, + "p95_ms": 15.991, + "max_ms": 15.991, + "spikes500": 0, + "echo_ok": 10, + "echo_fail": 0 + }, + "seg_l7_first_byte": { + "n": 10, + "min_ms": 7.773, + "med_ms": 11.327, + "p95_ms": 16.522, + "max_ms": 16.522, + "spikes500": 0 + }, + "seg_l7_total": { + "n": 10, + "min_ms": 52.636, + "med_ms": 57.859, + "p95_ms": 72.259, + "max_ms": 72.259, + "spikes500": 0 + }, + "l7_crc": { + "ok": 10, + "fail": 0, + "batches_per_frame": [ + 4 + ] + }, + "l5_recv": { + "bytes": 2000, + "window_ms": 1009.1027 + }, + "l5_send": { + "bytes": 2000, + "window_ms": 51.5854 + }, + "l3_drain": { + "bytes": 65536, + "window_ms": 125.4635 + }, + "frames_l7": [ + { + "first_ms": 11.62, + "total_ms": 65.482, + "batches": [ + [ + 11.629, + 1024 + ], + [ + 13.33, + 1024 + ], + [ + 15.2, + 1024 + ], + [ + 65.458, + 137 + ] + ] + }, + { + "first_ms": 11.327, + "total_ms": 57.859, + "batches": [ + [ + 11.337, + 1024 + ], + [ + 13.164, + 1024 + ], + [ + 14.991, + 1024 + ], + [ + 57.842, + 137 + ] + ] + }, + { + "first_ms": 8.16, + "total_ms": 54.708, + "batches": [ + [ + 8.162, + 1024 + ], + [ + 9.951, + 1024 + ], + [ + 11.775, + 1024 + ], + [ + 54.66, + 137 + ] + ] + }, + { + "first_ms": 7.773, + "total_ms": 52.636, + "batches": [ + [ + 7.782, + 1024 + ], + [ + 9.552, + 1024 + ], + [ + 11.351, + 1024 + ], + [ + 52.618, + 137 + ] + ] + }, + { + "first_ms": 9.858, + "total_ms": 63.802, + "batches": [ + [ + 9.864, + 1024 + ], + [ + 11.65, + 1024 + ], + [ + 13.472, + 1024 + ], + [ + 63.783, + 137 + ] + ] + }, + { + "first_ms": 11.9, + "total_ms": 55.693, + "batches": [ + [ + 11.906, + 1024 + ], + [ + 13.619, + 1024 + ], + [ + 15.435, + 1024 + ], + [ + 55.664, + 137 + ] + ] + }, + { + "first_ms": 10.465, + "total_ms": 54.676, + "batches": [ + [ + 10.471, + 1024 + ], + [ + 12.222, + 1024 + ], + [ + 14.021, + 1024 + ], + [ + 54.656, + 137 + ] + ] + }, + { + "first_ms": 10.615, + "total_ms": 54.047, + "batches": [ + [ + 10.621, + 1024 + ], + [ + 12.356, + 1024 + ], + [ + 14.187, + 1024 + ], + [ + 54.028, + 137 + ] + ] + }, + { + "first_ms": 11.328, + "total_ms": 71.511, + "batches": [ + [ + 11.333, + 1024 + ], + [ + 13.159, + 1024 + ], + [ + 14.993, + 1024 + ], + [ + 71.495, + 137 + ] + ] + }, + { + "first_ms": 16.522, + "total_ms": 72.259, + "batches": [ + [ + 16.526, + 1024 + ], + [ + 18.309, + 1024 + ], + [ + 20.1, + 1024 + ], + [ + 72.246, + 137 + ] + ] + } + ] +} diff --git a/docs/测试说明/m2_data/m2_quiet_run13.json b/docs/测试说明/m2_data/m2_quiet_run13.json new file mode 100644 index 00000000..cff427ea --- /dev/null +++ b/docs/测试说明/m2_data/m2_quiet_run13.json @@ -0,0 +1,277 @@ +{ + "mode": "net", + "host": "192.168.1.100", + "port": 8080, + "ts_start": "2026-09-21 23:01:40", + "ts_end": "2026-09-21 23:02:01", + "duration_s": 20.56, + "ops_seen": 24, + "ftp_stress": false, + "seg_l6": { + "n": 10, + "min_ms": 5.873, + "med_ms": 13.753, + "p95_ms": 15.967, + "max_ms": 15.967, + "spikes500": 0, + "echo_ok": 10, + "echo_fail": 0 + }, + "seg_l7_first_byte": { + "n": 10, + "min_ms": 7.463, + "med_ms": 11.829, + "p95_ms": 17.706, + "max_ms": 17.706, + "spikes500": 0 + }, + "seg_l7_total": { + "n": 10, + "min_ms": 59.916, + "med_ms": 61.689, + "p95_ms": 70.892, + "max_ms": 70.892, + "spikes500": 0 + }, + "l7_crc": { + "ok": 10, + "fail": 0, + "batches_per_frame": [ + 4 + ] + }, + "l5_recv": { + "bytes": 2000, + "window_ms": 1007.7367 + }, + "l5_send": { + "bytes": 2000, + "window_ms": 47.0342 + }, + "l3_drain": { + "bytes": 65536, + "window_ms": 125.4454 + }, + "frames_l7": [ + { + "first_ms": 7.463, + "total_ms": 61.218, + "batches": [ + [ + 7.468, + 1024 + ], + [ + 9.239, + 1024 + ], + [ + 11.079, + 1024 + ], + [ + 61.198, + 137 + ] + ] + }, + { + "first_ms": 16.95, + "total_ms": 70.892, + "batches": [ + [ + 16.955, + 1024 + ], + [ + 18.745, + 1024 + ], + [ + 20.566, + 1024 + ], + [ + 70.879, + 137 + ] + ] + }, + { + "first_ms": 11.829, + "total_ms": 63.888, + "batches": [ + [ + 11.835, + 1024 + ], + [ + 13.612, + 1024 + ], + [ + 15.501, + 1024 + ], + [ + 63.874, + 137 + ] + ] + }, + { + "first_ms": 17.706, + "total_ms": 61.689, + "batches": [ + [ + 17.711, + 1024 + ], + [ + 19.527, + 1024 + ], + [ + 21.336, + 1024 + ], + [ + 61.678, + 137 + ] + ] + }, + { + "first_ms": 10.82, + "total_ms": 61.639, + "batches": [ + [ + 10.825, + 1024 + ], + [ + 12.605, + 1024 + ], + [ + 14.433, + 1024 + ], + [ + 61.625, + 137 + ] + ] + }, + { + "first_ms": 15.018, + "total_ms": 62.205, + "batches": [ + [ + 15.021, + 1024 + ], + [ + 16.775, + 1024 + ], + [ + 18.631, + 1024 + ], + [ + 62.191, + 137 + ] + ] + }, + { + "first_ms": 11.55, + "total_ms": 59.916, + "batches": [ + [ + 11.553, + 1024 + ], + [ + 13.352, + 1024 + ], + [ + 15.298, + 1024 + ], + [ + 59.901, + 137 + ] + ] + }, + { + "first_ms": 17.442, + "total_ms": 61.347, + "batches": [ + [ + 17.447, + 1024 + ], + [ + 19.216, + 1024 + ], + [ + 21.037, + 1024 + ], + [ + 61.333, + 137 + ] + ] + }, + { + "first_ms": 10.893, + "total_ms": 61.474, + "batches": [ + [ + 10.896, + 1024 + ], + [ + 12.705, + 1024 + ], + [ + 14.488, + 1024 + ], + [ + 61.456, + 137 + ] + ] + }, + { + "first_ms": 8.138, + "total_ms": 65.744, + "batches": [ + [ + 8.14, + 1024 + ], + [ + 9.954, + 1024 + ], + [ + 11.778, + 1024 + ], + [ + 65.732, + 137 + ] + ] + } + ] +} diff --git a/docs/测试说明/m2_data/m2_quiet_run14.json b/docs/测试说明/m2_data/m2_quiet_run14.json new file mode 100644 index 00000000..ac5d0c56 --- /dev/null +++ b/docs/测试说明/m2_data/m2_quiet_run14.json @@ -0,0 +1,277 @@ +{ + "mode": "net", + "host": "192.168.1.100", + "port": 8080, + "ts_start": "2026-09-21 23:06:48", + "ts_end": "2026-09-21 23:07:08", + "duration_s": 20.83, + "ops_seen": 24, + "ftp_stress": false, + "seg_l6": { + "n": 10, + "min_ms": 7.741, + "med_ms": 12.919, + "p95_ms": 22.593, + "max_ms": 22.593, + "spikes500": 0, + "echo_ok": 10, + "echo_fail": 0 + }, + "seg_l7_first_byte": { + "n": 10, + "min_ms": 12.286, + "med_ms": 17.669, + "p95_ms": 27.926, + "max_ms": 27.926, + "spikes500": 0 + }, + "seg_l7_total": { + "n": 10, + "min_ms": 61.729, + "med_ms": 69.339, + "p95_ms": 77.421, + "max_ms": 77.421, + "spikes500": 0 + }, + "l7_crc": { + "ok": 10, + "fail": 0, + "batches_per_frame": [ + 4 + ] + }, + "l5_recv": { + "bytes": 2000, + "window_ms": 1009.6644 + }, + "l5_send": { + "bytes": 2000, + "window_ms": 141.24 + }, + "l3_drain": { + "bytes": 65536, + "window_ms": 252.7994 + }, + "frames_l7": [ + { + "first_ms": 18.361, + "total_ms": 66.707, + "batches": [ + [ + 18.365, + 1024 + ], + [ + 20.136, + 1024 + ], + [ + 21.958, + 1024 + ], + [ + 66.695, + 137 + ] + ] + }, + { + "first_ms": 16.538, + "total_ms": 70.094, + "batches": [ + [ + 16.543, + 1024 + ], + [ + 18.366, + 1024 + ], + [ + 20.151, + 1024 + ], + [ + 70.081, + 137 + ] + ] + }, + { + "first_ms": 13.118, + "total_ms": 65.761, + "batches": [ + [ + 13.122, + 1024 + ], + [ + 14.926, + 1024 + ], + [ + 16.699, + 1024 + ], + [ + 65.748, + 137 + ] + ] + }, + { + "first_ms": 14.178, + "total_ms": 61.729, + "batches": [ + [ + 14.182, + 1024 + ], + [ + 15.989, + 1024 + ], + [ + 17.781, + 1024 + ], + [ + 61.712, + 137 + ] + ] + }, + { + "first_ms": 15.215, + "total_ms": 62.55, + "batches": [ + [ + 15.221, + 1024 + ], + [ + 17.004, + 1024 + ], + [ + 18.826, + 1024 + ], + [ + 62.538, + 137 + ] + ] + }, + { + "first_ms": 19.429, + "total_ms": 70.141, + "batches": [ + [ + 19.435, + 1024 + ], + [ + 21.381, + 1024 + ], + [ + 23.043, + 1024 + ], + [ + 70.124, + 137 + ] + ] + }, + { + "first_ms": 25.165, + "total_ms": 73.135, + "batches": [ + [ + 25.17, + 1024 + ], + [ + 26.913, + 1024 + ], + [ + 28.72, + 1024 + ], + [ + 73.114, + 137 + ] + ] + }, + { + "first_ms": 17.669, + "total_ms": 65.542, + "batches": [ + [ + 17.674, + 1024 + ], + [ + 19.566, + 1024 + ], + [ + 21.363, + 1024 + ], + [ + 65.531, + 137 + ] + ] + }, + { + "first_ms": 27.926, + "total_ms": 77.421, + "batches": [ + [ + 27.931, + 1024 + ], + [ + 29.805, + 1024 + ], + [ + 31.647, + 1024 + ], + [ + 77.408, + 137 + ] + ] + }, + { + "first_ms": 12.286, + "total_ms": 69.339, + "batches": [ + [ + 12.29, + 1024 + ], + [ + 14.128, + 1024 + ], + [ + 16.004, + 1024 + ], + [ + 69.325, + 137 + ] + ] + } + ] +} diff --git a/docs/测试说明/m2_data/m2_serial_excerpt_run13.txt b/docs/测试说明/m2_data/m2_serial_excerpt_run13.txt new file mode 100644 index 00000000..769aa8ec --- /dev/null +++ b/docs/测试说明/m2_data/m2_serial_excerpt_run13.txt @@ -0,0 +1,87 @@ +[RX] 2026-09-21T23:01:37.691+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=4 +[RX] 2026-09-21T23:01:37.691+08:00 [2026-09-21 23:01:33][LAT_TEST] [LAT] peer re-HELLO -> session restart +[RX] 2026-09-21T23:01:37.702+08:00 [2026-09-21 23:01:33][LAT_TEST] [LAT] peer session start +[RX] 2026-09-21T23:01:37.723+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:37.775+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:37.817+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:37.869+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:37.921+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:37.963+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.015+08:00 [2026-09-21 23:01:33][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.067+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.129+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.171+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.223+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.275+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.317+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.369+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.421+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.472+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.514+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.576+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.628+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.680+08:00 [2026-09-21 23:01:34][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:38.680+08:00 [2026-09-21 23:01:34][LAT_TEST] [LAT] seg=queue_rt_recv n=20 min=5966 med=6008 p95=6969 max=6969 us spikes=0 +[RX] 2026-09-21T23:01:38.691+08:00 [2026-09-21 23:01:34][LAT_TEST] [LAT] verdict=OK seg=queue_rt_recv (med=6008ms warn<=10000 crit<=100000 spikes=0/0) +[RX] 2026-09-21T23:01:38.733+08:00 [2026-09-21 23:01:34][LAT_TEST] [LAT] seg=queue_rt_send n=20 min=835 med=988 p95=1213 max=1213 us spikes=0 +[RX] 2026-09-21T23:01:38.743+08:00 [2026-09-21 23:01:34][LAT_TEST] [LAT] verdict=OK seg=queue_rt_send (med=988ms warn<=10000 crit<=100000 spikes=0/0) +[RX] 2026-09-21T23:01:58.948+08:00 [2026-09-21 23:01:54][NET] recv sock2 len=4 +[RX] 2026-09-21T23:01:58.958+08:00 [2026-09-21 23:01:55][LAT_TEST] [LAT] peer session start +[RX] 2026-09-21T23:01:58.969+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.020+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.071+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.123+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.174+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.216+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.268+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.320+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.372+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.424+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.507+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.518+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.569+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.621+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.683+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.725+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.777+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.829+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.870+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.922+08:00 [2026-09-21 23:01:55][NET] recv sock2 len=100 +[RX] 2026-09-21T23:01:59.922+08:00 [2026-09-21 23:01:55][LAT_TEST] [LAT] seg=queue_rt_recv n=20 min=5392 med=6007 p95=6025 max=6025 us spikes=0 +[RX] 2026-09-21T23:01:59.932+08:00 [2026-09-21 23:01:55][LAT_TEST] [LAT] verdict=OK seg=queue_rt_recv (med=6007ms warn<=10000 crit<=100000 spikes=0/0) +[RX] 2026-09-21T23:01:59.985+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] seg=queue_rt_send n=20 min=833 med=987 p95=1209 max=1209 us spikes=0 +[RX] 2026-09-21T23:01:59.995+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] verdict=OK seg=queue_rt_send (med=987ms warn<=10000 crit<=100000 spikes=0/0) +[RX] 2026-09-21T23:02:00.141+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] seg=chunk_wait n=64 min=213 med=219 p95=238 max=377 us spikes=0 +[RX] 2026-09-21T23:02:00.152+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] verdict=OK seg=chunk_wait (med=219ms warn<=10000 crit<=500000 spikes=0/0) +[RX] 2026-09-21T23:02:00.214+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] seg=logtax_line n=10 min=4390 med=4396 p95=4414 max=4414 us spikes=0 +[RX] 2026-09-21T23:02:00.224+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] verdict=OK seg=logtax_line (med=4396ms warn<=10000 crit<=25000 spikes=0/0) +[RX] 2026-09-21T23:02:00.256+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.318+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.370+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.432+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.493+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.555+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.617+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.669+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.731+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.804+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=10 +[RX] 2026-09-21T23:02:00.804+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] seg=req2first_mcu n=10 min=263 med=318 p95=361 max=361 us spikes=0 +[RX] 2026-09-21T23:02:00.815+08:00 [2026-09-21 23:01:56][LAT_TEST] [LAT] verdict=OK seg=req2first_mcu (med=318ms warn<=20000 crit<=200000 spikes=0/0) +[RX] 2026-09-21T23:02:00.856+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:00.929+08:00 [2026-09-21 23:01:56][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:00.991+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.063+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.115+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.187+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.239+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.311+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.362+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.424+08:00 [2026-09-21 23:01:57][NET] recv sock2 len=9 +[RX] 2026-09-21T23:02:01.434+08:00 [2026-09-21 23:01:57][LAT_TEST] [LAT] seg=fullframe_mcu n=10 min=5966 med=6052 p95=6078 max=6078 us spikes=0 +[RX] 2026-09-21T23:02:01.444+08:00 [2026-09-21 23:01:57][LAT_TEST] [LAT] verdict=OK seg=fullframe_mcu (med=6052ms warn<=50000 crit<=500000 spikes=0/0) +[RX] 2026-09-21T23:02:01.465+08:00 [2026-09-21 23:01:57][LAT_TEST] [LAT] summary verdict=OK +[RX] 2026-09-21T23:02:01.476+08:00 [2026-09-21 23:01:57][LAT_TEST] [LAT] report complete +[RX] 2026-09-21T23:02:01.486+08:00 [2026-09-21 23:01:57][LAT_TEST] === Phase 13: 1/1 PASSED (failed=0) === +[RX] 2026-09-21T23:02:01.497+08:00 [2026-09-21 23:01:57][LAT_TEST] [LAT] note: CLOSED on abort -> re-listen via chip +[RX] 2026-09-21T23:02:02.100+08:00 [2026-09-21 23:01:58][NET] sock2 auto-relisten ok +[RX] 2026-09-21T23:02:53.179+08:00 [2026-09-21 23:02:49][NET] sock3 auto-relisten ok diff --git a/docs/测试说明/m2_data/m2_serial_excerpt_run15.txt b/docs/测试说明/m2_data/m2_serial_excerpt_run15.txt new file mode 100644 index 00000000..7e0635a0 --- /dev/null +++ b/docs/测试说明/m2_data/m2_serial_excerpt_run15.txt @@ -0,0 +1,62 @@ +[RX] 2026-09-21T23:09:54.768+08:00 [2026-09-21 23:09:50][LAT_TEST] === CH395F Phase 13 Tests (chip+queue latency, L3~L7/L10) === +[RX] 2026-09-21T23:09:54.768+08:00 [2026-09-21 23:09:50][LAT_TEST] [LAT] run_id=2910 phase=13 cal core_clk=168000000 us_per_1024cyc=6 +[RX] 2026-09-21T23:09:54.779+08:00 [2026-09-21 23:09:50][LAT_TEST] [LAT] waiting for peer HELLO (port 8080, latency_peer.py --mode net) +[RX] 2026-09-21T23:09:56.973+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=4 +[RX] 2026-09-21T23:09:56.973+08:00 [2026-09-21 23:09:53][LAT_TEST] [LAT] peer session start +[RX] 2026-09-21T23:09:56.994+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.046+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.088+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.140+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.193+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.245+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.287+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.340+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.393+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.447+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.510+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.552+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.593+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.645+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.698+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.739+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.792+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.854+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.906+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.948+08:00 [2026-09-21 23:09:53][NET] recv sock2 len=100 +[RX] 2026-09-21T23:09:57.959+08:00 [2026-09-21 23:09:53][LAT_TEST] [LAT] seg=queue_rt_recv n=20 min=5513 med=5997 p95=6033 max=6033 us spikes=0 +[RX] 2026-09-21T23:09:57.969+08:00 [2026-09-21 23:09:53][LAT_TEST] [LAT] verdict=OK seg=queue_rt_recv (med=5997ms warn<=10000 crit<=100000 spikes=0/0) +[RX] 2026-09-21T23:09:58.012+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] seg=queue_rt_send n=20 min=838 med=987 p95=1209 max=1209 us spikes=0 +[RX] 2026-09-21T23:09:58.022+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] verdict=OK seg=queue_rt_send (med=987ms warn<=10000 crit<=100000 spikes=0/0) +[RX] 2026-09-21T23:09:58.169+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] seg=chunk_wait n=64 min=214 med=219 p95=238 max=381 us spikes=0 +[RX] 2026-09-21T23:09:58.179+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] verdict=OK seg=chunk_wait (med=219ms warn<=10000 crit<=500000 spikes=0/0) +[RX] 2026-09-21T23:09:58.243+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] seg=logtax_line n=10 min=4394 med=4396 p95=4422 max=4422 us spikes=0 +[RX] 2026-09-21T23:09:58.253+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] verdict=OK seg=logtax_line (med=4396ms warn<=10000 crit<=25000 spikes=0/0) +[RX] 2026-09-21T23:09:58.285+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.338+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.400+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.462+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.525+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.587+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.640+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.692+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.744+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.806+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=10 +[RX] 2026-09-21T23:09:58.806+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] seg=req2first_mcu n=10 min=308 med=319 p95=338 max=338 us spikes=0 +[RX] 2026-09-21T23:09:58.817+08:00 [2026-09-21 23:09:54][LAT_TEST] [LAT] verdict=OK seg=req2first_mcu (med=319ms warn<=20000 crit<=200000 spikes=0/0) +[RX] 2026-09-21T23:09:58.859+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:58.922+08:00 [2026-09-21 23:09:54][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:58.975+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.038+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.090+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.152+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.204+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.266+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.319+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.392+08:00 [2026-09-21 23:09:55][NET] recv sock2 len=9 +[RX] 2026-09-21T23:09:59.402+08:00 [2026-09-21 23:09:55][LAT_TEST] [LAT] seg=fullframe_mcu n=10 min=5991 med=6028 p95=6066 max=6066 us spikes=0 +[RX] 2026-09-21T23:09:59.412+08:00 [2026-09-21 23:09:55][LAT_TEST] [LAT] verdict=OK seg=fullframe_mcu (med=6028ms warn<=50000 crit<=500000 spikes=0/0) +[RX] 2026-09-21T23:09:59.444+08:00 [2026-09-21 23:09:55][LAT_TEST] [LAT] summary verdict=OK +[RX] 2026-09-21T23:09:59.454+08:00 [2026-09-21 23:09:55][LAT_TEST] [LAT] report complete +[RX] 2026-09-21T23:09:59.454+08:00 [2026-09-21 23:09:55][LAT_TEST] === Phase 13: 1/1 PASSED (failed=0) === +[RX] 2026-09-21T23:09:59.475+08:00 [2026-09-21 23:09:55][LAT_TEST] [LAT] note: CLOSED on abort -> re-listen via chip +[RX] 2026-09-21T23:10:00.068+08:00 [2026-09-21 23:09:56][NET] sock2 auto-relisten ok diff --git a/docs/测试说明/m3_data/m3_dev_quiet.json b/docs/测试说明/m3_data/m3_dev_quiet.json new file mode 100644 index 00000000..3af9943f --- /dev/null +++ b/docs/测试说明/m3_data/m3_dev_quiet.json @@ -0,0 +1,2291 @@ +{ + "mode": "remo", + "host": "192.168.1.100", + "port": 7003, + "ts_start": "2026-09-21 23:23:47", + "ts_end": "2026-09-21 23:24:02", + "duration_s": 15.32, + "samples": 65, + "cadence_ms": 100.0, + "tcp_nodelay": false, + "stress_from": null, + "connect_ms": 2.604, + "reconnect_ms": 33.717, + "reconnect_list_ms": [ + 23.483, + 27.392, + 33.717 + ], + "reconnect_stat": { + "n": 3, + "min_ms": 23.483, + "med_ms": 27.392, + "p95_ms": 33.717, + "max_ms": 33.717, + "spikes500": 0 + }, + "init": { + "rtt_ms": 9.613, + "geom": { + "w": 160, + "h": 160, + "framebytes": 0, + "payload_len": 6 + } + }, + "cycle_all": { + "n": 65, + "min_ms": 53.291, + "med_ms": 66.021, + "p95_ms": 75.315, + "max_ms": 77.403, + "spikes500": 0 + }, + "cycle_quiet": { + "n": 65, + "min_ms": 53.291, + "med_ms": 66.021, + "p95_ms": 75.315, + "max_ms": 77.403, + "spikes500": 0 + }, + "cycle_stress": { + "n": 0 + }, + "first_byte_all": { + "n": 65, + "min_ms": 8.457, + "med_ms": 13.905, + "p95_ms": 19.064, + "max_ms": 19.216, + "spikes500": 0 + }, + "tail_lag_all": { + "n": 65, + "min_ms": 43.314, + "med_ms": 52.602, + "p95_ms": 58.475, + "max_ms": 61.513, + "spikes500": 0 + }, + "crc": { + "ok": 65, + "fail": 0 + }, + "bitmap": { + "nontrivial_ok": 65, + "nontrivial_fail": 0 + }, + "frames": [ + { + "cycle": 0, + "stress": false, + "first_ms": 18.762, + "total_ms": 67.623, + "tail_lag_ms": 48.769, + "batches": [ + [ + 18.762, + 8 + ], + [ + 18.809, + 1016 + ], + [ + 20.623, + 1024 + ], + [ + 22.416, + 1024 + ], + [ + 67.532, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 1, + "stress": false, + "first_ms": 15.855, + "total_ms": 72.933, + "tail_lag_ms": 57.02, + "batches": [ + [ + 15.855, + 8 + ], + [ + 15.885, + 1016 + ], + [ + 17.673, + 1024 + ], + [ + 19.584, + 1024 + ], + [ + 72.875, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 2, + "stress": false, + "first_ms": 16.358, + "total_ms": 72.487, + "tail_lag_ms": 56.062, + "batches": [ + [ + 16.358, + 8 + ], + [ + 16.421, + 1016 + ], + [ + 18.175, + 1024 + ], + [ + 19.997, + 1024 + ], + [ + 72.42, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 3, + "stress": false, + "first_ms": 19.216, + "total_ms": 74.855, + "tail_lag_ms": 55.565, + "batches": [ + [ + 19.216, + 8 + ], + [ + 19.269, + 1016 + ], + [ + 20.957, + 1024 + ], + [ + 22.855, + 1024 + ], + [ + 74.78, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 4, + "stress": false, + "first_ms": 8.633, + "total_ms": 56.466, + "tail_lag_ms": 47.767, + "batches": [ + [ + 8.633, + 8 + ], + [ + 8.699, + 1016 + ], + [ + 10.436, + 1024 + ], + [ + 12.329, + 1024 + ], + [ + 56.4, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 5, + "stress": false, + "first_ms": 16.747, + "total_ms": 72.633, + "tail_lag_ms": 55.825, + "batches": [ + [ + 16.747, + 8 + ], + [ + 16.8, + 1016 + ], + [ + 18.479, + 1024 + ], + [ + 20.407, + 1024 + ], + [ + 72.573, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 6, + "stress": false, + "first_ms": 8.824, + "total_ms": 54.46, + "tail_lag_ms": 45.572, + "batches": [ + [ + 8.824, + 8 + ], + [ + 8.859, + 1016 + ], + [ + 10.633, + 1024 + ], + [ + 12.542, + 1024 + ], + [ + 54.396, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 7, + "stress": false, + "first_ms": 19.064, + "total_ms": 69.583, + "tail_lag_ms": 50.45, + "batches": [ + [ + 19.064, + 8 + ], + [ + 19.096, + 1016 + ], + [ + 20.829, + 1024 + ], + [ + 22.694, + 1024 + ], + [ + 69.514, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 8, + "stress": false, + "first_ms": 13.905, + "total_ms": 72.675, + "tail_lag_ms": 58.7, + "batches": [ + [ + 13.905, + 8 + ], + [ + 13.946, + 1016 + ], + [ + 15.784, + 1024 + ], + [ + 17.582, + 1024 + ], + [ + 72.605, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 9, + "stress": false, + "first_ms": 17.137, + "total_ms": 66.021, + "tail_lag_ms": 48.816, + "batches": [ + [ + 17.137, + 8 + ], + [ + 17.169, + 1016 + ], + [ + 18.983, + 1024 + ], + [ + 20.793, + 1024 + ], + [ + 65.953, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 10, + "stress": false, + "first_ms": 15.933, + "total_ms": 63.236, + "tail_lag_ms": 47.235, + "batches": [ + [ + 15.933, + 8 + ], + [ + 15.977, + 1016 + ], + [ + 17.744, + 1024 + ], + [ + 19.578, + 1024 + ], + [ + 63.169, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 11, + "stress": false, + "first_ms": 17.402, + "total_ms": 60.784, + "tail_lag_ms": 43.314, + "batches": [ + [ + 17.402, + 8 + ], + [ + 17.435, + 1016 + ], + [ + 19.009, + 1024 + ], + [ + 20.921, + 1024 + ], + [ + 60.716, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 12, + "stress": false, + "first_ms": 10.314, + "total_ms": 67.107, + "tail_lag_ms": 56.71, + "batches": [ + [ + 10.314, + 8 + ], + [ + 10.356, + 1016 + ], + [ + 12.142, + 1024 + ], + [ + 13.981, + 1024 + ], + [ + 67.023, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 13, + "stress": false, + "first_ms": 18.939, + "total_ms": 65.071, + "tail_lag_ms": 46.069, + "batches": [ + [ + 18.939, + 8 + ], + [ + 18.985, + 1016 + ], + [ + 20.743, + 1024 + ], + [ + 22.64, + 1024 + ], + [ + 65.008, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 14, + "stress": false, + "first_ms": 18.843, + "total_ms": 77.157, + "tail_lag_ms": 58.241, + "batches": [ + [ + 18.843, + 8 + ], + [ + 18.895, + 1016 + ], + [ + 20.653, + 1024 + ], + [ + 22.42, + 1024 + ], + [ + 77.084, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 15, + "stress": false, + "first_ms": 17.388, + "total_ms": 66.687, + "tail_lag_ms": 49.236, + "batches": [ + [ + 17.388, + 8 + ], + [ + 17.443, + 1016 + ], + [ + 19.194, + 1024 + ], + [ + 21.025, + 1024 + ], + [ + 66.624, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 16, + "stress": false, + "first_ms": 15.482, + "total_ms": 69.575, + "tail_lag_ms": 54.017, + "batches": [ + [ + 15.482, + 8 + ], + [ + 15.53, + 1016 + ], + [ + 17.254, + 1024 + ], + [ + 19.124, + 1024 + ], + [ + 69.499, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 17, + "stress": false, + "first_ms": 10.267, + "total_ms": 61.582, + "tail_lag_ms": 51.251, + "batches": [ + [ + 10.267, + 8 + ], + [ + 10.326, + 1016 + ], + [ + 12.092, + 1024 + ], + [ + 13.906, + 1024 + ], + [ + 61.518, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 18, + "stress": false, + "first_ms": 13.109, + "total_ms": 70.59, + "tail_lag_ms": 57.409, + "batches": [ + [ + 13.109, + 8 + ], + [ + 13.152, + 1016 + ], + [ + 14.887, + 1024 + ], + [ + 16.742, + 1024 + ], + [ + 70.517, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 19, + "stress": false, + "first_ms": 17.945, + "total_ms": 69.036, + "tail_lag_ms": 51.016, + "batches": [ + [ + 17.945, + 8 + ], + [ + 17.993, + 1016 + ], + [ + 19.726, + 1024 + ], + [ + 21.611, + 1024 + ], + [ + 68.961, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 20, + "stress": false, + "first_ms": 13.681, + "total_ms": 67.924, + "tail_lag_ms": 54.176, + "batches": [ + [ + 13.681, + 8 + ], + [ + 13.719, + 1016 + ], + [ + 15.606, + 1024 + ], + [ + 17.393, + 1024 + ], + [ + 67.857, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 21, + "stress": false, + "first_ms": 10.497, + "total_ms": 62.947, + "tail_lag_ms": 52.369, + "batches": [ + [ + 10.497, + 8 + ], + [ + 10.538, + 1016 + ], + [ + 12.332, + 1024 + ], + [ + 14.103, + 1024 + ], + [ + 62.866, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 22, + "stress": false, + "first_ms": 11.798, + "total_ms": 64.828, + "tail_lag_ms": 52.959, + "batches": [ + [ + 11.798, + 8 + ], + [ + 11.834, + 1016 + ], + [ + 13.625, + 1024 + ], + [ + 15.663, + 1024 + ], + [ + 64.756, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 23, + "stress": false, + "first_ms": 11.628, + "total_ms": 68.135, + "tail_lag_ms": 56.437, + "batches": [ + [ + 11.628, + 8 + ], + [ + 11.678, + 1016 + ], + [ + 13.448, + 1024 + ], + [ + 15.275, + 1024 + ], + [ + 68.065, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 24, + "stress": false, + "first_ms": 18.997, + "total_ms": 65.056, + "tail_lag_ms": 45.987, + "batches": [ + [ + 18.997, + 8 + ], + [ + 19.044, + 1016 + ], + [ + 20.795, + 1024 + ], + [ + 22.663, + 1024 + ], + [ + 64.984, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 25, + "stress": false, + "first_ms": 18.376, + "total_ms": 72.906, + "tail_lag_ms": 54.448, + "batches": [ + [ + 18.376, + 8 + ], + [ + 18.428, + 1016 + ], + [ + 20.248, + 1024 + ], + [ + 22.093, + 1024 + ], + [ + 72.824, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 26, + "stress": false, + "first_ms": 9.837, + "total_ms": 55.061, + "tail_lag_ms": 45.142, + "batches": [ + [ + 9.837, + 8 + ], + [ + 9.874, + 1016 + ], + [ + 11.63, + 1024 + ], + [ + 13.489, + 1024 + ], + [ + 54.979, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 27, + "stress": false, + "first_ms": 8.457, + "total_ms": 60.955, + "tail_lag_ms": 52.427, + "batches": [ + [ + 8.457, + 8 + ], + [ + 8.482, + 1016 + ], + [ + 10.319, + 1024 + ], + [ + 12.171, + 1024 + ], + [ + 60.884, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 28, + "stress": false, + "first_ms": 11.94, + "total_ms": 70.283, + "tail_lag_ms": 58.265, + "batches": [ + [ + 11.94, + 8 + ], + [ + 11.974, + 1016 + ], + [ + 13.712, + 1024 + ], + [ + 15.474, + 1024 + ], + [ + 70.204, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 29, + "stress": false, + "first_ms": 16.592, + "total_ms": 61.277, + "tail_lag_ms": 44.608, + "batches": [ + [ + 16.592, + 8 + ], + [ + 16.644, + 1016 + ], + [ + 18.428, + 1024 + ], + [ + 20.141, + 1024 + ], + [ + 61.2, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 30, + "stress": false, + "first_ms": 8.915, + "total_ms": 65.742, + "tail_lag_ms": 56.762, + "batches": [ + [ + 8.915, + 8 + ], + [ + 8.953, + 1016 + ], + [ + 10.712, + 1024 + ], + [ + 12.564, + 1024 + ], + [ + 65.677, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 31, + "stress": false, + "first_ms": 18.997, + "total_ms": 71.677, + "tail_lag_ms": 52.602, + "batches": [ + [ + 18.997, + 8 + ], + [ + 19.035, + 1016 + ], + [ + 20.819, + 1024 + ], + [ + 22.655, + 1024 + ], + [ + 71.598, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 32, + "stress": false, + "first_ms": 12.037, + "total_ms": 67.799, + "tail_lag_ms": 55.677, + "batches": [ + [ + 12.037, + 8 + ], + [ + 12.074, + 1016 + ], + [ + 13.838, + 1024 + ], + [ + 15.654, + 1024 + ], + [ + 67.713, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 33, + "stress": false, + "first_ms": 8.851, + "total_ms": 56.825, + "tail_lag_ms": 47.897, + "batches": [ + [ + 8.851, + 8 + ], + [ + 8.889, + 1016 + ], + [ + 10.747, + 1024 + ], + [ + 12.58, + 1024 + ], + [ + 56.748, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 34, + "stress": false, + "first_ms": 16.62, + "total_ms": 70.004, + "tail_lag_ms": 53.319, + "batches": [ + [ + 16.62, + 8 + ], + [ + 16.659, + 1016 + ], + [ + 18.487, + 1024 + ], + [ + 20.338, + 1024 + ], + [ + 69.939, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 35, + "stress": false, + "first_ms": 11.078, + "total_ms": 72.667, + "tail_lag_ms": 61.513, + "batches": [ + [ + 11.078, + 8 + ], + [ + 11.122, + 1016 + ], + [ + 12.903, + 1024 + ], + [ + 14.732, + 1024 + ], + [ + 72.591, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 36, + "stress": false, + "first_ms": 13.832, + "total_ms": 71.578, + "tail_lag_ms": 57.66, + "batches": [ + [ + 13.832, + 8 + ], + [ + 13.861, + 1016 + ], + [ + 15.686, + 1024 + ], + [ + 17.461, + 1024 + ], + [ + 71.491, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 37, + "stress": false, + "first_ms": 17.866, + "total_ms": 70.966, + "tail_lag_ms": 53.031, + "batches": [ + [ + 17.866, + 8 + ], + [ + 17.907, + 1016 + ], + [ + 19.663, + 1024 + ], + [ + 21.524, + 1024 + ], + [ + 70.897, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 38, + "stress": false, + "first_ms": 11.835, + "total_ms": 57.653, + "tail_lag_ms": 45.739, + "batches": [ + [ + 11.835, + 8 + ], + [ + 11.887, + 1016 + ], + [ + 13.732, + 1024 + ], + [ + 15.551, + 1024 + ], + [ + 57.574, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 39, + "stress": false, + "first_ms": 19.032, + "total_ms": 74.759, + "tail_lag_ms": 55.65, + "batches": [ + [ + 19.032, + 8 + ], + [ + 19.076, + 1016 + ], + [ + 20.926, + 1024 + ], + [ + 22.742, + 1024 + ], + [ + 74.682, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 40, + "stress": false, + "first_ms": 8.76, + "total_ms": 53.926, + "tail_lag_ms": 45.082, + "batches": [ + [ + 8.76, + 8 + ], + [ + 8.798, + 1016 + ], + [ + 10.545, + 1024 + ], + [ + 12.381, + 1024 + ], + [ + 53.841, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 41, + "stress": false, + "first_ms": 19.208, + "total_ms": 74.362, + "tail_lag_ms": 55.078, + "batches": [ + [ + 19.208, + 8 + ], + [ + 19.254, + 1016 + ], + [ + 21.01, + 1024 + ], + [ + 22.861, + 1024 + ], + [ + 74.285, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 42, + "stress": false, + "first_ms": 9.366, + "total_ms": 54.408, + "tail_lag_ms": 44.953, + "batches": [ + [ + 9.366, + 8 + ], + [ + 9.398, + 1016 + ], + [ + 11.274, + 1024 + ], + [ + 12.99, + 1024 + ], + [ + 54.319, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 43, + "stress": false, + "first_ms": 8.496, + "total_ms": 64.741, + "tail_lag_ms": 56.169, + "batches": [ + [ + 8.496, + 8 + ], + [ + 8.537, + 1016 + ], + [ + 10.26, + 1024 + ], + [ + 12.728, + 1024 + ], + [ + 64.665, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 44, + "stress": false, + "first_ms": 8.753, + "total_ms": 56.451, + "tail_lag_ms": 47.617, + "batches": [ + [ + 8.753, + 8 + ], + [ + 8.804, + 1016 + ], + [ + 10.463, + 1024 + ], + [ + 12.183, + 1024 + ], + [ + 56.37, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 45, + "stress": false, + "first_ms": 16.606, + "total_ms": 63.869, + "tail_lag_ms": 47.184, + "batches": [ + [ + 16.606, + 8 + ], + [ + 16.646, + 1016 + ], + [ + 18.344, + 1024 + ], + [ + 20.209, + 1024 + ], + [ + 63.789, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 46, + "stress": false, + "first_ms": 17.214, + "total_ms": 61.89, + "tail_lag_ms": 44.489, + "batches": [ + [ + 17.214, + 8 + ], + [ + 17.25, + 1016 + ], + [ + 19.163, + 1024 + ], + [ + 20.967, + 1024 + ], + [ + 61.703, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 47, + "stress": false, + "first_ms": 8.948, + "total_ms": 64.95, + "tail_lag_ms": 55.931, + "batches": [ + [ + 8.948, + 8 + ], + [ + 8.982, + 1016 + ], + [ + 10.789, + 1024 + ], + [ + 12.605, + 1024 + ], + [ + 64.879, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 48, + "stress": false, + "first_ms": 8.984, + "total_ms": 60.912, + "tail_lag_ms": 51.852, + "batches": [ + [ + 8.984, + 8 + ], + [ + 9.044, + 1016 + ], + [ + 10.645, + 1024 + ], + [ + 12.498, + 1024 + ], + [ + 60.836, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 49, + "stress": false, + "first_ms": 12.627, + "total_ms": 69.381, + "tail_lag_ms": 56.683, + "batches": [ + [ + 12.627, + 8 + ], + [ + 12.669, + 1016 + ], + [ + 14.437, + 1024 + ], + [ + 16.305, + 1024 + ], + [ + 69.31, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 50, + "stress": false, + "first_ms": 8.723, + "total_ms": 53.291, + "tail_lag_ms": 44.49, + "batches": [ + [ + 8.723, + 8 + ], + [ + 8.777, + 1016 + ], + [ + 10.512, + 1024 + ], + [ + 12.267, + 1024 + ], + [ + 53.213, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 51, + "stress": false, + "first_ms": 9.146, + "total_ms": 62.136, + "tail_lag_ms": 52.914, + "batches": [ + [ + 9.146, + 8 + ], + [ + 9.187, + 1016 + ], + [ + 10.839, + 1024 + ], + [ + 12.739, + 1024 + ], + [ + 62.061, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 52, + "stress": false, + "first_ms": 11.52, + "total_ms": 55.583, + "tail_lag_ms": 43.983, + "batches": [ + [ + 11.52, + 8 + ], + [ + 11.576, + 1016 + ], + [ + 13.21, + 1024 + ], + [ + 15.169, + 1024 + ], + [ + 55.503, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 53, + "stress": false, + "first_ms": 9.294, + "total_ms": 55.522, + "tail_lag_ms": 46.151, + "batches": [ + [ + 9.294, + 8 + ], + [ + 9.329, + 1016 + ], + [ + 11.231, + 1024 + ], + [ + 13.037, + 1024 + ], + [ + 55.445, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 54, + "stress": false, + "first_ms": 18.249, + "total_ms": 75.315, + "tail_lag_ms": 56.993, + "batches": [ + [ + 18.249, + 8 + ], + [ + 18.288, + 1016 + ], + [ + 20.193, + 1024 + ], + [ + 21.854, + 1024 + ], + [ + 75.242, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 55, + "stress": false, + "first_ms": 18.441, + "total_ms": 71.399, + "tail_lag_ms": 52.876, + "batches": [ + [ + 18.441, + 8 + ], + [ + 18.482, + 1016 + ], + [ + 20.21, + 1024 + ], + [ + 22.101, + 1024 + ], + [ + 71.317, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 56, + "stress": false, + "first_ms": 11.796, + "total_ms": 59.345, + "tail_lag_ms": 47.471, + "batches": [ + [ + 11.796, + 8 + ], + [ + 11.831, + 1016 + ], + [ + 13.591, + 1024 + ], + [ + 15.455, + 1024 + ], + [ + 59.267, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 57, + "stress": false, + "first_ms": 16.912, + "total_ms": 75.472, + "tail_lag_ms": 58.475, + "batches": [ + [ + 16.912, + 8 + ], + [ + 16.948, + 1016 + ], + [ + 18.667, + 1024 + ], + [ + 20.524, + 1024 + ], + [ + 75.387, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 58, + "stress": false, + "first_ms": 17.034, + "total_ms": 64.459, + "tail_lag_ms": 47.357, + "batches": [ + [ + 17.034, + 8 + ], + [ + 17.072, + 1016 + ], + [ + 18.777, + 1024 + ], + [ + 20.587, + 1024 + ], + [ + 64.391, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 59, + "stress": false, + "first_ms": 16.883, + "total_ms": 71.605, + "tail_lag_ms": 54.642, + "batches": [ + [ + 16.883, + 8 + ], + [ + 16.934, + 1016 + ], + [ + 18.736, + 1024 + ], + [ + 20.515, + 1024 + ], + [ + 71.525, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 60, + "stress": false, + "first_ms": 10.02, + "total_ms": 55.269, + "tail_lag_ms": 45.175, + "batches": [ + [ + 10.02, + 8 + ], + [ + 10.066, + 1016 + ], + [ + 11.725, + 1024 + ], + [ + 13.55, + 1024 + ], + [ + 55.194, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 61, + "stress": false, + "first_ms": 19.212, + "total_ms": 77.403, + "tail_lag_ms": 58.114, + "batches": [ + [ + 19.212, + 8 + ], + [ + 19.279, + 1016 + ], + [ + 20.991, + 1024 + ], + [ + 22.844, + 1024 + ], + [ + 77.326, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 62, + "stress": false, + "first_ms": 17.34, + "total_ms": 61.182, + "tail_lag_ms": 43.767, + "batches": [ + [ + 17.34, + 8 + ], + [ + 17.384, + 1016 + ], + [ + 19.178, + 1024 + ], + [ + 21.009, + 1024 + ], + [ + 61.106, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 63, + "stress": false, + "first_ms": 9.536, + "total_ms": 68.248, + "tail_lag_ms": 58.634, + "batches": [ + [ + 9.536, + 8 + ], + [ + 9.583, + 1016 + ], + [ + 11.389, + 1024 + ], + [ + 13.16, + 1024 + ], + [ + 68.169, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 64, + "stress": false, + "first_ms": 16.505, + "total_ms": 65.637, + "tail_lag_ms": 49.059, + "batches": [ + [ + 16.505, + 8 + ], + [ + 16.535, + 1016 + ], + [ + 18.337, + 1024 + ], + [ + 20.233, + 1024 + ], + [ + 65.563, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + } + ] +} diff --git a/docs/测试说明/m3_data/m3_dev_quiet_fixed.json b/docs/测试说明/m3_data/m3_dev_quiet_fixed.json new file mode 100644 index 00000000..5b423c7f --- /dev/null +++ b/docs/测试说明/m3_data/m3_dev_quiet_fixed.json @@ -0,0 +1,2291 @@ +{ + "mode": "remo", + "host": "192.168.1.100", + "port": 7003, + "ts_start": "2026-09-21 23:56:39", + "ts_end": "2026-09-21 23:56:54", + "duration_s": 15.31, + "samples": 65, + "cadence_ms": 100.0, + "tcp_nodelay": false, + "stress_from": null, + "connect_ms": 12.621, + "reconnect_ms": 34.671, + "reconnect_list_ms": [ + 25.745, + 23.483, + 34.671 + ], + "reconnect_stat": { + "n": 3, + "min_ms": 23.483, + "med_ms": 25.745, + "p95_ms": 34.671, + "max_ms": 34.671, + "spikes500": 0 + }, + "init": { + "rtt_ms": 15.303, + "geom": { + "w": 160, + "h": 160, + "framebytes": 3200, + "payload_len": 8 + } + }, + "cycle_all": { + "n": 65, + "min_ms": 52.857, + "med_ms": 65.525, + "p95_ms": 75.338, + "max_ms": 80.37, + "spikes500": 0 + }, + "cycle_quiet": { + "n": 65, + "min_ms": 52.857, + "med_ms": 65.525, + "p95_ms": 75.338, + "max_ms": 80.37, + "spikes500": 0 + }, + "cycle_stress": { + "n": 0 + }, + "first_byte_all": { + "n": 65, + "min_ms": 8.293, + "med_ms": 13.267, + "p95_ms": 18.746, + "max_ms": 19.173, + "spikes500": 0 + }, + "tail_lag_all": { + "n": 65, + "min_ms": 43.614, + "med_ms": 51.992, + "p95_ms": 58.743, + "max_ms": 63.768, + "spikes500": 0 + }, + "crc": { + "ok": 65, + "fail": 0 + }, + "bitmap": { + "nontrivial_ok": 65, + "nontrivial_fail": 0 + }, + "frames": [ + { + "cycle": 0, + "stress": false, + "first_ms": 18.786, + "total_ms": 67.529, + "tail_lag_ms": 48.68, + "batches": [ + [ + 18.786, + 8 + ], + [ + 18.852, + 1016 + ], + [ + 20.56, + 1024 + ], + [ + 22.457, + 1024 + ], + [ + 67.466, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 1, + "stress": false, + "first_ms": 15.952, + "total_ms": 60.474, + "tail_lag_ms": 44.456, + "batches": [ + [ + 15.952, + 8 + ], + [ + 16.003, + 1016 + ], + [ + 17.819, + 1024 + ], + [ + 19.631, + 1024 + ], + [ + 60.408, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 2, + "stress": false, + "first_ms": 9.072, + "total_ms": 65.357, + "tail_lag_ms": 56.219, + "batches": [ + [ + 9.072, + 8 + ], + [ + 9.147, + 1016 + ], + [ + 10.822, + 1024 + ], + [ + 12.564, + 1024 + ], + [ + 65.291, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 3, + "stress": false, + "first_ms": 8.293, + "total_ms": 59.667, + "tail_lag_ms": 51.303, + "batches": [ + [ + 8.293, + 8 + ], + [ + 8.348, + 1016 + ], + [ + 10.082, + 1024 + ], + [ + 11.92, + 1024 + ], + [ + 59.596, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 4, + "stress": false, + "first_ms": 13.378, + "total_ms": 70.206, + "tail_lag_ms": 56.766, + "batches": [ + [ + 13.378, + 8 + ], + [ + 13.425, + 1016 + ], + [ + 15.186, + 1024 + ], + [ + 17.09, + 1024 + ], + [ + 70.144, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 5, + "stress": false, + "first_ms": 18.639, + "total_ms": 68.969, + "tail_lag_ms": 50.267, + "batches": [ + [ + 18.639, + 8 + ], + [ + 18.698, + 1016 + ], + [ + 20.42, + 1024 + ], + [ + 22.317, + 1024 + ], + [ + 68.906, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 6, + "stress": false, + "first_ms": 14.394, + "total_ms": 66.73, + "tail_lag_ms": 52.277, + "batches": [ + [ + 14.394, + 8 + ], + [ + 14.425, + 1016 + ], + [ + 16.251, + 1024 + ], + [ + 18.061, + 1024 + ], + [ + 66.67, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 7, + "stress": false, + "first_ms": 12.459, + "total_ms": 63.403, + "tail_lag_ms": 50.868, + "batches": [ + [ + 12.459, + 8 + ], + [ + 12.5, + 1016 + ], + [ + 14.294, + 1024 + ], + [ + 16.173, + 1024 + ], + [ + 63.327, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 8, + "stress": false, + "first_ms": 13.473, + "total_ms": 63.05, + "tail_lag_ms": 49.507, + "batches": [ + [ + 13.473, + 8 + ], + [ + 13.511, + 1016 + ], + [ + 15.257, + 1024 + ], + [ + 17.16, + 1024 + ], + [ + 62.979, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 9, + "stress": false, + "first_ms": 14.88, + "total_ms": 71.379, + "tail_lag_ms": 56.435, + "batches": [ + [ + 14.88, + 8 + ], + [ + 14.917, + 1016 + ], + [ + 16.736, + 1024 + ], + [ + 18.562, + 1024 + ], + [ + 71.315, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 10, + "stress": false, + "first_ms": 18.963, + "total_ms": 75.281, + "tail_lag_ms": 56.244, + "batches": [ + [ + 18.963, + 8 + ], + [ + 19.0, + 1016 + ], + [ + 20.779, + 1024 + ], + [ + 22.655, + 1024 + ], + [ + 75.208, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 11, + "stress": false, + "first_ms": 8.429, + "total_ms": 62.926, + "tail_lag_ms": 54.435, + "batches": [ + [ + 8.429, + 8 + ], + [ + 8.493, + 1016 + ], + [ + 10.233, + 1024 + ], + [ + 12.115, + 1024 + ], + [ + 62.864, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 12, + "stress": false, + "first_ms": 10.02, + "total_ms": 65.411, + "tail_lag_ms": 55.323, + "batches": [ + [ + 10.02, + 8 + ], + [ + 10.05, + 1016 + ], + [ + 11.854, + 1024 + ], + [ + 13.738, + 1024 + ], + [ + 65.342, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 13, + "stress": false, + "first_ms": 9.057, + "total_ms": 63.761, + "tail_lag_ms": 54.637, + "batches": [ + [ + 9.057, + 8 + ], + [ + 9.089, + 1016 + ], + [ + 10.945, + 1024 + ], + [ + 12.792, + 1024 + ], + [ + 63.694, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 14, + "stress": false, + "first_ms": 9.735, + "total_ms": 60.329, + "tail_lag_ms": 50.525, + "batches": [ + [ + 9.735, + 8 + ], + [ + 9.766, + 1016 + ], + [ + 11.576, + 1024 + ], + [ + 13.434, + 1024 + ], + [ + 60.26, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 15, + "stress": false, + "first_ms": 13.897, + "total_ms": 71.055, + "tail_lag_ms": 57.095, + "batches": [ + [ + 13.897, + 8 + ], + [ + 13.925, + 1016 + ], + [ + 15.761, + 1024 + ], + [ + 17.614, + 1024 + ], + [ + 70.992, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 16, + "stress": false, + "first_ms": 18.712, + "total_ms": 73.385, + "tail_lag_ms": 54.568, + "batches": [ + [ + 18.712, + 8 + ], + [ + 18.766, + 1016 + ], + [ + 20.555, + 1024 + ], + [ + 22.436, + 1024 + ], + [ + 73.281, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 17, + "stress": false, + "first_ms": 10.142, + "total_ms": 62.931, + "tail_lag_ms": 52.729, + "batches": [ + [ + 10.142, + 8 + ], + [ + 10.182, + 1016 + ], + [ + 11.864, + 1024 + ], + [ + 13.717, + 1024 + ], + [ + 62.871, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 18, + "stress": false, + "first_ms": 11.661, + "total_ms": 55.504, + "tail_lag_ms": 43.771, + "batches": [ + [ + 11.661, + 8 + ], + [ + 11.719, + 1016 + ], + [ + 13.481, + 1024 + ], + [ + 15.341, + 1024 + ], + [ + 55.432, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 19, + "stress": false, + "first_ms": 9.976, + "total_ms": 68.439, + "tail_lag_ms": 58.376, + "batches": [ + [ + 9.976, + 8 + ], + [ + 10.024, + 1016 + ], + [ + 11.744, + 1024 + ], + [ + 13.619, + 1024 + ], + [ + 68.351, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 20, + "stress": false, + "first_ms": 17.018, + "total_ms": 75.796, + "tail_lag_ms": 58.704, + "batches": [ + [ + 17.018, + 8 + ], + [ + 17.064, + 1016 + ], + [ + 18.796, + 1024 + ], + [ + 20.678, + 1024 + ], + [ + 75.723, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 21, + "stress": false, + "first_ms": 16.935, + "total_ms": 65.541, + "tail_lag_ms": 48.481, + "batches": [ + [ + 16.935, + 8 + ], + [ + 16.97, + 1016 + ], + [ + 18.796, + 1024 + ], + [ + 20.585, + 1024 + ], + [ + 65.416, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 22, + "stress": false, + "first_ms": 16.201, + "total_ms": 67.355, + "tail_lag_ms": 51.075, + "batches": [ + [ + 16.201, + 8 + ], + [ + 16.241, + 1016 + ], + [ + 18.036, + 1024 + ], + [ + 19.82, + 1024 + ], + [ + 67.276, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 23, + "stress": false, + "first_ms": 13.537, + "total_ms": 65.454, + "tail_lag_ms": 51.849, + "batches": [ + [ + 13.537, + 8 + ], + [ + 13.574, + 1016 + ], + [ + 15.305, + 1024 + ], + [ + 17.212, + 1024 + ], + [ + 65.385, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 24, + "stress": false, + "first_ms": 12.697, + "total_ms": 65.235, + "tail_lag_ms": 52.467, + "batches": [ + [ + 12.697, + 8 + ], + [ + 12.745, + 1016 + ], + [ + 14.57, + 1024 + ], + [ + 16.481, + 1024 + ], + [ + 65.164, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 25, + "stress": false, + "first_ms": 12.152, + "total_ms": 66.653, + "tail_lag_ms": 54.418, + "batches": [ + [ + 12.152, + 8 + ], + [ + 12.196, + 1016 + ], + [ + 13.893, + 1024 + ], + [ + 15.786, + 1024 + ], + [ + 66.57, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 26, + "stress": false, + "first_ms": 10.124, + "total_ms": 53.815, + "tail_lag_ms": 43.614, + "batches": [ + [ + 10.124, + 8 + ], + [ + 10.164, + 1016 + ], + [ + 11.858, + 1024 + ], + [ + 13.727, + 1024 + ], + [ + 53.738, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 27, + "stress": false, + "first_ms": 9.635, + "total_ms": 64.089, + "tail_lag_ms": 54.381, + "batches": [ + [ + 9.635, + 8 + ], + [ + 9.679, + 1016 + ], + [ + 11.345, + 1024 + ], + [ + 13.341, + 1024 + ], + [ + 64.016, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 28, + "stress": false, + "first_ms": 10.192, + "total_ms": 67.432, + "tail_lag_ms": 57.149, + "batches": [ + [ + 10.192, + 8 + ], + [ + 10.229, + 1016 + ], + [ + 12.119, + 1024 + ], + [ + 13.946, + 1024 + ], + [ + 67.34, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 29, + "stress": false, + "first_ms": 18.455, + "total_ms": 68.176, + "tail_lag_ms": 49.624, + "batches": [ + [ + 18.455, + 8 + ], + [ + 18.494, + 1016 + ], + [ + 20.242, + 1024 + ], + [ + 21.978, + 1024 + ], + [ + 68.079, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 30, + "stress": false, + "first_ms": 14.923, + "total_ms": 71.391, + "tail_lag_ms": 56.399, + "batches": [ + [ + 14.923, + 8 + ], + [ + 14.962, + 1016 + ], + [ + 16.656, + 1024 + ], + [ + 18.502, + 1024 + ], + [ + 71.322, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 31, + "stress": false, + "first_ms": 19.173, + "total_ms": 71.255, + "tail_lag_ms": 51.992, + "batches": [ + [ + 19.173, + 8 + ], + [ + 19.215, + 1016 + ], + [ + 20.999, + 1024 + ], + [ + 25.24, + 1024 + ], + [ + 71.165, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 32, + "stress": false, + "first_ms": 12.273, + "total_ms": 62.716, + "tail_lag_ms": 50.336, + "batches": [ + [ + 12.273, + 8 + ], + [ + 12.336, + 1016 + ], + [ + 14.032, + 1024 + ], + [ + 15.891, + 1024 + ], + [ + 62.608, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 33, + "stress": false, + "first_ms": 14.177, + "total_ms": 69.472, + "tail_lag_ms": 55.222, + "batches": [ + [ + 14.177, + 8 + ], + [ + 14.217, + 1016 + ], + [ + 15.864, + 1024 + ], + [ + 17.749, + 1024 + ], + [ + 69.399, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 34, + "stress": false, + "first_ms": 9.114, + "total_ms": 52.857, + "tail_lag_ms": 43.66, + "batches": [ + [ + 9.114, + 8 + ], + [ + 9.155, + 1016 + ], + [ + 10.968, + 1024 + ], + [ + 12.78, + 1024 + ], + [ + 52.773, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 35, + "stress": false, + "first_ms": 9.751, + "total_ms": 68.565, + "tail_lag_ms": 58.743, + "batches": [ + [ + 9.751, + 8 + ], + [ + 9.793, + 1016 + ], + [ + 11.645, + 1024 + ], + [ + 13.519, + 1024 + ], + [ + 68.494, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 36, + "stress": false, + "first_ms": 16.816, + "total_ms": 64.118, + "tail_lag_ms": 47.235, + "batches": [ + [ + 16.816, + 8 + ], + [ + 16.855, + 1016 + ], + [ + 18.553, + 1024 + ], + [ + 20.545, + 1024 + ], + [ + 64.051, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 37, + "stress": false, + "first_ms": 17.569, + "total_ms": 75.338, + "tail_lag_ms": 57.689, + "batches": [ + [ + 17.569, + 8 + ], + [ + 17.602, + 1016 + ], + [ + 19.224, + 1024 + ], + [ + 21.084, + 1024 + ], + [ + 75.258, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 38, + "stress": false, + "first_ms": 17.546, + "total_ms": 63.824, + "tail_lag_ms": 46.215, + "batches": [ + [ + 17.546, + 8 + ], + [ + 17.58, + 1016 + ], + [ + 19.353, + 1024 + ], + [ + 21.163, + 1024 + ], + [ + 63.761, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 39, + "stress": false, + "first_ms": 18.69, + "total_ms": 69.094, + "tail_lag_ms": 50.316, + "batches": [ + [ + 18.69, + 8 + ], + [ + 18.728, + 1016 + ], + [ + 20.482, + 1024 + ], + [ + 22.387, + 1024 + ], + [ + 69.006, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 40, + "stress": false, + "first_ms": 14.363, + "total_ms": 76.106, + "tail_lag_ms": 61.663, + "batches": [ + [ + 14.363, + 8 + ], + [ + 14.416, + 1016 + ], + [ + 16.201, + 1024 + ], + [ + 18.139, + 1024 + ], + [ + 76.026, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 41, + "stress": false, + "first_ms": 13.72, + "total_ms": 64.18, + "tail_lag_ms": 50.399, + "batches": [ + [ + 13.72, + 8 + ], + [ + 13.758, + 1016 + ], + [ + 15.404, + 1024 + ], + [ + 17.243, + 1024 + ], + [ + 64.119, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 42, + "stress": false, + "first_ms": 14.25, + "total_ms": 67.56, + "tail_lag_ms": 53.239, + "batches": [ + [ + 14.25, + 8 + ], + [ + 14.286, + 1016 + ], + [ + 16.08, + 1024 + ], + [ + 17.961, + 1024 + ], + [ + 67.49, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 43, + "stress": false, + "first_ms": 11.564, + "total_ms": 56.579, + "tail_lag_ms": 44.957, + "batches": [ + [ + 11.564, + 8 + ], + [ + 11.599, + 1016 + ], + [ + 13.444, + 1024 + ], + [ + 15.264, + 1024 + ], + [ + 56.521, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 44, + "stress": false, + "first_ms": 8.469, + "total_ms": 61.875, + "tail_lag_ms": 53.337, + "batches": [ + [ + 8.469, + 8 + ], + [ + 8.507, + 1016 + ], + [ + 10.3, + 1024 + ], + [ + 12.14, + 1024 + ], + [ + 61.806, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 45, + "stress": false, + "first_ms": 11.328, + "total_ms": 66.846, + "tail_lag_ms": 55.445, + "batches": [ + [ + 11.328, + 8 + ], + [ + 11.365, + 1016 + ], + [ + 13.13, + 1024 + ], + [ + 15.002, + 1024 + ], + [ + 66.773, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 46, + "stress": false, + "first_ms": 9.367, + "total_ms": 73.188, + "tail_lag_ms": 63.75, + "batches": [ + [ + 9.367, + 8 + ], + [ + 9.402, + 1016 + ], + [ + 11.236, + 1024 + ], + [ + 13.067, + 1024 + ], + [ + 73.117, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 47, + "stress": false, + "first_ms": 12.107, + "total_ms": 61.532, + "tail_lag_ms": 49.349, + "batches": [ + [ + 12.107, + 8 + ], + [ + 12.16, + 1016 + ], + [ + 13.871, + 1024 + ], + [ + 15.657, + 1024 + ], + [ + 61.456, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 48, + "stress": false, + "first_ms": 15.372, + "total_ms": 63.23, + "tail_lag_ms": 47.763, + "batches": [ + [ + 15.372, + 8 + ], + [ + 15.405, + 1016 + ], + [ + 17.227, + 1024 + ], + [ + 19.01, + 1024 + ], + [ + 63.135, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 49, + "stress": false, + "first_ms": 16.531, + "total_ms": 80.37, + "tail_lag_ms": 63.768, + "batches": [ + [ + 16.531, + 8 + ], + [ + 16.573, + 1016 + ], + [ + 18.337, + 1024 + ], + [ + 20.191, + 1024 + ], + [ + 80.299, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 50, + "stress": false, + "first_ms": 12.798, + "total_ms": 59.014, + "tail_lag_ms": 46.134, + "batches": [ + [ + 12.798, + 8 + ], + [ + 12.855, + 1016 + ], + [ + 14.639, + 1024 + ], + [ + 16.462, + 1024 + ], + [ + 58.932, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 51, + "stress": false, + "first_ms": 18.356, + "total_ms": 62.468, + "tail_lag_ms": 44.035, + "batches": [ + [ + 18.356, + 8 + ], + [ + 18.398, + 1016 + ], + [ + 20.137, + 1024 + ], + [ + 22.091, + 1024 + ], + [ + 62.391, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 52, + "stress": false, + "first_ms": 9.511, + "total_ms": 54.313, + "tail_lag_ms": 44.727, + "batches": [ + [ + 9.511, + 8 + ], + [ + 9.553, + 1016 + ], + [ + 11.226, + 1024 + ], + [ + 13.123, + 1024 + ], + [ + 54.238, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 53, + "stress": false, + "first_ms": 8.926, + "total_ms": 61.021, + "tail_lag_ms": 52.019, + "batches": [ + [ + 8.926, + 8 + ], + [ + 8.967, + 1016 + ], + [ + 10.53, + 1024 + ], + [ + 12.388, + 1024 + ], + [ + 60.945, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 54, + "stress": false, + "first_ms": 12.681, + "total_ms": 62.071, + "tail_lag_ms": 49.311, + "batches": [ + [ + 12.681, + 8 + ], + [ + 12.715, + 1016 + ], + [ + 14.471, + 1024 + ], + [ + 16.257, + 1024 + ], + [ + 61.993, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 55, + "stress": false, + "first_ms": 15.096, + "total_ms": 69.371, + "tail_lag_ms": 54.205, + "batches": [ + [ + 15.096, + 8 + ], + [ + 15.126, + 1016 + ], + [ + 16.94, + 1024 + ], + [ + 18.773, + 1024 + ], + [ + 69.3, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 56, + "stress": false, + "first_ms": 10.231, + "total_ms": 66.379, + "tail_lag_ms": 56.073, + "batches": [ + [ + 10.231, + 8 + ], + [ + 10.262, + 1016 + ], + [ + 12.138, + 1024 + ], + [ + 14.072, + 1024 + ], + [ + 66.304, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 57, + "stress": false, + "first_ms": 8.337, + "total_ms": 53.19, + "tail_lag_ms": 44.782, + "batches": [ + [ + 8.337, + 8 + ], + [ + 8.376, + 1016 + ], + [ + 10.162, + 1024 + ], + [ + 12.014, + 1024 + ], + [ + 53.119, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 58, + "stress": false, + "first_ms": 8.946, + "total_ms": 56.056, + "tail_lag_ms": 47.04, + "batches": [ + [ + 8.946, + 8 + ], + [ + 8.979, + 1016 + ], + [ + 10.786, + 1024 + ], + [ + 12.623, + 1024 + ], + [ + 55.986, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 59, + "stress": false, + "first_ms": 17.787, + "total_ms": 69.1, + "tail_lag_ms": 51.236, + "batches": [ + [ + 17.787, + 8 + ], + [ + 17.831, + 1016 + ], + [ + 19.618, + 1024 + ], + [ + 21.476, + 1024 + ], + [ + 69.023, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 60, + "stress": false, + "first_ms": 13.267, + "total_ms": 58.553, + "tail_lag_ms": 45.217, + "batches": [ + [ + 13.267, + 8 + ], + [ + 13.305, + 1016 + ], + [ + 15.129, + 1024 + ], + [ + 16.991, + 1024 + ], + [ + 58.484, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 61, + "stress": false, + "first_ms": 8.324, + "total_ms": 65.525, + "tail_lag_ms": 57.122, + "batches": [ + [ + 8.324, + 8 + ], + [ + 8.371, + 1016 + ], + [ + 10.142, + 1024 + ], + [ + 12.031, + 1024 + ], + [ + 65.447, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 62, + "stress": false, + "first_ms": 18.746, + "total_ms": 70.548, + "tail_lag_ms": 51.721, + "batches": [ + [ + 18.746, + 8 + ], + [ + 18.79, + 1016 + ], + [ + 20.582, + 1024 + ], + [ + 22.445, + 1024 + ], + [ + 70.468, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 63, + "stress": false, + "first_ms": 12.976, + "total_ms": 70.17, + "tail_lag_ms": 57.123, + "batches": [ + [ + 12.976, + 8 + ], + [ + 13.027, + 1016 + ], + [ + 14.794, + 1024 + ], + [ + 16.556, + 1024 + ], + [ + 70.099, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 64, + "stress": false, + "first_ms": 18.093, + "total_ms": 66.941, + "tail_lag_ms": 48.77, + "batches": [ + [ + 18.093, + 8 + ], + [ + 18.135, + 1016 + ], + [ + 20.059, + 1024 + ], + [ + 21.913, + 1024 + ], + [ + 66.863, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + } + ] +} diff --git a/docs/测试说明/m3_data/m3_dev_stress.json b/docs/测试说明/m3_data/m3_dev_stress.json new file mode 100644 index 00000000..2eb098b9 --- /dev/null +++ b/docs/测试说明/m3_data/m3_dev_stress.json @@ -0,0 +1,1614 @@ +{ + "mode": "remo", + "host": "192.168.1.100", + "port": 7003, + "ts_start": "2026-09-21 23:27:21", + "ts_end": "2026-09-21 23:27:48", + "duration_s": 27.14, + "samples": 45, + "cadence_ms": 100.0, + "tcp_nodelay": false, + "stress_from": 15, + "connect_ms": 24.755, + "reconnect_ms": 23.421, + "reconnect_list_ms": [ + 23.421 + ], + "reconnect_stat": { + "n": 1, + "min_ms": 23.421, + "med_ms": 23.421, + "p95_ms": 23.421, + "max_ms": 23.421, + "spikes500": 0 + }, + "init": { + "rtt_ms": 22.767, + "geom": { + "w": 160, + "h": 160, + "framebytes": 0, + "payload_len": 6 + } + }, + "cycle_all": { + "n": 45, + "min_ms": 55.826, + "med_ms": 67.029, + "p95_ms": 75.608, + "max_ms": 17717.921, + "spikes500": 1 + }, + "cycle_quiet": { + "n": 15, + "min_ms": 59.639, + "med_ms": 65.913, + "p95_ms": 75.831, + "max_ms": 75.831, + "spikes500": 0 + }, + "cycle_stress": { + "n": 30, + "min_ms": 55.826, + "med_ms": 69.148, + "p95_ms": 75.608, + "max_ms": 17717.921, + "spikes500": 1 + }, + "first_byte_all": { + "n": 45, + "min_ms": 8.302, + "med_ms": 14.048, + "p95_ms": 19.641, + "max_ms": 17673.064, + "spikes500": 1 + }, + "tail_lag_all": { + "n": 45, + "min_ms": 44.219, + "med_ms": 52.183, + "p95_ms": 58.029, + "max_ms": 61.933, + "spikes500": 0 + }, + "crc": { + "ok": 45, + "fail": 0 + }, + "bitmap": { + "nontrivial_ok": 45, + "nontrivial_fail": 0 + }, + "frames": [ + { + "cycle": 0, + "stress": false, + "first_ms": 18.528, + "total_ms": 75.831, + "tail_lag_ms": 57.234, + "batches": [ + [ + 18.528, + 8 + ], + [ + 18.579, + 1016 + ], + [ + 20.525, + 1024 + ], + [ + 21.79, + 1024 + ], + [ + 75.762, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 1, + "stress": false, + "first_ms": 18.958, + "total_ms": 69.228, + "tail_lag_ms": 50.201, + "batches": [ + [ + 18.958, + 8 + ], + [ + 18.995, + 1016 + ], + [ + 20.327, + 1024 + ], + [ + 21.654, + 1024 + ], + [ + 69.159, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 2, + "stress": false, + "first_ms": 11.136, + "total_ms": 62.369, + "tail_lag_ms": 51.173, + "batches": [ + [ + 11.136, + 8 + ], + [ + 11.167, + 1016 + ], + [ + 13.489, + 1024 + ], + [ + 14.683, + 1024 + ], + [ + 62.309, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 3, + "stress": false, + "first_ms": 13.139, + "total_ms": 64.236, + "tail_lag_ms": 51.023, + "batches": [ + [ + 13.139, + 8 + ], + [ + 13.173, + 1016 + ], + [ + 14.961, + 1024 + ], + [ + 16.796, + 1024 + ], + [ + 64.161, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 4, + "stress": false, + "first_ms": 13.425, + "total_ms": 68.804, + "tail_lag_ms": 55.32, + "batches": [ + [ + 13.425, + 8 + ], + [ + 13.461, + 1016 + ], + [ + 15.235, + 1024 + ], + [ + 17.066, + 1024 + ], + [ + 68.745, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 5, + "stress": false, + "first_ms": 9.423, + "total_ms": 64.6, + "tail_lag_ms": 55.12, + "batches": [ + [ + 9.423, + 8 + ], + [ + 9.481, + 1016 + ], + [ + 11.219, + 1024 + ], + [ + 13.065, + 1024 + ], + [ + 64.543, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 6, + "stress": false, + "first_ms": 9.32, + "total_ms": 59.888, + "tail_lag_ms": 50.508, + "batches": [ + [ + 9.32, + 8 + ], + [ + 9.354, + 1016 + ], + [ + 11.156, + 1024 + ], + [ + 13.061, + 1024 + ], + [ + 59.828, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 7, + "stress": false, + "first_ms": 14.048, + "total_ms": 60.298, + "tail_lag_ms": 46.189, + "batches": [ + [ + 14.048, + 8 + ], + [ + 14.12, + 1016 + ], + [ + 15.76, + 1024 + ], + [ + 17.628, + 1024 + ], + [ + 60.237, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 8, + "stress": false, + "first_ms": 18.15, + "total_ms": 67.296, + "tail_lag_ms": 49.082, + "batches": [ + [ + 18.15, + 8 + ], + [ + 18.206, + 1016 + ], + [ + 19.939, + 1024 + ], + [ + 21.755, + 1024 + ], + [ + 67.232, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 9, + "stress": false, + "first_ms": 12.549, + "total_ms": 65.913, + "tail_lag_ms": 53.292, + "batches": [ + [ + 12.549, + 8 + ], + [ + 12.582, + 1016 + ], + [ + 14.437, + 1024 + ], + [ + 16.269, + 1024 + ], + [ + 65.841, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 10, + "stress": false, + "first_ms": 11.237, + "total_ms": 59.639, + "tail_lag_ms": 48.334, + "batches": [ + [ + 11.237, + 8 + ], + [ + 11.291, + 1016 + ], + [ + 13.128, + 1024 + ], + [ + 14.902, + 1024 + ], + [ + 59.571, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 11, + "stress": false, + "first_ms": 16.064, + "total_ms": 74.205, + "tail_lag_ms": 58.029, + "batches": [ + [ + 16.064, + 8 + ], + [ + 16.096, + 1016 + ], + [ + 17.873, + 1024 + ], + [ + 19.741, + 1024 + ], + [ + 74.092, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 12, + "stress": false, + "first_ms": 17.646, + "total_ms": 63.712, + "tail_lag_ms": 46.001, + "batches": [ + [ + 17.646, + 8 + ], + [ + 17.677, + 1016 + ], + [ + 19.464, + 1024 + ], + [ + 21.331, + 1024 + ], + [ + 63.647, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 13, + "stress": false, + "first_ms": 18.701, + "total_ms": 72.616, + "tail_lag_ms": 53.851, + "batches": [ + [ + 18.701, + 8 + ], + [ + 18.737, + 1016 + ], + [ + 20.491, + 1024 + ], + [ + 22.365, + 1024 + ], + [ + 72.552, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 14, + "stress": false, + "first_ms": 10.858, + "total_ms": 66.203, + "tail_lag_ms": 55.276, + "batches": [ + [ + 10.858, + 8 + ], + [ + 10.902, + 1016 + ], + [ + 12.666, + 1024 + ], + [ + 14.456, + 1024 + ], + [ + 66.134, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 15, + "stress": true, + "first_ms": 17673.064, + "total_ms": 17717.921, + "tail_lag_ms": 44.783, + "batches": [ + [ + 17673.064, + 8 + ], + [ + 17673.1, + 1016 + ], + [ + 17675.038, + 1024 + ], + [ + 17676.22, + 1024 + ], + [ + 17717.846, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 16, + "stress": true, + "first_ms": 16.622, + "total_ms": 72.789, + "tail_lag_ms": 56.093, + "batches": [ + [ + 16.622, + 8 + ], + [ + 16.669, + 1016 + ], + [ + 18.43, + 1024 + ], + [ + 20.273, + 1024 + ], + [ + 72.715, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 17, + "stress": true, + "first_ms": 19.641, + "total_ms": 74.673, + "tail_lag_ms": 54.92, + "batches": [ + [ + 19.641, + 8 + ], + [ + 19.688, + 1016 + ], + [ + 21.548, + 1024 + ], + [ + 23.122, + 1024 + ], + [ + 74.562, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 18, + "stress": true, + "first_ms": 20.106, + "total_ms": 70.794, + "tail_lag_ms": 50.613, + "batches": [ + [ + 20.106, + 8 + ], + [ + 20.181, + 1016 + ], + [ + 21.86, + 1024 + ], + [ + 23.751, + 1024 + ], + [ + 70.719, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 19, + "stress": true, + "first_ms": 13.502, + "total_ms": 64.597, + "tail_lag_ms": 50.977, + "batches": [ + [ + 13.502, + 8 + ], + [ + 13.558, + 1016 + ], + [ + 15.298, + 1024 + ], + [ + 17.169, + 1024 + ], + [ + 64.479, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 20, + "stress": true, + "first_ms": 13.339, + "total_ms": 61.193, + "tail_lag_ms": 47.787, + "batches": [ + [ + 13.339, + 8 + ], + [ + 13.394, + 1016 + ], + [ + 15.094, + 1024 + ], + [ + 16.907, + 1024 + ], + [ + 61.125, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 21, + "stress": true, + "first_ms": 16.504, + "total_ms": 68.825, + "tail_lag_ms": 52.208, + "batches": [ + [ + 16.504, + 8 + ], + [ + 16.549, + 1016 + ], + [ + 18.335, + 1024 + ], + [ + 20.18, + 1024 + ], + [ + 68.712, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 22, + "stress": true, + "first_ms": 12.476, + "total_ms": 56.78, + "tail_lag_ms": 44.219, + "batches": [ + [ + 12.476, + 8 + ], + [ + 12.525, + 1016 + ], + [ + 14.305, + 1024 + ], + [ + 16.128, + 1024 + ], + [ + 56.695, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 23, + "stress": true, + "first_ms": 17.041, + "total_ms": 63.872, + "tail_lag_ms": 46.771, + "batches": [ + [ + 17.041, + 8 + ], + [ + 17.085, + 1016 + ], + [ + 18.712, + 1024 + ], + [ + 20.511, + 1024 + ], + [ + 63.812, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 24, + "stress": true, + "first_ms": 17.64, + "total_ms": 74.314, + "tail_lag_ms": 56.601, + "batches": [ + [ + 17.64, + 8 + ], + [ + 17.67, + 1016 + ], + [ + 19.454, + 1024 + ], + [ + 21.334, + 1024 + ], + [ + 74.241, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 25, + "stress": true, + "first_ms": 19.073, + "total_ms": 69.716, + "tail_lag_ms": 50.481, + "batches": [ + [ + 19.073, + 8 + ], + [ + 19.121, + 1016 + ], + [ + 20.807, + 1024 + ], + [ + 22.691, + 1024 + ], + [ + 69.554, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 26, + "stress": true, + "first_ms": 13.939, + "total_ms": 64.404, + "tail_lag_ms": 50.391, + "batches": [ + [ + 13.939, + 8 + ], + [ + 13.983, + 1016 + ], + [ + 15.725, + 1024 + ], + [ + 17.585, + 1024 + ], + [ + 64.33, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 27, + "stress": true, + "first_ms": 14.87, + "total_ms": 67.029, + "tail_lag_ms": 51.998, + "batches": [ + [ + 14.87, + 8 + ], + [ + 14.924, + 1016 + ], + [ + 16.689, + 1024 + ], + [ + 18.547, + 1024 + ], + [ + 66.867, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 28, + "stress": true, + "first_ms": 12.612, + "total_ms": 70.861, + "tail_lag_ms": 58.145, + "batches": [ + [ + 12.612, + 8 + ], + [ + 12.661, + 1016 + ], + [ + 14.373, + 1024 + ], + [ + 16.28, + 1024 + ], + [ + 70.757, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 29, + "stress": true, + "first_ms": 17.414, + "total_ms": 70.456, + "tail_lag_ms": 52.969, + "batches": [ + [ + 17.414, + 8 + ], + [ + 17.448, + 1016 + ], + [ + 19.214, + 1024 + ], + [ + 21.027, + 1024 + ], + [ + 70.383, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 30, + "stress": true, + "first_ms": 8.741, + "total_ms": 70.762, + "tail_lag_ms": 61.933, + "batches": [ + [ + 8.741, + 8 + ], + [ + 8.774, + 1016 + ], + [ + 10.512, + 1024 + ], + [ + 12.351, + 1024 + ], + [ + 70.674, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 31, + "stress": true, + "first_ms": 13.917, + "total_ms": 69.409, + "tail_lag_ms": 55.421, + "batches": [ + [ + 13.917, + 8 + ], + [ + 13.959, + 1016 + ], + [ + 15.605, + 1024 + ], + [ + 17.397, + 1024 + ], + [ + 69.338, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 32, + "stress": true, + "first_ms": 8.751, + "total_ms": 63.933, + "tail_lag_ms": 54.973, + "batches": [ + [ + 8.751, + 8 + ], + [ + 8.81, + 1016 + ], + [ + 10.602, + 1024 + ], + [ + 12.4, + 1024 + ], + [ + 63.724, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 33, + "stress": true, + "first_ms": 9.526, + "total_ms": 56.578, + "tail_lag_ms": 46.973, + "batches": [ + [ + 9.526, + 8 + ], + [ + 9.563, + 1016 + ], + [ + 11.402, + 1024 + ], + [ + 13.185, + 1024 + ], + [ + 56.498, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 34, + "stress": true, + "first_ms": 17.428, + "total_ms": 62.812, + "tail_lag_ms": 45.301, + "batches": [ + [ + 17.428, + 8 + ], + [ + 17.487, + 1016 + ], + [ + 19.223, + 1024 + ], + [ + 21.024, + 1024 + ], + [ + 62.729, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 35, + "stress": true, + "first_ms": 8.302, + "total_ms": 66.247, + "tail_lag_ms": 57.869, + "batches": [ + [ + 8.302, + 8 + ], + [ + 8.351, + 1016 + ], + [ + 10.044, + 1024 + ], + [ + 11.955, + 1024 + ], + [ + 66.171, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 36, + "stress": true, + "first_ms": 17.669, + "total_ms": 70.213, + "tail_lag_ms": 52.471, + "batches": [ + [ + 17.669, + 8 + ], + [ + 17.742, + 1016 + ], + [ + 19.459, + 1024 + ], + [ + 21.259, + 1024 + ], + [ + 70.139, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 37, + "stress": true, + "first_ms": 8.898, + "total_ms": 59.15, + "tail_lag_ms": 50.171, + "batches": [ + [ + 8.898, + 8 + ], + [ + 8.955, + 1016 + ], + [ + 10.718, + 1024 + ], + [ + 12.505, + 1024 + ], + [ + 59.069, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 38, + "stress": true, + "first_ms": 14.07, + "total_ms": 69.148, + "tail_lag_ms": 54.999, + "batches": [ + [ + 14.07, + 8 + ], + [ + 14.115, + 1016 + ], + [ + 15.877, + 1024 + ], + [ + 17.738, + 1024 + ], + [ + 69.069, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 39, + "stress": true, + "first_ms": 9.398, + "total_ms": 55.826, + "tail_lag_ms": 46.353, + "batches": [ + [ + 9.398, + 8 + ], + [ + 9.454, + 1016 + ], + [ + 11.206, + 1024 + ], + [ + 13.064, + 1024 + ], + [ + 55.752, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 40, + "stress": true, + "first_ms": 18.217, + "total_ms": 75.608, + "tail_lag_ms": 57.234, + "batches": [ + [ + 18.217, + 8 + ], + [ + 18.267, + 1016 + ], + [ + 20.032, + 1024 + ], + [ + 21.861, + 1024 + ], + [ + 75.45, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 41, + "stress": true, + "first_ms": 17.991, + "total_ms": 75.227, + "tail_lag_ms": 57.049, + "batches": [ + [ + 17.991, + 8 + ], + [ + 18.05, + 1016 + ], + [ + 19.72, + 1024 + ], + [ + 21.558, + 1024 + ], + [ + 75.04, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 42, + "stress": true, + "first_ms": 18.199, + "total_ms": 70.464, + "tail_lag_ms": 52.183, + "batches": [ + [ + 18.199, + 8 + ], + [ + 18.251, + 1016 + ], + [ + 20.012, + 1024 + ], + [ + 21.818, + 1024 + ], + [ + 70.382, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 43, + "stress": true, + "first_ms": 12.643, + "total_ms": 65.612, + "tail_lag_ms": 52.889, + "batches": [ + [ + 12.643, + 8 + ], + [ + 12.683, + 1016 + ], + [ + 14.424, + 1024 + ], + [ + 16.229, + 1024 + ], + [ + 65.532, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 44, + "stress": true, + "first_ms": 8.544, + "total_ms": 59.288, + "tail_lag_ms": 50.572, + "batches": [ + [ + 8.544, + 8 + ], + [ + 8.578, + 1016 + ], + [ + 10.389, + 1024 + ], + [ + 12.204, + 1024 + ], + [ + 59.116, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + } + ] +} diff --git a/docs/测试说明/m3_data/m3_dev_stress_fixed.json b/docs/测试说明/m3_data/m3_dev_stress_fixed.json new file mode 100644 index 00000000..556c113a --- /dev/null +++ b/docs/测试说明/m3_data/m3_dev_stress_fixed.json @@ -0,0 +1,1614 @@ +{ + "mode": "remo", + "host": "192.168.1.100", + "port": 7003, + "ts_start": "2026-09-21 23:51:48", + "ts_end": "2026-09-21 23:51:58", + "duration_s": 9.89, + "samples": 45, + "cadence_ms": 100.0, + "tcp_nodelay": false, + "stress_from": 15, + "connect_ms": 25.7, + "reconnect_ms": 26.759, + "reconnect_list_ms": [ + 26.759 + ], + "reconnect_stat": { + "n": 1, + "min_ms": 26.759, + "med_ms": 26.759, + "p95_ms": 26.759, + "max_ms": 26.759, + "spikes500": 0 + }, + "init": { + "rtt_ms": 15.333, + "geom": { + "w": 160, + "h": 160, + "framebytes": 3200, + "payload_len": 8 + } + }, + "cycle_all": { + "n": 45, + "min_ms": 56.106, + "med_ms": 73.751, + "p95_ms": 90.538, + "max_ms": 106.761, + "spikes500": 0 + }, + "cycle_quiet": { + "n": 15, + "min_ms": 56.106, + "med_ms": 71.452, + "p95_ms": 75.106, + "max_ms": 75.106, + "spikes500": 0 + }, + "cycle_stress": { + "n": 30, + "min_ms": 70.892, + "med_ms": 74.725, + "p95_ms": 90.836, + "max_ms": 106.761, + "spikes500": 0 + }, + "first_byte_all": { + "n": 45, + "min_ms": 8.842, + "med_ms": 22.362, + "p95_ms": 34.572, + "max_ms": 56.186, + "spikes500": 0 + }, + "tail_lag_all": { + "n": 45, + "min_ms": 43.191, + "med_ms": 52.871, + "p95_ms": 58.451, + "max_ms": 59.436, + "spikes500": 0 + }, + "crc": { + "ok": 45, + "fail": 0 + }, + "bitmap": { + "nontrivial_ok": 45, + "nontrivial_fail": 0 + }, + "frames": [ + { + "cycle": 0, + "stress": false, + "first_ms": 18.212, + "total_ms": 61.464, + "tail_lag_ms": 43.191, + "batches": [ + [ + 18.212, + 8 + ], + [ + 18.23, + 1016 + ], + [ + 20.045, + 1024 + ], + [ + 21.898, + 1024 + ], + [ + 61.404, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 1, + "stress": false, + "first_ms": 10.354, + "total_ms": 56.376, + "tail_lag_ms": 45.895, + "batches": [ + [ + 10.354, + 8 + ], + [ + 10.381, + 1016 + ], + [ + 12.209, + 1024 + ], + [ + 14.059, + 1024 + ], + [ + 56.248, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 2, + "stress": false, + "first_ms": 18.627, + "total_ms": 73.627, + "tail_lag_ms": 54.928, + "batches": [ + [ + 18.627, + 8 + ], + [ + 18.66, + 1016 + ], + [ + 20.461, + 1024 + ], + [ + 22.299, + 1024 + ], + [ + 73.555, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 3, + "stress": false, + "first_ms": 9.776, + "total_ms": 58.497, + "tail_lag_ms": 48.662, + "batches": [ + [ + 9.776, + 8 + ], + [ + 9.803, + 1016 + ], + [ + 11.596, + 1024 + ], + [ + 13.466, + 1024 + ], + [ + 58.438, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 4, + "stress": false, + "first_ms": 16.101, + "total_ms": 73.962, + "tail_lag_ms": 57.805, + "batches": [ + [ + 16.101, + 8 + ], + [ + 16.13, + 1016 + ], + [ + 17.91, + 1024 + ], + [ + 19.776, + 1024 + ], + [ + 73.906, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 5, + "stress": false, + "first_ms": 17.904, + "total_ms": 74.213, + "tail_lag_ms": 56.16, + "batches": [ + [ + 17.904, + 8 + ], + [ + 17.937, + 1016 + ], + [ + 19.724, + 1024 + ], + [ + 21.58, + 1024 + ], + [ + 74.064, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 6, + "stress": false, + "first_ms": 19.222, + "total_ms": 72.528, + "tail_lag_ms": 53.214, + "batches": [ + [ + 19.222, + 8 + ], + [ + 19.261, + 1016 + ], + [ + 21.201, + 1024 + ], + [ + 22.884, + 1024 + ], + [ + 72.436, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 7, + "stress": false, + "first_ms": 11.405, + "total_ms": 58.578, + "tail_lag_ms": 47.08, + "batches": [ + [ + 11.405, + 8 + ], + [ + 11.423, + 1016 + ], + [ + 13.269, + 1024 + ], + [ + 15.115, + 1024 + ], + [ + 58.484, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 8, + "stress": false, + "first_ms": 17.339, + "total_ms": 74.326, + "tail_lag_ms": 56.896, + "batches": [ + [ + 17.339, + 8 + ], + [ + 17.384, + 1016 + ], + [ + 19.16, + 1024 + ], + [ + 21.035, + 1024 + ], + [ + 74.235, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 9, + "stress": false, + "first_ms": 18.829, + "total_ms": 71.76, + "tail_lag_ms": 52.871, + "batches": [ + [ + 18.829, + 8 + ], + [ + 18.859, + 1016 + ], + [ + 20.623, + 1024 + ], + [ + 22.401, + 1024 + ], + [ + 71.701, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 10, + "stress": false, + "first_ms": 11.674, + "total_ms": 56.106, + "tail_lag_ms": 44.311, + "batches": [ + [ + 11.674, + 8 + ], + [ + 11.702, + 1016 + ], + [ + 13.489, + 1024 + ], + [ + 15.371, + 1024 + ], + [ + 55.985, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 11, + "stress": false, + "first_ms": 8.997, + "total_ms": 57.974, + "tail_lag_ms": 48.915, + "batches": [ + [ + 8.997, + 8 + ], + [ + 9.022, + 1016 + ], + [ + 10.853, + 1024 + ], + [ + 12.705, + 1024 + ], + [ + 57.912, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 12, + "stress": false, + "first_ms": 15.51, + "total_ms": 71.452, + "tail_lag_ms": 55.801, + "batches": [ + [ + 15.51, + 8 + ], + [ + 15.539, + 1016 + ], + [ + 17.326, + 1024 + ], + [ + 19.228, + 1024 + ], + [ + 71.311, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 13, + "stress": false, + "first_ms": 8.842, + "total_ms": 56.521, + "tail_lag_ms": 47.625, + "batches": [ + [ + 8.842, + 8 + ], + [ + 8.879, + 1016 + ], + [ + 10.657, + 1024 + ], + [ + 12.527, + 1024 + ], + [ + 56.467, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 14, + "stress": false, + "first_ms": 17.171, + "total_ms": 75.106, + "tail_lag_ms": 57.822, + "batches": [ + [ + 17.171, + 8 + ], + [ + 17.206, + 1016 + ], + [ + 19.011, + 1024 + ], + [ + 20.854, + 1024 + ], + [ + 74.993, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 15, + "stress": true, + "first_ms": 28.174, + "total_ms": 77.06, + "tail_lag_ms": 48.821, + "batches": [ + [ + 28.174, + 8 + ], + [ + 28.2, + 1016 + ], + [ + 30.0, + 1024 + ], + [ + 31.857, + 1024 + ], + [ + 76.995, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 16, + "stress": true, + "first_ms": 19.875, + "total_ms": 71.302, + "tail_lag_ms": 51.36, + "batches": [ + [ + 19.875, + 8 + ], + [ + 19.9, + 1016 + ], + [ + 21.608, + 1024 + ], + [ + 23.405, + 1024 + ], + [ + 71.235, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 17, + "stress": true, + "first_ms": 35.126, + "total_ms": 90.538, + "tail_lag_ms": 55.345, + "batches": [ + [ + 35.126, + 8 + ], + [ + 35.148, + 1016 + ], + [ + 36.962, + 1024 + ], + [ + 38.836, + 1024 + ], + [ + 90.471, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 18, + "stress": true, + "first_ms": 56.186, + "total_ms": 106.761, + "tail_lag_ms": 50.49, + "batches": [ + [ + 56.186, + 8 + ], + [ + 56.214, + 1016 + ], + [ + 58.297, + 1024 + ], + [ + 59.85, + 1024 + ], + [ + 106.675, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 19, + "stress": true, + "first_ms": 17.911, + "total_ms": 72.63, + "tail_lag_ms": 54.646, + "batches": [ + [ + 17.911, + 8 + ], + [ + 17.946, + 1016 + ], + [ + 19.727, + 1024 + ], + [ + 21.569, + 1024 + ], + [ + 72.557, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 20, + "stress": true, + "first_ms": 31.711, + "total_ms": 89.772, + "tail_lag_ms": 57.989, + "batches": [ + [ + 31.711, + 8 + ], + [ + 31.739, + 1016 + ], + [ + 33.427, + 1024 + ], + [ + 35.22, + 1024 + ], + [ + 89.7, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 21, + "stress": true, + "first_ms": 22.362, + "total_ms": 73.856, + "tail_lag_ms": 51.405, + "batches": [ + [ + 22.362, + 8 + ], + [ + 22.387, + 1016 + ], + [ + 24.276, + 1024 + ], + [ + 26.096, + 1024 + ], + [ + 73.767, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 22, + "stress": true, + "first_ms": 29.21, + "total_ms": 73.751, + "tail_lag_ms": 44.487, + "batches": [ + [ + 29.21, + 8 + ], + [ + 29.241, + 1016 + ], + [ + 31.027, + 1024 + ], + [ + 32.885, + 1024 + ], + [ + 73.697, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 23, + "stress": true, + "first_ms": 29.631, + "total_ms": 73.37, + "tail_lag_ms": 43.681, + "batches": [ + [ + 29.631, + 8 + ], + [ + 29.653, + 1016 + ], + [ + 31.477, + 1024 + ], + [ + 33.317, + 1024 + ], + [ + 73.311, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 24, + "stress": true, + "first_ms": 30.681, + "total_ms": 87.683, + "tail_lag_ms": 56.868, + "batches": [ + [ + 30.681, + 8 + ], + [ + 30.704, + 1016 + ], + [ + 32.38, + 1024 + ], + [ + 34.209, + 1024 + ], + [ + 87.549, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 25, + "stress": true, + "first_ms": 29.625, + "total_ms": 73.718, + "tail_lag_ms": 44.029, + "batches": [ + [ + 29.625, + 8 + ], + [ + 29.649, + 1016 + ], + [ + 31.45, + 1024 + ], + [ + 33.25, + 1024 + ], + [ + 73.655, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 26, + "stress": true, + "first_ms": 30.679, + "total_ms": 87.653, + "tail_lag_ms": 56.91, + "batches": [ + [ + 30.679, + 8 + ], + [ + 30.713, + 1016 + ], + [ + 32.457, + 1024 + ], + [ + 34.305, + 1024 + ], + [ + 87.589, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 27, + "stress": true, + "first_ms": 29.592, + "total_ms": 88.11, + "tail_lag_ms": 58.451, + "batches": [ + [ + 29.592, + 8 + ], + [ + 29.625, + 1016 + ], + [ + 31.388, + 1024 + ], + [ + 33.234, + 1024 + ], + [ + 88.043, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 28, + "stress": true, + "first_ms": 28.008, + "total_ms": 74.725, + "tail_lag_ms": 46.648, + "batches": [ + [ + 28.008, + 8 + ], + [ + 28.028, + 1016 + ], + [ + 29.816, + 1024 + ], + [ + 31.744, + 1024 + ], + [ + 74.656, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 29, + "stress": true, + "first_ms": 21.915, + "total_ms": 73.885, + "tail_lag_ms": 51.897, + "batches": [ + [ + 21.915, + 8 + ], + [ + 21.936, + 1016 + ], + [ + 23.738, + 1024 + ], + [ + 25.599, + 1024 + ], + [ + 73.812, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 30, + "stress": true, + "first_ms": 17.452, + "total_ms": 71.261, + "tail_lag_ms": 53.752, + "batches": [ + [ + 17.452, + 8 + ], + [ + 17.485, + 1016 + ], + [ + 19.311, + 1024 + ], + [ + 21.127, + 1024 + ], + [ + 71.204, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 31, + "stress": true, + "first_ms": 33.042, + "total_ms": 90.836, + "tail_lag_ms": 57.729, + "batches": [ + [ + 33.042, + 8 + ], + [ + 33.065, + 1016 + ], + [ + 34.91, + 1024 + ], + [ + 36.748, + 1024 + ], + [ + 90.772, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 32, + "stress": true, + "first_ms": 28.756, + "total_ms": 88.146, + "tail_lag_ms": 59.322, + "batches": [ + [ + 28.756, + 8 + ], + [ + 28.787, + 1016 + ], + [ + 30.628, + 1024 + ], + [ + 32.462, + 1024 + ], + [ + 88.078, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 33, + "stress": true, + "first_ms": 27.19, + "total_ms": 72.993, + "tail_lag_ms": 45.738, + "batches": [ + [ + 27.19, + 8 + ], + [ + 27.216, + 1016 + ], + [ + 29.023, + 1024 + ], + [ + 30.812, + 1024 + ], + [ + 72.928, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 34, + "stress": true, + "first_ms": 23.883, + "total_ms": 73.902, + "tail_lag_ms": 49.956, + "batches": [ + [ + 23.883, + 8 + ], + [ + 23.912, + 1016 + ], + [ + 25.674, + 1024 + ], + [ + 27.401, + 1024 + ], + [ + 73.84, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 35, + "stress": true, + "first_ms": 24.391, + "total_ms": 72.964, + "tail_lag_ms": 48.492, + "batches": [ + [ + 24.391, + 8 + ], + [ + 24.423, + 1016 + ], + [ + 26.236, + 1024 + ], + [ + 28.022, + 1024 + ], + [ + 72.884, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 36, + "stress": true, + "first_ms": 20.038, + "total_ms": 73.05, + "tail_lag_ms": 52.947, + "batches": [ + [ + 20.038, + 8 + ], + [ + 20.073, + 1016 + ], + [ + 21.886, + 1024 + ], + [ + 23.702, + 1024 + ], + [ + 72.985, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 37, + "stress": true, + "first_ms": 33.841, + "total_ms": 88.316, + "tail_lag_ms": 54.336, + "batches": [ + [ + 33.841, + 8 + ], + [ + 33.871, + 1016 + ], + [ + 35.667, + 1024 + ], + [ + 37.432, + 1024 + ], + [ + 88.177, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 38, + "stress": true, + "first_ms": 31.926, + "total_ms": 89.458, + "tail_lag_ms": 57.474, + "batches": [ + [ + 31.926, + 8 + ], + [ + 31.948, + 1016 + ], + [ + 33.791, + 1024 + ], + [ + 35.609, + 1024 + ], + [ + 89.401, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 39, + "stress": true, + "first_ms": 29.163, + "total_ms": 88.662, + "tail_lag_ms": 59.436, + "batches": [ + [ + 29.163, + 8 + ], + [ + 29.203, + 1016 + ], + [ + 30.965, + 1024 + ], + [ + 32.837, + 1024 + ], + [ + 88.599, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 40, + "stress": true, + "first_ms": 20.853, + "total_ms": 70.892, + "tail_lag_ms": 49.975, + "batches": [ + [ + 20.853, + 8 + ], + [ + 20.881, + 1016 + ], + [ + 22.682, + 1024 + ], + [ + 24.492, + 1024 + ], + [ + 70.828, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 41, + "stress": true, + "first_ms": 24.592, + "total_ms": 73.151, + "tail_lag_ms": 48.492, + "batches": [ + [ + 24.592, + 8 + ], + [ + 24.617, + 1016 + ], + [ + 26.431, + 1024 + ], + [ + 28.267, + 1024 + ], + [ + 73.085, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 42, + "stress": true, + "first_ms": 19.901, + "total_ms": 72.134, + "tail_lag_ms": 52.174, + "batches": [ + [ + 19.901, + 8 + ], + [ + 19.924, + 1016 + ], + [ + 21.695, + 1024 + ], + [ + 23.552, + 1024 + ], + [ + 72.075, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 43, + "stress": true, + "first_ms": 34.572, + "total_ms": 88.875, + "tail_lag_ms": 54.242, + "batches": [ + [ + 34.572, + 8 + ], + [ + 34.609, + 1016 + ], + [ + 36.379, + 1024 + ], + [ + 38.262, + 1024 + ], + [ + 88.814, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 44, + "stress": true, + "first_ms": 32.258, + "total_ms": 88.409, + "tail_lag_ms": 56.089, + "batches": [ + [ + 32.258, + 8 + ], + [ + 32.281, + 1016 + ], + [ + 34.086, + 1024 + ], + [ + 35.967, + 1024 + ], + [ + 88.347, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + } + ] +} diff --git a/docs/测试说明/m3_data/m3_sim_quiet.json b/docs/测试说明/m3_data/m3_sim_quiet.json new file mode 100644 index 00000000..7d4ef57a --- /dev/null +++ b/docs/测试说明/m3_data/m3_sim_quiet.json @@ -0,0 +1,1168 @@ +{ + "mode": "remo", + "host": "127.0.0.1", + "port": 7003, + "ts_start": "2026-09-21 23:18:55", + "ts_end": "2026-09-21 23:19:02", + "duration_s": 6.78, + "samples": 50, + "cadence_ms": 100.0, + "tcp_nodelay": false, + "stress_from": null, + "connect_ms": 10.501, + "reconnect_ms": 31.965, + "init": { + "rtt_ms": 6.491, + "geom": { + "w": 160, + "h": 160, + "framebytes": 0, + "payload_len": 6 + } + }, + "cycle_all": { + "n": 50, + "min_ms": 0.572, + "med_ms": 6.778, + "p95_ms": 13.05, + "max_ms": 15.67, + "spikes500": 0 + }, + "cycle_quiet": { + "n": 50, + "min_ms": 0.572, + "med_ms": 6.778, + "p95_ms": 13.05, + "max_ms": 15.67, + "spikes500": 0 + }, + "cycle_stress": { + "n": 0 + }, + "first_byte_all": { + "n": 50, + "min_ms": 0.481, + "med_ms": 6.689, + "p95_ms": 12.954, + "max_ms": 15.579, + "spikes500": 0 + }, + "tail_lag_all": { + "n": 50, + "min_ms": 0.019, + "med_ms": 0.035, + "p95_ms": 0.067, + "max_ms": 0.105, + "spikes500": 0 + }, + "crc": { + "ok": 50, + "fail": 0 + }, + "bitmap": { + "nontrivial_ok": 50, + "nontrivial_fail": 0 + }, + "frames": [ + { + "cycle": 0, + "stress": false, + "first_ms": 6.233, + "total_ms": 6.331, + "tail_lag_ms": 0.047, + "batches": [ + [ + 6.233, + 8 + ], + [ + 6.28, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 1, + "stress": false, + "first_ms": 4.14, + "total_ms": 4.265, + "tail_lag_ms": 0.052, + "batches": [ + [ + 4.14, + 8 + ], + [ + 4.192, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 2, + "stress": false, + "first_ms": 5.304, + "total_ms": 5.421, + "tail_lag_ms": 0.06, + "batches": [ + [ + 5.304, + 8 + ], + [ + 5.364, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 3, + "stress": false, + "first_ms": 5.579, + "total_ms": 5.665, + "tail_lag_ms": 0.034, + "batches": [ + [ + 5.579, + 8 + ], + [ + 5.614, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 4, + "stress": false, + "first_ms": 0.624, + "total_ms": 0.712, + "tail_lag_ms": 0.034, + "batches": [ + [ + 0.624, + 8 + ], + [ + 0.657, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 5, + "stress": false, + "first_ms": 0.608, + "total_ms": 0.727, + "tail_lag_ms": 0.059, + "batches": [ + [ + 0.608, + 8 + ], + [ + 0.666, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 6, + "stress": false, + "first_ms": 4.293, + "total_ms": 4.37, + "tail_lag_ms": 0.026, + "batches": [ + [ + 4.293, + 8 + ], + [ + 4.319, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 7, + "stress": false, + "first_ms": 3.284, + "total_ms": 3.358, + "tail_lag_ms": 0.023, + "batches": [ + [ + 3.284, + 8 + ], + [ + 3.307, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 8, + "stress": false, + "first_ms": 1.675, + "total_ms": 1.833, + "tail_lag_ms": 0.051, + "batches": [ + [ + 1.675, + 8 + ], + [ + 1.725, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 9, + "stress": false, + "first_ms": 5.608, + "total_ms": 5.73, + "tail_lag_ms": 0.038, + "batches": [ + [ + 5.608, + 8 + ], + [ + 5.646, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 10, + "stress": false, + "first_ms": 7.011, + "total_ms": 7.092, + "tail_lag_ms": 0.031, + "batches": [ + [ + 7.011, + 8 + ], + [ + 7.042, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 11, + "stress": false, + "first_ms": 3.011, + "total_ms": 3.184, + "tail_lag_ms": 0.042, + "batches": [ + [ + 3.011, + 8 + ], + [ + 3.053, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 12, + "stress": false, + "first_ms": 15.579, + "total_ms": 15.67, + "tail_lag_ms": 0.037, + "batches": [ + [ + 15.579, + 8 + ], + [ + 15.617, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 13, + "stress": false, + "first_ms": 7.147, + "total_ms": 7.227, + "tail_lag_ms": 0.03, + "batches": [ + [ + 7.147, + 8 + ], + [ + 7.176, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 14, + "stress": false, + "first_ms": 2.614, + "total_ms": 2.698, + "tail_lag_ms": 0.028, + "batches": [ + [ + 2.614, + 8 + ], + [ + 2.642, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 15, + "stress": false, + "first_ms": 13.601, + "total_ms": 13.77, + "tail_lag_ms": 0.067, + "batches": [ + [ + 13.601, + 8 + ], + [ + 13.668, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 16, + "stress": false, + "first_ms": 9.779, + "total_ms": 9.858, + "tail_lag_ms": 0.021, + "batches": [ + [ + 9.779, + 8 + ], + [ + 9.799, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 17, + "stress": false, + "first_ms": 0.648, + "total_ms": 0.738, + "tail_lag_ms": 0.026, + "batches": [ + [ + 0.648, + 8 + ], + [ + 0.674, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 18, + "stress": false, + "first_ms": 7.121, + "total_ms": 7.217, + "tail_lag_ms": 0.045, + "batches": [ + [ + 7.121, + 8 + ], + [ + 7.165, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 19, + "stress": false, + "first_ms": 1.146, + "total_ms": 1.223, + "tail_lag_ms": 0.019, + "batches": [ + [ + 1.146, + 8 + ], + [ + 1.165, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 20, + "stress": false, + "first_ms": 8.336, + "total_ms": 8.415, + "tail_lag_ms": 0.026, + "batches": [ + [ + 8.336, + 8 + ], + [ + 8.362, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 21, + "stress": false, + "first_ms": 12.954, + "total_ms": 13.05, + "tail_lag_ms": 0.035, + "batches": [ + [ + 12.954, + 8 + ], + [ + 12.989, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 22, + "stress": false, + "first_ms": 5.716, + "total_ms": 5.794, + "tail_lag_ms": 0.025, + "batches": [ + [ + 5.716, + 8 + ], + [ + 5.741, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 23, + "stress": false, + "first_ms": 11.108, + "total_ms": 11.282, + "tail_lag_ms": 0.105, + "batches": [ + [ + 11.108, + 8 + ], + [ + 11.213, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 24, + "stress": false, + "first_ms": 4.639, + "total_ms": 4.713, + "tail_lag_ms": 0.025, + "batches": [ + [ + 4.639, + 8 + ], + [ + 4.664, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 25, + "stress": false, + "first_ms": 2.854, + "total_ms": 2.934, + "tail_lag_ms": 0.023, + "batches": [ + [ + 2.854, + 8 + ], + [ + 2.877, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 26, + "stress": false, + "first_ms": 7.43, + "total_ms": 7.519, + "tail_lag_ms": 0.028, + "batches": [ + [ + 7.43, + 8 + ], + [ + 7.457, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 27, + "stress": false, + "first_ms": 7.692, + "total_ms": 7.783, + "tail_lag_ms": 0.031, + "batches": [ + [ + 7.692, + 8 + ], + [ + 7.723, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 28, + "stress": false, + "first_ms": 7.869, + "total_ms": 7.981, + "tail_lag_ms": 0.045, + "batches": [ + [ + 7.869, + 8 + ], + [ + 7.913, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 29, + "stress": false, + "first_ms": 6.571, + "total_ms": 6.672, + "tail_lag_ms": 0.034, + "batches": [ + [ + 6.571, + 8 + ], + [ + 6.605, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 30, + "stress": false, + "first_ms": 7.751, + "total_ms": 7.85, + "tail_lag_ms": 0.036, + "batches": [ + [ + 7.751, + 8 + ], + [ + 7.787, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 31, + "stress": false, + "first_ms": 4.391, + "total_ms": 4.493, + "tail_lag_ms": 0.038, + "batches": [ + [ + 4.391, + 8 + ], + [ + 4.428, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 32, + "stress": false, + "first_ms": 0.481, + "total_ms": 0.572, + "tail_lag_ms": 0.029, + "batches": [ + [ + 0.481, + 8 + ], + [ + 0.51, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 33, + "stress": false, + "first_ms": 6.91, + "total_ms": 7.109, + "tail_lag_ms": 0.067, + "batches": [ + [ + 6.91, + 8 + ], + [ + 6.977, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 34, + "stress": false, + "first_ms": 6.731, + "total_ms": 6.83, + "tail_lag_ms": 0.035, + "batches": [ + [ + 6.731, + 8 + ], + [ + 6.766, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 35, + "stress": false, + "first_ms": 9.027, + "total_ms": 9.134, + "tail_lag_ms": 0.036, + "batches": [ + [ + 9.027, + 8 + ], + [ + 9.063, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 36, + "stress": false, + "first_ms": 5.955, + "total_ms": 6.073, + "tail_lag_ms": 0.053, + "batches": [ + [ + 5.955, + 8 + ], + [ + 6.009, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 37, + "stress": false, + "first_ms": 7.049, + "total_ms": 7.137, + "tail_lag_ms": 0.03, + "batches": [ + [ + 7.049, + 8 + ], + [ + 7.079, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 38, + "stress": false, + "first_ms": 7.133, + "total_ms": 7.228, + "tail_lag_ms": 0.035, + "batches": [ + [ + 7.133, + 8 + ], + [ + 7.167, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 39, + "stress": false, + "first_ms": 3.18, + "total_ms": 3.279, + "tail_lag_ms": 0.033, + "batches": [ + [ + 3.18, + 8 + ], + [ + 3.213, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 40, + "stress": false, + "first_ms": 7.359, + "total_ms": 7.464, + "tail_lag_ms": 0.041, + "batches": [ + [ + 7.359, + 8 + ], + [ + 7.4, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 41, + "stress": false, + "first_ms": 7.666, + "total_ms": 7.756, + "tail_lag_ms": 0.031, + "batches": [ + [ + 7.666, + 8 + ], + [ + 7.697, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 42, + "stress": false, + "first_ms": 7.037, + "total_ms": 7.119, + "tail_lag_ms": 0.031, + "batches": [ + [ + 7.037, + 8 + ], + [ + 7.068, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 43, + "stress": false, + "first_ms": 7.619, + "total_ms": 7.716, + "tail_lag_ms": 0.036, + "batches": [ + [ + 7.619, + 8 + ], + [ + 7.655, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 44, + "stress": false, + "first_ms": 7.174, + "total_ms": 7.271, + "tail_lag_ms": 0.036, + "batches": [ + [ + 7.174, + 8 + ], + [ + 7.21, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 45, + "stress": false, + "first_ms": 5.039, + "total_ms": 5.127, + "tail_lag_ms": 0.032, + "batches": [ + [ + 5.039, + 8 + ], + [ + 5.072, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 46, + "stress": false, + "first_ms": 6.395, + "total_ms": 6.49, + "tail_lag_ms": 0.032, + "batches": [ + [ + 6.395, + 8 + ], + [ + 6.426, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 47, + "stress": false, + "first_ms": 7.135, + "total_ms": 7.272, + "tail_lag_ms": 0.056, + "batches": [ + [ + 7.135, + 8 + ], + [ + 7.19, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 48, + "stress": false, + "first_ms": 5.258, + "total_ms": 5.358, + "tail_lag_ms": 0.037, + "batches": [ + [ + 5.258, + 8 + ], + [ + 5.295, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 49, + "stress": false, + "first_ms": 6.689, + "total_ms": 6.778, + "tail_lag_ms": 0.035, + "batches": [ + [ + 6.689, + 8 + ], + [ + 6.724, + 3201 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + } + ] +} diff --git a/docs/测试说明/m3_data/m3_tail_ackfast.json b/docs/测试说明/m3_data/m3_tail_ackfast.json new file mode 100644 index 00000000..4d905b51 --- /dev/null +++ b/docs/测试说明/m3_data/m3_tail_ackfast.json @@ -0,0 +1,1101 @@ +{ + "mode": "remo", + "host": "192.168.1.100", + "port": 7003, + "ts_start": "2026-09-22 00:03:41", + "ts_end": "2026-09-22 00:03:47", + "duration_s": 5.09, + "samples": 30, + "cadence_ms": 100.0, + "tcp_nodelay": false, + "immediate_ack": true, + "in_band_ack_probe": false, + "stress_from": null, + "connect_ms": 24.299, + "reconnect_ms": 23.447, + "reconnect_list_ms": [ + 23.447 + ], + "reconnect_stat": { + "n": 1, + "min_ms": 23.447, + "med_ms": 23.447, + "p95_ms": 23.447, + "max_ms": 23.447, + "spikes500": 0 + }, + "init": { + "rtt_ms": 10.358, + "geom": { + "w": 160, + "h": 160, + "framebytes": 3200, + "payload_len": 8 + } + }, + "cycle_all": { + "n": 30, + "min_ms": 17.502, + "med_ms": 20.684, + "p95_ms": 21.434, + "max_ms": 23.943, + "spikes500": 0 + }, + "cycle_quiet": { + "n": 30, + "min_ms": 17.502, + "med_ms": 20.684, + "p95_ms": 21.434, + "max_ms": 23.943, + "spikes500": 0 + }, + "cycle_stress": { + "n": 0 + }, + "first_byte_all": { + "n": 30, + "min_ms": 13.592, + "med_ms": 16.765, + "p95_ms": 17.598, + "max_ms": 19.051, + "spikes500": 0 + }, + "tail_lag_all": { + "n": 30, + "min_ms": 3.786, + "med_ms": 3.858, + "p95_ms": 4.789, + "max_ms": 5.607, + "spikes500": 0 + }, + "crc": { + "ok": 30, + "fail": 0 + }, + "bitmap": { + "nontrivial_ok": 30, + "nontrivial_fail": 0 + }, + "frames": [ + { + "cycle": 0, + "stress": false, + "first_ms": 19.051, + "total_ms": 23.943, + "tail_lag_ms": 4.789, + "batches": [ + [ + 19.051, + 8 + ], + [ + 19.09, + 1016 + ], + [ + 20.538, + 1024 + ], + [ + 22.714, + 1024 + ], + [ + 23.84, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 1, + "stress": false, + "first_ms": 15.535, + "total_ms": 21.194, + "tail_lag_ms": 5.607, + "batches": [ + [ + 15.535, + 8 + ], + [ + 15.575, + 1016 + ], + [ + 17.08, + 1024 + ], + [ + 19.394, + 1024 + ], + [ + 21.142, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 2, + "stress": false, + "first_ms": 15.062, + "total_ms": 19.595, + "tail_lag_ms": 4.475, + "batches": [ + [ + 15.062, + 8 + ], + [ + 15.11, + 1016 + ], + [ + 16.399, + 1024 + ], + [ + 18.349, + 1024 + ], + [ + 19.537, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 3, + "stress": false, + "first_ms": 15.703, + "total_ms": 19.613, + "tail_lag_ms": 3.858, + "batches": [ + [ + 15.703, + 8 + ], + [ + 15.747, + 1016 + ], + [ + 17.486, + 1024 + ], + [ + 19.333, + 1024 + ], + [ + 19.561, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 4, + "stress": false, + "first_ms": 16.782, + "total_ms": 20.682, + "tail_lag_ms": 3.852, + "batches": [ + [ + 16.782, + 8 + ], + [ + 16.814, + 1016 + ], + [ + 18.608, + 1024 + ], + [ + 20.449, + 1024 + ], + [ + 20.633, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 5, + "stress": false, + "first_ms": 17.598, + "total_ms": 21.434, + "tail_lag_ms": 3.786, + "batches": [ + [ + 17.598, + 8 + ], + [ + 17.653, + 1016 + ], + [ + 19.371, + 1024 + ], + [ + 21.178, + 1024 + ], + [ + 21.384, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 6, + "stress": false, + "first_ms": 16.771, + "total_ms": 20.71, + "tail_lag_ms": 3.885, + "batches": [ + [ + 16.771, + 8 + ], + [ + 16.806, + 1016 + ], + [ + 18.573, + 1024 + ], + [ + 20.455, + 1024 + ], + [ + 20.656, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 7, + "stress": false, + "first_ms": 16.691, + "total_ms": 20.551, + "tail_lag_ms": 3.811, + "batches": [ + [ + 16.691, + 8 + ], + [ + 16.749, + 1016 + ], + [ + 18.506, + 1024 + ], + [ + 20.297, + 1024 + ], + [ + 20.502, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 8, + "stress": false, + "first_ms": 13.839, + "total_ms": 17.776, + "tail_lag_ms": 3.871, + "batches": [ + [ + 13.839, + 8 + ], + [ + 13.874, + 1016 + ], + [ + 15.611, + 1024 + ], + [ + 17.537, + 1024 + ], + [ + 17.709, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 9, + "stress": false, + "first_ms": 16.701, + "total_ms": 20.595, + "tail_lag_ms": 3.84, + "batches": [ + [ + 16.701, + 8 + ], + [ + 16.764, + 1016 + ], + [ + 18.503, + 1024 + ], + [ + 20.36, + 1024 + ], + [ + 20.541, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 10, + "stress": false, + "first_ms": 16.62, + "total_ms": 20.491, + "tail_lag_ms": 3.819, + "batches": [ + [ + 16.62, + 8 + ], + [ + 16.674, + 1016 + ], + [ + 18.432, + 1024 + ], + [ + 20.241, + 1024 + ], + [ + 20.439, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 11, + "stress": false, + "first_ms": 16.902, + "total_ms": 20.797, + "tail_lag_ms": 3.846, + "batches": [ + [ + 16.902, + 8 + ], + [ + 16.939, + 1016 + ], + [ + 18.78, + 1024 + ], + [ + 20.555, + 1024 + ], + [ + 20.748, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 12, + "stress": false, + "first_ms": 16.698, + "total_ms": 20.578, + "tail_lag_ms": 3.832, + "batches": [ + [ + 16.698, + 8 + ], + [ + 16.757, + 1016 + ], + [ + 18.46, + 1024 + ], + [ + 20.351, + 1024 + ], + [ + 20.529, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 13, + "stress": false, + "first_ms": 16.77, + "total_ms": 20.725, + "tail_lag_ms": 3.888, + "batches": [ + [ + 16.77, + 8 + ], + [ + 16.808, + 1016 + ], + [ + 18.59, + 1024 + ], + [ + 20.433, + 1024 + ], + [ + 20.658, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 14, + "stress": false, + "first_ms": 16.912, + "total_ms": 20.822, + "tail_lag_ms": 3.86, + "batches": [ + [ + 16.912, + 8 + ], + [ + 16.953, + 1016 + ], + [ + 18.799, + 1024 + ], + [ + 20.586, + 1024 + ], + [ + 20.772, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 15, + "stress": false, + "first_ms": 16.661, + "total_ms": 20.608, + "tail_lag_ms": 3.893, + "batches": [ + [ + 16.661, + 8 + ], + [ + 16.694, + 1016 + ], + [ + 18.533, + 1024 + ], + [ + 20.349, + 1024 + ], + [ + 20.554, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 16, + "stress": false, + "first_ms": 16.6, + "total_ms": 20.511, + "tail_lag_ms": 3.857, + "batches": [ + [ + 16.6, + 8 + ], + [ + 16.632, + 1016 + ], + [ + 18.435, + 1024 + ], + [ + 20.262, + 1024 + ], + [ + 20.457, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 17, + "stress": false, + "first_ms": 16.857, + "total_ms": 20.79, + "tail_lag_ms": 3.881, + "batches": [ + [ + 16.857, + 8 + ], + [ + 16.934, + 1016 + ], + [ + 18.68, + 1024 + ], + [ + 20.536, + 1024 + ], + [ + 20.737, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 18, + "stress": false, + "first_ms": 13.592, + "total_ms": 17.502, + "tail_lag_ms": 3.856, + "batches": [ + [ + 13.592, + 8 + ], + [ + 13.624, + 1016 + ], + [ + 15.394, + 1024 + ], + [ + 17.2, + 1024 + ], + [ + 17.448, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 19, + "stress": false, + "first_ms": 16.938, + "total_ms": 20.828, + "tail_lag_ms": 3.838, + "batches": [ + [ + 16.938, + 8 + ], + [ + 16.99, + 1016 + ], + [ + 18.743, + 1024 + ], + [ + 20.589, + 1024 + ], + [ + 20.776, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 20, + "stress": false, + "first_ms": 16.742, + "total_ms": 20.642, + "tail_lag_ms": 3.848, + "batches": [ + [ + 16.742, + 8 + ], + [ + 16.777, + 1016 + ], + [ + 18.559, + 1024 + ], + [ + 20.403, + 1024 + ], + [ + 20.591, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 21, + "stress": false, + "first_ms": 16.849, + "total_ms": 20.784, + "tail_lag_ms": 3.881, + "batches": [ + [ + 16.849, + 8 + ], + [ + 16.887, + 1016 + ], + [ + 18.794, + 1024 + ], + [ + 20.54, + 1024 + ], + [ + 20.731, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 22, + "stress": false, + "first_ms": 16.836, + "total_ms": 20.727, + "tail_lag_ms": 3.838, + "batches": [ + [ + 16.836, + 8 + ], + [ + 16.872, + 1016 + ], + [ + 18.625, + 1024 + ], + [ + 20.474, + 1024 + ], + [ + 20.675, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 23, + "stress": false, + "first_ms": 16.976, + "total_ms": 20.9, + "tail_lag_ms": 3.874, + "batches": [ + [ + 16.976, + 8 + ], + [ + 17.022, + 1016 + ], + [ + 18.809, + 1024 + ], + [ + 20.639, + 1024 + ], + [ + 20.851, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 24, + "stress": false, + "first_ms": 17.017, + "total_ms": 20.926, + "tail_lag_ms": 3.855, + "batches": [ + [ + 17.017, + 8 + ], + [ + 17.049, + 1016 + ], + [ + 18.857, + 1024 + ], + [ + 20.637, + 1024 + ], + [ + 20.872, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 25, + "stress": false, + "first_ms": 16.765, + "total_ms": 20.684, + "tail_lag_ms": 3.857, + "batches": [ + [ + 16.765, + 8 + ], + [ + 16.799, + 1016 + ], + [ + 18.579, + 1024 + ], + [ + 20.428, + 1024 + ], + [ + 20.622, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 26, + "stress": false, + "first_ms": 16.552, + "total_ms": 20.47, + "tail_lag_ms": 3.86, + "batches": [ + [ + 16.552, + 8 + ], + [ + 16.597, + 1016 + ], + [ + 18.379, + 1024 + ], + [ + 20.209, + 1024 + ], + [ + 20.412, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 27, + "stress": false, + "first_ms": 16.92, + "total_ms": 20.833, + "tail_lag_ms": 3.851, + "batches": [ + [ + 16.92, + 8 + ], + [ + 16.967, + 1016 + ], + [ + 18.734, + 1024 + ], + [ + 20.577, + 1024 + ], + [ + 20.771, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 28, + "stress": false, + "first_ms": 13.627, + "total_ms": 17.565, + "tail_lag_ms": 3.878, + "batches": [ + [ + 13.627, + 8 + ], + [ + 13.668, + 1016 + ], + [ + 15.451, + 1024 + ], + [ + 17.294, + 1024 + ], + [ + 17.505, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 29, + "stress": false, + "first_ms": 16.252, + "total_ms": 20.333, + "tail_lag_ms": 3.873, + "batches": [ + [ + 16.252, + 8 + ], + [ + 16.294, + 1016 + ], + [ + 18.078, + 1024 + ], + [ + 19.918, + 1024 + ], + [ + 20.125, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + } + ] +} diff --git a/docs/测试说明/m3_data/m3_tail_ackprobe.json b/docs/测试说明/m3_data/m3_tail_ackprobe.json new file mode 100644 index 00000000..92d5f573 --- /dev/null +++ b/docs/测试说明/m3_data/m3_tail_ackprobe.json @@ -0,0 +1,1099 @@ +{ + "mode": "remo", + "host": "192.168.1.100", + "port": 7003, + "ts_start": "2026-09-22 00:01:54", + "ts_end": "2026-09-22 00:01:59", + "duration_s": 4.94, + "samples": 30, + "cadence_ms": 100.0, + "tcp_nodelay": false, + "stress_from": null, + "connect_ms": 3.275, + "reconnect_ms": 27.578, + "reconnect_list_ms": [ + 27.578 + ], + "reconnect_stat": { + "n": 1, + "min_ms": 27.578, + "med_ms": 27.578, + "p95_ms": 27.578, + "max_ms": 27.578, + "spikes500": 0 + }, + "init": { + "rtt_ms": 9.171, + "geom": { + "w": 160, + "h": 160, + "framebytes": 3200, + "payload_len": 8 + } + }, + "cycle_all": { + "n": 30, + "min_ms": 13.244, + "med_ms": 13.702, + "p95_ms": 24.313, + "max_ms": 24.418, + "spikes500": 0 + }, + "cycle_quiet": { + "n": 30, + "min_ms": 13.244, + "med_ms": 13.702, + "p95_ms": 24.313, + "max_ms": 24.418, + "spikes500": 0 + }, + "cycle_stress": { + "n": 0 + }, + "first_byte_all": { + "n": 30, + "min_ms": 9.251, + "med_ms": 9.724, + "p95_ms": 19.777, + "max_ms": 19.79, + "spikes500": 0 + }, + "tail_lag_all": { + "n": 30, + "min_ms": 3.737, + "med_ms": 3.902, + "p95_ms": 4.85, + "max_ms": 4.89, + "spikes500": 0 + }, + "crc": { + "ok": 30, + "fail": 0 + }, + "bitmap": { + "nontrivial_ok": 30, + "nontrivial_fail": 0 + }, + "frames": [ + { + "cycle": 0, + "stress": false, + "first_ms": 18.993, + "total_ms": 23.94, + "tail_lag_ms": 4.89, + "batches": [ + [ + 18.993, + 8 + ], + [ + 19.055, + 1016 + ], + [ + 20.818, + 1024 + ], + [ + 22.706, + 1024 + ], + [ + 23.883, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 1, + "stress": false, + "first_ms": 19.79, + "total_ms": 24.418, + "tail_lag_ms": 4.49, + "batches": [ + [ + 19.79, + 8 + ], + [ + 19.915, + 1016 + ], + [ + 21.664, + 1024 + ], + [ + 23.087, + 1024 + ], + [ + 24.279, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 2, + "stress": false, + "first_ms": 19.777, + "total_ms": 24.313, + "tail_lag_ms": 4.43, + "batches": [ + [ + 19.777, + 8 + ], + [ + 19.867, + 1016 + ], + [ + 21.619, + 1024 + ], + [ + 23.029, + 1024 + ], + [ + 24.206, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 3, + "stress": false, + "first_ms": 9.251, + "total_ms": 14.204, + "tail_lag_ms": 4.85, + "batches": [ + [ + 9.251, + 8 + ], + [ + 9.336, + 1016 + ], + [ + 11.072, + 1024 + ], + [ + 12.896, + 1024 + ], + [ + 14.101, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 4, + "stress": false, + "first_ms": 19.22, + "total_ms": 23.132, + "tail_lag_ms": 3.857, + "batches": [ + [ + 19.22, + 8 + ], + [ + 19.302, + 1016 + ], + [ + 20.993, + 1024 + ], + [ + 22.861, + 1024 + ], + [ + 23.077, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 5, + "stress": false, + "first_ms": 9.966, + "total_ms": 13.952, + "tail_lag_ms": 3.932, + "batches": [ + [ + 9.966, + 8 + ], + [ + 10.042, + 1016 + ], + [ + 11.944, + 1024 + ], + [ + 13.69, + 1024 + ], + [ + 13.899, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 6, + "stress": false, + "first_ms": 17.67, + "total_ms": 21.649, + "tail_lag_ms": 3.929, + "batches": [ + [ + 17.67, + 8 + ], + [ + 17.753, + 1016 + ], + [ + 19.554, + 1024 + ], + [ + 21.323, + 1024 + ], + [ + 21.599, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 7, + "stress": false, + "first_ms": 9.64, + "total_ms": 13.702, + "tail_lag_ms": 3.933, + "batches": [ + [ + 9.64, + 8 + ], + [ + 9.722, + 1016 + ], + [ + 11.492, + 1024 + ], + [ + 13.302, + 1024 + ], + [ + 13.574, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 8, + "stress": false, + "first_ms": 9.554, + "total_ms": 13.572, + "tail_lag_ms": 3.91, + "batches": [ + [ + 9.554, + 8 + ], + [ + 9.664, + 1016 + ], + [ + 11.305, + 1024 + ], + [ + 13.186, + 1024 + ], + [ + 13.464, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 9, + "stress": false, + "first_ms": 9.332, + "total_ms": 13.244, + "tail_lag_ms": 3.859, + "batches": [ + [ + 9.332, + 8 + ], + [ + 9.425, + 1016 + ], + [ + 11.149, + 1024 + ], + [ + 12.974, + 1024 + ], + [ + 13.191, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 10, + "stress": false, + "first_ms": 9.534, + "total_ms": 13.442, + "tail_lag_ms": 3.848, + "batches": [ + [ + 9.534, + 8 + ], + [ + 9.645, + 1016 + ], + [ + 11.361, + 1024 + ], + [ + 13.168, + 1024 + ], + [ + 13.382, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 11, + "stress": false, + "first_ms": 9.585, + "total_ms": 13.54, + "tail_lag_ms": 3.902, + "batches": [ + [ + 9.585, + 8 + ], + [ + 9.787, + 1016 + ], + [ + 11.443, + 1024 + ], + [ + 13.221, + 1024 + ], + [ + 13.486, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 12, + "stress": false, + "first_ms": 9.517, + "total_ms": 13.477, + "tail_lag_ms": 3.907, + "batches": [ + [ + 9.517, + 8 + ], + [ + 9.583, + 1016 + ], + [ + 11.365, + 1024 + ], + [ + 13.197, + 1024 + ], + [ + 13.424, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 13, + "stress": false, + "first_ms": 9.482, + "total_ms": 13.406, + "tail_lag_ms": 3.872, + "batches": [ + [ + 9.482, + 8 + ], + [ + 9.559, + 1016 + ], + [ + 11.279, + 1024 + ], + [ + 13.137, + 1024 + ], + [ + 13.354, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 14, + "stress": false, + "first_ms": 9.955, + "total_ms": 13.877, + "tail_lag_ms": 3.863, + "batches": [ + [ + 9.955, + 8 + ], + [ + 10.053, + 1016 + ], + [ + 11.778, + 1024 + ], + [ + 13.596, + 1024 + ], + [ + 13.818, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 15, + "stress": false, + "first_ms": 10.734, + "total_ms": 14.643, + "tail_lag_ms": 3.859, + "batches": [ + [ + 10.734, + 8 + ], + [ + 10.831, + 1016 + ], + [ + 12.509, + 1024 + ], + [ + 14.376, + 1024 + ], + [ + 14.594, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 16, + "stress": false, + "first_ms": 9.68, + "total_ms": 13.665, + "tail_lag_ms": 3.912, + "batches": [ + [ + 9.68, + 8 + ], + [ + 9.779, + 1016 + ], + [ + 11.511, + 1024 + ], + [ + 13.344, + 1024 + ], + [ + 13.591, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 17, + "stress": false, + "first_ms": 17.502, + "total_ms": 21.39, + "tail_lag_ms": 3.834, + "batches": [ + [ + 17.502, + 8 + ], + [ + 17.596, + 1016 + ], + [ + 19.292, + 1024 + ], + [ + 21.121, + 1024 + ], + [ + 21.336, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 18, + "stress": false, + "first_ms": 9.911, + "total_ms": 13.932, + "tail_lag_ms": 3.913, + "batches": [ + [ + 9.911, + 8 + ], + [ + 10.011, + 1016 + ], + [ + 11.704, + 1024 + ], + [ + 13.596, + 1024 + ], + [ + 13.824, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 19, + "stress": false, + "first_ms": 9.408, + "total_ms": 13.396, + "tail_lag_ms": 3.933, + "batches": [ + [ + 9.408, + 8 + ], + [ + 9.453, + 1016 + ], + [ + 11.24, + 1024 + ], + [ + 13.094, + 1024 + ], + [ + 13.341, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 20, + "stress": false, + "first_ms": 9.826, + "total_ms": 13.895, + "tail_lag_ms": 3.866, + "batches": [ + [ + 9.826, + 8 + ], + [ + 9.922, + 1016 + ], + [ + 11.656, + 1024 + ], + [ + 13.484, + 1024 + ], + [ + 13.693, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 21, + "stress": false, + "first_ms": 9.48, + "total_ms": 13.442, + "tail_lag_ms": 3.905, + "batches": [ + [ + 9.48, + 8 + ], + [ + 9.569, + 1016 + ], + [ + 11.258, + 1024 + ], + [ + 13.107, + 1024 + ], + [ + 13.385, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 22, + "stress": false, + "first_ms": 9.724, + "total_ms": 13.655, + "tail_lag_ms": 3.878, + "batches": [ + [ + 9.724, + 8 + ], + [ + 9.81, + 1016 + ], + [ + 11.562, + 1024 + ], + [ + 13.361, + 1024 + ], + [ + 13.602, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 23, + "stress": false, + "first_ms": 9.965, + "total_ms": 13.752, + "tail_lag_ms": 3.737, + "batches": [ + [ + 9.965, + 8 + ], + [ + 10.053, + 1016 + ], + [ + 11.633, + 1024 + ], + [ + 13.479, + 1024 + ], + [ + 13.703, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 24, + "stress": false, + "first_ms": 9.581, + "total_ms": 13.529, + "tail_lag_ms": 3.899, + "batches": [ + [ + 9.581, + 8 + ], + [ + 9.706, + 1016 + ], + [ + 11.427, + 1024 + ], + [ + 13.238, + 1024 + ], + [ + 13.481, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 25, + "stress": false, + "first_ms": 9.459, + "total_ms": 13.404, + "tail_lag_ms": 3.889, + "batches": [ + [ + 9.459, + 8 + ], + [ + 9.524, + 1016 + ], + [ + 11.279, + 1024 + ], + [ + 13.12, + 1024 + ], + [ + 13.347, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 26, + "stress": false, + "first_ms": 9.783, + "total_ms": 13.66, + "tail_lag_ms": 3.818, + "batches": [ + [ + 9.783, + 8 + ], + [ + 9.872, + 1016 + ], + [ + 11.565, + 1024 + ], + [ + 13.378, + 1024 + ], + [ + 13.6, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 27, + "stress": false, + "first_ms": 9.719, + "total_ms": 13.678, + "tail_lag_ms": 3.897, + "batches": [ + [ + 9.719, + 8 + ], + [ + 9.82, + 1016 + ], + [ + 11.519, + 1024 + ], + [ + 13.258, + 1024 + ], + [ + 13.616, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 28, + "stress": false, + "first_ms": 17.563, + "total_ms": 21.556, + "tail_lag_ms": 3.935, + "batches": [ + [ + 17.563, + 8 + ], + [ + 17.678, + 1016 + ], + [ + 19.474, + 1024 + ], + [ + 21.218, + 1024 + ], + [ + 21.498, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + }, + { + "cycle": 29, + "stress": false, + "first_ms": 9.626, + "total_ms": 13.563, + "tail_lag_ms": 3.879, + "batches": [ + [ + 9.626, + 8 + ], + [ + 9.706, + 1016 + ], + [ + 11.438, + 1024 + ], + [ + 13.282, + 1024 + ], + [ + 13.506, + 137 + ] + ], + "bytes": 3209, + "tag": 187, + "cmd": 3, + "crc_ok": true, + "nontrivial": true + } + ] +} diff --git a/docs/测试说明/链路延迟测试说明.md b/docs/测试说明/链路延迟测试说明.md new file mode 100644 index 00000000..1abf8724 --- /dev/null +++ b/docs/测试说明/链路延迟测试说明.md @@ -0,0 +1,377 @@ +# 链路延迟测试说明(CH395F 网络传输分段时延基线) + +> **模块名称**:CH395F 网络链路分段时间延测试 +> **文档目的**:把"一次画面更新/一次收发"拆成独立可测的段,为每段建立**正常基线 + 阈值判定**, +> 输出自动 verdict,使"延迟"从主观感觉变为"哪一段超了基线"的客观判断依据。 +> **日期**:2026-09-22 +> **状态**:M1(Phase 12,SPI 层基线)已实现;M2~M5 待排期 +> **配套文档**:`docs/问题记录/远程显示延迟分析.md`(延迟预算与 D1~D10 候选原因)、 +> `CH395F_Test_Guide.md`(套件构建模式与执行顺序)、`自动化测试流程.md`(@test 闭环) + +--- + +## 0. 目标与设计原则 + +**目标**:对 CH395F 网络传输链路的每一段分别测量、分别判定,建立可重复回归的时延基线。 +任一段超标即能定位到对应原因(D1~D10,见《远程显示延迟分析》)并指向对应解决方案。 + +| 原则 | 说明 | +|---|---| +| **同一把尺子量两端** | PC 端 peer 脚本对**模拟器**和**MCU**使用完全相同的测量流程(同一协议、同一计时点),结果直接相减,差值 = CH395F 栈的净贡献(Δ,见 §1),消除"客户端成本"这个混淆项 | +| **分段隔离 + 统计量** | 每段单独测 N 次,报 `min/med/p95/max` + **≥500ms 尖峰计数**(TCP 重传 500ms 粒度的指纹,D1 自动显形);Wireshark 仅用于需要逐段精确视图时的补充手段 | +| **非侵入、非破坏** | 新增测量代码藏在 `#ifdef TEST_LATENCY_PROBE`(仅测试构建定义)→ 产品构建零代码零开销;不改任何设备持久状态(不写 IP/卷/BBT)。但 Phase 12/13/14 独占 CH395F 或占用 :7003 → **仅限台架**,与现有 HW 层约定一致 | +| **基线锁板** | 初始阈值取自静态推算;**首块台架实测后以实测值重锁为该板基线**(更新 §4 表),后续每次回归与基线比对 | + +## 1. 链路分段与测量点 + +| 段 | 名称 | 测什么 | 测点 | 方法 | 预期正常值* | 超标指向 | +|---|---|---|---|---|---|---| +| **L1** | SPI 字节命令 | 一条小 SPI 事务(GET_IC_VER / CHECK_EXIST / GLOB_INT,4~7B)端到端 | MCU,DWT µs | **Phase 12(M1)**,N=100/命令 | ≤60µs | SPI 走线/PCB/分频/DMA 配置 | +| **L2** | SPI DMA | `ch395f_read_recv_buf(N)` 完整 DMA 事务(含 CS 切换),N ∈ {4, 64, 1024, 2048} | MCU,DWT µs | **Phase 12(M1)**,N=100/档 | 1KB ≈ 1.56ms(5.25Mbit/s 理论)+0.1~0.3ms 固定开销 | SPI2/DMA/总线异常 | +| **L3** | 发送分块握手 | `write_send_buf(1KB)` → `SENBUF_FREE` 的等待时长(= `net_send_locked` 每 chunk 的真实等待) | MCU(netTask 上下文) | Phase 13(M2),每帧 4 chunk × 50 帧 | 1~5ms | 芯片发送路径 / **重传(D1) / 链路(D7)** | +| **L4** | 芯片→线上+PC | 数据交入芯片协议栈 → PC 首见字节 / 收齐全帧(peer 逐 `recv()` 批次打时间戳 → 字节到达曲线) | PC(peer) | Phase 13/14(M2/M3) | 首段 <10ms;**段间间隙 ≥500ms = 重传** | **D1/D4/D7**("网络延迟"直接证据) | +| **L5** | 队列往返 | 调用方视角 `net_recv(DONTWAIT)` / `net_send` 全程 = 入队 + netTask 调度 + 处理 + 通知 | MCU(调用方计时) | Phase 13(M2),N=100,**两档**:静默基线 + 压力档(并发 syslog/FTP 流量) | 静默 ≤5ms;压力档 ≤100ms | **D3(E-02 头阻塞)** / D5 调度 | +| **L6** | 请求→首字节(测试路径) | peer 发 10B 请求 → MCU 收齐并启动应答(服务端视角);与 PC 侧"发→首字节"对照,差值 = 线路 + PC 栈 | MCU + PC 双侧 | Phase 13(M2) | ≤50ms | MCU 收路径/调度 | +| **L7** | 整帧 3209B 往返 | LCDMEM 全帧:PC 发 → 收齐(含 L3+L4) | PC 为主 | Phase 13(M2) | ≤150ms;**spike(≥500ms)=0** | D1+D2+D4 综合 | +| **L8** | 生产路径帧周期 | **真实 uiTask + 真实 `ui_net_sink`**(:7003)的完整帧周期:请求拾取(10ms 拍)+ 队列 + 发送 + L4 + peer 侧编码 | MCU 探针 + PC | Phase 14(M3),N=50 帧 | 110~200ms(含 peer 100ms 节拍) | 全段(用户真实体感) | +| **L9** | 连接/重监听 | connect→ESTABLISHED;close→relisten 成功 | MCU(net_poll 观测点) | Phase 14(M3) | ≤150ms / ≤10ms | 握手/队列;**≥500ms = D10 的 `osDelay(500)` 路径** | +| **L10** | 日志税 | 一行 60 字符 `printf`(现 fputc 逐字节 @115200)耗时 | MCU | Phase 13 附带(M2) | ≈5ms(**预期值本身偏高**,作为 D6 的量化证据) | D6(信息项) | +| **L11** | uiTask 节拍 | 生产循环实测周期(`osDelay(10)` 之外多花多少 = LVGL 渲染 + 渲染中排空) | MCU 探针 | Phase 14(M3) | 10~15ms | **D5/D9** | +| **Δ** | CH395F 净贡献 | `L8(MCU) − L8(模拟器)`,同一 peer 交替跑 | PC | Phase 14 收尾(M3) | ≤80ms | 直接回答"网络延迟"占多少 | + +\* 初始阈值取自《远程显示延迟分析》§1.2/§1.3 的静态推算;首块台架实测后按 §4 重锁。 + +## 2. 测试架构 + +### 2.1 文件与注册 + +| 文件 | 职责 | 注册 | +|---|---|---| +| `test/net_latency_test.h` | 计时/统计/报告 API + Phase 入口声明 | 头文件随工程包含路径,无需注册 | +| `test/net_latency_test.c` | DWT µs 时基 + 统计 + Phase 12 用例(M1);Phase 13/14 用例(M2/M3 追加) | **仅 `MDK-ARM/STM32F407-Demo.uvprojx`**(Test 组;模拟器 CMake 不编 `test/*.c`,与现有测试文件一致) | +| `test/latency_peer.py` | PC 端 peer(纯标准库):`--mode net`(M2)/`--mode remo`(M3),`--samples N`,`--json out.json` | 无(Python) | +| 本文档 | 规划/流程/判定表/基线 | — | + +### 2.2 阶段编号(沿用 `ch395f_test.h` 互斥体系) + +``` +ENABLE_PHASE12_TESTS [硬] SPI 时延基线(L1/L2)—— testTask 独占 CH395F,**无需 PC** +ENABLE_PHASE13_TESTS [网] 芯片+队列时延(L3~L7/L10)—— 需 latency_peer.py --mode net (M2) +ENABLE_PHASE14_TESTS [网] 生产路径时延(L8~L11 + Δ)—— 真实 uiTask/:7003,peer --mode remo (M3) +``` + +- Phase 12 归 HW 组,13/14 归 NET 组 → 与现有"两层互斥 `#error`"自动兼容; +- 13 与 14 同属 NET 组可同启(13 用 testTask 私有监听端口,14 用生产 :7003,socket 不冲突); +- 全部非破坏性 → 只读级,**台架执行**。 + +### 2.3 时基(DWT CYCCNT) + +- 工程内首个 DWT 用户(`sys_clock` 是纯 HAL tick,无 µs 能力);`lat_time_init()` 幂等使能 + `CoreDebug->DEMCR | TRCENA` + `DWT->CYCCNT=0` + `DWT->CTRL | CYCCNTENA`; +- 分辨率 1/168MHz ≈ 5.95ns;换算用 `SystemCoreClock`(不写死频率); +- **32 位回绕 = 25.17ms**:M1 各段(<5ms)安全;M2/M3 的长段(整帧/重传尖峰)改用 + **累积采样**(等待循环中每 ~10ms 采样一次计数差并累加),届时在 Phase 13/14 代码内实现; +- 每次运行先打一行校准线 `[LAT] cal core_clk=...`,台架可用逻辑分析仪核对时基正确性。 + +### 2.4 统计与报告格式 + +- 每段 N 次采样(静态数组,零堆):`min / med / p95 / max` + `spikes`(≥ 尖峰阈值计数); +- MCU 串口报告行(**纯 ASCII**,`verify_log.py` 可校验): + +``` +[LAT] run_id= phase=12 +[LAT] cal core_clk=168000000 us_per_1024cyc=6 +[LAT] seg=spi_cmd_getver n=100 min=10 med=10 p95=10 max=12 us spikes=0 +[LAT] seg=spi_dma_1024 n=100 min=1611 med=1611 p95=1612 max=1612 us spikes=0 +[LAT] verdict=OK seg=spi_dma_1024 (med=1611us warn<=3200 crit<=6400 spikes=0/0) +[LAT] summary verdict=OK +[LAT] report complete +``` + +- verdict 语义:`OK`(全部段达标)/ `WARN`(某段 med 超 warn 阈值,**计 PASS**,提示重锁阈值)/ + `FAIL`(某段 med 超 crit 阈值或 spikes 超限 → 套件判 FAIL); +- **字段名刻意用 `crit` 而不用 `fail`**(2026-09-22 首跑实证):`verify_log.py` 默认 + Forbidden 集含大小写不敏感的 `FAIL`,报告行里出现 `fail<=` 字面量会把**自己的 OK 行** + 判成 Forbidden 命中;`verdict=FAIL` 行才是真正的失败信号,须被捕获; +- 校验标记(**按构建模式区分**,`Network Init` 仅 netTask 的 NET 分支打印,硬件层模式 + netTask 自挂起 → 无此标记): + - 硬件层(Phase 12):Required `App init OK;net init: OK;PHY: 100M FULL;[LAT] report complete` + - NET 层(Phase 13/14):Required `App init OK;Network Init;net init: OK;[LAT] report complete` + - **Forbidden 必须覆盖默认集**(2026-09-22 首跑实证):套件 TEST_REPORT 行 + `(... failed=0)` 含 "fail" 子串会被默认 `FAIL` 模式自捕获 → + `--forbidden "HardFault;assert;[ERR];[FAIL];verdict=FAIL"` + (真失败仍被捕获:TEST_CHECK 失败行含 `[FAIL]`+`[ERR]`,时延段失败含 `verdict=FAIL`) +- **M2 追加判定**(Phase 13):Required 再含 `=== Phase 13: 1/1 PASSED`;peer 侧以 + **退出码 0**(收到 `0xFF` 并打印汇总)= PASS,JSON 里 `l7_crc.fail == 0` 为帧格式正确性证据。 + +### 2.5 PC 端 peer 与关联(M2/M3) + +- `latency_peer.py`:纯标准库(socket/json/time/argparse);`--mode net` 走测试协议 + (M2 已实现),`--mode remo` 模拟 DTU-RemoteLCD 客户端(M3); + 输出每帧 `req2first / total / recv 批次到达时间` + 直方图 + spike 计数 + JSON 报告; +- **M2 测试协议(`--mode net`,端口 8080,服务器驱动,MCU 逐个下发操作字节)**: + + | 操作 | 值 | 语义 | peer 侧动作 | + |---|---|---|---| + | HELLO | `5A A5 58 01` | 会话起始 | 连接后立即发送;MCU 识别后重置会话状态机 | + | L5_RECV | `0x01` | 对端灌入 | 20×100B,**50ms 间隔**(≈2KB/s,低于 MCU 抽干速率);测非阻塞收的队列往返 | + | L5_SEND | `0x02` | MCU 连发 | 收 2000B(20×100B 阻塞发) | + | L6 | `0x03` | 小请求/回显 | 发 10B 模式请求 → 校验 MCU 原样回显;逐样本重复 | + | L7 | `0x04` | 全帧读取 | 发 9B LCDMEM 请求 → 收 3209B 全帧(4 批),逐样本重复 | + | L3 | `0x05` | 4KB flood | 排空 16×4096B(=64KB);MCU 侧 chunk 等待由探针测 | + | DONE | `0xFF` | 会话结束 | 打印汇总并退出(exit 0 = PASS) | + +- **帧格式严格照生产 RemoDispBus**(`ui_net_sink.c` `remo_handle`/`remo_send`): + `[TAG][CMD][LEN_H][LEN_L][DATA][CRC]`,**CRC = 仅对 DATA 段逐字节异或**, + 整帧 = 4 + len + 1;LCDMEM 请求 = `AA 03 00 04 00 00 00 00 00`(LEN=4 即"4B 偏移"的 + 数据段长度,偏移须为 0),应答 = `BB 03 0C 84` + `[偏移u32=0][位图3200B]` + CRC = **3209B**; +- **peer 重试包装**:3 次会话尝试(首次预算 `--wait`,其后 ≤60s),遇连接错误/超时重建连接 + 并重发 HELLO;MCU 侧 abort 后进"重连等待态"(见 §3.2),两侧构成闭环; +- **尾批诊断/缓解选项(`--mode remo`,均无需抓包)**:`--ack-probe`(帧未收齐时发 1 个解析器忽略的字节, + 让 TCP 段捎带 ACK)、`--ack-fast`(Windows 关闭本 socket 延迟 ACK:`SIO_TCP_SET_ACK_FREQUENCY=1`); + 两者把帧尾 52ms 降到 3.9ms(结论见 `链路时延实测数据分析.md` §3.3); +- **关联**:`serial.log` 行与 peer JSON 均带**主机时间戳** → 同一时间轴直接合并,无需 NTP; + 需要逐段精确视图时再并行跑 Wireshark(`tcp.port==8080`)按时间对齐。 + +### 2.6 MCU 侧探针(M3,`#ifdef TEST_LATENCY_PROBE`) + +产品代码(`net_socket.c`/`ui_net_sink.c`/`ui_task.c`/`ui_port.c`)内各数行打点, +经静态环形缓冲(总计 <1.5KB BSS)上报;**产品构建下该宏未定义,探针代码编译消失**, +两种构建配置都必须过 `@build`(0 错 0 警)。 + +## 3. 执行流程 + +### 3.1 M1:Phase 12(SPI 层基线,**无需 PC**) + +```powershell +# 0) 前置:test/test_config.h 已启用 TEST_SUITE_CH395F + ENABLE_PHASE12_TESTS +# (当前工作副本已按此配置;产品构建 = 全部注释掉) +# 1) 构建(0 错 0 警) +& "MDK-ARM\build.bat" +# 2) 一键闭环:开串口+清日志 → 烧录(reset.bat flash,Reset&Run)→ wait-idle → 标记校验 +# 硬件层模式无 "Network Init" 标记(netTask 自挂起),且 TEST_REPORT 行含 "failed=0" +# → Required/Forbidden 均用下方定制集(见 §2.4) +& '.venv\Scripts\python.exe' test\run_boot_test.py ` + --required "App init OK;net init: OK;PHY: 100M FULL;[LAT] report complete" ` + --forbidden "HardFault;assert;[ERR];[FAIL];verdict=FAIL" +# 3) 提取 [LAT] 报告行(直接读日志文件最可靠) +& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py log --grep "\[LAT\]" +``` + +台架首跑已完成(2026-09-22):7 段全部 OK、零尖峰、p95≈min(零抖动);§4.1 基线已按实测重锁。 + +### 3.2 M2:Phase 13(芯片+队列基线,**需 PC peer**) + +```powershell +# 0) 前置:test/test_config.h 启用 TEST_SUITE_CH395F,test/ch395f_test.h 仅启用 +# ENABLE_PHASE13_TESTS(Phase 13 必须独占 8080 socket,勿与其他 NET 层 Phase 同启) +& "MDK-ARM\build.bat" # 必须 0 错 0 警 +# 1) 先 clear 再起 peer(顺序不可反:wait-idle 需要看到会话活跃期) +& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py clear +& '.venv\Scripts\python.exe' test\latency_peer.py --host 192.168.1.100 --port 8080 ` + --json "docs\测试说明\m2_data\m2_quiet.json" --wait 170 # 后台作业 +# 2) 烧录(Reset&Run 不可省) +& "MDK-ARM\download.bat" +# 3) 等空闲:会话本身约 2s,但 peer 首连常因"芯片空闲超时 RST"失败一次并 2s 后重试 +& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py wait-idle ` + --timeout 120 --settle-ms 10000 --max-ms 110000 +# 4) 判定:**直接 Grep serial.log 文件**(勿用 log --grep,勿只看 wait-idle) +# Required: "App init OK" / "net init: OK" / "[LAT] report complete" / "=== Phase 13: 1/1 PASSED" +# Forbidden: HardFault / assert / [ERR] / [FAIL] / verdict=FAIL +``` + +- **会话顺序(run3 重排后的最终顺序)**:HELLO → L5_RECV → L5_SEND → L3 → L10 → L6 → L7 → DONE。 + 易失步的 RX 协议段(L6/L7)置于末位,避免失步污染前置段;L6/L7 样本数 10,L3 = 16×4KB; +- **abort 后自动进"重连等待态"**(120s):DONTWAIT 轮询 scan `5A A5 58 01` → 命中即 + `p13_reinit()` + 重启会话(实测:attempt1 失败后 attempt2 在 +20s 自动接上并跑完); + 期间周期自愈(10s 无 peer 且状态仍 LISTENING → 重发芯片重监听),**干净完成后停止自愈** + (否则每 10s 拆建监听器,日志永不静默、台架无法判空闲); +- **安静档判定**:`ftp_stress=0`(不加 `--ftp-stress`),即无 FTP/对时干扰的静默基线。 + +M3 追加:`--mode remo --port 7003`(生产 RemoDispBus 客户端)+ `--ftp-stress`(E-02 复现)。 +M4(可选)将 3.1/3.2 编排为一键 `test/run_latency_test.py`(open/clear/flash/wait-idle/peer/collect/merge/verdict)。 + +## 4. 判定基线表 + +### 4.1 M1 段(Phase 12)—— **已按台架首跑实测重锁**(2026-09-22,COM6 台架) + +| 段(seg) | 实测 med(首跑,n=100) | 实测 min/max | warn(med >) | crit(med >) | +|---|---|---|---|---| +| `spi_cmd_getver` | **10µs** | 10/12 | 40µs | 100µs | +| `spi_cmd_chkexist` | **15µs** | 15/16 | 40µs | 100µs | +| `spi_cmd_gint16` | **15µs** | 15/16 | 40µs | 100µs | +| `spi_dma_4` | **20µs** | 20/20 | 40µs | 100µs | +| `spi_dma_64` | **114µs** | 114/115 | 250µs | 600µs | +| `spi_dma_1024` | **1611µs** | 1611/1612 | 3200µs | 6400µs | +| `spi_dma_2048` | **3208µs** | 3208/3210 | 6400µs | 12800µs | + +- 重锁规则:warn ≈ 2×med,crit ≈ 2×warn(兼顾板间/温度漂移与回归灵敏度: + SPI 时钟减半 → 超 warn;再减半 → 超 crit;DMA 超时 1s → 必超 crit); +- 首跑特征:p95 ≈ min(**零抖动**),时基校准行核对无误(168MHz,6µs/1024 周期); +- DMA 斜率实测 1.56µs/B(≈ 5.25 Mbit/s,与 SPI2 分频 8 @42MHz APB1 设计值一致), + 固定开销 ≈ 14µs(CS 切换 + DMA 配置)→ **每 chunk(1KB)SPI 成本基线 = 1.61ms**。 + +### 4.2 M2 段(Phase 13,芯片 + 队列)—— **已按台架实测重锁**(2026-09-22,COM6 台架;run12/run13/run15 三次独立 PASS) + +**MCU 侧(`[LAT]` 报告,安静档 `ftp_stress=0`)** + +| 段(seg) | 测量含义 | run12 med(min/max, n) | run13 med(min/max, n) | **run15(最终代码)med(min/max, n)** | warn(med >) | crit(med >) | +|---|---|---|---|---|---|---| +| `queue_rt_recv` | L5 对端灌 100B×20@50ms,MCU 非阻塞收的队列往返 | 6014µs(5365/6020, 19) | 6007µs(5392/6025, 20) | **5997µs**(5513/6033, 20) | 10ms | 100ms | +| `queue_rt_send` | L5 MCU 阻塞发 100B×20 | 986µs(836/1204, 20) | 987µs(833/1209, 20) | **987µs**(838/1209, 20) | 10ms | 100ms | +| `chunk_wait` | L3 16×4KB,芯片 chunk 内 SENBUF 等待(探针) | 235µs(215/238, 64) | 219µs(213/377, 64) | **219µs**(214/381, 64) | 10ms | 500ms(spike) | +| `logtax_line` | 60 字符 printf @115200(本地) | 4396µs | 4396µs | **4396µs**(4394/4422) | 10ms | 25ms | +| `req2first_mcu` | L6 10B 请求到齐 → 回显发出 | 310µs(296/338, 10) | 318µs(263/361, 10) | **319µs**(308/338, 10) | 20ms | 200ms | +| `fullframe_mcu` | L7 9B 请求 → 3209B 全帧发给芯片(netTask 内 4 分块) | 6021µs(5986/6103, 10) | 6052µs(5966/6078, 10) | **6028µs**(5991/6066, 10) | 50ms | 500ms | + +**PC 侧端到端(peer JSON,含线路 + Windows 栈)** + +| 段 | run12 med(min/max) | run13 med(min/max) | run15 med(min/max) | 说明 | +|---|---|---|---|---| +| L6 端到端(op→收到回显) | 12.5ms(5.5/16.0) | 13.8ms(5.9/16.0) | 8.9ms(6.3/16.0) | 比 MCU 侧 `req2first_mcu` 多 ~10ms → PC/线路段 | +| L7 首字节 | 10.9ms(7.9/16.2) | 11.8ms(7.5/17.7) | 11.3ms(7.8/16.5) | 同上量级 | +| L7 全帧 3209B | 61.1ms(54.2/61.5) | 61.7ms(59.9/70.9) | 57.9ms(52.6/72.3) | 每帧固定 4 个 recv 批次 | + +> **⚠ 测量方法学(run14 实证)**:探针不得在 net 热路径逐调用打日志——曾加 `[NET] wait pick/block` +> 两行后日志量 40KB → 1.04MB(9,644/9,779 行 = 98.6%),115200 下冲掉 `[LAT]` 证据行 +> (段行被截断/丢失)、`wait-idle` 永远判不出空闲,且把 `queue_rt_send` med 从 **987µs 抬到 +> 7685µs**(约 7.8×)、`queue_rt_recv` p95 抬到 21ms —— 即"观测行为本身改变被测对象"。 +> 该轮(run14)的 MCU 侧数据**不作为基线**,只保留 peer 侧 PASS 事实。 + +| L7 CRC 校验 | 10/0 | 10/0 | 帧格式/CRC 正确(生产格式对齐后) | + +- **结论一:安静档不存在 500ms 台阶**——三轮 6 段全部 `verdict=OK`、零尖峰、p95≈med; + 会话级路径的 MCU 净成本量级为 **~6ms**(`queue_rt_recv`),与 netTask 轮询节拍同量级; +- **结论二:芯片 chunk 级不是瓶颈**——`chunk_wait` med ≈ 219µs ≈ 每 chunk SPI 成本(1.61ms)的 1/7, + 说明 `SENBUF_FREE` 等待远小于 SPI 搬运本身; +- **结论三(M2 关键发现,待 M3 定性)**:L7 全帧的 **最后 137B 尾巴稳定迟到 ~50ms**—— + 10/10 帧的前 3 批 1024B 在 ~4ms 内到齐,第 4 批 137B 落在 +50ms(如 first=7.46ms、 + 尾批 61.20ms);该尾批主导整帧时长(total ≈ 61ms),MCU 侧发完 3209B 仅 6.0ms + → 延迟不在 MCU 发送路径,而在**芯片 TCP 尾段发送/对端 ACK 交互**(候选:Nagle 式 + 小段等待 + 对端延迟 ACK)。M3 用 Wireshark 验证后再定对策(勿在未证实前改协议); +- 重锁规则:warn ≈ 现状 ×1.7~10(保留回归灵敏度),crit 取"明显故障"档; + `chunk_wait` 仍以 500ms spike 为 crit(D1 重传指纹)。 + +### 4.3 M3 段(Phase 14,生产路径 + 模拟器 Δ)—— **已按台架实测重锁**(2026-09-22,COM6 台架) + +**MCU 侧(`[LAT]` 报告,真实 uiTask + `ui_net_sink` :7003)** + +| 段(seg) | 测量含义 | 实测 med(min/max, n) | warn(med >) | crit(med >) | +|---|---|---|---|---| +| `ui_loop`(L11) | uiTask 生产循环实测周期(渲染+轮询+osDelay(10)) | **10986µs**(10970/21000, 120) | 25ms | 50ms | +| `prod_cycle_mcu`(L8) | LCDMEM 取 1bpp + 组帧 + net_send 4 分块(不含 10ms 拾取拍) | **7142µs**(7111/7179, 50) | 20ms | 200ms | +| `relisten`(L9) | 断线后 auto-relisten 全程(**修复后**:单次尝试 + netTask 延迟重试) | **89000µs ×3**(三次同值) | 100ms | 500ms | + +> `relisten` 阈值重锁:修复前 588000µs(Trap 09 `osDelay(500)` 忙等)→ crit 500ms 即 D10 指纹; +> **D10 修复后实测 89ms**(= 30ms 重试间隔 + 约 60ms 芯片沉降),故 warn 由 50ms 放宽到 **100ms**,crit 保持 500ms +> (仍能捕获"退回阻塞重试"的回归)。修复前后对照见 `链路时延实测数据分析.md` §7.1。 + +**PC 侧端到端(peer `--mode remo`,与模拟器 A/B)** + +| 指标 | 真机(65 帧) | 模拟器(50 帧) | Δ(真机−模拟器) | 阈值 | +|---|---|---|---|---| +| 帧周期 total | **66.0ms**(53.3/75.3) | **6.8ms**(0.6/13.1) | **+59.2ms** | ≤80ms | +| 首字节 | 13.9ms(8.5/19.1) | 6.7ms(0.5/13.0) | +7.2ms | — | +| **尾批延迟 tail lag** | **52.6ms**(43.3/61.5) | **0.0ms**(0/0.1) | **+52.6ms** | — | +| 帧 CRC / 位图非平凡 | 65/0、65/65 | 50/0、50/50 | — | 全通过 | +| 连接 / 断开重连(+INIT) | 2.6ms / 23.5~34.7ms | 10.5ms / 32.0ms | — | ≤150ms | + +- **一帧预算闭合**:66.0ms ≈ 拾取调度 6.8ms + MCU 净处理 7.1ms + **芯片尾批 52.6ms**; + 尾批 = 不满块的 137B(=3209−3×1024),**与负载无关**(压力档 tail 中位 52.2ms 与安静档 52.6ms 一致); +- **控制实验(都不需要抓包工具)**:① 同一客户端打模拟器 tail=0 → 非 PC 栈;② M2 `L3` 满块批量(16×4096B=64KB/125.4ms ≈2ms/KB) + 无尾批惩罚 → 满 MSS 段不受影响;③ 压力档下尾批不变 → 属芯片 TX 行为,非调度拥塞; +- **根因(M3 已证实)**:芯片 **MSS=1024**、发送 FIFO=1024B → MCU 每次 1024B 写 = 一个**满 MSS 段**(立即发), + 而帧尾 137B 是**次 MSS 段** → 被 Nagle 扣住等 ACK;**Windows 的对端延迟 ACK** 使该 ACK 晚到 ~50ms; +- **解法(客户端侧,实测)**:`--ack-probe`(帧未收齐即补 1 个解析器忽略的字节,捎带 ACK)或 `--ack-fast` + (Windows 每 socket `SIO_TCP_SET_ACK_FREQUENCY=1`)→ tail **52→3.9ms**、帧周期 **65.5→13.7ms**; + **设备侧无可行解**(帧长 3209 为质数、TX FIFO=MSS、CH395F 命令表无 Nagle/立即发送开关)。 + +**压力档(`--ftp-stress`,E-02 复现)** + +| 分桶 | n | med | p95 | max | 尖峰(≥500ms) | +|---|---|---|---|---|---| +| quiet | 15 | 65.9ms | 75.8ms | 75.8ms | 0 | +| stress | 30 | 69.1ms | 75.6ms | **17717.9ms** | **1** | + +- 受压首帧等待 **17.7s**(= FTP 客户端空闲时长),`QUIT` 后立即恢复 55~75ms 正常服务; +- 机制:`lftpd` 控制行用**阻塞** `net_recv(0)`,而 netTask 是全部 socket 操作的唯一执行者 → 单会话可冻结全网; +- 上限:单次 recv 受 `NET_RECV_TIMEOUT_MS`(30s) 限制,但 `read_line` 会再次阻塞 → **空闲 FTP 连接可长期占用 netTask**。 + +> 完整分析(时间预算拆解、三区制、缺陷量化、修复优先级)见 **`链路时延实测数据分析.md`**。 + +## 5. 里程碑 + +| 里程碑 | 内容 | 验收 | 状态 | +|---|---|---|---| +| **M1** | 计时助手(DWT µs)+ Phase 12(L1/L2 SPI 基线)+ 本文档 + uvprojx 注册 | `@build` 0/0;`@test` 出 `[LAT]` 报告;产出 L1/L2 首份基线 | **完成**(2026-09-22 台架首跑 PASS,7 段全 OK,基线已重锁) | +| **M2** | `latency_peer.py`(--mode net) + Phase 13(L3~L7/L10)+ `[LAT]` 标记进 @test | 台架首跑产出"芯片+队列"基线;可回答"500ms 台阶是否存在" | **完成**(2026-09-22 台架,run12/run13/run15 三次独立 PASS;6 段全 OK、零尖峰;顺带根治 net 层 D12 通知值缺陷;§4.2 已重锁;产出 `docs/测试说明/m2_data/`) | +| **M3** | Phase 14:`TEST_LATENCY_PROBE` 探针 + `--mode remo` + 模拟器 Δ 对照 + 压力档(E-02 复现)+ §4.2 定稿 | 完整判断依据:任一段超标 → 故障对照表 | **完成**(2026-09-22 台架:L8/L9/L11 + Δ + E-02 全部实测;D10 量化为 588ms、E-02 量化为 17.7s;产出 `m3_data/` 与 `链路时延实测数据分析.md`) | +| **M4(可选)** | `run_latency_test.py` 一键编排 | 一条命令出报告 | 待排期 | +| **M5(可选,在役设备)** | 非独占采样器:事件触发,10s 旁路打点入 syslog | 现场不动台架也能测生产链路 | 设计预留 | + +## 6. 故障对照表(哪段超标 → 查什么) + +| 现象(段超标) | 首要怀疑 | 动作 | +|---|---|---| +| L1/L2 超 fail | SPI 走线/PCB/SPI2 分频或 DMA 配置 | 对照 `docs/问题记录/CH395F_Trap_Records.md`;逻辑分析仪抓 SCK/CS | +| L3 spike ≥500ms | **D1 重传 / D7 链路** | Wireshark `tcp.analysis.retransmission`;查 PHY 协商/网线/Wi-Fi | +| L3 基线(非 spike)抬高 | 芯片发送路径 / 中断响应 | 对照 CH395F 手册 §5.17/5.18;`GET_CMD_STATUS` 逐段核对 | +| L5 压力档爆表 | **D3(E-02 头阻塞)** | 定位触发源(阻塞 recv 的 FTP/对时)→ 方案 S6 | +| L5/L6 静默档抬高 | D5 调度(任务抢占/节拍) | 查任务优先级与 osDelay;`stack_report.py` 核对栈水位 | +| L6/L7 整体抬高且 L1~L5 正常 | 线路+PC 段(L4) | Wireshark 段间间隙;`Δ` 对照定位 MCU 侧净贡献 | +| **L7 尾批固定迟到 ~50ms**(M2/M3 实测) | **已定性(无需抓包)**:芯片 MSS=1024 → 帧长 3209=3×1024+**137**,末段是次 MSS 段被 Nagle 扣住,等对端 ACK;**Windows 延迟 ACK** 把这一等推迟 ~50ms | **客户端侧解法已实测**(见 `链路时延实测数据分析.md` §3.3):帧未收齐时补发 1 字节捎带 ACK(`--ack-probe`)或关本 socket 延迟 ACK(`--ack-fast`)→ tail **52→3.9ms**、帧周期 **65.5→13.7ms**;**设备侧无解**(帧长为质数、TX FIFO=MSS、CH395F 无 Nagle 开关),不必再投入 | +| **L5 出现重复投递**(收到 > 发送) | **D11**:芯片 TCP RX 饱和下重复递交 + 过度 ACK | 降低灌入速率/加抽干;见 §6 下方 D11 行 | +| **会话在 L6/L7 无征兆失步**(结果错位/挂死) | **D12**:net 层任务通知值语义(清除掩码必须全 1 + 以返回值判定;32 位整体作结果) | 见 §6 下方 D12/D12-a 行;核对 `net_wait_result` 与 `net_process_messages` 的通知打包 | +| **L7 一直超时 / peer 报 `l7_crc.fail`** | **D15**:测试帧与生产 RemoDispBus 格式不一致 / peer 超读吃掉操作字节 | 见 §6 下方 D15 行;对照 `ui_net_sink.c` 的 `remo_send`/`remo_handle` | +| **会话跑完一段后无限循环、peer 空等 300s** | **D16**:Phase 13 状态机迁移目标错(L7 曾回跳 L3) | 见 §6 下方 D16 行;最终顺序 L5→L3→L10→L6→L7→DONE | +| **`[LAT]` 段行缺失/截断、wait-idle 判不出空闲** | **D12-b**:探针在 net 热路径逐调用打日志(日志洪水) | 见 §6 下方 D12-b 行;删除热路径日志 | +| **`relisten` 段 FAIL、每次断线固定 ~588ms** | **D10**:`net_relisten_locked` 的 Trap 09 重试含 `osDelay(500)`(netTask 忙等) | **已修复**:单次尝试 + netTask 延迟重试(间隔 30ms/上限 2s)→ 实测 **89ms 且不阻塞**;另:客户端 600ms 内重连被拒的问题同时消除 | +| **远端显示整帧卡住十几秒后自恢复** | **E-02**:`lftpd` 阻塞 `net_recv(0)` 占用 netTask | **已修复**:控制行/数据读改分片有界读(`net_recv_to` 5ms 片 + 每片让出 1ms,总预算仍 30s)→ 受压帧 **17.7s → 74.7ms(max 106.8ms,零尖峰)** | +| **INIT 应答帧字节数字段恒为 0** | **D17**:`remo_send_init` 只发出 8B 数据段的前 6B(u32 被截半) | **已修复**:改发 8B(字段定义本就是 `[W u16][H u16][FB u32]`)→ peer 读到 **framebytes=3200**;协议文档已同步勘误 | +| **同一 FTP 会话内第二个数据连接失败(425)** | **D20**(已定位未修,见 `链路时延实测数据分析.md` §7.4) | 准确条件 = **每次开机后的第一个 FTP 会话**在其第 2 个数据连接失败(之后会话 4/4 通过);日志签名 = 数据连接建立后 ~10ms 芯片把新连接拆掉(`DISCONNECTED (chip tcp=0x00)`)→ `accept aborted` → 425。属芯片"同一 Socket 快速复用"竞态;已回退两次不稳定修法,保留安全硬化(陈旧 DISCONNECT 甄别 + 2 字节状态缓冲越界修复)。复现:`test/ftp_regression.py`;后续优先做数据 Socket 轮换,现场可用客户端重试规避 | +| **`[LAT]` 多段数值雷同 / 全 0 却 verdict=OK** | **D18/D19**(测试工装):多站点共用全局探针窗口跨段污染;漏调 `lat_time_init()` → DWT 恒 0 假绿 | 均已修复:`lat_probe_add_seg()` 按段名过滤 + 补时基初始化 + `med==0` 判无效 | +| **连接建立后 15~30s 无流量即被 RST** | **D13**:芯片空闲超时(MCU 尚未就绪) | 见 §6 下方 D13 行;客户端须容忍并重连 | +| **peer 重连后 MCU 状态滞留、新连接字节被吞** | **D14**:芯片静默回收 Socket(无 DISCONNECT 中断) | 见 §6 下方 D14 行;用 `net_resync_listen`/`net_relisten` 自愈 | +| L8 超而 L7 正常 | 生产路径附加成本(uiTask 节拍/LVGL/日志) | 对照 L10/L11;方案 P0-b/S7/S8 | +| L9 relisten ≥500ms | **D10 `osDelay(500)` 重试路径** | 方案 S6(非阻塞重试) | +| Δ 显著 >80ms | CH395F 栈净开销异常 | 回到 L1~L9 逐段排查 | + +### 6.1 M2 台架实证缺陷与修复(D11~D14) + +| 编号 | 现象(台架实证) | 根因 | 修复/状态 | +|---|---|---|---| +| **D11** | L5 灌入 5000B 时对端实收 11884B(+138%)并触发过度 ACK → 对端 RST | 芯片 TCP RX 缓冲饱和下重复递交(retransmit 再投递) | 灌入降到 20×100B@50ms + MCU 侧冗余字节守卫(`> 2000 + 2×256` 即 abort)+ L10 后 DONTWAIT 抽干;**未根治(芯片行为),靠应用侧限速规避** | +| **D12** | L7 段 MCU 侧挂死 35s / 结果整段错位(run3~run11) | **net 层结果回传(任务通知值)语义**:`xTaskNotifyWait(0, 0, &v, ...)` 的**清除掩码为 0 = 根本不清值**(FreeRTOS 以位掩码 `ulNotifiedValue &= ~mask` 清值)→ 上一次调用的结果残留,被后续每次零等待重读成"本次结果" → 表现为"结果错位一格"或永久等待 | **已修复(最小改动)**:`net_wait_result()` 两段式 + **清除掩码全 1(读取即消费)** + **以 `xTaskNotifyWait` 返回值 `pdTRUE/pdFALSE` 判定是否已落地**。通知值 **32 位整体仍是结果**(不切分位域)。三次 PASS 验证 | +| **D12-a** | ——(修复过程中自查发现的同类陷阱,务必记住) | ① 若把序列号压进通知值高 16 位,单次结果上限被压到 **32767**,而 `TEST_BUF_SIZE=32768`(Phase 6/8/9)会溢出成 **负数(假错误)**;② 若用 `v != 0` 判定"通知是否已落地",合法的 **0 结果**(`recv` 返回 0 = 对端关闭)会被误判成"无通知"→ 进入阻塞等待 → **永久挂死** | 二者均已避开:不做位域切分;只用返回值判定。**后续改这条路径前必须复核这两点** | +| **D12-b** | 探针日志把测量对象改坏(run14) | 在 `net_wait_result` 内逐调用打 `pick/block` 日志 → 日志量 40KB→1.04MB(98.6% 是这两行)→ 115200 下丢行、`[LAT]` 证据被截断、`wait-idle` 判不出空闲,且 `queue_rt_send` med **987µs→7685µs(7.8×)**、`queue_rt_recv` p95→21ms | **已修复**:删除 net 热路径的逐调用日志(诊断改为在 `[LAT]` 段报告侧聚合)。**规则:热路径禁止逐调用打点** | +| **D13** | 连接建立后 15~30s 无流量即被 RST(`WinError 10054`) | 芯片空闲超时:MCU 尚在启动/忙,握手已在芯片侧完成但无数据 → 芯片 RST | 测试侧:peer 3 次重试包装(run12/13/15 均首连失败、+2s 重连成功);**生产含义**:E-02 类 30s netTask 冻结会让已连客户端被芯片 RST → 触发重连风暴(与 D11/D3 叠加)→ 客户端须容忍重连 | +| **D14** | peer 重连后 MCU 协议状态错乱、新连接 HELLO 被旧会话吞掉(run7 attempt3) | 芯片在 peer FIN 后**静默回收** Socket 回监听且**不发 DISCONNECT 中断** → MCU 侧 `state` 滞留 ESTABLISHED | **已修复(两层)**:① 新增只读 `net_sock_state()` + `net_resync_listen()`(仅修 MCU 侧状态,不发芯片命令——run8 实证发 close 会摧毁芯片侧健康监听器 → 连接永拒 170s);② 新增 `net_relisten()`(无条件芯片侧重监听,含 Trap 09 重试——run9 实证一次性 `net_listen` 落 Trap 09 窗口失败且被静默忽略 → 监听器永久丢失);abort 后按状态分派 + 10s 周期自愈(**干净完成后停止自愈**,否则每 10s 拆建监听器、台架永不判空闲) | +| **D15** | L7 段从未完成:MCU 报 `L7 peer timeout` / peer 收 3210B 且 `l7_crc.fail=1`(run10) | 测试侧两处:① 测试 L7 帧与生产 RemoDispBus 格式不一致(响应 LEN 应为 3204 而非 3200、缺 4B 偏移字段、CRC 应只覆盖数据段、且多发 4B 垃圾;请求 LEN 应为 4);② peer `recv(4096)` **超读**,把下一个操作字节 `0x04` 当帧体吃掉 → 长度 3210 → 校验失败 → peer 关闭 | **已修复**:MCU 建帧/校验与 peer 请求/校验全部对齐生产(`remo_send`/`remo_handle` 语义);peer 改 `recv(min(4096, 剩余))` 有界读;离线自检(正确帧通过、错误 LEN/短帧/坏位图/坏 CRC 全拒) | +| **D16** | 会话跑完 L7 后**永不结束**、L3→L10→L6→L7 无限循环、peer 空等 300s(run11) | Phase 13 状态机 L7 段的迁移目标是 `P13_ST_L3`(run3 重排前的旧顺序遗留)→ 永不下发 `0xFF`(DONE) | **已修复**:L7 → `P13_ST_DONE`;并在该处加注释说明最终顺序为 L5→L3→L10→L6→L7→DONE(防再次回跳) | + +## 7. 约束核对(AGENTS.md) + +- 每里程碑结束 `@build`(0 错 0 警)+ `@test` 硬件闭环; +- 新 `.c` 注册:`test/*.c` 仅 MCU 端编译 → **只注册 `MDK-ARM/STM32F407-Demo.uvprojx`**(Test 组; + 模拟器 CMake 不含 test 文件,与 `ch395f_test_task.c` 等现状一致); +- 新增 C 字符串字面量**纯 ASCII**(`[LAT]` 报告行均为英文),中文只写注释; +- 自研测试代码**零 C 库堆**:样本/缓冲全部静态(M1 新增 BSS ≈ 1.1KB),改前后跑 `scripts/ram_report.py` 留证; +- `testTask` 栈 2048 words:用例函数只用几十字节局部变量; +- 套件互斥宏体系不动:Phase 12 挂 HW 组,13/14 挂 NET 组,`#error` 自动防两层同启; +- 非破坏性:不改 IP/卷/BBT;HW 层阶段独占 CH395F(网络中断)→ 文档标注"台架专用"; +- M3 触碰生产代码的探针全部在 `#ifdef TEST_LATENCY_PROBE` 内,产品构建编译消失,两种构建都须过 `@build`。 + +## 8. 变更记录 + +| 2026-09-22 | **M2 实现**:`test/latency_peer.py`(`--mode net` 服务器驱动协议 + 重试包装 + JSON 报告);`test/net_latency_test.{h,c}` Phase 13 状态机(HELLO/L5_RECV/L5_SEND/L3/L10/L6/L7/DONE,含 abort 重连等待态与周期自愈);`test/ch395f_test.h` 增 `ENABLE_PHASE13_TESTS`;`test/net_test_task.c` 监听循环在 Phase 13 活动期跳过 `net_accept`(避免与协议消息争抢 netTask 队列)。**net 层(生产代码)修复**:`net_wait_result` 两段式 + 全 1 清除掩码 + 按返回值判定(D12)、`net_sock_state`/`net_resync_listen`/`net_relisten`(D14) | +| 2026-09-22 | **M2 台架调试与判定**(COM6,run1~run15):run1 发现 **D11**(RX 饱和重复投递);run2 修 WOULDBLOCK 误判 EOF;run3~run11 定位 **D12**(`xTaskNotifyWait` 清除掩码 0 = 不清值 → 陈旧结果被重读,L7 挂死或结果错位)、**D13**(芯片空闲 RST)、**D14**(静默回收失步);run10 修 **D15**(L7 帧格式 + peer 超读);run11 修 **D16**(L7→DONE 跳转);run12/run13 两次独立完整 PASS;run14 暴露 **D12-b**(热路径探针日志洪水:9800 行/98.6%,并实测把 `queue_rt_send` 抬高 7.8×)→ 删日志;**run15(最终代码)第三次完整 PASS**(6 段全 OK、零尖峰、`1/1 PASSED`、peer exit 0、wait-idle 15.5s 判空闲),§4.2 按实测重锁 | +| 2026-09-22 | **M2 交付物**:`docs/测试说明/m2_data/`(`m2_quiet.json`=run15 最终代码、`m2_quiet_run13.json`=run13、`m2_quiet_run14.json`=run14(仅 peer 侧有效,MCU 侧因日志洪水不作基线)、`m2_serial_excerpt_run13.txt`=串口 `[LAT]/[NET]` 证据片段、`README.md`=数据说明与逐帧明细);修复 `App/cfg_file.c` BOM 丢失(Edit 剥 BOM → ARMCC 按 CP936 解码中文字面量报 `#870-D`,`@build` 因此首轮 4 警告);生产构建与测试构建均 `@build` 0/0 | +| 2026-09-22 | **M3 实现**:`test/net_latency_test.{h,c}` Phase 14 状态机(ui_loop → prod_cycle_mcu → relisten 三个探针窗口 + `lat_tmark_t`/`lat_mark` 跨 >25ms 安全计时 + `lat_probe_add_seg` 按段名过滤防跨段污染);生产代码探针(`ui_task.c` L11 循环周期、`ui_net_sink.c` LCDMEM 处理净成本、`net_socket.c` relisten 全程,全部在 `#ifdef TEST_LATENCY_PROBE` 内);`test/latency_peer.py` 增 `--mode remo`(生产 RemoDispBus 客户端:INIT/LCDMEM/重连多轮/有界读/JSON 报告/`--ftp-stress` 分桶);`test/ch395f_test.h` 切到 `ENABLE_PHASE14_TESTS` | +| 2026-09-22 | **M3 台架实测**(COM6,run16~run19):修 **D18**(多站点探针跨段污染 → prod/relisten 读出 ui_loop 值)与 **D19**(Phase 14 漏调 `lat_time_init()` → 三段全 0 假绿,并加 `med==0` 判无效);修正运行顺序(**先烧录后起 peer**,否则首帧由旧固件应答);测得 **L11=10986µs、L8(MCU)=7142µs、L9 relisten=588000µs×3(D10 确认)**;peer 侧真机 65 帧 cycle med **66.0ms**、tail **52.6ms**,模拟器 50 帧 cycle med **6.8ms**、tail **0.0ms** → **Δ=59.2ms**;压力档 **E-02 量化 17.7s**(quiet/stress 分桶);新发现 **D17**(INIT 应答 u32 截半 → framebytes 恒 0) | +| 2026-09-22 | **M3 交付物**:新增 **`docs/测试说明/链路时延实测数据分析.md`**(M1+M2+M3 汇总分析:全段基线总表、一帧时间预算拆解、三条对照实验归因、两种数据区制、缺陷量化清单 D10~D19、修复优先级建议);`docs/测试说明/m3_data/`(`m3_dev_quiet.json`/`m3_dev_stress.json`/`m3_sim_quiet.json`);本文档 §4.3 重锁 M3 段、§5 M3 完成、§6 新增 D10/E-02/D17/D18/D19 行 | +| 2026-09-22 | **D20 调查(未完全修复,已定位)**:量化复现条件 = **每次开机后的第一个 FTP 会话**在其第 2 个数据连接返回 425(steady-state 会话 4/4 全通过);设备日志签名 = 数据连接 accept 后约 10~20ms 芯片把新连接拆掉(`sock4 DISCONNECTED, releasing (chip tcp=0x00)` → `data socket closed, accept aborted` → `cmd_stor accept failed` → 425),机制属芯片"同一 Socket 关闭→重开"快速复用竞态(与 D10/Trap 09 同族)。尝试并回退两种修法(① 传输后不关数据连接+复用监听器 → **破坏 FTP EOF 语义**,客户端等不到 EOF 整会话挂死;② 新增 `net_shutdown`(发 FIN 优雅断开)+ 等 auto_relisten 复用 + relisten 兜底 → 部分会话通过但不稳定)。**保留纯增益硬化**:`handle_disconnect_event` 在 ESTABLISHED 态回读芯片 TCP 状态、仍 ESTABLISHED 则丢弃陈旧 DISCONNECT;修复 `net_socket.c` 两处 `ch395f_get_socket_status` **只给 1 字节缓冲**(该 API 输出 2 字节)导致的栈越界 1 字节(`ch395f.h` 补显式告警)。新增复现/回归脚本 `test/ftp_regression.py`(单会话 7 次数据连接 + 1KB/32KB 往返校验)。后续:数据 Socket 轮换、或客户端遇 425 重试。`@build` 测试/产品两构建均 0/0 | +| 2026-09-22 | **尾批解法实装到生产客户端**(`DTU-RemtoeLCD\remo_disp_server.py`):`connect()` 后自动关闭本 socket 延迟确认(Windows:`ctypes` 直调 `WSAIoctl(SIO_TCP_SET_ACK_FREQUENCY=1)`,因 Python `socket.ioctl()` 白名单不接受该码),失败则退化为"入站轻推"(帧中停顿 ≥5ms 补发 1 个 `0x00` 捎带 ACK;`NUDGE_POLL_S=5ms`/`NUDGE_INTERVAL=20ms`/上限 64 次)。用该客户端自身 `connect()+fetch_screen()` 实测:帧耗时 **68.8ms → 20.7ms(Windows 关延迟确认)/ 29.4ms(轻推兜底)**;Web 服务冒烟 HTTP 200 正常。README 增补"延迟确认抑制"章节;分析文档 §3.3 同步记录 | +| 2026-09-22 | **L7/生产帧 ~52ms 尾批定性 + 客户端侧解法(不用抓包工具)**:机理 = 芯片 MSS=1024、发送 FIFO=1024B,MCU 每次 1024B 写为满 MSS 段(立即发送),帧尾 137B(3209=3×1024+137)为**次 MSS 段**被 Nagle 扣住等 ACK,而 Windows 的**延迟 ACK** 使该 ACK 晚到 ~50ms。三组带内实验定性:① 帧未收齐时补发 1 字节(`--ack-probe`,新增)→ tail 52→**3.9ms**;② Windows 每 socket `SIO_TCP_SET_ACK_FREQUENCY=1`(`--ack-fast`,新增,ctypes 调 `WSAIoctl`,不发任何字节)→ tail **3.9ms**(一锤定音:根因在对端延迟 ACK);③ 对照(模拟器 tail=0、满块批量无惩罚)不变。帧周期实测 **65.5→13.7ms**。**设备侧无解**(帧长 3209 为质数、TX FIFO=MSS、手册命令表无 Nagle/立即发送开关)→ 结论与数据写入 `链路时延实测数据分析.md` §3.3,数据文件 `m3_data/m3_tail_ack{probe,fast}.json` | +| 2026-09-22 | **M3 缺陷修复与复验**(当日完成 D10/E-02/D17):**D10** `net_relisten_locked` 去掉 `osDelay(500)` 忙等 → 单次尝试 + `netTask` 每轮延迟重试(`net_relisten_try`/`net_relisten_retry_tick`,间隔 30ms/上限 2s,含防饥饿与防 fd 残留)→ relisten **588ms → 89ms** 且不再冻结 netTask,`[LAT]` 段由 FAIL 转 **OK**(阈值按实测重锁 warn 100ms);**E-02** `lftpd` 控制行/数据读改**分片有界读**(新增 `net_recv_to` + `net_msg_t.timeout_ms`;5ms 片 + 每片 `vTaskDelay(1)` 让出 CPU,总预算仍 30s)→ 受压帧 **17718ms → 74.7ms(max 106.8ms,零尖峰)**;**D17** `remo_send_init` 由 len=6 改 **8** → INIT 应答 `framebytes` **0 → 3200**,协议文档同步勘误。修复后安静档无回归(65.7ms vs 66.0ms)。另经 git 回退版 A/B 双跑确认 FTP 行为完全一致(并记录既有缺陷 **D20**:同一会话第二个数据连接恒 425);修复过程暴露"Copy-Item 还原保留旧 mtime → Keil 跳过重编译"的工作流陷阱,已写入 `链路时延实测数据分析.md` §7.2。`@build` 测试/产品两构建均 0/0 | +| 2026-09-22 | 创建:完整规划(分段/架构/流程/基线/里程碑);**M1 实现**(`test/net_latency_test.h/.c`:DWT µs 时基 + 统计报告 + Phase 12 SPI 字节命令/DMA 时延基线;`ch395f_test.h` 增 `ENABLE_PHASE12_TESTS`;uvprojx Test 组注册) | +| 2026-09-22 | **M1 台架首跑**(COM6):7 段全 OK、零尖峰、p95≈min;`spi_dma_1024` med=1611µs(每 chunk SPI 成本基线)。按实测重锁 §4.1 阈值(代码 `LAT_P12_CMD_*`/`a_p12_dma_warn/fail` 同步更新);修复报告行 `fail<=` → `crit<=`(防默认 Forbidden 自捕获);确定硬件层/NET 层两套 Required 标记集(硬件层无 `Network Init`)+ 测试构建 Forbidden 定制集(`TEST_REPORT` 行 `failed=0` 会自捕获默认 `FAIL` 模式);`test_parse_ip` 守卫精确到 Phase 2/5(仅 Phase 12 构建的 #177-D 实证)。`@test` 复跑 PASS | diff --git a/docs/测试说明/链路时延实测数据分析.md b/docs/测试说明/链路时延实测数据分析.md new file mode 100644 index 00000000..7461e2b3 --- /dev/null +++ b/docs/测试说明/链路时延实测数据分析.md @@ -0,0 +1,344 @@ +# 链路时延实测数据分析(M1 + M2 + M3 汇总) + +> 本文是**测量数据的汇总分析**:把 M1(SPI)、M2(芯片+队列)、M3(生产路径 + 模拟器 Δ + 压力档) +> 三轮台架实测数据放在一起,回答一个工程问题——**远端显示"每帧要多久、慢在哪一段、能不能改"**。 +> +> - 测试规划/流程/判定阈值:`链路延迟测试说明.md`(本文 §6 的阈值与之一致) +> - 原始数据:`m2_data/`、`m3_data/`(JSON + 串口证据片段),本文所有数字均可回溯到这些文件 +> - 台架:COM6 @115200,STM32F407ZGTx @168MHz + CH395F(SPI2 分频 8),2026-09-22,安静档无外部干扰 +> - 代码版本:M3 = Phase 14 构建(`TEST_SUITE_CH395F` + `ENABLE_PHASE14_TESTS`),`@build` 测试/产品两构建均 0 错误 0 警告 + +--- + +## 1. 一句话结论 + +**远端显示的一帧(3209B 全帧)端到端实测中位 66.0ms,其中约 52.6ms(79%)是帧尾那 137B 小段的等待**; +MCU 侧真实处理只占 7.1ms,任务拾取/调度约 6.8ms。同一客户端、同一台 PC 打 PC 模拟器只要 6.8ms(Δ = 59.2ms,且模拟器**完全没有**这个尾批延迟)。 +→ **根因已确认(§3.3,无需抓包即可判定):芯片按 Nagle 扣住帧尾的次 MSS 段(MSS=1024,帧长 3209=3×1024+137), +等对端 ACK;Windows 的延迟 ACK 把这一等推迟了 ~50ms。** +**解法在客户端一侧**(帧未收齐时补发一个字节以捎带 ACK,或关闭本 socket 的延迟 ACK):实测 tail **52→3.9ms**、帧周期 **65.5→13.7ms(≈4.8×)**,**协议与设备代码都不需要改**。 + +另有两个**生产级缺陷**被本轮量化,并已于当日**修复 + 台架复验**(详见 §7): +- **D10**:断线自动重监听固定 **588ms**(Trap 09 的 `osDelay(500)` 忙等,期间 netTask 完全冻结)→ 修复后 **89ms 且不阻塞**; +- **E-02**:一条**空闲的 FTP 控制连接**让 netTask 阻塞 **17.7s**(远端显示整帧无响应)→ 修复后受压帧 **med 74.7ms / max 106.8ms,零尖峰**; +- **D17**:`INIT` 应答把 `帧字节数u32` 截成 6B 发出 → 客户端读到的帧字节数**恒为 0** → 修复后**正确返回 3200**。 + +修复后安静档无性能回归(帧周期 65.7ms vs 修复前 66.0ms,尾批 50.6ms vs 52.6ms)。 + +--- + +## 2. 全段基线总表(台架实测) + +| 段 | 含义 | 实测(med) | min/max | n | 阈值 warn/crit | 判定 | +|---|---|---|---|---|---|---| +| **L1** `spi_cmd_*` | SPI 字节命令(3 条) | 10 / 15 / 15µs | 10~16µs | 100 | 40/100µs | OK | +| **L2** `spi_dma_*` | SPI DMA 读(4/64/1024/2048B) | 20 / 114 / **1611** / 3208µs | ±2µs | 100 | 见 §4.1 | OK | +| **L3** `chunk_wait` | 写发送缓冲 → `SENBUF_FREE`(每 chunk) | **219µs** | 213/381µs | 64 | 10ms/500ms | OK | +| **L5** `queue_rt_recv` | 调用方 `net_recv(DONTWAIT)` 队列全程 | **5997µs** | 5513/6033µs | 20 | 10ms/100ms | OK | +| **L5** `queue_rt_send` | 调用方 `net_send` 队列全程 | **987µs** | 838/1209µs | 20 | 10ms/100ms | OK | +| **L6** `req2first_mcu` | 10B 请求到齐 → 回显发出(MCU 侧) | **319µs** | 308/338µs | 10 | 20ms/200ms | OK | +| **L7** `fullframe_mcu` | 9B 请求 → 3209B 全帧交给芯片 | **6028µs** | 5991/6066µs | 10 | 50ms/500ms | OK | +| **L8** `prod_cycle_mcu` | **生产路径**:LCDMEM 取帧+组帧+net_send(MCU 净成本) | **7142µs** | 7111/7179µs | 50 | 20ms/200ms | OK | +| **L10** `logtax_line` | 一行 60 字符 `printf` @115200 | **4396µs** | 4394/4422µs | 10 | 10ms/25ms | OK(D6 量化) | +| **L11** `ui_loop` | uiTask 生产循环实测周期 | **10986µs** | 10970/21000µs | 120 | 25ms/50ms | OK | +| **L9** `relisten` | 断线后自动重监听耗时(MCU 侧) | **588000µs** | 588000µs ×3 | 3 | 50ms/500ms | **FAIL(D10)** | +| **L4/L8** peer cycle | 生产客户端完整帧周期(发→收齐 3209B) | **66.0ms** | 53.3/77.4ms | 65 | — | 见 §3 | +| **Δ** | `peer cycle(设备) − peer cycle(模拟器)` | **59.2ms** | — | 65/50 | ≤80ms | 见 §4 | + +数据来源:L1/L2 = `链路延迟测试说明.md` §4.1;其余 MCU 侧 = M2 run15 / M3 run18 串口 `[LAT]` 行 +(`m2_data/m2_serial_excerpt_run15.txt`、`m3_data/` 各 JSON);peer 侧 = `m3_data/*.json`。 + +--- + +## 3. 生产路径一帧的时间预算(M3 核心结果) + +同一客户端(`latency_peer.py --mode remo`,100ms 节拍)对**真机 :7003** 与**PC 模拟器 :7003** 各跑一轮: + +| 指标 | 真机(65 帧) | 模拟器(50 帧) | 差值 | +|---|---|---|---| +| 帧周期 total(发请求 → 收齐 3209B) | **66.0ms**(53.3/75.3,max 77.4) | **6.8ms**(0.6/13.1,max 15.7) | **+59.2ms** | +| 首字节 first byte | 13.9ms(8.5/19.1) | 6.7ms(0.5/13.0) | +7.2ms | +| **尾批延迟 tail lag**(末批 → 首批) | **52.6ms**(43.3/61.5) | **0.0ms**(0/0.1) | **+52.6ms** | +| recv 批次数/帧 | 5(8B 头 + 1016 + 1024 + 1024 + **137**) | 2 | — | +| CRC / 位图非平凡 | 65/0、65/65 | 50/0、50/50 | — | + +**真机 66.0ms 的构成**(三段相加 ≈ 66.5,闭合): + +``` +[T+0] 客户端发 9B LCDMEM 请求 +[T+6.8ms] 实测推算:uiTask 10ms 轮询拾取 + netTask 调度(≈ L11 的一半) +[T+13.9ms] 首字节到达 = 6.8 + MCU 净处理 7.1ms(prod_cycle_mcu:取 1bpp + 组帧 + net_send 4 分块) +[T+66.0ms] 全帧收齐 —— 最后的 137B 尾巴比前三批(1024B×3,前 4ms 内到齐)晚 52.6ms +``` + +- 前三批 1024B 在**首字节后约 4ms 内**全部到齐; +- 第 4 批只有 **137B = 3209 − 3×1024**(不满块),固定迟到 43~62ms; +- 该延迟**与负载无关**:压力档下 tail_lag 中位 52.2ms(安静档 52.6ms),说明不是调度/队列拥塞,而是**发送侧对不满块尾段的固定惩罚**。 + +### 3.1 交叉对照实验(三条独立证据 → 归因) + +| 对照 | 观测 | 说明 | +|---|---|---| +| ① 同一客户端 → 模拟器 | tail = **0.0ms**,cycle 6.8ms | 客户端栈、Windows、请求格式全部相同 → 尾批**不是 PC 侧**造成 | +| ② M2 `L3` 批量排空(16×4096B,全满块) | 65536B / **125.4ms**(≈2ms/KB) | 16 个满块若各带 50ms 尾巴应 ≥800ms → **满块无此惩罚** | +| ③ 压力档(FTP 头阻塞)下的 L7/生产帧 | tail 仍 43~62ms | 尾批**不受 netTask 拥塞影响** → 属芯片 TX 行为 | + +**归因结论(已用带内实验证实,无需抓包)**:3209B 被分块发送后,芯片 TCP 侧对"最后一个不满块的小段"按 +**Nagle 规则扣住**(有未确认数据时不发次 MSS 小段),**对端(Windows)的延迟 ACK** 把"解除扣住"的时点推迟了约 50ms +→ 见 §3.3 的三组判别实验(最终由"对端立即 ACK"实验一锤定音:不发任何数据也能把尾批从 52ms 降到 3.9ms)。 + +### 3.3 尾批 ~52ms:不装 Wireshark 的定性与解法(M3 追加实验) + +**机理**(已确认):驱动把芯片 **MSS 设为 1024**(`CH395F_TCP_MSS`),发送 FIFO = 2×512B = 1024B +→ MCU 每次 1024B 写恰好是**一个满 MSS 段**(Nagle 允许满段立即发送);帧长 3209B = 3×1024 + **137B**, +最后这 137B 是**次 MSS 段** → 芯片按 Nagle 扣住,等前一数据被 ACK;Windows 的延迟 ACK 使这个 ACK 晚到 ~50ms。 + +**判别实验(全部只用现有工具,不需要抓包)**: + +| # | 实验 | 做法 | tail_lag(med) | 帧周期(med) | 结论 | +|---|---|---|---|---|---| +| 0 | 基线 | 串行"请求→等整帧→睡 100ms" | **52.0ms** | 65.5ms | 现象复现 | +| 1 | **带内 ACK 探针** `--ack-probe` | 帧未收齐时立刻发 **1 个解析器会忽略的字节**(0x00,非 TAG 0xAA)→ 该 TCP 段**捎带**对已收数据的 ACK | **3.9ms** | **13.7ms** | 扣住**可被对端 ACK 解除** ⇒ 等 ACK(Nagle 式),非芯片固定定时器 | +| 2 | **对端立即 ACK** `--ack-fast` | Windows 每 socket 关延迟 ACK:`SIO_TCP_SET_ACK_FREQUENCY=1`(ctypes 调 `WSAIoctl`,**不发任何探测字节**) | **3.9ms** | **20.7ms** | **根因 = 对端延迟 ACK**(去掉它,尾批自行消失) | +| 3 | 对照:模拟器(同 peer/同 Windows) | — | 0.0ms | 6.8ms | 芯片侧才有该扣住行为 | +| 4 | 对照:满块批量(M2 L3,16×4096B) | — | 无累积惩罚(64KB/125ms) | — | **满 MSS 段不受影响** | + +> 实验 1/2 已固化为 peer 选项:`--ack-probe`(可移植:任何 OS/客户端只要在帧未收齐时发一个字节即可)、 +> `--ack-fast`(仅 Windows,无需改协议)。原始数据:`m3_data/m3_tail_ackprobe.json`、`m3_data/m3_tail_ackfast.json`。 + +**可用解法(按推荐度)**: + +| 方案 | 做法 | 实测效果 | 代价 / 约束 | +|---|---|---|---| +| **① 客户端"补一个段"** | 帧未收齐时发 1 字节(无效字节即可)或 `KEEPLIVE`,或干脆把**下一个请求提前发** | tail **52→3.9ms**,帧周期 **65→13.7ms**(≈4.8×) | 纯客户端改动,**协议不需变**;无需任何抓包/系统设置 | +| **② Windows 关延迟 ACK** | 每 socket `SIO_TCP_SET_ACK_FREQUENCY=1`(或注册表 `TcpAckFrequency=1`) | tail **52→3.9ms** | 仅 Windows 客户端;注册表方式需管理员+重启 | +| **③ 客户端固定节拍(不串行等待)** | 请求按固定 100ms 节拍发,响应按序收(下一个请求的段天然捎带 ACK) | 尾批同样被提前解除,且显示帧率由 ~6fps 提到 ~10fps | 客户端需按序配对响应 | +| **④ 设备侧** | 无可行解(见下) | — | — | + +**生产客户端已实装(2026-09-22)**:`DTU-RemtoeLCD\remo_disp_server.py`(远程显示工具,Web UI + exe 打包) +把上述解法做成两条**自动择一**的路径,连接时打印生效模式: + +| 路径 | 触发条件 | 实现 | 实测帧耗时(客户端自身代码,100ms 节拍) | +|---|---|---|---| +| **A. 关延迟确认** | Windows(默认) | `ctypes` 直调 `WS2_32!WSAIoctl(SIO_TCP_SET_ACK_FREQUENCY=1)`——Python `socket.ioctl()` 有控制码白名单,不接受该码 | **20.7ms**(改善前 68.8ms,**3.3×**) | +| **B. 入站"轻推"兜底** | 非 Windows / A 失败 | 收帧途中帧已开始但停顿 ≥5ms → 补发 1 个 `0x00`(装置状态机只认帧头 `0xBB`,丢弃该字节)→ 该段捎带 ACK | **29.4ms**(2.3×) | + +> 剩余 ~20ms = 客户端 Python 逐字节解析 3209B + 首字节等待(9~18ms)+ 尾块 ~4ms; +> 该工具的刷新线程为"拉一帧 + 等 100ms",故整体显示帧率由 ~6fps 提升到 ~9fps。 +> 两条路径**都不需要改协议、不需要改装置固件**;`connect()` 会打印 `[connect] 延迟确认抑制: ...` 便于现场确认。 + +**为什么设备侧无解**(避免后续重复投入): +- 帧长 **3209 是质数**,无论 MSS / 分块怎么取,最后一个 TCP 段必然 < MSS(除非补字节改线格式); +- **发送 FIFO = MSS = 1024B**(`NET_SEND_CHUNK_MAX`),单次写不可能超过 MSS ⇒ 无法用"一次写整帧"绕过; +- 手册命令表(`docs/芯片手册/CH395F.md` §5)**没有** Nagle / 立即发送 / TCP_NODELAY 类开关(只有 + `CMD_SET_TCP_MSS`、收发缓冲、重传与 keepalive 参数;且手册 §9.2.8 明确"MSS 越小以太网小包越多")。 + +### 3.4 两种数据区制(对容量规划的启示) + +| 区制 | 实测 | 主导成本 | 适用 | +|---|---|---|---| +| **小帧交互**(≤1 chunk 附近) | 3209B → 66ms | **固定尾批惩罚 ~52ms** | LCDMEM 整帧轮询 | +| **批量流式**(多满块) | 4KB/7.8ms、64KB/125ms ≈ 2ms/KB | **SPI DMA(L2 = 1.61ms/KB)** | 大文件/FTP 传数据 | + +→ 小帧交互的 52ms 尾批**已定位并给出客户端侧解法**(§3.3:实测可降到 3.9ms); +批量流式仍受 SPI DMA 约束(≈2ms/KB),两者是独立的两条优化线。 + +--- + +## 4. 队列与调度:静默档不存在台阶 + +- **L5 队列往返**:收 5997µs / 发 987µs —— 与 netTask 10ms 轮询节拍同量级,属**调度周期**而非排队拥塞; + `queue_rt_send` 的 987µs ≈ L2 的 1 个 chunk(1611µs)量级,说明发送路径没有额外排队。 +- **L3 chunk 级**:219µs ≈ 每 chunk SPI 成本(1611µs)的 1/7 → **芯片 TX 缓冲不是瓶颈**,SPI 搬运才是。 +- **L11 uiTask 周期**:10986µs(= osDelay(10) + 渲染开销 ≈ 1ms);窗口内出现 1 次 21ms(开机菜单构建)。 + → **LVGL 渲染只占约 1ms/帧**,uiTask 不是延迟来源。 +- **M2 三次完整 PASS(run12/13/15)六段全 OK、零尖峰**:安静档下"500ms 台阶"现象**不存在**; + 台阶只在特定条件下出现(见 §5)。 + +--- + +## 5. 压力档:两个"秒级冻结"缺陷(E-02 与 D10) + +### 5.1 E-02:一条空闲 FTP 控制连接冻结全网 17.7s + +`--mode remo --ftp-stress`(第 15 帧起让 FTP 停留在"已 USER、未 PASS"的半登录态): + +| 分桶 | n | med | p95 | max | 尖峰(≥500ms) | +|---|---|---|---|---|---| +| quiet(0~14 帧) | 15 | 65.9ms | 75.8ms | 75.8ms | 0 | +| stress(15~44 帧) | 30 | 69.1ms | 75.6ms | **17717.9ms** | **1** | + +- 症状:**第 15 帧(受压后第一帧)等待 17.7s 才拿到应答**,期间远端显示完全无响应; +- 恢复:FTP 侧发出 `QUIT` 后**立即恢复** 55~75ms 正常服务; +- 机制:`lftpd` 的 `read_line` 走**阻塞** `net_recv(0)`,而 netTask 是全部 socket 操作的唯一执行者 + → 该阻塞期间**所有**任务的 net 消息(含 uiTask 的 LCDMEM 应答)排队等待; +- 边界:单次 `net_recv` 受 `NET_RECV_TIMEOUT_MS`(30s)限制,但 `read_line` 会再次进入阻塞 + → **只要客户端连接后不发完整行,netTask 就被长期占用**(实测 17.7s = 客户端空闲时长); +- 生产含义:现场一个半开 FTP 连接 / 一次网络抖动中的 FTP 会话,即可让远端显示"整帧不动"十几秒, + 与《远程显示延迟分析》里的 D3(E-02 头阻塞)指纹完全一致,**本轮首次给出定量证据**。 + +### 5.2 D10:断线重监听固定 588ms(Trap 09 `osDelay(500)`) + +peer 侧三次"断开 → 1.5s → 重连"(`--reconnect-rounds 3`): + +| 通道 | 实测 | +|---|---| +| peer 侧:断开后重连 + INIT | 23.5 / 27.4 / 33.7ms(另两轮 23.2 / 34.7ms) | +| **MCU 侧:auto-relisten 全程** | **588000µs × 3(三次全部 588ms)** → `verdict=FAIL` | + +- 588ms ≈ `net_relisten_locked()` 的 Trap 09 重试:**首次 `open` 必失败 → `close` → `osDelay(500)` → 重开成功**; +- 这 500ms 是**真·忙等**(`osDelay` 在 netTask 里)→ 期间同样**全网冻结**; +- 客户端可见影响:**断开后 600ms 内重连会被拒**(监听器尚未恢复);M2 调试期"170s 连接被拒"即该路径失败一次后的连锁表现; +- 判定说明:`relisten` 段 FAIL 是**结论**(阈值 500ms 就是 D10 指纹),不是测试故障。 + +--- + +## 6. 缺陷清单与量化(实测汇总) + +| 编号 | 缺陷 | 实测证据 | 状态 | +|---|---|---|---| +| **D10** | 重监听走 `osDelay(500)` 阻塞重试 | relisten **588ms ×3**(M3) | **已修复并验证**(588ms → **89ms**,非阻塞;§7.1) | +| **D11** | 芯片 TCP RX 饱和下重复投递(+138%) | M2 run1:发 5000B 实收 11884B + RST | 应用侧限速规避 | +| **D12** | net 层 `xTaskNotifyWait` **清除掩码 0 = 不清值** → 陈旧结果被重读 | M2 run3~11:L7 挂死 35s / 结果整段错位 | **已修复**(全 1 掩码 + 按返回值判定 + 32 位整体结果) | +| **D12-a** | 位域切分与 `v!=0` 判定两个同类陷阱 | code review:32768 溢出为负、0 结果误判挂死 | 已避开并写入注释 | +| **D12-b** | net 热路径逐调用日志 → 观测改变被测对象 | M2 run14:日志 40KB→1.04MB(98.6%),`queue_rt_send` 987µs→**7685µs** | **已修复**(删热路径日志) | +| **D13** | 芯片空闲超时 RST(MCU 未就绪) | M2/M3 每轮首连均 10054,+2s 重连成功 | 客户端须容忍重连 | +| **D14** | 芯片静默回收 socket → MCU 状态滞留 | M2 run7~9 | **已修复**(`net_sock_state`/`net_resync_listen`/`net_relisten`) | +| **D15** | 测试 L7 帧与生产格式不一致 + peer 超读 | M2 run10:收 3210B、CRC fail | **已修复**(对齐 `remo_send`/`remo_handle` + 有界读) | +| **D16** | Phase 13 L7 → 回跳 L3 造成无限循环 | M2 run11:永不下发 0xFF,peer 空等 300s | **已修复** | +| **D17** | **INIT 应答把 u32 截成 6B → 帧字节数字段恒 0** | M3:真机与模拟器均 `framebytes=0`(应为 3200) | **已修复并验证**(发满 8B → `framebytes=3200`;§7.1) | +| **D18** | **多打点站点共用全局探针窗口 → 跨段污染** | M3 run17:prod/relisten 都读出 ui_loop 的 ~11ms 值 | **已修复**(`lat_probe_add_seg` 按段名过滤) | +| **D19** | **Phase 14 漏调 `lat_time_init()` → 全 0 假绿** | M3 run16:三段全 0 却 `verdict=OK` | **已修复**(补初始化 + med==0 判无效) | +| **D20** | **FTP 同一会话的第 2 个数据连接失败(425)** | 复现条件已量化:**每次开机后的第一个 FTP 会话**在该会话第 2 个数据连接处失败(之后的会话 4/4 全通过);设备日志签名 = 数据连接 accept 后约 10~20ms 出现 `DISCONNECTED, releasing (chip tcp=0x00)` → `data socket closed, accept aborted` → 425 | **已定位未修**(详见 §7.4):属芯片"同一 Socket 快速复用(关闭→重开)"的时序竞态;两次修法(优雅断开+复用监听器 / relisten 兜底)都不稳定已回退;**保留安全硬化**(陈旧 DISCONNECT 甄别 + 两处 1 字节栈越界修复);复现脚本 `test/ftp_regression.py` | +| **E-02** | 空闲 FTP 控制连接冻结 netTask(头阻塞) | M3:受压帧 **17718ms**(`QUIT` 即恢复) | **已修复并验证**(→ **74.7ms** med / 106.8ms max,零尖峰;§7.1) | + +> D17/D18/D19 为本轮 M3 自查发现(D18/D19 属测试工装缺陷,会导致"绿而无效"的数据); +> D20 为修复过程中的 A/B 对照**顺带发现**的既有缺陷(已用 git 回退版固件双跑确认与本次改动无关)。 + +--- + +## 7. 缺陷修复与验证(2026-09-22,修复当日台架复测) + +### 7.1 三项已修复缺陷的前后对照 + +| 缺陷 | 修复内容 | 修复前实测 | **修复后实测** | 判定依据 | +|---|---|---|---|---| +| **D10** | `net_relisten_locked` 去掉 `osDelay(500)` 忙等 → 单次尝试 + `netTask` 每轮延迟重试(间隔 30ms,上限 2s) | relisten **588 000µs ×3**(且 netTask 冻结 500ms) | **89 000µs(89ms)**,`verdict=OK`,期间其他任务不受阻 | `[LAT] seg=relisten` + 日志 `relisten retry ok` | +| **E-02** | `lftpd` 控制行/数据读改**分片有界读**(`net_recv_to` 5ms 片 + 每次让出 1ms,总预算仍 30s) | 受压帧 **17 718ms**,≥500ms 尖峰 1 次 | **med 74.7ms / p95 90.8ms / max 106.8ms,尖峰 0** | `m3_dev_stress_fixed.json`(30 受压帧) | +| **D17** | `remo_send_init` 由 `len=6` 改 `len=8`(字段定义本就是 `[W u16][H u16][FB u32]`) | `framebytes=0`(u32 被截半) | **`framebytes=3200`,`payload_len=8`** | peer JSON `init.geom` | + +**修复后安静档(无回归)**:`ui_loop` 10986µs、`prod_cycle_mcu` 7140µs、帧周期 med **65.7ms**、尾批 **50.6ms**、 +CRC 65/0、`=== Phase 14: 1/1 PASSED ===` —— 与修复前(66.0ms / 52.6ms)在噪声内一致 ⇒ **修复未引入性能回归**。 + +**关键实现要点(供后续维护)**: +1. **D10 重试必须防饥饿**:调用方(accept/recv 路径每 10ms 一次)只登记一次重试,实际重试由 netTask 轮询到点执行; + 若让调用方每次立即重试并重置时刻,会永不重试。重试间隔 100ms→30ms 后恢复时间 147ms→89ms(芯片沉降约 60ms)。 +2. **E-02 分片长度 = 其他任务的最坏等待**:100ms 片下受压帧仍 ~750ms(多片排队),收到 **5ms** 后才降到 ~75ms。 +3. **分片循环必须显式让出 CPU**:某片可能因 `net_poll` 自身耗时而在首轮即返回超时 → 本任务(Normal)立即重排队 + 会形成忙循环并**饿死 BelowNormal 的 uiTask**(实测 2.4~3.4s 周期尖刺);加 `vTaskDelay(1)` 后尖刺归零。 + +### 7.2 关键教训(工程流程) + +修复过程中出现一次**"改了代码但没进固件"**:A/B 对照用 `git checkout` 回退后再用 `Copy-Item` 还原, +`Copy-Item` 保留了**旧 mtime**,比回退版编出的 `.o` 更旧 → Keil 判定"源比目标旧"→ **未重新编译**, +固件里仍是回退版(表现为"改了却没效果")。此后必须在还原后 **touch 源文件**(或核对编译日志里出现该文件)。 +排障路径(可复用):在改动处加临时日志 → 若无输出,即可判定"代码没进二进制",先用特征字符串在 `.axf` 里验证。 + +### 7.4 D20 调查记录(FTP 数据连接 425,未完全修复) + +**现象与准确复现条件**(2026-09-22 台架,多轮统计): + +| 会话(同一固件、不重启) | 结果 | +|---|---| +| 开机后第 1 个 FTP 会话 | **失败在会话内第 2 个数据连接**:`LIST/NLST` 成功 → 随后的 `STOR` 返回 **425 Can't open data connection**;控制连接随后被对端关闭(客户端见 EOF) | +| 第 2 个及之后的会话(稳态) | **全部 11/11 通过**(含 1KB 与 32KB 的 STOR/RETR 往返、连续 7 次数据连接) | + +即:**不是"每会话第 2 个数据连接必失败",而是"每次开机后的第一个会话"才会命中**(此前把两次 A/B 都在开机首会话做的,误判为确定性缺陷)。 + +**设备日志签名**(首会话失败时): + +``` +CMD PASV → sock4 standalone accept(数据连接建立) +CMD STOR +sock4 DISCONNECTED, releasing (chip tcp=0x00) ← 建立后 ~10ms 芯片把新连接拆掉 +[ERR] data socket closed, accept aborted → cmd_stor accept failed → 425 +sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复 +``` + +**机理(已定位)**:FTP 数据通道每次传输结束都要 `net_close`(对端以 EOF 判"传完"),随后下一次 PASV 又要在**同一芯片 Socket**上 `net_socket/bind/listen` 重开;在开机首个会话的时序下,上一条连接的 teardown 与本次重开交叠,芯片在新连接建立后立刻将其拆除(读到 chip TCP = CLOSED)。与 D10(Trap 09 沉降)、D14(静默回收)同属"单连接 Socket 复用"家族。 + +**试过但未采用的修法**(均已回退,保留结论备查): + +| 尝试 | 结果 | +|---|---| +| 传输结束**不关**数据连接(保留监听器供复用) | **破坏 FTP 语义**:服务端不发 FIN 客户端就等不到 EOF,整会话挂死 → 必须关闭数据连接 | +| 新增 `net_shutdown()`(发 FIN 优雅断开)+ 复用监听器(等 auto_relisten 回 LISTEN,最多 1s;兜底改走 `net_relisten()`) | 部分会话 11/11,但**不稳定**(3 会话中仍有 1 次 425,且后续会话出现控制连接连不上)→ 回退 | + +**保留的安全硬化**(与 D20 同源事件竞态,纯增益): + +1. `handle_disconnect_event`:ESTABLISHED 状态下收到 DISCONNECT 时**回读芯片 TCP 状态**,仍为 `CH395F_TCP_ESTABLISHED` 判为陈旧事件丢弃(防"上一条连接的迟到 teardown 误杀新连接"); +2. `net_socket.c` 两处 `ch395f_get_socket_status` 调用**只给了 1 字节缓冲**(该 API 输出 2 字节:`[0]`Socket 状态、`[1]`TCP 状态)→ 栈越界 1 字节;已改为 2 字节缓冲(`ch395f.h` 补显式告警)。 + +**后续建议**(按性价比): + +1. **数据 Socket 轮换**:不要总是复用最低空闲 fd(现固定为 sock4),让连续两次数据连接落在不同芯片 Socket 上(需 net 层支持"分配偏好/最小索引",或 lftpd 保留一个 spare 监听器占位)→ 直接绕开"同一 Socket 快速复用"竞态; +2. **客户端重试**:425 属 RFC 959 的瞬态 4xx,控制器/上位机在数据连接失败后重试一次即可规避(现场最省事的规避手段); +3. **寄存器级定位**:在首会话复现窗口内打印 `CMD_GET_SOCKET_STATUS_SN`(2 字节)与 SINT 序列,确认芯片在 teardown/reopen 交叠时的内部状态迁移; +4. 复现脚本:`test/ftp_regression.py --host `(单会话内 7 次数据连接 + 1KB/32KB 往返内容校验,退出码 = 是否全通过)。 + +### 7.5 建议的后续项(按性价比) + +1. **尾批 ~52ms(占一帧 77%)—— 已定性并给出解法(§3.3)**:客户端在帧未收齐时补发一个字节 + (`--ack-probe`)或关闭本 socket 的延迟 ACK(`--ack-fast`),实测 **65.5ms → 13.7 / 20.7ms**,协议与设备代码均不改。 + 若客户端无法改动(第三方 App),退路是**协议补位**(把应答补齐为 1024 的整数倍,使尾段变为满 MSS 段), + 但需客户端同步改解析(多出的补位字节须被跳过),优先级低于客户端侧解法。 +2. **D20(FTP 数据连接 425)**:见 §7.4 —— 已定位为"开机首会话 + 芯片同 Socket 快速复用"竞态;优先做**数据 Socket 轮换**, + 现场可用**客户端重试**规避。 +3. **日志税**:一行 `printf` 4.4ms(L10);热路径同步日志会直接污染测量与时延(D12-b 实测 7.8×)→ 热路径禁止日志。 +4. **任务模型**:L11 显示 uiTask 渲染仅 ~1ms/帧、L5 队列往返 ~6ms 属轮询节拍 + ⇒ **调优先级/栈不会改善体感**;预算应花在上面第 1、2 项。 + +--- + +## 8. 复现与数据文件清单 + +```powershell +# M3(生产路径,需要设备已烧 Phase 14 固件) +& "MDK-ARM\build.bat" # 0 错 0 警(Phase 14 构建) +& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py clear +& "MDK-ARM\download.bat" # 先烧录,再起 peer(顺序不可反!) +& '.venv\Scripts\python.exe' test\latency_peer.py --mode remo --host 192.168.1.100 --port 7003 ` + --samples 65 --cadence-ms 100 --reconnect-rounds 3 --wait 120 ` + --json "docs\测试说明\m3_data\m3_dev_quiet.json" +# 压力档(E-02) +& '.venv\Scripts\python.exe' test\latency_peer.py --mode remo --host 192.168.1.100 --port 7003 ` + --samples 45 --stress-from 15 --ftp-stress --json "docs\测试说明\m3_data\m3_dev_stress.json" +# 模拟器基线(Δ 的对照端) +& "simulator\build_sim.bat"; .\simulator\build\Debug\lvgl_sim.exe # 等 7003 就绪 +& '.venv\Scripts\python.exe' test\latency_peer.py --mode remo --host 127.0.0.1 --port 7003 ` + --samples 50 --json "docs\测试说明\m3_data\m3_sim_quiet.json" +# 尾批定性/解法(都不需要抓包工具) +& '.venv\Scripts\python.exe' test\latency_peer.py --mode remo --host 192.168.1.100 --port 7003 ` + --samples 30 --ack-probe --json "docs\测试说明\m3_data\m3_tail_ackprobe.json" # 带内补 ACK +& '.venv\Scripts\python.exe' test\latency_peer.py --mode remo --host 192.168.1.100 --port 7003 ` + --samples 30 --ack-fast --json "docs\测试说明\m3_data\m3_tail_ackfast.json" # 关延迟 ACK +``` + +| 数据文件 | 内容 | +|---|---| +| `m3_data/m3_dev_quiet.json` | 真机 65 帧生产周期(**修复前**基线) | +| `m3_data/m3_dev_stress.json` | E-02 压力档(**修复前**:17.7s 尖峰) | +| `m3_data/m3_dev_quiet_fixed.json` | 真机 65 帧(**修复后**:帧周期 65.7ms、relisten 已 OK、INIT framebytes=3200) | +| `m3_data/m3_dev_stress_fixed.json` | E-02 压力档(**修复后**:med 74.7ms / max 106.8ms,零尖峰) | +| `m3_data/m3_sim_quiet.json` | 模拟器基线 50 帧(Δ 对照,tail=0) | +| `m3_data/m3_tail_ackprobe.json` | 尾批实验 ①:帧未收齐即回 1 字节(捎带 ACK)→ tail 3.9ms | +| `m3_data/m3_tail_ackfast.json` | 尾批实验 ②:Windows 立即 ACK(不发探测字节)→ tail 3.9ms | +| `m2_data/m2_quiet.json`(run15)等 | M2 芯片+队列基线与串口证据 | +| `m2_data/README.md` | M2 数据说明(含逐帧尾批明细、方法学教训) | + +**复现注意**(M3 实测踩到的坑,务必遵守): +1. **先烧录、后起 peer**——否则 peer 会连到**上一版固件**的 :7003(实测:首帧由旧固件应答后被复位 RST); +2. peer 首连常因芯片空闲超时被 RST(10054),`--wait` 重试是**正常路径**; +3. 探针三段窗口是**串行全局窗口**,多打点站点必须用 `lat_probe_add_seg()` 按段名过滤; +4. `[LAT]`/peer JSON 都带主机时间戳,可直接合并;设备 RTC 比主机慢约 4s(显示差异,非测量误差); +5. **还原被回退的源文件后必须 touch**(`Copy-Item` 保留旧 mtime,Keil 会因此跳过重编译 → "改了却没效果",见 §7.2)。 diff --git a/docs/芯片手册/GD5F2GQ5UExxG.md b/docs/芯片手册/GD5F2GQ5UExxG.md index 196cfa06..c34a98bf 100644 --- a/docs/芯片手册/GD5F2GQ5UExxG.md +++ b/docs/芯片手册/GD5F2GQ5UExxG.md @@ -1,200 +1,154 @@ -## GD5F2GQ5xExxG +# GD5F2GQ5xExxG -## DATASHEET +DATASHEET 2G-bit 2K+128BPageSize with E Version ## Contents - -1 FEATURE .... - -2 GENERAL DESCRIPTION ...... - -2.1 PRODUCT LIST ..... - -2.2 CONNECTION DIAGRAM. . 8 - -2.3 PIN DESCRIPTION..... 9 - -2.4 BLOCK DIAGRAM ...... - -3 MEMORY MAPPING... ..10 - -4 ARRAY ORGANIZATION....... 11 - -5 DEVICE OPERATION.... .12 - -5.1 SPI MODES....... ..... 12 - -5.2 HOLD MODE...... ..... 13 - -5.3 WRITE PROTECTION.... .. 13 - -5.4 POWER OFF TIMING ... . 14 - -6 COMMANDS DESCRIPTION ... .15 - -7 WRITE OPERATIONS ..... - -7.1 WRITE ENABLE (WREN) (06H) ..... . 17 - -7.2 WRITE DISABLE (WRDI) (04H)... . 17 - -8 READ OPERATIONS..... ..18 - -8.1 PAGE READ ....... ..... 18 - -8.2 PAGE READ TO CACHE (13H) . . 19 - -8.3 CACHE READ FUNCTION (31H/3FH).. . 20 - -8.4 READ FROM CACHE (03H OR 0BH)...... - -8.5 READ FROM CACHE X2 (3BH) .... .... 26 - -8.6 READ FROM CACHE X4 (6BH) ...... . 27 - -8.7 READ FROM CACHE DUAL IO (BBH) . .. 28 - -8.8 READ FROM CACHE QUAD IO (EBH)..... - -8.9 READ FROM CACHE QUAD I/O DTR (EEH) . .. 30 - -8.10 READ ID (9FH)...... .... 31 - -8.11 READ UID . 32 - -8.12 READ PARAMETER PAGE..... - -## 9 PROGRAM OPERATIONS.... ..38 - -9.1 PAGE PROGRAM ........ .... 38 -9.2 PROGRAM LOAD (PL) (02H)... .. 39 -9.3 PROGRAM LOAD X4 (PL X4) (32H) ...... -9.4 PROGRAM EXECUTE (PE) (10H) . . 41 -9.5 PROGRAM EXECUTE BACKGROUND (10H + ADDRESS + 15H).. ..... 42 -9.6 INTERNAL DATA MOVE .. . 44 -9.7 PROGRAM LOAD RANDOM DATA (84H) ..... ...... 45 -9.8 PROGRAM LOAD RANDOM DATA X4 (C4H/34H)...... ...... 46 - -## 10 ERASE OPERATIONS .. ..47 - -10.1 BLOCK ERASE (D8H) . . 47 - -## 11 RESET OPERATIONS..... - -11.1 SOFT RESET (FFH) . .. 48 -11.2 ENABLE POWER ON RESET (66H) AND POWER ON RESET (99H).. ..... 49 - -## 12 FEATURE OPERATIONS...... .50 - -12.1 GET FEATURES (0FH) AND SET FEATURES (1FH) . . 50 -12.2 STATUS REGISTER AND DRIVER REGISTER.. . 53 -12.3 OTP REGION....... .... 54 -12.4 ASSISTANT BAD BLOCK MANAGEMENT ... .. 55 -12.5 BLOCK PROTECTION.. .. 56 -12.6 INTERNAL ECC.. . 57 - -## 13 POWER ON TIMING..... .59 - -## 14 ABSOLUTE MAXIMUM RATINGS .... - -## 15 CAPACITANCE MEASUREMENT CONDITIONS..... ....61 - -## 16 DC CHARACTERISTIC .... .....62 - -## 17 AC CHARACTERISTICS ....... .....63 - -## 18 PERFORMANCE AND TIMING .... - -## 19 ORDERING INFORMATION... ...66 - -20 PACKAGE INFORMATION..... ....67 -REVISION HISTORY .... ....70 +- 1 FEATURE +- 2 GENERAL DESCRIPTION + - 2.1 PRODUCT LIST + - 2.2 CONNECTION DIAGRAM + - 2.3 PIN DESCRIPTION + - 2.4 BLOCK DIAGRAM +- 3 MEMORY MAPPING +- 4 ARRAY ORGANIZATION +- 5 DEVICE OPERATION + - 5.1 SPI MODES + - 5.2 HOLD MODE + - 5.3 WRITE PROTECTION + - 5.4 POWER OFF TIMING +- 6 COMMANDS DESCRIPTION +- 7 WRITE OPERATIONS + - 7.1 WRITE ENABLE (WREN) (06H) + - 7.2 WRITE DISABLE (WRDI) (04H) +- 8 READ OPERATIONS + - 8.1 PAGE READ + - 8.2 PAGE READ TO CACHE (13H) + - 8.3 CACHE READ FUNCTION (31H/3FH) + - 8.4 READ FROM CACHE (03H OR 0BH) + - 8.5 READ FROM CACHE X2 (3BH) + - 8.6 READ FROM CACHE X4 (6BH) + - 8.7 READ FROM CACHE DUAL IO (BBH) + - 8.8 READ FROM CACHE QUAD IO (EBH) + - 8.9 READ FROM CACHE QUAD I/O DTR (EEH) + - 8.10 READ ID (9FH) + - 8.11 READ UID + - 8.12 READ PARAMETER PAGE +- 9 PROGRAM OPERATIONS + - 9.1 PAGE PROGRAM + - 9.2 PROGRAM LOAD (PL) (02H) + - 9.3 PROGRAM LOAD X4 (PL X4) (32H) + - 9.4 PROGRAM EXECUTE (PE) (10H) + - 9.5 PROGRAM EXECUTE BACKGROUND (10H + ADDRESS + 15H) + - 9.6 INTERNAL DATA MOVE + - 9.7 PROGRAM LOAD RANDOM DATA (84H) + - 9.8 PROGRAM LOAD RANDOM DATA X4 (C4H/34H) +- 10 ERASE OPERATIONS + - 10.1 BLOCK ERASE (D8H) +- 11 RESET OPERATIONS + - 11.1 SOFT RESET (FFH) + - 11.2 ENABLE POWER ON RESET (66H) AND POWER ON RESET (99H) +- 12 FEATURE OPERATIONS + - 12.1 GET FEATURES (0FH) AND SET FEATURES (1FH) + - 12.2 STATUS REGISTER AND DRIVER REGISTER + - 12.3 OTP REGION + - 12.4 ASSISTANT BAD BLOCK MANAGEMENT + - 12.5 BLOCK PROTECTION + - 12.6 INTERNAL ECC +- 13 POWER ON TIMING +- 14 ABSOLUTE MAXIMUM RATINGS +- 15 CAPACITANCE MEASUREMENT CONDITIONS +- 16 DC CHARACTERISTIC +- 17 AC CHARACTERISTICS +- 18 PERFORMANCE AND TIMING +- 19 ORDERING INFORMATION +- 20 PACKAGE INFORMATION +- REVISION HISTORY ## 1 FEATURE -◆ 2Gb SLC NAND Flash +- 2Gb SLC NAND Flash -◆ Page Size +- Page Size -\- Internal ECC On (ECC\_EN=1, default): + - Internal ECC On (ECC_EN=1, default): Page Size:2048-Byte+64-Byte -\- Internal ECC Off (ECC\_EN=0): + - Internal ECC Off (ECC_EN=0): Page Size:2048-Byte+128-Byte -◆ Standard, Dual, Quad SPI,DTR +- Standard, Dual, Quad SPI,DTR -\- Standard SPI: SCLK, CS#, SI, SO, WP#, HOLD# + - Standard SPI: SCLK, CS#, SI, SO, WP#, HOLD# -\- Dual SPI: SCLK, CS#, SIO0, SIO1, WP#, HOLD# + - Dual SPI: SCLK, CS#, SIO0, SIO1, WP#, HOLD# -\- Quad SPI: SCLK, CS#, SIO0, SIO1, SIO2, SIO3 + - Quad SPI: SCLK, CS#, SIO0, SIO1, SIO2, SIO3 -\- DTR(Double Transfer Rate) Read : SCLK, CS#, SIO0, SIO1, SIO2, SIO3, DQS + - DTR(Double Transfer Rate) Read : SCLK, CS#, SIO0, SIO1, SIO2, SIO3, DQS -◆ High Speed Clock Frequency +- High Speed Clock Frequency -\- 3.3V: 104MHz for fast read with 30pF load + - 3.3V: 104MHz for fast read with 30pF load -\- 1.8V: 80MHz for fast read with 30pF load + - 1.8V: 80MHz for fast read with 30pF load -\- 3.3V: Quad I/O Data transfer up to 416Mbits/s + - 3.3V: Quad I/O Data transfer up to 416Mbits/s -\- 1.8V: Quad I/O Data transfer up to 320Mbits/s + - 1.8V: Quad I/O Data transfer up to 320Mbits/s -◆ Software/Hardware Write Protection +- Software/Hardware Write Protection -\- Write protect all/portion of memory via software + - Write protect all/portion of memory via software -\- Register protection with WP# Pin + - Register protection with WP# Pin -◆ Single Power Supply Voltage +- Single Power Supply Voltage -\- Full voltage range for 1.8V: 1.7V \~ 2.0V + - Full voltage range for 1.8V: 1.7V ~ 2.0V -\- Full voltage range for 3.3V: 2.7V \~ 3.6V + - Full voltage range for 3.3V: 2.7V ~ 3.6V -◆ Advanced security Features +- Advanced security Features -\- 8K-Byte OTP Region + - 8K-Byte OTP Region -◆ Program/Erase/Read Speed +- Program/Erase/Read Speed -\- Page Program time: 300us typical + - Page Program time: 300us typical -\- Block Erase time: 3ms typical + - Block Erase time: 3ms typical -\- Page read time: 60us maximum + - Page read time: 60us maximum -◆ Low Power Consumption +- Low Power Consumption -\- 30mA maximum active current + - 30mA maximum active current -\- 50uA maximum standby current + - 50uA maximum standby current -◆ Enhanced access performance +- Enhanced access performance -\- 2Kbyte cache for fast random read + - 2Kbyte cache for fast random read -\- Cache read and cache program + - Cache read and cache program -◆ Advanced Feature for NAND +- Advanced Feature for NAND -\- Factory good block0 + - Factory good block0 -◆ Reliability- Factory good block0 +- Reliability- Factory good block0 -\- P/E cycles with ECC: 100K + - P/E cycles with ECC: 100K -\- Data retention: 10 Years + - Data retention: 10 Years -◆ Internal ECC +- Internal ECC -\- 4bits /528Byte + - 4bits /528Byte Note: (1) ECC is on default, which can be disable by user. @@ -202,30 +156,49 @@ Note: (1) ECC is on default, which can be disable by user. SPI (Serial Peripheral Interface) NAND Flash provides an ultra-cost effective while high density non-volatile memory storage solution for embedded systems, based on an industry-standard NAND Flash memory core. It is an attractive alternative to SPI-NOR and standard parallel NAND Flash, with advanced features. -• Total pin count is 8, including VCC and GND -• Density 2Gb -• Superior write performance and cost per bit over SPI-NOR -• Significant low cost than parallel NAND +- Total pin count is 8, including VCC and GND +- Density 2Gb +- Superior write performance and cost per bit over SPI-NOR +- Significant low cost than parallel NAND This low-pin-count NAND Flash memory follows the industry-standard serial peripheral interface, and always remains the same pin out from one density to another. The command sets resemble common SPI-NOR command sets, modified to handle NAND specific functions and added new features. GigaDevice SPI NAND is an easy-to-integrate NAND Flash memory, with specified designed features to ease host management: -• User-selectable internal ECC. ECC parity is generated internally during a page program operation. When a page is read to the cache register, the ECC parity is detected and corrects the errors when necessary. The device outputs corrected data and returns an ECC error status. -• Internal data move or copy back with internal ECC. The device can be easily refreshed and manage garbage collection task, without need of shift in and out of data. This command string can only be used on blocks with the same parity attribute. -• Power on Read with internal ECC. The device will automatically read first page of fist block to cache after power on, then host can directly read data from cache for easy boot. Also the data is promised correct by internal ECC when ECC enabled. +- User-selectable internal ECC. ECC parity is generated internally during a page program operation. When a page is read to the cache register, the ECC parity is detected and corrects the errors when necessary. The device outputs corrected data and returns an ECC error status. +- Internal data move or copy back with internal ECC. The device can be easily refreshed and manage garbage collection task, without need of shift in and out of data. This command string can only be used on blocks with the same parity attribute. +- Power on Read with internal ECC. The device will automatically read first page of fist block to cache after power on, then host can directly read data from cache for easy boot. Also the data is promised correct by internal ECC when ECC enabled. It is programmed and read in page-based operations, and erased in block-based operations. Data is transferred to or from the NAND Flash memory array, page by page, to a data register and a cache register. The cache register is closest to I/O control circuits and acts as a data buffer for the I/O data; the data register is closest to the memory array and acts as a data buffer for the NAND Flash memory array operation. The cache register functions as the buffer memory to enable page and random data READ/WRITE and copy back operations. These devices also use a SPI status register that reports the status of device operation. -## 2.1 Product List +### 2.1 Product List Please contact GigaDevice regional sales for the latest product selection and available form factors -
Product NumberDensityVoltagePackage TypeTemperature
GD5F2GQ5REYIG2Gbit1.7V to 2.0VWSON8(8*6mm)-40°C to 85°C
GD5F2GQ5REBIG2Gbit1.7V to 2.0VTFBGA24(5*5 Ball Array)-40°C to 85°C
GD5F2GQ5REZIG2Gbit1.7V to 2.0VTFBGA24(4*6 Ball Array)-40°C to 85°C
GD5F2GQ5UEYIG2Gbit2.7V to 3.6VWSON8(8*6mm)-40°C to 85°C
GD5F2GQ5UEBIG2Gbit2.7V to 3.6VTFBGA24(5*5 Ball Array)-40°C to 85°C
GD5F2GQ5UEZIG2Gbit2.7V to 3.6VTFBGA24(4*6 Ball Array)-40°C to 85°C
GD5F2GQ5REYFG*2Gbit1.7V to 2.0VWSON8(8*6mm)-40°C to 85°C
GD5F2GQ5REBFG*2Gbit1.7V to 2.0VTFBGA24(5*5 Ball Array)-40°C to 85°C
GD5F2GQ5REZFG*2Gbit1.7V to 2.0VTFBGA24(4*6 Ball Array)-40°C to 85°C
GD5F2GQ5UEYFG*2Gbit2.7V to 3.6VWSON8(8*6mm)-40°C to 85°C
GD5F2GQ5UEBFG*2Gbit2.7V to 3.6VTFBGA24(5*5 Ball Array)-40°C to 85°C
GD5F2GQ5UEZFG*2Gbit2.7V to 3.6VTFBGA24(4*6 Ball Array)-40°C to 85°C
GD5F2GQ5REYJG2Gbit1.7V to 2.0VWSON8(8*6mm)-40°C to 105°C
GD5F2GQ5REBJG2Gbit1.7V to 2.0VTFBGA24(5*5 Ball Array)-40°C to 105°C
GD5F2GQ5REZJG2Gbit1.7V to 2.0VTFBGA24(4*6 Ball Array)-40°C to 105°C
GD5F2GQ5UEYJG2Gbit2.7V to 3.6VWSON8(8*6mm)-40°C to 105°C
GD5F2GQ5UEBJG2Gbit2.7V to 3.6VTFBGA24(5*5 Ball Array)-40°C to 105°C
GD5F2GQ5UEZJG2Gbit2.7V to 3.6VTFBGA24(4*6 Ball Array)-40°C to 105°C
+| **Product Number** | **Density** | **Voltage** | **Package Type** | **Temperature** | +| --- | --- | --- | --- | --- | +| GD5F2GQ5REYIG | 2Gbit | 1.7V to 2.0V | WSON8(8*6mm) | -40°C to 85°C | +| GD5F2GQ5REBIG | 2Gbit | 1.7V to 2.0V | TFBGA24(5*5 Ball Array) | -40°C to 85°C | +| GD5F2GQ5REZIG | 2Gbit | 1.7V to 2.0V | TFBGA24(4*6 Ball Array) | -40°C to 85°C | +| GD5F2GQ5UEYIG | 2Gbit | 2.7V to 3.6V | WSON8(8*6mm) | -40°C to 85°C | +| GD5F2GQ5UEBIG | 2Gbit | 2.7V to 3.6V | TFBGA24(5*5 Ball Array) | -40°C to 85°C | +| GD5F2GQ5UEZIG | 2Gbit | 2.7V to 3.6V | TFBGA24(4*6 Ball Array) | -40°C to 85°C | +| GD5F2GQ5REYFG* | 2Gbit | 1.7V to 2.0V | WSON8(8*6mm) | -40°C to 85°C | +| GD5F2GQ5REBFG* | 2Gbit | 1.7V to 2.0V | TFBGA24(5*5 Ball Array) | -40°C to 85°C | +| GD5F2GQ5REZFG* | 2Gbit | 1.7V to 2.0V | TFBGA24(4*6 Ball Array) | -40°C to 85°C | +| GD5F2GQ5UEYFG* | 2Gbit | 2.7V to 3.6V | WSON8(8*6mm) | -40°C to 85°C | +| GD5F2GQ5UEBFG* | 2Gbit | 2.7V to 3.6V | TFBGA24(5*5 Ball Array) | -40°C to 85°C | +| GD5F2GQ5UEZFG* | 2Gbit | 2.7V to 3.6V | TFBGA24(4*6 Ball Array) | -40°C to 85°C | +| GD5F2GQ5REYJG | 2Gbit | 1.7V to 2.0V | WSON8(8*6mm) | -40°C to 105°C | +| GD5F2GQ5REBJG | 2Gbit | 1.7V to 2.0V | TFBGA24(5*5 Ball Array) | -40°C to 105°C | +| GD5F2GQ5REZJG | 2Gbit | 1.7V to 2.0V | TFBGA24(4*6 Ball Array) | -40°C to 105°C | +| GD5F2GQ5UEYJG | 2Gbit | 2.7V to 3.6V | WSON8(8*6mm) | -40°C to 105°C | +| GD5F2GQ5UEBJG | 2Gbit | 2.7V to 3.6V | TFBGA24(5*5 Ball Array) | -40°C to 105°C | +| GD5F2GQ5UEZJG | 2Gbit | 2.7V to 3.6V | TFBGA24(4*6 Ball Array) | -40°C to 105°C | Note: (1) Industrial+: F grade has implemented additional test flows to ensure higher product quality than I grade. -## 2.2 CONNECTION DIAGRAM +### 2.2 CONNECTION DIAGRAM -Figure 2-1.Connect Diagram +**Figure 2-1. Connect Diagram** ![](images/aabcb50ecd150cac1381cae03d26d2043e276d1247bf7ba20937e6ab3390da9f.jpg) ![](images/b196abbdc8befa63ccd45faeb7c5c8adae6ca3a07fa73a08d3dce0ccd02f3d92.jpg) @@ -250,17 +223,28 @@ NC NC NC NC 24-BALL TFBGA (4x6 ball array) -## 2.3 PIN DESCRIPTION +### 2.3 PIN DESCRIPTION -
Pin NameI/ODescription
CS#IChip Select input, active low
SO/SIO1I/OSerial Data Output / Serial Data Input Output 1
WP#/SIO2I/OWrite Protect, active low / Serial Data Input Output 2
VSSGroundGround
SI/SIO0I/OSerial Data Input / Serial Data Input Output 0
SCLKISerial Clock input
HOLD#/SIO3I/OHold Input/Serial Data Input Output 3
DQS (only for BGA24)OData Strobe Signal Output
VCCSupplyPower Supply
NCNot Connect, Not internal connection; can be driven or floated.
+| **Pin Name** | **I/O** | **Description** | +| --- | --- | --- | +| CS# | I | Chip Select input, active low | +| SO/SIO1 | I/O | Serial Data Output / Serial Data Input Output 1 | +| WP#/SIO2 | I/O | Write Protect, active low / Serial Data Input Output 2 | +| VSS | Ground | Ground | +| SI/SIO0 | I/O | Serial Data Input / Serial Data Input Output 0 | +| SCLK | I | Serial Clock input | +| HOLD#/SIO3 | I/O | Hold Input/Serial Data Input Output 3 | +| DQS (only for BGA24) | O | Data Strobe Signal Output | +| VCC | Supply | Power Supply | +| NC | | Not Connect, Not internal connection; can be driven or floated. | Note: 1. CS# must be driven high if chip is not selected. Please don’t leave CS# floating any time after power is on. 2. If the DQS Function is not used, this pin must be floating. -## 2.4 BLOCK DIAGRAM +### 2.4 BLOCK DIAGRAM -Figure 2-2.Block Diagram +**Figure 2-2. Block Diagram** ![](images/ceba4f83b170efebf3759e0c718679d5ae2af33e9bea308e57867c09d42e091a.jpg)
@@ -321,11 +305,16 @@ Note: ## 4 ARRAY ORGANIZATION -Table 3-1.Array Organization +**Table 3-1. Array Organization** -
Each device hasEach block hasEach page has
2Gb
256M+16M128K+8K2K+128bytes
2048 x 6464-pages
2048--blocks
+| **Each device has** | **Each block has** | **Each page has** | | +| --- | --- | --- | --- | +| 2Gb | | | | +| 256M+16M | 128K+8K | 2K+128 | bytes | +| 2048 x 64 | 64 | - | pages | +| 2048 | - | - | blocks | -Figure 3-1. Array Organization +**Figure 3-1. Array Organization** ![](images/fc43d0e740b6032fe3d47e5bf3dc4ee18e86164889ab96549816ee6b0dca0319.jpg) @@ -370,47 +359,47 @@ SI Internal ECC = ON -## Note: +**Note:** 1.When Internal ECC is enabled,user can program the first 64 bytes of the entire 128 bytes spare area and the last 64 bytes of the whole spare area cannot be programed,user can read the entire 128 Byte spare area. 2.When Internal ECC is disabled,user can read and program the entire 128 bytes spare area. ## 5 DEVICE OPERATION -## 5.1 SPI Modes +### 5.1 SPI Modes SPI NAND supports two SPI modes: -• CPOL = 0, CPHA = 0 (Mode 0) -• CPOL = 1, CPHA = 1 (Mode 3) +- CPOL = 0, CPHA = 0 (Mode 0) +- CPOL = 1, CPHA = 1 (Mode 3) Input data is latched on the rising edge of SCLK and data shifts out on the falling edge of SCLK for both modes. All timing diagrams shown in this data sheet are mode 0. See Figure5-1 for more details. -Figure 5-1. SPI Modes Timing Diagram +**Figure 5-1. SPI Modes Timing Diagram** ![](images/5adddb568ef448df4679e84c8dbeb3c7ac66462b4314ad576c31571a21816932.jpg) Note: While CS# is HIGH, keep SCLK at VCC or GND (determined by mode 0 or mode 3). Do not toggle SCLK until CS# is driven LOW. We recommend that the user pull CS# to high when user don’t use SPI flash, otherwise the flash is always in the read state, which is not good for flash. When CS# is high and SCLK at VCC or GND state, the device is in idle state. -## Standard SPI +#### Standard SPI SPI NAND Flash features a standard serial peripheral interface on 4 signals bus: Serial Clock (SCLK), Chip Select (CS#), Serial Data Input (SI) and Serial Data Output (SO). -## Dual SPI +#### Dual SPI SPI NAND Flash supports Dual SPI operation when using the x2 and dual IO commands. These commands allow data to be transferred to or from the device at two times the rate of the standard SPI. When using the Dual SPI command the SI and SO pins become bidirectional I/O pins: SIO0 and SIO1. -## Quad SPI +#### Quad SPI SPI NAND Flash supports Quad SPI operation when using the x4 and Quad IO commands. These commands allow data to be transferred to or from the device at four times the rate of the standard SPI. When using the Quad SPI command the SI and SO pins become bidirectional I/O pins: SIO0 and SIO1, and WP# and HOLD# pins become SIO2 and SIO3. -## DTR Quad SPI +#### DTR Quad SPI The device supports DTR Quad SPI operation when using the “DTR Quad I/O Fast Read” command. These command allow data to be transferred to or from the device at eight times the rate of the standard SPI, and data output will be latched on both rising and falling edges of the serial clock. When using the DTR Quad SPI command the SI and SO pins become bidirectional I/O pins: IO0 and IO1, and WP# and HOLD# pins become IO2 and IO3. DTR Quad SPI commands require the Quad Enable bit (QE) in Status Register to be enable. The device has the DQS pin (Only for BGA24 Package). A data strobe (DQS) is transmitted externally, along with data, for use in data capture at the receiver -## 5.2 HOLD Mode +### 5.2 HOLD Mode The HOLD# function is only available when QE=0. If QE=1, the HOLD# functions is disabled, the pin acts as dedicated data I/O pin. @@ -420,7 +409,7 @@ The operation of HOLD, need CS# keep low, and starts on falling edge of the HOLD The SO is high impedance, both SI and SCLK don’t care during the HOLD operation, if CS# drives high during HOLD operation, it will reset the internal logic of the device. To re-start communication with chip, the HOLD# must be at high and then CS# must be at low. -Figure5-2. Hold Condition +**Figure 5-2. Hold Condition** ![](images/26cc48d89db717a20fc4ba65d779836eafd444f616474db40cd1a1f0f15f8085.jpg)
@@ -433,27 +422,53 @@ HOLD→ HOLD→
-## 5.3 Write Protection +### 5.3 Write Protection SPI NAND provides Hardware Protection Mode besides the Software Mode. Write Protect (WP#) prevents the block lock bits (BP0, BP1, BP2 and INV, CMP) from being over written. If the BRWD bit is set to 1 and WP# is LOW, the block protect bits cannot be altered. To enable the Write Protection, the Quad Enable bit (QE) of feature (B0[0]) must be set to 0. -## 5.4 Power Off Timing +### 5.4 Power Off Timing Please do not turn off the power before Write/Erase operation is completed. Avoid using the device when the battery is low. Power shortage and/or power failure before Write/Erase operation is complete will cause loss of data and/or damage to data. ## 6 COMMANDS DESCRIPTION -Table 6-1. Commands Set +**Table 6-1. Commands Set** -
Command NameByte1Byte2Byte3Byte4Byte5Byte6Byte 7
Write Enable06H
Write Disable04H
Get Features0FHA7-A0D7-D0 $Wrap^{(7)}$
Set Feature1FHA7-A0D7-D0
Page Read (to cache)13HA23-A16A15-A8A7-A0
Next Page Read (to cache)31H
Next Page Cache Read Random13HA23-A16A15-A8A7-A031H
Last Page Read (to cache)3FH
Read From Cache03H/0BHA15-A8 $A7-A0^{(2)}$ $Dummy^{(1)}$ D7-D0
Read From Cache x 23BHA15-A8 $A7-A0^{(2)}$ $Dummy^{(1)}$ D7-D0
Read From Cache x 46BHA15-A8 $A7-A0^{(2)}$ $Dummy^{(1)}$ D7-D0
Read From Cache Dual IOBBHA15-A8 $A7-A0^{(2)}$ $Dummyx2^{(1)}$ D7-D0
Read From Cache Quad IOEBHA15-A8 $A7-A0^{(2)}$ $Dummyx4^{(1)}$ D7-D0
Read From Cache Quad I/O DTREEHA31-A24A23-A16A15-A8 $A7-A0^{(2)}$ Dummy $x8^{(1)}$ D7-D0
$Read ID^{(4)}$ 9FHDummyMIDDID
Read parameter page13H00H00H04H
Read UID13H00H00H06H
Program Load02HA15-A8 $A7-A0^{(3)}$ D7-D0Next byte
Program Load x432HA15-A8 $A7-A0^{(3)}$ D7-D0Next byte
Program Execute10HA23-A16A15-A8A7-A0
Program Execute Background10HA23-A16A15-A8A7-A015H
Program Load Random Data84HA15-A8 $A7-A0^{(3)}$ D7-D0Next byte
Program Load Random Data x4C4H/34HA15-A8 $A7-A0^{(3)}$ D7-D0Next byte
Block Erase(128K)D8HA23-A16A15-A8A7-A0
$Reset^{(5)}$ FFH
Enable Power on Reset66h
$Power on Reset^{(6)}$ 99h
+| **Command Name** | **Byte1** | **Byte2** | **Byte3** | **Byte4** | **Byte5** | **Byte6** | **Byte 7** | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Write Enable | 06H | | | | | | | +| Write Disable | 04H | | | | | | | +| Get Features | 0FH | A7-A0 | D7-D0 | $Wrap^{(7)}$ | | | | +| Set Feature | 1FH | A7-A0 | D7-D0 | | | | | +| Page Read (to cache) | 13H | A23-A16 | A15-A8 | A7-A0 | | | | +| Next Page Read (to cache) | 31H | | | | | | | +| Next Page Cache Read Random | 13H | A23-A16 | A15-A8 | A7-A0 | 31H | | | +| Last Page Read (to cache) | 3FH | | | | | | | +| Read From Cache | 03H/0BH | A15-A8 | $A7-A0^{(2)}$ | $Dummy^{(1)}$ | D7-D0 | | | +| Read From Cache x 2 | 3BH | A15-A8 | $A7-A0^{(2)}$ | $Dummy^{(1)}$ | D7-D0 | | | +| Read From Cache x 4 | 6BH | A15-A8 | $A7-A0^{(2)}$ | $Dummy^{(1)}$ | D7-D0 | | | +| Read From Cache Dual IO | BBH | A15-A8 | $A7-A0^{(2)}$ | $Dummyx2^{(1)}$ | D7-D0 | | | +| Read From Cache Quad IO | EBH | A15-A8 | $A7-A0^{(2)}$ | $Dummyx4^{(1)}$ | D7-D0 | | | +| Read From Cache Quad I/O DTR | EEH | A31-A24 | A23-A16 | A15-A8 | $A7-A0^{(2)}$ | Dummy $x8^{(1)}$ | D7-D0 | +| $Read ID^{(4)}$ | 9FH | Dummy | MID | DID | | | | +| Read parameter page | 13H | 00H | 00H | 04H | | | | +| Read UID | 13H | 00H | 00H | 06H | | | | +| Program Load | 02H | A15-A8 | $A7-A0^{(3)}$ | D7-D0 | Next byte | | | +| Program Load x4 | 32H | A15-A8 | $A7-A0^{(3)}$ | D7-D0 | Next byte | | | +| Program Execute | 10H | A23-A16 | A15-A8 | A7-A0 | | | | +| Program Execute Background | 10H | A23-A16 | A15-A8 | A7-A0 | 15H | | | +| Program Load Random Data | 84H | A15-A8 | $A7-A0^{(3)}$ | D7-D0 | Next byte | | | +| Program Load Random Data x4 | C4H/34H | A15-A8 | $A7-A0^{(3)}$ | D7-D0 | Next byte | | | +| Block Erase(128K) | D8H | A23-A16 | A15-A8 | A7-A0 | | | | +| $Reset^{(5)}$ | FFH | | | | | | | +| Enable Power on Reset | 66h | | | | | | | +| $Power on Reset^{(6)}$ | 99h | | | | | | | -## Note: +**Note:** -1. The dummy has 8 clock. -03H/0BH/3BH/6BH has 1 byte dummy. BBH has 2 bytes dummy. -EBH has 4 bytes dummy. EEH has 8 bytes dummy. +1. The dummy has 8 clock. 03H/0BH/3BH/6BH has 1 byte dummy. BBH has 2 bytes dummy. EBH has 4 bytes dummy. EEH has 8 bytes dummy. 2. The A15-A0 (03H/0BH/3BH/6BH) has 16 clock, include 4 clock dummy. @@ -467,8 +482,8 @@ The A31-A0 (EEH) has 4 clock, include 2.5 clock dummy. 4. MID is Manufacture ID (C8h for GigaDevice), DID is Device ID. 5. Reset command: -• Reset will reset PAGE READ/PROGRAM/ERASE operation. -• Reset will reset status register bits P\_FAIL/E\_FAIL/WEL/OIP/CBSY/ECCS/ECCSE. +- Reset will reset PAGE READ/PROGRAM/ERASE operation. +- Reset will reset status register bits P_FAIL/E_FAIL/WEL/OIP/CBSY/ECCS/ECCSE. 6. Power on reset: Retrieve status register and data in cache to power on status. @@ -477,17 +492,17 @@ Retrieve status register and data in cache to power on status. ## 7 WRITE OPERATIONS -## 7.1 Write Enable (WREN) (06H) +### 7.1 Write Enable (WREN) (06H) The Write Enable (WREN) command is for setting the Write Enable Latch (WEL) bit. The Write Enable Latch (WEL) bit must be set prior to following operations that change the contents of the memory array: -• Page program -• OTP program/OTP protection -• Block erase +- Page program +- OTP program/OTP protection +- Block erase The WEL bit can be cleared after a reset command. -Figure 7-1.Write Enable Timing Diagram +**Figure 7-1. Write Enable Timing Diagram** ![](images/343f7b3c31c7222d5f6f45041cdcd903fbb8b6f15ea145f0a79be413b11bbf7b.jpg)
@@ -503,15 +518,15 @@ High-Z SO
-## 7.2 Write Disable (WRDI) (04H) +### 7.2 Write Disable (WRDI) (04H) The Write Disable command is for resetting the Write Enable Latch (WEL) bit. The WEL bit is reset by following condition: -• Page program -• OTP program/OTP protection -• Block erase +- Page program +- OTP program/OTP protection +- Block erase -Figure 7-2.Write Disable Timing Diagram +**Figure 7-2. Write Disable Timing Diagram** ![](images/7441b7c3bb1b3d617b08706cc77d8099c3e619b8f2ad82d6751a3fd0bf589a1c.jpg)
@@ -529,27 +544,27 @@ SO ## 8 READ OPERATIONS -## 8.1 Page Read +### 8.1 Page Read The PAGE READ (13H) command transfers the data from the NAND Flash array to the cache register. The command sequence is as follows: -• 13H (PAGE READ to cache) -• 0FH (GET FEATURES command to read the status) -• 03H or 0BH (Read from cache)/3BH (Read from cache x2)/6BH (Read from cache x4)/BBH/EBH/EEH (Read from cache DTR x4) +- 13H (PAGE READ to cache) +- 0FH (GET FEATURES command to read the status) +- 03H or 0BH (Read from cache)/3BH (Read from cache x2)/6BH (Read from cache x4)/BBH/EBH/EEH (Read from cache DTR x4) The PAGE READ command requires a 24-bit address. After the block/page addresses are registered, the device starts the transfer from the main array to the cache register, and is busy for tRD time. During this time, the GET FEATURE (0FH) command can be issued to monitor the status. Followed the page read operation, the RANDOM DATA READ (03H/0BH/3BH/6BH/EEH) command must be issued in order to read out the data from cache. The output data starts at the initial address specified in the command, once it reaches the ending boundary of whole page section, the output will wrap around from the beginning boundary until CS# is pulled high to terminate this operation. Refer waveforms to view the entire READ operation. Note:(1) The command 6BH (Read from cache x4)/EBH (Read from cache Quad IO)/EEH (Read from cache Quad IO DTR) is only available with the QE enable. (2) When user read to the end of 64-Byte spare area, it won’t wrap around from the beginning boundary and an additional 64Byte ECC code will be read. (Internal ECC enabled) -## 8.2 Page Read to Cache (13H) +### 8.2 Page Read to Cache (13H) The command page read to cache is read the data from flash array to cache register. -Figure 8-1.Page Read to cache Timing Diagram +**Figure 8-1. Page Read to cache Timing Diagram** ![](images/4af5305727b1bb17fb0a9669a56b6401806a645327f604a28dc2c9743ba940ea.jpg) -## 8.3 Cache Read Function (31H/3FH) +### 8.3 Cache Read Function (31H/3FH) A “Cache Read” function has been implemented in SPI series to improve the overall read throughput. It is possible to transfer the data from array to the Data Register simultaneously while a Read Data command is being performed to read out data from the Cache Register. @@ -561,27 +576,31 @@ While the device is transferring the next page array data to the Data Register, If the current page address is the last page of a block or the last page of the data being read out, the host should issue “Last Page Cache Read (3Fh)” instead of “Next Page Cache Read (31h)”, and proceed with the last Read from cache command. If the data being read out is more than one block, another “Page Read to Cache (13h)” command is needed to specify the first page of the next block and initiate the “Cache Read” operation again in the next block. -Table 8-1.Cache Read instruction description +**Table 8-1. Cache Read instruction description** -
InstructionCommand CodeDescription
Next Page Cache Read31hIssue prior to current page “Read From Cache” and read next page data into Data Register.
Next Page Cache Read Random13h+addr+31hIssue prior to current page “Read From Cache” and read special page data into Data Register.
Last Page Cache Read3FhIssue prior to last page “Read From Cache” at the end of a block or the end of the data being read.
+| **Instruction** | **Command Code** | **Description** | +| --- | --- | --- | +| Next Page Cache Read | 31h | Issue prior to current page “Read From Cache” and read next page data into Data Register. | +| Next Page Cache Read Random | 13h+addr+31h | Issue prior to current page “Read From Cache” and read special page data into Data Register. | +| Last Page Cache Read | 3Fh | Issue prior to last page “Read From Cache” at the end of a block or the end of the data being read. | -## Notes: +**Notes:** 1. Upon powered up, SPI NAND will automatically load Block-0/Page-0 data into the Cache Register. If this is the starting page of the data that is to be read out, it is not necessary to issue a “Page Read to Cache (13h)” command to initiate the “Cache Read” operation. 2. Before issuing 31h/3Fh, CBSY bit must be checked to make sure CBSY=0, device is not performing any internal operations. The command sequence is as follows: -• 13H (PAGE READ to cache) -• 0FH (GET FEATURES command to read the status until OIP status bit is changed from 1 to 0) -• 31H (NEXT PAGE CACHE READ command to transfer data from data register to cache register and kick off the next page transfer from array to data register) -• 0FH (GET FEATURES command to read the status until CBSY=0) -• 03H or 0BH (Read from cache)/3BH (Read from cache x2)/6BH (Read from cache x4)/BBH/EBH/EEH (Read from cache DTR x4) -• 3FH (LAST PAGE CACHE READ command to end the read page cache sequence and copy a last page from the data register to cache register) -• 0FH (GET FEATURES command to read the status until CBSY=0) -• 03H or 0BH (Read from cache)/3BH (Read from cache x2)/6BH (Read from cache x4)/BBH/EBH/EEH (Read from cache DTR x4) +- 13H (PAGE READ to cache) +- 0FH (GET FEATURES command to read the status until OIP status bit is changed from 1 to 0) +- 31H (NEXT PAGE CACHE READ command to transfer data from data register to cache register and kick off the next page transfer from array to data register) +- 0FH (GET FEATURES command to read the status until CBSY=0) +- 03H or 0BH (Read from cache)/3BH (Read from cache x2)/6BH (Read from cache x4)/BBH/EBH/EEH (Read from cache DTR x4) +- 3FH (LAST PAGE CACHE READ command to end the read page cache sequence and copy a last page from the data register to cache register) +- 0FH (GET FEATURES command to read the status until CBSY=0) +- 03H or 0BH (Read from cache)/3BH (Read from cache x2)/6BH (Read from cache x4)/BBH/EBH/EEH (Read from cache DTR x4) -Figure 8-2.Cache Read operation flow chart +**Figure 8-2. Cache Read operation flow chart** ![](images/e3503671df72f45ed106fb3d776cc9470593584d6bad0a8b281258044b32c694.jpg)
@@ -611,7 +630,7 @@ graph TD ```
-Figure 8-3.Page Read to Cache Timing Diagram +**Figure 8-3. Page Read to Cache Timing Diagram** ![](images/ea8cff647c04b2e0ac8d1a3060e1bd9d9c4f50cbd06ce45c8542cc1559200a18.jpg)
@@ -647,7 +666,7 @@ SO MSB
-Figure 8-4.Page Read to Cache Timing Diagram +**Figure 8-4. Page Read to Cache Timing Diagram** ![](images/ea764a257b8125933e6403664d7a9d31cec67e8730b18b3daeafca6a3145a3f4.jpg) @@ -688,7 +707,7 @@ SO MSB
-Figure 8-5.Page Read to Cache Timing Diagram +**Figure 8-5. Page Read to Cache Timing Diagram** ![](images/31c34d53aa5fbde9fac6df83c5c12c8dbd4bfd5158ed1898f585f3103e18bb97.jpg)
@@ -724,7 +743,7 @@ SO MSB
-Figure 8-6.Page Read to Cache Timing Diagram +**Figure 8-6. Page Read to Cache Timing Diagram** ![](images/339900a74423153c5c2f48ae3b9f82a7f463ca764e808410d077aa8b6ca0c835.jpg)
@@ -800,17 +819,17 @@ SI 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5
-## Note: +**Note:** 1. Command 03h/0Bh/3Bh/6Bh/BBh/EBh is available to read out the data in the Cache Register. 2. For high speed performance, we recommend to use EBh to read out the data in the Cache Register. (Please refer to 8.8 Read From Cache Quad IO) 3. We recommend to use GET FEATURES command (0Fh) to read the status until CBSY=0. -## 8.4 Read From Cache (03H or 0BH) +### 8.4 Read From Cache (03H or 0BH) The command sequence is shown below. -Figure 8-7.Read From Cache Timing Diagram +**Figure 8-7. Read From Cache Timing Diagram** ![](images/09c5172dc7c3328e5e10b532a26a7e97ffd02c5da26241e7ee3520222914159f.jpg)
@@ -840,11 +859,11 @@ MSB MSB
-## 8.5 Read From Cache x2 (3BH) +### 8.5 Read From Cache x2 (3BH) The command sequence is shown below. -Figure 8-8.Read From Cache x2 Timing Diagram +**Figure 8-8. Read From Cache x2 Timing Diagram** ![](images/c935e090628694cb5b7c2d5b5e5cdd970504fbe6ce4b4164ea41e6d1a37437bf.jpg)
@@ -879,11 +898,11 @@ MSB 7 5 3
-## 8.6 Read From Cache x4 (6BH) +### 8.6 Read From Cache x4 (6BH) The Quad Enable bit (QE) of feature (B0[0]) must be set to enable the read from cache x4 command. The command sequence is shown below. -Figure 8-9.Read From Cache x4 Timing Diagram +**Figure 8-9. Read From Cache x4 Timing Diagram** ![](images/4654ffcb1c75a72cebdd19cdd5562ac345b2d2f84f37efb7cce5ac83a3043823.jpg)
@@ -907,11 +926,11 @@ HOLD#(SIO3) 7 3 7 3 7 3 7 3 7 Byte0 Byte1 Byte2 Byte3
-## 8.7 Read From Cache Dual IO (BBH) +### 8.7 Read From Cache Dual IO (BBH) The Read from Cache Dual I/O command (BBH) is similar to the Read form Cache x2 command (3BH) but with the capability to input the 4 Dummy bits, followed by a 12-bit column address for the starting byte address and dummy bytes by SIO0 and SIO1, each bit being latched in during the rising edge of SCLK, then the cache contents are shifted out 2-bit per clock cycle from SIO0 and SIO1. The first address byte can be at any location. The address increments automatically to the next higher address after each byte of data shifted out. The command sequence is shown below. -Figure 8-10.Read From Cache Dual IO Timing Diagram +**Figure 8-10. Read From Cache Dual IO Timing Diagram** ![](images/e2e726866d1895649ec7e388b5035a8500e43a91b7baa3d45c2c7f0637baad86.jpg)
@@ -949,11 +968,11 @@ Data byte 3
-## 8.8 Read From Cache Quad IO (EBH) +### 8.8 Read From Cache Quad IO (EBH) The Read from Cache Quad IO command is similar to the Read from Cache x4 command but with the capability to input the 4 dummy bits, followed a 12-bit column address for the starting byte address and dummy bytes by SIO0, SIO1, SIO3, SIO4, each bit being latched in during the rising edge of SCLK, then the cache contents are shifted out 4-bit per clock cycle from SIO0, SIO1, SIO2, SIO3. The first byte addressed can be at any location. The address is automatically incremented to the next higher address after each byte of data is shifted out. The Quad Enable bit (QE) of feature (B0[0]) must be set to enable the read from cache quad IO command. The command sequence is shown below. -Figure 8-11.Read From Cache Quad IO Timing Diagram +**Figure 8-11. Read From Cache Quad IO Timing Diagram** ![](images/b4f48eb41fe925af5d8eefab289e736bdec20ce1083c66cc7aabc2b65ef62d11.jpg)
@@ -970,13 +989,13 @@ HOLD#(SIO3) A11 - A0 Dummy Byte0 Byte1
-## 8.9 Read From Cache Quad I/O DTR (EEH) +### 8.9 Read From Cache Quad I/O DTR (EEH) The DTR QIO command enables Double Transfer Rate throughput on quad I/O of Serial Flash in read mode. A Quad Enable (QE) bit of status Register must be set to “1” before sending the DTR QIO command. The address (interleave on 4 I/O pins) is latched on both rising and falling edge of SCLK, and data (interleave on 4 I/O pins) shift out on both rising and falling edge of SCLK. The 8-bit address can be latched-in at one clock, and 8-bit data can be read out at one clock, which means four bits at rising edge of clock, the other four bits at falling edge of clock. The first address Byte can be at any location. The address is automatically increased to the next higher address after each Byte data is shifted out, so the whole page can be read out at a single DTR QIO command. The address counter rolls over to 0 when the highest address has been reached. -Figure 8-12.Read From Cache Quad I/O DTR Timing Diagram +**Figure 8-12. Read From Cache Quad I/O DTR Timing Diagram** ![](images/65b603938a3331f3f449c2c6ed5cd6aa5126d90244b8116c97c76afa4d3407f1.jpg)
@@ -1005,7 +1024,7 @@ Byte 0Byte 1 19 20
-## Note: +**Note:** Please contact GigaDevice when there is a need to use the EEh command for DTR. @@ -1015,13 +1034,13 @@ However, in general, most of PCB designs have output loading much less than 30pf GigaDevice recommend customers measure the tCLQV and then set the clock rate to match the SPI host data sampling data setup time and hold time. -## 8.10 Read ID (9FH) +### 8.10 Read ID (9FH) The READ ID command is used to identify the NAND Flash device. -• With address 00H, the READ ID command outputs the Manufacturer ID and the device ID. See Table 8-2 for details. +- With address 00H, the READ ID command outputs the Manufacturer ID and the device ID. See Table 8-2 for details. -Figure 8-13.Read ID Timing Diagram +**Figure 8-13. Read ID Timing Diagram** ![](images/4fdbdfd9298bda9175599f45dc0d85b9a37c97dcb8ce4fdf5f0e2037c6691d9f.jpg)
@@ -1049,27 +1068,33 @@ MSB MSB
-Table 8-2. READ ID Table +**Table 8-2. READ ID Table** -
Part NoMIDDID1
GD5F2GQ5UExxGC8H52H
GD5F2GQ5RExxGC8H42H
+| **Part No** | **MID** | **DID1** | +| --- | --- | --- | +| GD5F2GQ5UExxG | C8H | 52H | +| GD5F2GQ5RExxG | C8H | 42H | -## 8.11 Read UID +### 8.11 Read UID The Read Unique ID function is used to retrieve the 16 bytes unique ID (UID) for the device. The unique ID when combined with the device manufacturer shall be unique. The UID data may be stored within the Flash array. To allow the host to determine if the UID is without bit errors, the UID is returned with its complement. If the XOR of the UID and its bit-wise complement is all ones, then the UID is valid. To accommodate robust retrieval of the UID in the case of bit errors, sixteen copies of the UID and the corresponding complement are stored by the target. For example, reading byte 32-63 returns to the host another copies of the UID and its complement. -
BytesValue
0-15UID
16-31UID complement (bit-wise)
+| **Bytes** | **Value** | +| --- | --- | +| 0-15 | UID | +| 16-31 | UID complement (bit-wise) | -## Sequence is as follows: +#### Sequence is as follows: -1. Use Set Feature command to set B0 register, to enable OTP\_EN. -2. Use Get Feature command to get data from B0 register and check if the OTP\_EN is enable. +1. Use Set Feature command to set B0 register, to enable OTP_EN. +2. Use Get Feature command to get data from B0 register and check if the OTP_EN is enable. 3. Use Page Read to Cache (13h) command with address 24’h000006h, read data from array to cache. 4. Use 0FH (GET FEATURES command) read the status. 5. User can use Read from cache command (03H/0BH), read 16 bytes UID from cache. -Figure 8-14. Read UID to cache and Get Feature command Timing Diagram +**Figure 8-14. Read UID to cache and Get Feature command Timing Diagram** ![](images/64d79f7353b7ae65411a1c43d8d7b3d0a12c6e96df699da85b21a00f2133abad.jpg)
@@ -1107,49 +1132,135 @@ SO MSB
-## 8.12 Read Parameter Page +### 8.12 Read Parameter Page The Read Parameter Page function retrieves the data structure that describes the chip’s organization, features, timing and other behavioral parameters. This data structure enables the host processor to automatically recognize the SPI-NAND Flash configuration of a device. A minimum of three copies of the parameter page are stored in the device. The Read from Cache command can be used to change the location of data output. Sequences as follows: -1. Use Set Feature command to set B0 register, to enable OTP\_EN. -2. Use Get Feature command to get data from B0 register and check if the OTP\_EN is enable. +1. Use Set Feature command to set B0 register, to enable OTP_EN. +2. Use Get Feature command to get data from B0 register and check if the OTP_EN is enable. 3. Use Page Read to Cache (13h) command with address 24’h000004. Load parameter page from array to cache. 4. Use 0Fh (GET FEATURES command) read the status 5. User can use Read from cache command (03h/0Bh), read parameter page from cache. -Figure 8-15. Read parameter page to cache and Get Feature command Timing Diagram +**Figure 8-15. Read parameter page to cache and Get Feature command Timing Diagram** ![](images/09ca7246ee158e984bf4611476216ff1782ecd917d21b0a0ccc3d34b29398ee8.jpg) Parameter page table as follow -
ByteO/MDescription3.3V/1.8V
0-3MParameter page signature4FH
Byte 0: 4FH, “O”4EH
Byte 1: 4EH, “N”46H
Byte 2: 46H, “F”49H
Byte 3: 49H, “I”
4-5MRevision number00H
0-15 Reserved (0)00H
6-7MFeatures supported00H
0-15 Reserved (0)00H
8-9MReserved (0)00H
00H
10-31Reserved (0)00H
...
00H
Manufacturer Information block
32-43MDevice manufacturer (12 ASCII characters)“GIGADEVICE ”47H
49H
47H
41H
44H
45H
56H
49H
43H
45H
20H
20H
44-63MDevice model (20 ASCII characters)47H
Device ModelORGANIZATIONVCC RANGE44H
“GD5F2GQ5U”X42.7v ~ 3.6v35H
“GD5F2GQ5R”X41.7v ~ 2.0v46H
32H
47H
51H
35H
55H/52H
20H
20H
20H
20H
20H
+| **Byte** | **O/M** | **Description** | | | **3.3V/1.8V** | +| --- | --- | --- | --- | --- | --- | +| 0-3 | M | Parameter page signature | | | 4FH | +| | | Byte 0: 4FH, “O” | | | 4EH | +| | | Byte 1: 4EH, “N” | | | 46H | +| | | Byte 2: 46H, “F” | | | 49H | +| | | Byte 3: 49H, “I” | | | | +| 4-5 | M | Revision number | | | 00H | +| | | 0-15 Reserved (0) | | | 00H | +| 6-7 | M | Features supported | | | 00H | +| | | 0-15 Reserved (0) | | | 00H | +| 8-9 | M | Reserved (0) | | | 00H | +| | | | | | 00H | +| 10-31 | | Reserved (0) | | | 00H | +| | | | | | ... | +| | | | | | 00H | +| | | Manufacturer Information block | | | | +| 32-43 | M | Device manufacturer (12 ASCII characters)“GIGADEVICE ” | | | 47H | +| | | | | | 49H | +| | | | | | 47H | +| | | | | | 41H | +| | | | | | 44H | +| | | | | | 45H | +| | | | | | 56H | +| | | | | | 49H | +| | | | | | 43H | +| | | | | | 45H | +| | | | | | 20H | +| | | | | | 20H | +| 44-63 | M | Device model (20 ASCII characters) | | | 47H | +| | | Device Model | ORGANIZATION | VCC RANGE | 44H | +| | | “GD5F2GQ5U” | X4 | 2.7v ~ 3.6v | 35H | +| | | “GD5F2GQ5R” | X4 | 1.7v ~ 2.0v | 46H | +| | | | | | 32H | +| | | | | | 47H | +| | | | | | 51H | +| | | | | | 35H | +| | | | | | 55H/52H | +| | | | | | 20H | +| | | | | | 20H | +| | | | | | 20H | +| | | | | | 20H | +| | | | | | 20H | -
20H20H20H20H20H
64MJEDEC manufacturer ID“C8”C8H
65-66ODate code00H00H
67-79Reserved00H00H00H
Memory organization block
80-83MNumber of data bytes per page00H08H00H00H
84-85MNumber of spare bytes per page80H00H
86-89MNumber of data bytes per partial page00H02H00H00H
90-91MNumber of spare bytes per partial page20H00H
92-95MNumber of pages per block40H00H00H00H
96-99MNumber of blocks per logical unit00H08H00H00H
100MNumber of logical units01H
101MReserved00H
102MNumber of bits per cell01H
103-104MBad blocks maximum per logical unit28H00H
105-106MBlock endurance01H05H
107MGuaranteed valid blocks at beginning of target01H
108-109MBlock endurance for guaranteed valid blocks00H00H
110MNumber of programs per page04H
111MPartial programming attributes5-7 Reserved4 1 = partial page layout is partial page data followed by partial page spare1-3 Reserved0 1 = partial page programming has constraints00H
112MNumber of bits ECC correctability00H
113MNumber of interleaved address bits4-7 Reserved (0)0-3 Number of interleaved address bits00H
114OInterleaved operation attributes4-7 Reserved (0)3 Address restrictions for program cache2 1 = program cache supported1 1 = no block address restrictions0 Overlapped / concurrent interleaving support00H
115-127Reserved00H...00H
Electrical parameters block
128MI/O capacitance06H
129-130MIO clock support3-1 5 Reserved (0)2 1 = supports 80MHz1 1 = supports 104MHz0 1 = supports 120MHz02H/04H00H
131-132OReserved (0)00H00H
133-134MtPROG Maximum page program time (us)58H02H
135-136MtBERS Maximum block erase time (us)88H13H
137-138MtR Maximum page read time (us)3CH00H
139-140MReserved00H00H
141-163Reserved00H
Vendor block
164-165MVendor specific Revision number00H
166-253Vendor specific00H
254-255MIntegrity CRCSet on test
Redundant parameter pages
256-511MValue of bytes 0-255
512-767MValue of bytes 0-255
768+OAdditional redundant parameter pages
+| | | | **20H20H20H20H20H** | +| --- | --- | --- | --- | +| 64 | M | JEDEC manufacturer ID“C8” | C8H | +| 65-66 | O | Date code | 00H00H | +| 67-79 | | Reserved | 00H00H00H | +| | | Memory organization block | | +| 80-83 | M | Number of data bytes per page | 00H08H00H00H | +| 84-85 | M | Number of spare bytes per page | 80H00H | +| 86-89 | M | Number of data bytes per partial page | 00H02H00H00H | +| 90-91 | M | Number of spare bytes per partial page | 20H00H | +| 92-95 | M | Number of pages per block | 40H00H00H00H | +| 96-99 | M | Number of blocks per logical unit | 00H08H00H00H | +| 100 | M | Number of logical units | 01H | +| 101 | M | Reserved | 00H | +| 102 | M | Number of bits per cell | 01H | +| 103-104 | M | Bad blocks maximum per logical unit | 28H00H | +| 105-106 | M | Block endurance | 01H05H | +| 107 | M | Guaranteed valid blocks at beginning of target | 01H | +| 108-109 | M | Block endurance for guaranteed valid blocks | 00H00H | +| 110 | M | Number of programs per page | 04H | +| 111 | M | Partial programming attributes5-7 Reserved4 1 = partial page layout is partial page data followed by partial page spare1-3 Reserved0 1 = partial page programming has constraints | 00H | +| 112 | M | Number of bits ECC correctability | 00H | +| 113 | M | Number of interleaved address bits4-7 Reserved (0)0-3 Number of interleaved address bits | 00H | +| 114 | O | Interleaved operation attributes4-7 Reserved (0)3 Address restrictions for program cache2 1 = program cache supported1 1 = no block address restrictions0 Overlapped / concurrent interleaving support | 00H | +| 115-127 | | Reserved | 00H...00H | +| | | Electrical parameters block | | +| 128 | M | I/O capacitance | 06H | +| 129-130 | M | IO clock support3-1 5 Reserved (0)2 1 = supports 80MHz1 1 = supports 104MHz0 1 = supports 120MHz | 02H/04H00H | +| 131-132 | O | Reserved (0) | 00H00H | +| 133-134 | M | tPROG Maximum page program time (us) | 58H02H | +| 135-136 | M | tBERS Maximum block erase time (us) | 88H13H | +| 137-138 | M | tR Maximum page read time (us) | 3CH00H | +| 139-140 | M | Reserved | 00H00H | +| 141-163 | | Reserved | 00H | +| | | Vendor block | | +| 164-165 | M | Vendor specific Revision number | 00H | +| 166-253 | | Vendor specific | 00H | +| 254-255 | M | Integrity CRC | Set on test | +| | | Redundant parameter pages | | +| 256-511 | M | Value of bytes 0-255 | | +| 512-767 | M | Value of bytes 0-255 | | +| 768+ | O | Additional redundant parameter pages | | -## Notes: +**Notes:** 1. “O” Stands for Optional, “M” for Mandatory 2. The Integrity CRC (Cycling Redundancy Check) field is used to verify that the contents of the parameters page were transferred correctly to the host. Please refer to ONFI 1.0 specifications for details. The CRC shall be calculated using the following 16-bit generator polynomial: $\mathsf { G } ( \mathsf { X } ) = \mathsf { X } ^ { 1 6 } + \mathsf { X } ^ { 1 5 } + \mathsf { X } ^ { 2 } + 1$ ,This polynomial in hex may be represented as 8005h. 3.The CRC value shall be initialized with a value of 4F4Eh before the calculation begins. There is no XOR applied to the final CRC value after it is calculated. There is no reversal of the data bytes or the CRC calculated value. -
Device ModelORGANIZATIONVCC RANGECRC value B254/B255
“GD5F2GQ5UxxxG”X42.7v ~ 3.6v5BH/05H
“GD5F2GQ5RxxxG”X41.7v ~ 2.0v96H/48H
+| **Device Model** | **ORGANIZATION** | **VCC RANGE** | **CRC value B254/B255** | +| --- | --- | --- | --- | +| “GD5F2GQ5UxxxG” | X4 | 2.7v ~ 3.6v | 5BH/05H | +| “GD5F2GQ5RxxxG” | X4 | 1.7v ~ 2.0v | 96H/48H | ## 9 PROGRAM OPERATIONS -## 9.1 Page Program +### 9.1 Page Program The PAGE PROGRAM operation sequence programs 1 byte to whole page bytes of data within a page. The page program sequence is as follows: -• 02H (PROGRAM LOAD)/32H (PROGRAM LOAD x4) -• 06H (WRITE ENABLE) -• 10H (PROGRAM EXECUTE) -• 0FH (GET FEATURE command to read the status) +- 02H (PROGRAM LOAD)/32H (PROGRAM LOAD x4) +- 06H (WRITE ENABLE) +- 10H (PROGRAM EXECUTE) +- 0FH (GET FEATURE command to read the status) Firstly, a PROGRAM LOAD (02H/32H) command is issued. PROGRAM LOAD consists of an 8-bit Op code, followed by 4 dummy bits and a 12-bit column address, then the data bytes to be programmed. The Program address should be in sequential order in a block. The data bytes are loaded into a cache register that is whole page long. If more than one page data are loaded, then those additional bytes are ignored by the cache register. The command sequence ends when CS# goes from LOW to HIGH. Figure 9-1 shows the PROGRAM LOAD operation. Secondly, prior to performing the PROGRAM EXECUTE operation, a WRITE ENABLE (06H) command must be issued. As with any command that changes the memory contents, the WRITE ENABLE must be executed in order to set the WEL bit. If this command is not issued, then the rest of the program sequence is ignored. -## Note: +**Note:** 1. The contents of Cache Register don’t reset when Program Random Load (84h) command and RESET (FFh) command. 2. When Program Execute (10h) command was issued just after Program Load (02h) command, the 0xFF is output to the address that data was not loaded by Program Load (02h) command. @@ -1157,11 +1268,11 @@ Firstly, a PROGRAM LOAD (02H/32H) command is issued. PROGRAM LOAD consists of an 4. The Program address should be in sequential order in a block. 5. Program Load x4 is only available with the QE enable. -## 9.2 Program Load (PL) (02H) +### 9.2 Program Load (PL) (02H) The command sequence is shown below. -Figure 9-1. Program Load Timing Diagram +**Figure 9-1. Program Load Timing Diagram** ![](images/96a851b255ea13b19d69011401becadc5fda55a205daa3061da824f5d39269bb.jpg)
@@ -1188,22 +1299,22 @@ MSB Note: When internal ECC disabled the Data Byte is 2176, when internal ECC enabled the Data Byte is 2112. -## 9.3 Program Load x4 (PL x4) (32H) +### 9.3 Program Load x4 (PL x4) (32H) The Program Load x4 command (32H) is similar to the Program Load command (02H) but with the capability to input the data bytes by four pins: SIO0, SIO1, SIO2, and SIO3. The Quad Enable bit (QE) of feature (B0[0]) must be set to enable the program load x4 command. The command sequence is shown below. -Figure 9-2. Program Load x4 Timing Diagram +**Figure 9-2. Program Load x4 Timing Diagram** ![](images/b660c2be6cd0f35871a725005cd39d61d55e0712c188b10f3060221330d68b26.jpg) Note: When internal ECC disabled the Data Byte is 2176, when internal ECC enabled the Data Byte is 2112. -## 9.4 Program Execute (PE) (10H) +### 9.4 Program Execute (PE) (10H) After the data is loaded, a PROGRAM EXECUTE (10H) command must be issued to initiate the transfer of data from the cache registers to the main array. PROGRAM EXECUTE consists of an 8-bit Op code, followed by a 24-bit address. After the page/block address is registered, the memory device starts the transfer from the cache register to the main array, and is busy for tPROG time. This operation shown in Figure 9-3. During this busy time, the status register can be polled to monitor the status of the operation (refer to Status Register). When the operation completes successfully, the next series of data can be loaded with the PROGRAM LOAD command. -Figure 9-3. Program Execute Timing Diagram +**Figure 9-3. Program Execute Timing Diagram** ![](images/23355b92f7c8598e09f40a15770d923a15aee870f89915343a1335487c3e83b6.jpg) -## 9.5 Program Execute Background (10h + address + 15h) +### 9.5 Program Execute Background (10h + address + 15h) A “Cache Program” function has been implemented in SPI series to improve the overall program throughput. It is possible to program the data from Data Register to array the simultaneously while a Load Data command is being performed to write data to the Cache Register. @@ -1215,7 +1326,7 @@ When the last page of one block to be program and the OIP bit is 0, the program The program execute command (10h+address+15h) is allowed to cross blocks before reaching the last block. -Figure 9-4. Program Execute Background Operation Flow Chart +**Figure 9-4. Program Execute Background Operation Flow Chart** ![](images/305761c95e760f28ce942527a3b942b95011ae4b7b7fb6b139818940ca41ae56.jpg)
@@ -1239,7 +1350,7 @@ graph TD ```
-Figure 9-5. Program Execute Background Timing +**Figure 9-5. Program Execute Background Timing** ![](images/58ae5c4adb2ba5406689db21c0730acd47b49e05982433f44acabd1fbf1353e2.jpg)
@@ -1300,23 +1411,23 @@ SO High-Z
-## 9.6 Internal Data Move +### 9.6 Internal Data Move The INTERNAL DATA MOVE command sequence programs or replaces data in a page with existing data. The INTERNAL DATA MOVE command sequence is as follows: -• 13H (PAGE READ to cache) -• Optional 84H/C4H/34H (PROGRAM LOAD RANDOM DATA) -• 06H (WRITE ENABLE) -• 10H (PROGRAM EXECUTE) -• 0FH (GET FEATURE command to read the status) +- 13H (PAGE READ to cache) +- Optional 84H/C4H/34H (PROGRAM LOAD RANDOM DATA) +- 06H (WRITE ENABLE) +- 10H (PROGRAM EXECUTE) +- 0FH (GET FEATURE command to read the status) Prior to performing an internal data move operation, the target page content must be read out into the cache register by issuing a PAGE READ (13H) command. The PROGRAM LOAD RANDOM DATA (84H/C4H) command can be issued, if user wants to update bytes of data in the page. New data is loaded in the 12-bit column address. If the random data is not sequential, another PROGRAM LOAD RANDOM DATA (84H/C4H) command must be issued with the new column address. After the data is loaded, the WRITE ENABLE command must be issued, and then PROGRAMEXECUTE (10H) command can be issued to start the programming operation. Only the block with the same parity attribute can use the command. -## 9.7 Program Load Random Data (84H) +### 9.7 Program Load Random Data (84H) The Program Load Random Data command programs or replaces data in a page with existing data. This command consists of an 8-bit Op code, followed by 4 dummy bits, and a 12-bit column address. New data is loaded in the column address provided with the 12 bits. If the random data is not sequential, then another PROGRAM LOAD RANDOM DATA (84H) command must be issued with a new column address, see Figure 9-6 for details. -Figure 9-6. Program Load Random Data Timing Diagram +**Figure 9-6. Program Load Random Data Timing Diagram** ![](images/3a5cd052e863303d1a72acb4cfc8001882b3bba1c697960e9933f0d3ced6b269.jpg)
@@ -1335,11 +1446,11 @@ SI 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 MSB
-## 9.8 Program Load Random Data x4 (C4H/34H) +### 9.8 Program Load Random Data x4 (C4H/34H) The Program Load Random Data x4 command (C4H/34H) is similar to the Program Load Random Data command (84H) but with the capability to input the data bytes by four pins: SIO0, SIO1, SIO2, and SIO3. The command sequence is shown below. The Quad Enable bit (QE) of feature (B0[0]) must be set to enable for the program load random data x4 command. See Figure 9-7 for details. -Figure 9-7. Program Load Random Data x4 Timing Diagram +**Figure 9-7. Program Load Random Data x4 Timing Diagram** ![](images/d2689b1999b01197d306d53971378631bbe3472f017cbb2affe7c340e362d484.jpg)
@@ -1361,30 +1472,30 @@ WP#(SIO2) 6 2 6 2 6 2 6 2 6 2 6 2 6 2 6 2 6 2 HOLD#(SIO3) 7 3 7 3 7 3 7 3 7 3 7 3 7 3 7 3 7 3
-## 10ERASE OPERATIONS +## 10 ERASE OPERATIONS -## 10.1 Block Erase (D8H) +### 10.1 Block Erase (D8H) The BLOCK ERASE (D8H) command is used to erase at the block level. The BLOCK ERASE command (D8H) operates on one block at a time. The command sequence for the BLOCK ERASE operation is as follows: -• 06H (WRITE ENBALE command) -• D8H (BLOCK ERASE command) -• 0FH (GET FEATURES command to read the status register) +- 06H (WRITE ENBALE command) +- D8H (BLOCK ERASE command) +- 0FH (GET FEATURES command to read the status register) Prior to performing the BLOCK ERASE operation, the WRITE ENABLE (06H) command must be issued. As with any command that changes the memory contents, the WRITE ENABLE command must be executed in order to set the WEL bit. If the WRITE ENABLE command is not issued, then the rest of the erase sequence is ignored. The WRITE ENABLE command must be followed by the BLOCK ERASE (D8H) command. This command requires a 24-bit address. After the row address is registered, the control logic automatically controls timing and erase-verify operations. The device is busy for tBERS time during the BLOCK ERASE operation. The GET FEATURES (0FH) command can be used to monitor the status of the operation. -Figure10-1. Block Erase Timing Diagram +**Figure 10-1. Block Erase Timing Diagram** ![](images/f921480c0cb742615c5a7244738d62e37798d90a0da0dbed5107f7f047e9df06.jpg) ## 11 RESET OPERATIONS -## 11.1 Soft Reset (FFH) +### 11.1 Soft Reset (FFH) The RESET (FFH) command stops all operations and the status. For example, in case of a program or erase or read operation, the reset command can make the device enter the idle state. During a cache program or cache read, a reset can also stops the previous operation and the pending operation. -Figure11-1. Reset Timing Diagram +**Figure 11-1. Reset Timing Diagram** ![](images/15f15746a12a6271d2735432e42186d37c619e700e811c711153eda6a69acecc.jpg)
@@ -1422,13 +1533,13 @@ MSB Note: The Register bit value after soft reset refers to Table 12-2. Register bit Descriptions. -## 11.2 Enable Power on Reset (66H) and Power on Reset (99H) +### 11.2 Enable Power on Reset (66H) and Power on Reset (99H) If the Power on Reset command is accepted, any on-going internal operation will be terminated and the device will return to its default power-on state and lose all the current feature settings. The “Enable Reset (66H)” and the “Reset (99H)” commands can be issued in SPI mode. The “Reset (99H)” command sequence as follow: CS# goes low -> Sending Enable Reset command ->CS# goes high ->CS# goes low.->Sending Reset command ->CS# goes high. Once the Reset command is accepted by the device, the device will take approximately tVSL to reset. During this period, no command will be accepted. It is recommended to check the OIP bit in Status Register before issuing any other command sequence. The contents of the memory location being programmed or the block being erased are no longer valid. -Figure11-2. Reset Timing Diagram +**Figure 11-2. Reset Timing Diagram** ![](images/b5cf335087123fa59f61f0fcb0621602888481ae28eab8e186d1d981f87357aa.jpg)
@@ -1447,30 +1558,62 @@ High-Z High-Z
-## 12FEATURE OPERATIONS +## 12 FEATURE OPERATIONS -## 12.1 Get Features (0FH) and Set Features (1FH) +### 12.1 Get Features (0FH) and Set Features (1FH) The GET FEATURES (0FH) and SET FEATURES (1FH) commands are used to monitor the device status and alter the device behavior. These commands use a 1-byte feature address to determine which feature is to be read or modified. Feature such as OTP can be enabled or disabled by setting specific feature bits (shown in the below table).The status registers (C0H/F0H) is mostly read, except WEL, which is a writable bit with the WRITE ENABLE (06H) command. When a feature (A0H/B0H/D0H) is set, it remains active until the device is power cycled or the feature is written to. Unless otherwise specified in the following table, once the device is set, it remains set, even if a RESET (FFH) command is issued. -Table 12-1. Features Settings +**Table 12-1. Features Settings** -
RegisterAddr.76543210
ProtectionA0HBRWDReservedBP2BP1BP0INVCMPReserved
FeatureB0HOTP_PRTOTP_ENReservedECC_ENReservedReservedReservedQE
StatusC0HReservedReservedECCS1ECCS0P_FAILE_FAILWELOIP
FeatureD0HReservedDS_IO[1]DS_IO[0]ReservedReservedReservedReservedReserved
StatusF0HReservedReservedECCSE1ECCSE0BPSReservedReservedCBSY
+| **Register** | **Addr.** | **7** | **6** | **5** | **4** | **3** | **2** | **1** | **0** | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Protection | A0H | BRWD | Reserved | BP2 | BP1 | BP0 | INV | CMP | Reserved | +| Feature | B0H | OTP_PRT | OTP_EN | Reserved | ECC_EN | Reserved | Reserved | Reserved | QE | +| Status | C0H | Reserved | Reserved | ECCS1 | ECCS0 | P_FAIL | E_FAIL | WEL | OIP | +| Feature | D0H | Reserved | DS_IO[1] | DS_IO[0] | Reserved | Reserved | Reserved | Reserved | Reserved | +| Status | F0H | Reserved | Reserved | ECCSE1 | ECCSE0 | BPS | Reserved | Reserved | CBSY | Note: 1. If BRWD is enabled and WP# is LOW, then the block lock register cannot be changed. 2. If QE is enabled, the quad IO operations can be executed. 3. All the reserved bits must be held low when the feature is set. 4. These registers A0H/B0H/D0H are write/read type, and Registers C0H/F0H are read only. -5. The OTP\_PRT is non-volatile, others bits are volatile. +5. The OTP_PRT is non-volatile, others bits are volatile. 6. The Register Bit default value after power-up refers to Table 12-2. Register Bit Descriptions. -Table 12-2. Register Bit Descriptions +**Table 12-2. Register Bit Descriptions** -
BitBit NameAfter Power up or Power on Reset(66H-99H)After Reset command (FFH)Description
BRWDBlock register write disable0No ChangeWhich is used combined with WP#, If BRWD is high enabled and WP# is LOW, then the Protection register cannot be changed
BP2Block1No ChangeUsed combination, refer to chapter Block Protection
BP11
BP0Protection1
INVbits0
CMP0
OTP_PRTOTP0No ChangeUsed combination, refer to chapter OTP Region
OTP_ENRegion0
bitsBefore OTP Set
ECC_ENECC1No ChangeThe device offers data corruption protection by offering optional internal ECC. READs and PROGRAMs with internal ECC can be enabled or disabled by setting feature bit ECC_EN. ECC is enabled by default when device powered on, so the default READ and PROGRAM commands operate with internal ECC in the “active” state when ECC enable.
Enable
Latch
QEThe Quad0No ChangeThis bit indicates that whether the quad IO operations can be executed. If QE is set to 1, the quad IO operations can be executed.
Enable bit
ECCS0ECC StatusPage 0 Status0ECCS provides ECC status as the following table. ECCS and ECCSE are set to 00b either following a RESET, or at the beginning of the READ. They are then updated after the device completes a valid READ operation. ECCS and ECCSE are invalid if internal ECC is disabled (via a SET FEATURES command to reset ECC_EN to 0). After power-on RESET, ECC status is set to reflect the contents of block 0, page 0.
ECCS10
ECCSE00
ECCSE10
P_FAILProgram Fail00This bit indicates that a program failure has occurred (P_FAIL =1). It will also be set if the user attempts to program a protected region, including the OTP area. This bit is cleared during the PROGRAM EXECUTE command sequence or a RESET command (P_FAIL = 0).
E_FAILErase Fail00This bit indicates that an erase failure has occurred (E_FAIL = 1). It will also be set if the user attempts to erase a locked region. This bit is cleared (E_FAIL = 0) at the start of the BLOCK ERASE command sequence or the RESET command.
WELWrite Enable Latch00This bit indicates the current status of the write enable latch (WEL) and must be set (WEL = 1), prior to issuing a PROGRAM EXECUTE or BLOCK ERASE command. It is set by issuing the WRITE ENABLE command. WEL can also be disabled (WEL = 0), by issuing the WRITE DISABLE command.
OIPOperation In Progress00This bit is set (OIP = 1) when a PROGRAM EXECUTE, PAGE READ, BLOCK ERASE, or RESET command is executing, indicating the device is busy. When the bit is 0, the interface is in the ready state.
DS_IO[0] DS_IO[1]Driven Strength register00No ChangeIO driver strength setting. Default is 00b.
BPSBlock Protection Status1No ChangeBlock protection statusBPS is 1, selected block is protectedBPS is 0, selected block is unprotected.
CBSYCache Busy status bit00CBSY is to indicate whether cache is busy, non-available for data read or data load. This bit is the status, which indicates if the cache is busy or ready, 1 is busy, 0 is ready.
+| **Bit** | **Bit Name** | **After Power up or Power on Reset(66H-99H)** | **After Reset command (FFH)** | **Description** | +| --- | --- | --- | --- | --- | +| BRWD | Block register write disable | 0 | No Change | Which is used combined with WP#, If BRWD is high enabled and WP# is LOW, then the Protection register cannot be changed | +| BP2 | Block | 1 | No Change | Used combination, refer to chapter Block Protection | +| BP1 | | 1 | | | +| BP0 | Protection | 1 | | | +| INV | bits | 0 | | | +| CMP | | 0 | | | +| OTP_PRT | OTP | 0 | No Change | Used combination, refer to chapter OTP Region | +| OTP_EN | Region | 0 | | | +| | bits | Before OTP Set | | | +| ECC_EN | ECC | 1 | No Change | The device offers data corruption protection by offering optional internal ECC. READs and PROGRAMs with internal ECC can be enabled or disabled by setting feature bit ECC_EN. ECC is enabled by default when device powered on, so the default READ and PROGRAM commands operate with internal ECC in the “active” state when ECC enable. | +| | Enable | | | | +| | Latch | | | | +| QE | The Quad | 0 | No Change | This bit indicates that whether the quad IO operations can be executed. If QE is set to 1, the quad IO operations can be executed. | +| | Enable bit | | | | +| ECCS0 | ECC Status | Page 0 Status | 0 | ECCS provides ECC status as the following table. ECCS and ECCSE are set to 00b either following a RESET, or at the beginning of the READ. They are then updated after the device completes a valid READ operation. ECCS and ECCSE are invalid if internal ECC is disabled (via a SET FEATURES command to reset ECC_EN to 0). After power-on RESET, ECC status is set to reflect the contents of block 0, page 0. | +| ECCS1 | | | 0 | | +| ECCSE0 | | | 0 | | +| ECCSE1 | | | 0 | | +| P_FAIL | Program Fail | 0 | 0 | This bit indicates that a program failure has occurred (P_FAIL =1). It will also be set if the user attempts to program a protected region, including the OTP area. This bit is cleared during the PROGRAM EXECUTE command sequence or a RESET command (P_FAIL = 0). | +| E_FAIL | Erase Fail | 0 | 0 | This bit indicates that an erase failure has occurred (E_FAIL = 1). It will also be set if the user attempts to erase a locked region. This bit is cleared (E_FAIL = 0) at the start of the BLOCK ERASE command sequence or the RESET command. | +| WEL | Write Enable Latch | 0 | 0 | This bit indicates the current status of the write enable latch (WEL) and must be set (WEL = 1), prior to issuing a PROGRAM EXECUTE or BLOCK ERASE command. It is set by issuing the WRITE ENABLE command. WEL can also be disabled (WEL = 0), by issuing the WRITE DISABLE command. | +| OIP | Operation In Progress | 0 | 0 | This bit is set (OIP = 1) when a PROGRAM EXECUTE, PAGE READ, BLOCK ERASE, or RESET command is executing, indicating the device is busy. When the bit is 0, the interface is in the ready state. | +| DS_IO[0] DS_IO[1] | Driven Strength register | 00 | No Change | IO driver strength setting. Default is 00b. | +| BPS | Block Protection Status | 1 | No Change | Block protection statusBPS is 1, selected block is protectedBPS is 0, selected block is unprotected. | +| CBSY | Cache Busy status bit | 0 | 0 | CBSY is to indicate whether cache is busy, non-available for data read or data load. This bit is the status, which indicates if the cache is busy or ready, 1 is busy, 0 is ready. | -Figure12-1. Get Features Timing Diagram +**Figure 12-1. Get Features Timing Diagram** ![](images/755b7da828ecf51f8b7bb32f3e782c4fe37dd23584f907ad1032a5bba08b0251.jpg)
@@ -1492,7 +1635,7 @@ MSB MSB
-Figure12-2. Set Features Timing Diagram +**Figure 12-2. Set Features Timing Diagram** ![](images/84a2b2bbda5fd48f94be290045d18e4ed46209ceb284bdc5da7ff6d48b8195a8.jpg)
@@ -1512,44 +1655,61 @@ High-Z SO
-## 12.2 Status Register and Driver Register +### 12.2 Status Register and Driver Register The NAND Flash device has the status registers (C0H/F0H) that software can read during the device operation for operation state query. The status register can be read by issuing the GET FEATURES (0FH) command, followed by the feature address C0H or F0H (see FEATURE OPERATION). The Output Driver Register can be set and read by issuing the SET FEATURE (0FH) and GET FEATURE command followed by the feature address D0H (see FEATURE OPERATION). -Table 12-3.ECC Error Bits Descriptions +**Table 12-3. ECC Error Bits Descriptions** -
ECCS1ECCS0ECCSE1ECCSE0Description
00xxNo bit errors were detected during the previous read algorithm
0100Bit errors(=1) were detected and corrected
0101Bit errors (=2) were detected and corrected.
0110Bit errors (=3) were detected and corrected.
0111Bit errors (=4) were detected and corrected.
11xxReserved
10xxBit errors greater than ECC capability(4 bits) and not corrected
+| **ECCS1** | **ECCS0** | **ECCSE1** | **ECCSE0** | **Description** | +| --- | --- | --- | --- | --- | +| 0 | 0 | x | x | No bit errors were detected during the previous read algorithm | +| 0 | 1 | 0 | 0 | Bit errors(=1) were detected and corrected | +| 0 | 1 | 0 | 1 | Bit errors (=2) were detected and corrected. | +| 0 | 1 | 1 | 0 | Bit errors (=3) were detected and corrected. | +| 0 | 1 | 1 | 1 | Bit errors (=4) were detected and corrected. | +| 1 | 1 | x | x | Reserved | +| 1 | 0 | x | x | Bit errors greater than ECC capability(4 bits) and not corrected | -12-4. Driver Register Bits Descriptions +**12-4. Driver Register Bits Descriptions** -
DS_IO[1]DS_IO[0]Driver Strength
00100%
0175%
1050%
1125%
+| **DS_IO[1]** | **DS_IO[0]** | **Driver Strength** | +| --- | --- | --- | +| 0 | 0 | 100% | +| 0 | 1 | 75% | +| 1 | 0 | 50% | +| 1 | 1 | 25% | -## 12.3 OTP Region +### 12.3 OTP Region -The serial device offers a protected, One-Time Programmable NAND Flash memory area. 4 full pages are available on the device. Customers can use the OTP area as they prefer, like programming serial numbers, or other data, for permanent storage. When delivered from factory, feature bit OTP\_PRT is 0. To access the OTP feature, the user must set feature bits OTP\_EN/OTP\_PRT by SET FEATURES command. When the OTP is ready for access, only pages 00h–03H can be programmed in sequential order by PROGRAM LOAD (02H) and PROGRAM EXECUTE (10H) commands (when not yet protected), and read out by PAGE READ (13H) command and output data by READ from CACHE(03H/0BH/3BH/6BH). When ECC is enabled, data written in the OTP area is ECC protected. +The serial device offers a protected, One-Time Programmable NAND Flash memory area. 4 full pages are available on the device. Customers can use the OTP area as they prefer, like programming serial numbers, or other data, for permanent storage. When delivered from factory, feature bit OTP_PRT is 0. To access the OTP feature, the user must set feature bits OTP_EN/OTP_PRT by SET FEATURES command. When the OTP is ready for access, only pages 00h–03H can be programmed in sequential order by PROGRAM LOAD (02H) and PROGRAM EXECUTE (10H) commands (when not yet protected), and read out by PAGE READ (13H) command and output data by READ from CACHE(03H/0BH/3BH/6BH). When ECC is enabled, data written in the OTP area is ECC protected. -Table 12-5.OTP States +**Table 12-5. OTP States** -
OTP_PRTOTP_ENState
x0Normal Operation
01Access OTP region, read and program data
111. When the device power on state OTP_PRT is 0, user can set feature bit OTP_PRT and OTP_EN to 1, then issue PROGRAM EXECUTE (10H) to lock OTP, and after that OTP_PRT will permanently remain 1.2. When the device power on state OTP_PRT is 1, user can only read the OTP region data
+| **OTP_PRT** | **OTP_EN** | **State** | +| --- | --- | --- | +| x | 0 | Normal Operation | +| 0 | 1 | Access OTP region, read and program data | +| 1 | 1 | 1. When the device power on state OTP_PRT is 0, user can set feature bit OTP_PRT and OTP_EN to 1, then issue PROGRAM EXECUTE (10H) to lock OTP, and after that OTP_PRT will permanently remain 1.2. When the device power on state OTP_PRT is 1, user can only read the OTP region data | Note: The OTP space cannot be erased and after it has been protected, it cannot be programmed again, please use this function carefully. Access to OTP data -• Issue the SET FEATURES command (1FH) -• Set feature bit OTP\_EN -• Issue the PAGE PROGRAM (only when OTP\_PRT is 0) or PAGE READ command +- Issue the SET FEATURES command (1FH) +- Set feature bit OTP_EN +- Issue the PAGE PROGRAM (only when OTP_PRT is 0) or PAGE READ command Protect OTP region -Only when the following steps are completed, the OTP\_PRT will be set and users can get this feature out with 0FH command. +Only when the following steps are completed, the OTP_PRT will be set and users can get this feature out with 0FH command. -• Issue the SET FEATURES command (1FH) -• Set feature bit OTP\_EN and OTP\_PRT -• 06H (WRITE ENABLE) -• Issue the PROGRAM EXECUTE (10H) command. +- Issue the SET FEATURES command (1FH) +- Set feature bit OTP_EN and OTP_PRT +- 06H (WRITE ENABLE) +- Issue the PROGRAM EXECUTE (10H) command. -## 12.4 Assistant Bad Block Management +### 12.4 Assistant Bad Block Management As a NAND Flash, the device may have blocks that are invalid when shipped from the factory, and a minimum number of valid blocks (NVB) of the total available blocks are specified. An invalid block is one that contains at least one page that has more bad bits than can be corrected by the minimum required ECC. Additional bad blocks may develop with use. However, the total number of available blocks will not fall below NVB during the endurance life of the product. @@ -1561,34 +1721,67 @@ System software should initially check the first spare area location for non-FFH To simplify the system requirement and guard the data integration, GigaDevice SPI NAND provides assistant Management options as below. -Table 12-6. Bad Block Mark information (2Gb) +**Table 12-6. Bad Block Mark information (2Gb)** -
DescriptionRequirement
Minimum number of valid blocks (NVB)2008
Total available blocks per die2048
First spare area locationByte 2048
Bad-block mark00h(use non FFH to check)
+| **Description** | **Requirement** | +| --- | --- | +| Minimum number of valid blocks (NVB) | 2008 | +| Total available blocks per die | 2048 | +| First spare area location | Byte 2048 | +| Bad-block mark | 00h(use non FFH to check) | -## 12.5 Block Protection +### 12.5 Block Protection -The block lock feature provides the ability to protect the entire device, or ranges of blocks, from the PROGRAM and ERASE operations. After power-up, the device is in the “locked” state, i.e., feature bits BP0, BP1and BP2 are set to 1, INV, CMP and BRWD are set to 0. To unlock all the blocks, or a range of blocks, the SET FEATURES command must be issued to alter the state of protection feature bits. When BRWD is set and WP# is LOW, none of the writable protection feature bits can be set. Also, when a PROGRAM/ERASE command is issued to a locked block, status bit OIP remains 0. When an ERASE command is issued to a locked block, the erase failure, status bit E\_FAIL set to 1. When a PROGRAM command is issued to a locked block, program failure, status bit P\_FAIL set to 1. +The block lock feature provides the ability to protect the entire device, or ranges of blocks, from the PROGRAM and ERASE operations. After power-up, the device is in the “locked” state, i.e., feature bits BP0, BP1and BP2 are set to 1, INV, CMP and BRWD are set to 0. To unlock all the blocks, or a range of blocks, the SET FEATURES command must be issued to alter the state of protection feature bits. When BRWD is set and WP# is LOW, none of the writable protection feature bits can be set. Also, when a PROGRAM/ERASE command is issued to a locked block, status bit OIP remains 0. When an ERASE command is issued to a locked block, the erase failure, status bit E_FAIL set to 1. When a PROGRAM command is issued to a locked block, program failure, status bit P_FAIL set to 1. To enable the Write Protection (WP#), the Quad Enable bit (QE) of feature (B0[0]) must be set to 0. -Table 12-7. Block Lock Register Block Protect Bits (2Gb) +**Table 12-7. Block Lock Register Block Protect Bits (2Gb)** -
CMPINVBP2BP1BP0Protect Row AddressProtect Rows
2Gb
xx000NONENone—all unlocked
000011F800h ~ 1FFFFhUpper 1/64 locked
000101F000h ~ 1FFFFhUpper 1/32 locked
000111E000h ~ 1FFFFhUpper 1/16 locked
001001C000h ~ 1FFFFhUpper 1/8 locked
0010118000h ~ 1FFFFhUpper 1/4 locked
0011010000h ~ 1FFFFhUpper 1/2 locked
xx1110000h ~ 1FFFFhAll locked (default)
010010000h ~7FFhLower 1/64 locked
010100000h ~FFFhLower 1/32 locked
010110000h ~ 1FFFhLower 1/16 locked
011000000h ~ 3FFFhLower 1/8 locked
011010000h ~ 7FFFhLower 1/4 locked
011100000h ~ FFFFhLower 1/2 locked
100010000h ~ 1F7FFhLower 63/64 locked
100100000h ~ 1FFFFhLower31/32 locked
100110000h ~ 1DFFFhLower 15/16 locked
101000000h ~ 1BFFFhLower7/8 locked
101010000h ~ 17FFFhLower3/4 locked
101100000h ~ 003FhBlock0
110010800h ~ 1FFFFhUpper 63/64 locked
110101000h ~ 1FFFFhUpper31/32 locked
110112000h ~ 1FFFFhUpper 15/16 locked
111004000h ~ 1FFFFhUpper7/8 locked
111018000h ~ 1FFFFhUpper3/4 locked
111100000h ~ 003FhBlock0
+| **CMP** | **INV** | **BP2** | **BP1** | **BP0** | **Protect Row Address** | **Protect Rows** | +| --- | --- | --- | --- | --- | --- | --- | +| | | | | | 2Gb | | +| x | x | 0 | 0 | 0 | NONE | None—all unlocked | +| 0 | 0 | 0 | 0 | 1 | 1F800h ~ 1FFFFh | Upper 1/64 locked | +| 0 | 0 | 0 | 1 | 0 | 1F000h ~ 1FFFFh | Upper 1/32 locked | +| 0 | 0 | 0 | 1 | 1 | 1E000h ~ 1FFFFh | Upper 1/16 locked | +| 0 | 0 | 1 | 0 | 0 | 1C000h ~ 1FFFFh | Upper 1/8 locked | +| 0 | 0 | 1 | 0 | 1 | 18000h ~ 1FFFFh | Upper 1/4 locked | +| 0 | 0 | 1 | 1 | 0 | 10000h ~ 1FFFFh | Upper 1/2 locked | +| x | x | 1 | 1 | 1 | 0000h ~ 1FFFFh | All locked (default) | +| 0 | 1 | 0 | 0 | 1 | 0000h ~7FFh | Lower 1/64 locked | +| 0 | 1 | 0 | 1 | 0 | 0000h ~FFFh | Lower 1/32 locked | +| 0 | 1 | 0 | 1 | 1 | 0000h ~ 1FFFh | Lower 1/16 locked | +| 0 | 1 | 1 | 0 | 0 | 0000h ~ 3FFFh | Lower 1/8 locked | +| 0 | 1 | 1 | 0 | 1 | 0000h ~ 7FFFh | Lower 1/4 locked | +| 0 | 1 | 1 | 1 | 0 | 0000h ~ FFFFh | Lower 1/2 locked | +| 1 | 0 | 0 | 0 | 1 | 0000h ~ 1F7FFh | Lower 63/64 locked | +| 1 | 0 | 0 | 1 | 0 | 0000h ~ 1FFFFh | Lower31/32 locked | +| 1 | 0 | 0 | 1 | 1 | 0000h ~ 1DFFFh | Lower 15/16 locked | +| 1 | 0 | 1 | 0 | 0 | 0000h ~ 1BFFFh | Lower7/8 locked | +| 1 | 0 | 1 | 0 | 1 | 0000h ~ 17FFFh | Lower3/4 locked | +| 1 | 0 | 1 | 1 | 0 | 0000h ~ 003Fh | Block0 | +| 1 | 1 | 0 | 0 | 1 | 0800h ~ 1FFFFh | Upper 63/64 locked | +| 1 | 1 | 0 | 1 | 0 | 1000h ~ 1FFFFh | Upper31/32 locked | +| 1 | 1 | 0 | 1 | 1 | 2000h ~ 1FFFFh | Upper 15/16 locked | +| 1 | 1 | 1 | 0 | 0 | 4000h ~ 1FFFFh | Upper7/8 locked | +| 1 | 1 | 1 | 0 | 1 | 8000h ~ 1FFFFh | Upper3/4 locked | +| 1 | 1 | 1 | 1 | 0 | 0000h ~ 003Fh | Block0 | When WP# is not LOW, user can issue bellows commands to alter the protection states as want. -• Issue SET FEATURES register write (1FH) -• Issue the feature bit address (A0h) and the feature bits combination as the table +- Issue SET FEATURES register write (1FH) +- Issue the feature bit address (A0h) and the feature bits combination as the table -## 12.6 Internal ECC +### 12.6 Internal ECC -The device offers data corruption protection by offering optional internal ECC. READs and PROGRAMs with internal ECC can be enabled or disabled by setting feature bit ECC\_EN. ECC is enabled by default when device powered on, so the default READ and PROGRAM commands operate with internal ECC in the “active” state when ECC enable. +The device offers data corruption protection by offering optional internal ECC. READs and PROGRAMs with internal ECC can be enabled or disabled by setting feature bit ECC_EN. ECC is enabled by default when device powered on, so the default READ and PROGRAM commands operate with internal ECC in the “active” state when ECC enable. To enable/disable ECC, perform the following command sequence: -• Issue the SET FEATURES command (1FH) to set the feature bit ECC\_EN: +- Issue the SET FEATURES command (1FH) to set the feature bit ECC_EN: -1. To enable ECC, Set ECC\_EN to 1. -2. To disable ECC, Clear ECC\_EN to 0. +1. To enable ECC, Set ECC_EN to 1. +2. To disable ECC, Clear ECC_EN to 0. During a PROGRAM operation, the device calculates an ECC code on the 2k page in the cache register, before the page is written to the NAND Flash array. @@ -1596,27 +1789,47 @@ During a READ operation, the page data is read from the array to the cache regis The ECC protection format as follow: -• User meta data I is not protected by internal ECC and User meta data II is protected by internal ECC. +- User meta data I is not protected by internal ECC and User meta data II is protected by internal ECC. Any data wrote to the ECC parity data area are ignored when ECC enabled. -Table 12-8. The Distribution of ECC Segment and Spare Area in a Page +**Table 12-8. The Distribution of ECC Segment and Spare Area in a Page** -
Main Area(2KB)Spare Area(128B)
User dataUser meta data(I+II)ECC Parity Data
Main0Main1Main2Main3Spare0Spare1Spare2Spare3Spare0Spare1Spare2Spare3
(512B)(512B)(512B)(512B)(4B+12B)(4B+12B)(4B+12B)(4B+12B)(16B)(16B)(16B)(16B)
+| **Main Area(2KB)** | | | | **Spare Area(128B)** | | | | | | | | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| User data | | | | User meta data(I+II) | | | | ECC Parity Data | | | | +| Main0 | Main1 | Main2 | Main3 | Spare0 | Spare1 | Spare2 | Spare3 | Spare0 | Spare1 | Spare2 | Spare3 | +| (512B) | (512B) | (512B) | (512B) | (4B+12B) | (4B+12B) | (4B+12B) | (4B+12B) | (16B) | (16B) | (16B) | (16B) | -Table 12-9. ECC Protection and Spare Area +**Table 12-9. ECC Protection and Spare Area** -
Min Byte AddressMax Byte AddressECC ProtectedAreaDescription
000H1FFHYesMain 0User data 0
200H3FFHYesMain 1User data 1
400H5FFHYesMain 2User data 2
600H7FFHYesMain 3User data 3
800H803HNoSpare 0User meta 0 data I
804H80FHYesSpare 0User meta 0 data II
810H813HNoSpare 1User meta 1 data I
814H81FHYesSpare 1User meta 1 data II
820H823HNoSpare 2User meta 2 data I
824H82FHYesSpare 2User meta 2 data II
830H833HNoSpare 3User meta 3 data I
834H83FHYesSpare 3User meta 3 data II
840H84FHYesSpare 0ECC Parity Data
850H85FHYesSpare 1ECC Parity Data
860H86FHYesSpare 2ECC Parity Data
870H87FHYesSpare 3ECC Parity Data
+| **Min Byte Address** | **Max Byte Address** | **ECC Protected** | **Area** | **Description** | +| --- | --- | --- | --- | --- | +| 000H | 1FFH | Yes | Main 0 | User data 0 | +| 200H | 3FFH | Yes | Main 1 | User data 1 | +| 400H | 5FFH | Yes | Main 2 | User data 2 | +| 600H | 7FFH | Yes | Main 3 | User data 3 | +| 800H | 803H | No | Spare 0 | User meta 0 data I | +| 804H | 80FH | Yes | Spare 0 | User meta 0 data II | +| 810H | 813H | No | Spare 1 | User meta 1 data I | +| 814H | 81FH | Yes | Spare 1 | User meta 1 data II | +| 820H | 823H | No | Spare 2 | User meta 2 data I | +| 824H | 82FH | Yes | Spare 2 | User meta 2 data II | +| 830H | 833H | No | Spare 3 | User meta 3 data I | +| 834H | 83FH | Yes | Spare 3 | User meta 3 data II | +| 840H | 84FH | Yes | Spare 0 | ECC Parity Data | +| 850H | 85FH | Yes | Spare 1 | ECC Parity Data | +| 860H | 86FH | Yes | Spare 2 | ECC Parity Data | +| 870H | 87FH | Yes | Spare 3 | ECC Parity Data | -## Note +**Note** 1. 800H is reserved for initial bad block mark. -2. When Internal ECC is enabled, user cannot program the Address 840H\~87FH, but user can read the Address 840H\~87FH. -When Internal ECC is disabled, the whole page area is open for user. And we recommend the user to provide external ECC protection. +2. When Internal ECC is enabled, user cannot program the Address 840H~87FH, but user can read the Address 840H~87FH. When Internal ECC is disabled, the whole page area is open for user. And we recommend the user to provide external ECC protection. -## 13POWER ON TIMING +## 13 POWER ON TIMING -Figure13-1. Power on Timing Sequence +**Figure 13-1. Power on Timing Sequence** ![](images/fc2ca4121a17b855a4c8a71f284d70319d2bc8b9fca9d2b20f302b19c512cd19.jpg)
@@ -1631,17 +1844,27 @@ Figure13-1. Power on Timing Sequence | Device is fully accessible | Accessible device status |
-Table 13-1. Power-On Timing and Write Inhibit Threshold for 1.8V/3.3V +**Table 13-1. Power-On Timing and Write Inhibit Threshold for 1.8V/3.3V** -
SymbolParameterMinMaxUnit
tVSLVCC(min) To CS# Low1ms
VWIWrite Inhibit Voltage1.8V1.4V
3.3V2.5
+| **Symbol** | **Parameter** | | **Min** | **Max** | **Unit** | +| --- | --- | --- | --- | --- | --- | +| tVSL | VCC(min) To CS# Low | | 1 | | ms | +| VWI | Write Inhibit Voltage | 1.8V | | 1.4 | V | +| | | 3.3V | | 2.5 | | -## 14ABSOLUTE MAXIMUM RATINGS +## 14 ABSOLUTE MAXIMUM RATINGS -Table 14-1.Absolute Maximum Ratings +**Table 14-1. Absolute Maximum Ratings** -
ParameterValueUnit
Ambient Operating Temperature-40 to 105°C
Storage Temperature-65 to 150°C
Applied Input / Output Voltage-0.6 to VCC+0.4V
VCC(3.3V)-0.6 to 4.0V
VCC(1.8V)-0.6 to 2.5V
+| **Parameter** | **Value** | **Unit** | +| --- | --- | --- | +| Ambient Operating Temperature | -40 to 105 | °C | +| Storage Temperature | -65 to 150 | °C | +| Applied Input / Output Voltage | -0.6 to VCC+0.4 | V | +| VCC(3.3V) | -0.6 to 4.0 | V | +| VCC(1.8V) | -0.6 to 2.5 | V | -Figure14-1. Input Test Waveform and Measurement Level +**Figure 14-1. Input Test Waveform and Measurement Level** Maximum Negative Overshoot Waveform ![](images/5ab9164ef39545ab8995032a9593308a8e83278273c448fa9d38eccfff56b99c.jpg) @@ -1668,11 +1891,19 @@ Vcc + 2.0V 20ns
-## 15CAPACITANCE MEASUREMENT CONDITIONS +## 15 CAPACITANCE MEASUREMENT CONDITIONS -
SymbolParameterMinTypMaxUnitConditions
CINInput Capacitance6pFVIN=0V
COUTOutput Capacitance8pFVOUT=0V
CLLoad Capacitance30pF
Input Rise And Fall time5ns
Input Pulse Voltage0.1VCC to 0.8VCCV
Input Timing Reference Voltage0.2VCC to 0.7VCCV
Output Timing Reference Voltage0.5VCCV
+| **Symbol** | **Parameter** | **Min** | **Typ** | **Max** | **Unit** | **Conditions** | +| --- | --- | --- | --- | --- | --- | --- | +| CIN | Input Capacitance | | | 6 | pF | VIN=0V | +| COUT | Output Capacitance | | | 8 | pF | VOUT=0V | +| CL | Load Capacitance | 30 | | | pF | | +| | Input Rise And Fall time | | | 5 | ns | | +| | Input Pulse Voltage | 0.1VCC to 0.8VCC | | | V | | +| | Input Timing Reference Voltage | 0.2VCC to 0.7VCC | | | V | | +| | Output Timing Reference Voltage | 0.5VCC | | | V | | -Figure15-1. Input Test Waveform and Measurement Level +**Figure 15-1. Input Test Waveform and Measurement Level** ![](images/e9b06380ede32fb2048221570af522de84505438d4d429602d78b8d5248f9ea0.jpg)
@@ -1689,25 +1920,73 @@ Output timing reference level Note: Input pulse rise and fall time are<5ns -## 16DC CHARACTERISTIC +## 16 DC CHARACTERISTIC -(T= -40℃\~85℃/-40℃\~105℃, VCC=2.7\~3.6V) +(T= -40℃~85℃/-40℃~105℃, VCC=2.7~3.6V) -
SymbolParameterTest ConditionMin.Typ.Max.Unit.
$I_{LI}$ Input Leakage Current±2μA
$I_{LO}$ Output Leakage Current±2μA
$I_{CC1}$ Standby CurrentCS#=VCC, $V_{IN}$ =VCC or VSS50μA
$I_{CC2}$ Operating Current (Read)CLK=0.1VCC / 0.9VCCat 104MHz,Q=Open(*1,*2,*4 I/O)30mA
$I_{CC3}$ Operating Current (Program)30mA
$I_{CC4}$ Operating Current (Erase)30mA
$V_{IL}$ Input Low Voltage-0.50.2VCCV
$V_{IH}$ Input High Voltage0.8VCCVCC+0.4V
$V_{OL}$ Output Low Voltage $I_{OL}$ =1.6mA0.4V
$V_{OH}$ Output High Voltage $I_{OH}$ =-100μAVCC-0.2V
+| **Symbol** | **Parameter** | **Test Condition** | **Min.** | **Typ.** | **Max.** | **Unit.** | +| --- | --- | --- | --- | --- | --- | --- | +| $I_{LI}$ | Input Leakage Current | | | | ±2 | μA | +| $I_{LO}$ | Output Leakage Current | | | | ±2 | μA | +| $I_{CC1}$ | Standby Current | CS#=VCC, $V_{IN}$ =VCC or VSS | | | 50 | μA | +| $I_{CC2}$ | Operating Current (Read) | CLK=0.1VCC / 0.9VCCat 104MHz,Q=Open(*1,*2,*4 I/O) | | | 30 | mA | +| $I_{CC3}$ | Operating Current (Program) | | | | 30 | mA | +| $I_{CC4}$ | Operating Current (Erase) | | | | 30 | mA | +| $V_{IL}$ | Input Low Voltage | | -0.5 | | 0.2VCC | V | +| $V_{IH}$ | Input High Voltage | | 0.8VCC | | VCC+0.4 | V | +| $V_{OL}$ | Output Low Voltage | $I_{OL}$ =1.6mA | | | 0.4 | V | +| $V_{OH}$ | Output High Voltage | $I_{OH}$ =-100μA | VCC-0.2 | | | V | -(T= -40℃\~85℃/-40℃\~105℃, VCC=1.7\~2.0V) +(T= -40℃~85℃/-40℃~105℃, VCC=1.7~2.0V) -
SymbolParameterTest ConditionMin.Typ.Max.Unit.
$I_{LI}$ Input Leakage Current±2μA
$I_{LO}$ Output Leakage Current±2μA
$I_{CC1}$ Standby CurrentCS#=VCC, $V_{IN}$ =VCC or VSS50μA
$I_{CC2}$ Operating Current (Read)CLK=0.1VCC / 0.9VCCat 80MHz,Q=Open(*1,*2,*4 I/O)30mA
$I_{CC3}$ Operating Current (Program)30mA
$I_{CC4}$ Operating Current (Erase)30mA
$V_{IL}$ Input Low Voltage-0.50.2VCCV
$V_{IH}$ Input High Voltage0.8VCCVCC+0.4V
$V_{OL}$ Output Low Voltage $I_{OL}$ =1.6mA0.4V
$V_{OH}$ Output High Voltage $I_{OH}$ =-100μAVCC-0.2V
+| **Symbol** | **Parameter** | **Test Condition** | **Min.** | **Typ.** | **Max.** | **Unit.** | +| --- | --- | --- | --- | --- | --- | --- | +| $I_{LI}$ | Input Leakage Current | | | | ±2 | μA | +| $I_{LO}$ | Output Leakage Current | | | | ±2 | μA | +| $I_{CC1}$ | Standby Current | CS#=VCC, $V_{IN}$ =VCC or VSS | | | 50 | μA | +| $I_{CC2}$ | Operating Current (Read) | CLK=0.1VCC / 0.9VCCat 80MHz,Q=Open(*1,*2,*4 I/O) | | | 30 | mA | +| $I_{CC3}$ | Operating Current (Program) | | | | 30 | mA | +| $I_{CC4}$ | Operating Current (Erase) | | | | 30 | mA | +| $V_{IL}$ | Input Low Voltage | | -0.5 | | 0.2VCC | V | +| $V_{IH}$ | Input High Voltage | | 0.8VCC | | VCC+0.4 | V | +| $V_{OL}$ | Output Low Voltage | $I_{OL}$ =1.6mA | | | 0.4 | V | +| $V_{OH}$ | Output High Voltage | $I_{OH}$ =-100μA | VCC-0.2 | | | V | Note: Value guaranteed by design and/or characterization, not 100% tested in production -## 17AC CHARACTERISTICS +## 17 AC CHARACTERISTICS -(T= -40℃\~85℃/-40℃\~105℃, VCC=1.7\~2.0V/2.7\~3.6V, CL=30pF) +(T= -40℃~85℃/-40℃~105℃, VCC=1.7~2.0V/2.7~3.6V, CL=30pF) -
SymbolParameter1.8V3.3VUnit.
Min.Max.Min.Max.
FC1Serial Clock Frequency80104MHz
FC2*Serial Clock Frequency for DTR4545MHZ
tCHSerial Clock High Time44ns
tCLSerial Clock Low Time44ns
tCLCHSerial Clock Rise Time (Slew Rate)0.20.2V/ns
tCHCLSerial Clock Fall Time (Slew Rate)0.20.2V/ns
tCHSHCS# Active Hold Time55ns
tSHCHCS# Not Active Setup Time55ns
tSLCHCS# Active Setup Time75ns
tCHSLCS# Not Active Hold Time55ns
tSHSL/tCSCS# High Time2020ns
tSHQZOutput Disable Time2020ns
tCLQXOutput Hold Time22ns
tDVCHData In Setup Time22ns
tCHDXData In Hold Time22ns
tHLCHHold# Low Setup Time (relative to Clock)55ns
tHHCHHold# High Setup Time (relative to Clock)55ns
tCHHLHold# High Hold Time (relative to Clock)55ns
tCHHHHold# Low Hold Time (relative to Clock)55ns
tHLQZHold# Low To High-Z Output1515ns
tHHQXHold# High To Low-Z Output1515ns
tCLQVClock Low To Output Valid119ns
tWHSLWP# Setup Time Before CS# Low2020ns
tSHWLWP# Hold Time After CS# High100100ns
+| **Symbol** | **Parameter** | **1.8V** | | **3.3V** | | **Unit.** | +| --- | --- | --- | --- | --- | --- | --- | +| | | Min. | Max. | Min. | Max. | | +| FC1 | Serial Clock Frequency | | 80 | | 104 | MHz | +| FC2* | Serial Clock Frequency for DTR | | 45 | | 45 | MHZ | +| tCH | Serial Clock High Time | 4 | | 4 | | ns | +| tCL | Serial Clock Low Time | 4 | | 4 | | ns | +| tCLCH | Serial Clock Rise Time (Slew Rate) | 0.2 | | 0.2 | | V/ns | +| tCHCL | Serial Clock Fall Time (Slew Rate) | 0.2 | | 0.2 | | V/ns | +| tCHSH | CS# Active Hold Time | 5 | | 5 | | ns | +| tSHCH | CS# Not Active Setup Time | 5 | | 5 | | ns | +| tSLCH | CS# Active Setup Time | 7 | | 5 | | ns | +| tCHSL | CS# Not Active Hold Time | 5 | | 5 | | ns | +| tSHSL/tCS | CS# High Time | 20 | | 20 | | ns | +| tSHQZ | Output Disable Time | | 20 | | 20 | ns | +| tCLQX | Output Hold Time | 2 | | 2 | | ns | +| tDVCH | Data In Setup Time | 2 | | 2 | | ns | +| tCHDX | Data In Hold Time | 2 | | 2 | | ns | +| tHLCH | Hold# Low Setup Time (relative to Clock) | 5 | | 5 | | ns | +| tHHCH | Hold# High Setup Time (relative to Clock) | 5 | | 5 | | ns | +| tCHHL | Hold# High Hold Time (relative to Clock) | 5 | | 5 | | ns | +| tCHHH | Hold# Low Hold Time (relative to Clock) | 5 | | 5 | | ns | +| tHLQZ | Hold# Low To High-Z Output | | 15 | | 15 | ns | +| tHHQX | Hold# High To Low-Z Output | | 15 | | 15 | ns | +| tCLQV | Clock Low To Output Valid | | 11 | | 9 | ns | +| tWHSL | WP# Setup Time Before CS# Low | 20 | | 20 | | ns | +| tSHWL | WP# Hold Time After CS# High | 100 | | 100 | | ns | -## Note: +**Note:** 1. Value guaranteed by design and/or characterization, not 100% tested in production 2. Please contact GigaDevice when there is a need to use the EEh command for DTR. @@ -1718,11 +1997,22 @@ However, in general, most of PCB designs have output loading much less than 30pf GigaDevice recommend customers measure the tCLQV and then set the clock rate to match the SPI host data sampling data setup time and hold time. -## 18PERFORMANCE AND TIMING +## 18 PERFORMANCE AND TIMING -
SymbolParameterMin.Typ.Max.Unit.
tRSTCS# High To Next Command After Reset(FFh)500us
tRDRead From Array25us
tRD_ECCRead From Array with ECC4560us
tPROGPage Programming Time300600us
tPROG_ECCPage Programming Time with ECC400600us
tBERSBlock Erase Time35ms
tCBSYWCache busy time for Cache Program5tPROGus
tCBSYW_ECCCache busy time for Cache Program with ECC30tPROG_ECCus
tCBSYRCache busy time for Cache Read5tRDus
tCBSYR_ECCCache busy time for Cache Read with ECC30tRD_ECCus
+| **Symbol** | **Parameter** | **Min.** | **Typ.** | **Max.** | **Unit.** | +| --- | --- | --- | --- | --- | --- | +| tRST | CS# High To Next Command After Reset(FFh) | | | 500 | us | +| tRD | Read From Array | | | 25 | us | +| tRD_ECC | Read From Array with ECC | | 45 | 60 | us | +| tPROG | Page Programming Time | | 300 | 600 | us | +| tPROG_ECC | Page Programming Time with ECC | | 400 | 600 | us | +| tBERS | Block Erase Time | | 3 | 5 | ms | +| tCBSYW | Cache busy time for Cache Program | | 5 | tPROG | us | +| tCBSYW_ECC | Cache busy time for Cache Program with ECC | | 30 | tPROG_ECC | us | +| tCBSYR | Cache busy time for Cache Read | | 5 | tRD | us | +| tCBSYR_ECC | Cache busy time for Cache Read with ECC | | 30 | tRD_ECC | us | -Figure18-1. Serial Input Timing +**Figure 18-1. Serial Input Timing** ![](images/881b2dfbbbb8230fb7af423e755404286621abc6443eff5afc5c06cc0c664d47.jpg)
@@ -1745,7 +2035,7 @@ tSHCL tSHSL
-Figure18-2. Output Timing +**Figure 18-2. Output Timing** ![](images/862fdcc50b0a0a0903dfcef387b75249359e0361a7d63a958d4a67596c973807.jpg)
@@ -1766,7 +2056,7 @@ SI Least significant address bit (LIB) in
-Figure18-3. Hold Timing +**Figure 18-3. Hold Timing** ![](images/6e6e3f492d6a3bcaf451ad36a791b73d3bd48ab15ff9722b61aa57f64c01c4d3.jpg)
@@ -1786,14 +2076,14 @@ HOLD# Sl do not care during HOLD operation. -## 19ORDERING INFORMATION +## 19 ORDERING INFORMATION ![](images/35356c13b1070de72632d77901f1c5c94ab8bdcb460e745577a05a11ca95ea6b.jpg) Note: (1) Industrial+: F grade has implemented additional test flows to ensure higher product quality than I grade. ## 20 PACKAGE INFORMATION -Figure 20-1.WSON8 (8\*6mm) +**Figure 20-1. WSON8 (8*6mm)** ![](images/d72dc2e6eb040b370a50a8b5b5b57cf54cc5974bd404967d05fd4adf0cfe3562.jpg) Bottom View @@ -1811,28 +2101,60 @@ Side View Dimensions -
SymbolAA1cbDD2EE2eL
Unit
mmMin0.700.000.1800.357.903.305.904.201.270.45
Nom0.750.020.2030.408.003.406.004.300.50
Max0.800.050.2500.458.103.506.104.400.55
InchMin0.02800.0070.0140.3110.1300.2320.1650.050.018
Nom0.0300.0010.0080.0160.3150.1340.2360.1690.020
Max0.0320.0020.0100.0180.3190.1380.2400.1730.022
+| **Symbol** | | **A** | **A1** | **c** | **b** | **D** | **D2** | **E** | **E2** | **e** | **L** | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Unit | | | | | | | | | | | | +| mm | Min | 0.70 | 0.00 | 0.180 | 0.35 | 7.90 | 3.30 | 5.90 | 4.20 | 1.27 | 0.45 | +| | Nom | 0.75 | 0.02 | 0.203 | 0.40 | 8.00 | 3.40 | 6.00 | 4.30 | | 0.50 | +| | Max | 0.80 | 0.05 | 0.250 | 0.45 | 8.10 | 3.50 | 6.10 | 4.40 | | 0.55 | +| Inch | Min | 0.028 | 0 | 0.007 | 0.014 | 0.311 | 0.130 | 0.232 | 0.165 | 0.05 | 0.018 | +| | Nom | 0.030 | 0.001 | 0.008 | 0.016 | 0.315 | 0.134 | 0.236 | 0.169 | | 0.020 | +| | Max | 0.032 | 0.002 | 0.010 | 0.018 | 0.319 | 0.138 | 0.240 | 0.173 | | 0.022 | -Figure 20-2.TFBGA-24BALL (5\*5-1 ball array) +**Figure 20-2. TFBGA-24BALL (5*5-1 ball array)** ![](images/309918a65f99632a28c75a9aeae076c813083d9dfa76271cea0dd3611eea32b9.jpg) Dimensions -
SymbolAA1A2bDD1EE1eSESD
Unit
mmMin0.250.750.355.904.00 BSC7.904.00 BSC1.00 BSC1.00 TYP1.00 TYP
Nom0.300.800.406.008.00
Max1.200.350.850.456.108.10
InchMin0.0100.0300.0140.2320.157 BSC0.3110.157 BSC0.039 BSC0.039 TYP0.039 TYP
Nom0.0120.0310.0160.2360.315
Max0.0470.0140.0330.0180.2400.319
+| **Symbol** | | **A** | **A1** | **A2** | **b** | **D** | **D1** | **E** | **E1** | **e** | **SE** | **SD** | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Unit | | | | | | | | | | | | | +| mm | Min | | 0.25 | 0.75 | 0.35 | 5.90 | 4.00 BSC | 7.90 | 4.00 BSC | 1.00 BSC | 1.00 TYP | 1.00 TYP | +| | Nom | | 0.30 | 0.80 | 0.40 | 6.00 | | 8.00 | | | | | +| | Max | 1.20 | 0.35 | 0.85 | 0.45 | 6.10 | | 8.10 | | | | | +| Inch | Min | | 0.010 | 0.030 | 0.014 | 0.232 | 0.157 BSC | 0.311 | 0.157 BSC | 0.039 BSC | 0.039 TYP | 0.039 TYP | +| | Nom | | 0.012 | 0.031 | 0.016 | 0.236 | | 0.315 | | | | | +| | Max | 0.047 | 0.014 | 0.033 | 0.018 | 0.240 | | 0.319 | | | | | Note:Both package length and width do not include mold flash. -Figure20-3. TFBGA-24BALL (4\*6 ball array) +**Figure 20-3. TFBGA-24BALL (4*6 ball array)** ![](images/08d3fc107d74ce1b70a342f0d55cddefae442ab529e89cfab2eef1f18c12f9d6.jpg) Dimensions -
SymbolAA1A2bDD1EE1eSESD
Unit
mmMin0.250.700.355.903.00 BSC7.905.00 BSC1.00 BSC0.50 TYP0.50 TYP
Nom0.300.800.406.008.00
Max1.200.350.850.456.108.10
InchMin0.0100.0280.0140.2320.118 BSC0.3110.197 BSC0.039 BSC0.020 TYP0.020 TYP
Nom0.0120.0310.0160.2360.315
Max0.0470.0140.0340.0180.2400.319
+| **Symbol** | | **A** | **A1** | **A2** | **b** | **D** | **D1** | **E** | **E1** | **e** | **SE** | **SD** | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Unit | | | | | | | | | | | | | +| mm | Min | | 0.25 | 0.70 | 0.35 | 5.90 | 3.00 BSC | 7.90 | 5.00 BSC | 1.00 BSC | 0.50 TYP | 0.50 TYP | +| | Nom | | 0.30 | 0.80 | 0.40 | 6.00 | | 8.00 | | | | | +| | Max | 1.20 | 0.35 | 0.85 | 0.45 | 6.10 | | 8.10 | | | | | +| Inch | Min | | 0.010 | 0.028 | 0.014 | 0.232 | 0.118 BSC | 0.311 | 0.197 BSC | 0.039 BSC | 0.020 TYP | 0.020 TYP | +| | Nom | | 0.012 | 0.031 | 0.016 | 0.236 | | 0.315 | | | | | +| | Max | 0.047 | 0.014 | 0.034 | 0.018 | 0.240 | | 0.319 | | | | | Note: Both the package length and width do not include the mold flash. -21 REVISION HISTORY +## 21 REVISION HISTORY -
Version NoDescriptionPage NumberDate
1.0Initial Release2019-08-01
1.1Modify the 03H command description in Figure 8-6.Page Read to Cache Timing Diagram.Add Note in Figure 8-6 Page Read to Cache Timing Diagram about Read From Cache command.Modify the “P_FAIL” “E_FAIL” default value after power up from ‘Page 0’ to ‘0’.2525512019-08-13
1.2Add Note of EEH DTR command in 8.9 & 17 AC Characteristics..Modify the descriptions of Industrial+ F grade in Ordering information.Modify the descriptions of FFH command.Add the Register bit value after reset command (FFH).Move Table 12.2 Register bit description to 12.1Modify the Figure 3-1 Array Organization.29/617/6416/4649/5049102019-08-27
1.3Add tCBSYR_ECC and tCBSYW_ECC in 18 Performance and Timing.622019-09-11
1.4Update the tSLCH Value from 5ns to 7ns for 1.8V.Update the tVSL Value from 5ms to 1ms.Update the VWI Value from 1.7V to 1.4V.6157572019-11-15
1.5Update the tRD_ECC Max Value from 50us to 60usAdd Industrial 105° productModify the Storage Temperature to -65°C~150°CUpdate the 8.12 Parameter Page Table Byte137/138 and recalculate the CRC Byte 254/2556275835/362020-03-09
1.6Add the description of the OTP Area ECC protected.Modify the description of the initial Bad Block Mark with internal ECC on.Remove the description of the 84h/C4h/34h must be use in internal data move.Modify the error description “Program an invalid address will cause P_Fail”525616/43/44512021-11-13
+| **Version No** | **Description** | **Page Number** | **Date** | +| --- | --- | --- | --- | +| 1.0 | Initial Release | | 2019-08-01 | +| 1.1 | Modify the 03H command description in Figure 8-6.Page Read to Cache Timing Diagram.Add Note in Figure 8-6 Page Read to Cache Timing Diagram about Read From Cache command.Modify the “P_FAIL” “E_FAIL” default value after power up from ‘Page 0’ to ‘0’. | 252551 | 2019-08-13 | +| 1.2 | Add Note of EEH DTR command in 8.9 & 17 AC Characteristics..Modify the descriptions of Industrial+ F grade in Ordering information.Modify the descriptions of FFH command.Add the Register bit value after reset command (FFH).Move Table 12.2 Register bit description to 12.1Modify the Figure 3-1 Array Organization. | 29/617/6416/4649/504910 | 2019-08-27 | +| 1.3 | Add tCBSYR_ECC and tCBSYW_ECC in 18 Performance and Timing. | 62 | 2019-09-11 | +| 1.4 | Update the tSLCH Value from 5ns to 7ns for 1.8V.Update the tVSL Value from 5ms to 1ms.Update the VWI Value from 1.7V to 1.4V. | 615757 | 2019-11-15 | +| 1.5 | Update the tRD_ECC Max Value from 50us to 60usAdd Industrial 105° productModify the Storage Temperature to -65°C~150°CUpdate the 8.12 Parameter Page Table Byte137/138 and recalculate the CRC Byte 254/255 | 6275835/36 | 2020-03-09 | +| 1.6 | Add the description of the OTP Area ECC protected.Modify the description of the initial Bad Block Mark with internal ECC on.Remove the description of the 84h/C4h/34h must be use in internal data move.Modify the error description “Program an invalid address will cause P_Fail” | 525616/43/4451 | 2021-11-13 | ## Important Notice diff --git a/docs/芯片手册/RTL8305NBI-CG.md b/docs/芯片手册/RTL8305NBI-CG.md index 201860ad..ec5af0e7 100644 --- a/docs/芯片手册/RTL8305NBI-CG.md +++ b/docs/芯片手册/RTL8305NBI-CG.md @@ -1,12 +1,12 @@ -## NOT FOR PUBLIC RELEASE +NOT FOR PUBLIC RELEASE -## RTL8305NBI-CG +# RTL8305NBI-CG -## DRAFT +DRAFT -# SINGLE-CHIP 5-PORT 10/100M ETHERNET SWITCH CONTROLLER +SINGLE-CHIP 5-PORT 10/100M ETHERNET SWITCH CONTROLLER -## DATASHEET +DATASHEET (CONFIDENTIAL: Development Partners Only) @@ -34,214 +34,215 @@ This document is intended for the software engineer's reference and provides det Though every effort has been made to ensure that this document is current and accurate, more information may have become available subsequent to the production of this guide. -REVISION HISTORY +## REVISION HISTORY -
RevisionRelease DateSummary
1.02015/05/30First release.
+| **Revision** | **Release Date** | **Summary** | +| 1.0 | 2015/05/30 | First release. | ## Table of Contents -1. GENERAL DESCRIPTION....1 -2. FEATURES....2 -3. SYSTEM APPLICATIONS....3 -4. BLOCK DIAGRAM....4 -5. PIN ASSIGNMENTS ....5 +- 1. GENERAL DESCRIPTION +- 2. FEATURES +- 3. SYSTEM APPLICATIONS +- 4. BLOCK DIAGRAM +- 5. PIN ASSIGNMENTS -5.1. PIN ASSIGNMENTS DIAGRAM....5 -5.2. PACKAGE IDENTIFICATION....5 -5.3. PIN ASSIGNMENTS TABLE....6 + - 5.1. PIN ASSIGNMENTS DIAGRAM + - 5.2. PACKAGE IDENTIFICATION + - 5.3. PIN ASSIGNMENTS TABLE -6. PIN DESCRIPTIONS....7 +- 6. PIN DESCRIPTIONS -6.1. PIN ASSIGNMENT CODES 7 -6.2. MEDIA CONNECTION PINS 7 -6.3. MISCELLANEOUS PINS 8 -6.4. PORT LED PINS 8 -6.5. STRAPPING PINS....9 -6.6. REGULATOR PINS....9 -6.7. POWER AND GND PINS....9 + - 6.1. PIN ASSIGNMENT CODES + - 6.2. MEDIA CONNECTION PINS + - 6.3. MISCELLANEOUS PINS + - 6.4. PORT LED PINS + - 6.5. STRAPPING PINS + - 6.6. REGULATOR PINS + - 6.7. POWER AND GND PINS -7. BASIC FUNCTION DESCRIPTIONS....10 +- 7. BASIC FUNCTION DESCRIPTIONS -7.1. SWITCH CORE FUNCTION OVERVIEW....10 + - 7.1. SWITCH CORE FUNCTION OVERVIEW -7.1.1. Flow Control....10 + - 7.1.1. Flow Control -7.1.1.1 IEEE 802.3x Full Duplex Flow Control....10 -7.1.1.2 Half Duplex Back Pressure....10 + - 7.1.1.1 IEEE 802.3x Full Duplex Flow Control + - 7.1.1.2 Half Duplex Back Pressure -7.1.2. Address Search, Learning, and Aging....11 -7.1.3. Half Duplex Operation....11 -7.1.4. InterFrame Gap....11 -7.1.5. Illegal Frame....11 + - 7.1.2. Address Search, Learning, and Aging + - 7.1.3. Half Duplex Operation + - 7.1.4. InterFrame Gap + - 7.1.5. Illegal Frame -7.2. PHYSICAL LAYER FUNCTIONAL OVERVIEW 12 + - 7.2. PHYSICAL LAYER FUNCTIONAL OVERVIEW -7.2.1. Auto-Negotiation....12 -7.2.2. 10Base-T Transmit Function....12 -7.2.3. 10Base-T Receive Function....12 -7.2.4. Link Monitor....12 -7.2.5. 100Base-TX Transmit Function....12 -7.2.6. 100Base-TX Receive Function....12 -7.2.7. Power-Down Mode....13 -7.2.8. Crossover Detection and Auto Correction....13 -7.2.9. Polarity Detection and Correction....13 + - 7.2.1. Auto-Negotiation + - 7.2.2. 10Base-T Transmit Function + - 7.2.3. 10Base-T Receive Function + - 7.2.4. Link Monitor + - 7.2.5. 100Base-TX Transmit Function + - 7.2.6. 100Base-TX Receive Function + - 7.2.7. Power-Down Mode + - 7.2.8. Crossover Detection and Auto Correction + - 7.2.9. Polarity Detection and Correction -7.3. GENERAL FUNCTION OVERVIEW....14 + - 7.3. GENERAL FUNCTION OVERVIEW -7.3.1. Power-On Sequence 14 -7.3.2. Setup and Configuration....15 -7.3.3. Serial EEPROM Example....16 + - 7.3.1. Power-On Sequence + - 7.3.2. Setup and Configuration + - 7.3.3. Serial EEPROM Example -7.3.3.1 EEPROM Device Operation....16 -7.3.3.2 EEPROM Size Selection....18 + - 7.3.3.1 EEPROM Device Operation + - 7.3.3.2 EEPROM Size Selection -7.3.4. SMI....18 -7.3.5. Head-Of-Line Blocking 18 -7.3.6. Filtering/Forwarding Reserved Control Frame....19 -7.3.7. Loop Detection....19 -7.3.8. Reg.0.14 PHY Digital Loopback Return to Internal....21 -7.3.9. LDO for 1.0V Power Generation 22 -7.3.10. Crystal/Oscillator 22 + - 7.3.4. SMI + - 7.3.5. Head-Of-Line Blocking + - 7.3.6. Filtering/Forwarding Reserved Control Frame + - 7.3.7. Loop Detection + - 7.3.8. Reg.0.14 PHY Digital Loopback Return to Internal + - 7.3.9. LDO for 1.0V Power Generation + - 7.3.10. Crystal/Oscillator -## 8. ADVANCED FUNCTION DESCRIPTIONS ....23 +- 8. ADVANCED FUNCTION DESCRIPTIONS -8.1. VLAN FUNCTION ....23 + - 8.1. VLAN FUNCTION -8.1.1. VLAN Description ....23 -8.1.2. Port-Based VLAN....24 -8.1.3. IEEE 802.1Q Tagged-VID Based VLAN 24 -8.1.4. Insert/Remove/Replace Tag....24 -8.1.5. Ingress and Egress Rules....25 + - 8.1.1. VLAN Description + - 8.1.2. Port-Based VLAN + - 8.1.3. IEEE 802.1Q Tagged-VID Based VLAN + - 8.1.4. Insert/Remove/Replace Tag + - 8.1.5. Ingress and Egress Rules -8.2. IEEE 802.1P REMARKING FUNCTION....25 + - 8.2. IEEE 802.1P REMARKING FUNCTION -8.3. QoS FUNCTION....26 + - 8.3. QoS FUNCTION -8.3.1. Bandwidth Control 26 + - 8.3.1. Bandwidth Control -8.3.1.1 Output (TX) Bandwidth Control....26 -8.3.1.2 Input (RX) Bandwidth Control 27 + - 8.3.1.1 Output (TX) Bandwidth Control + - 8.3.1.2 Input (RX) Bandwidth Control -8.3.2. Priority Assignment....27 + - 8.3.2. Priority Assignment -8.3.2.1 Queue Number Selection....27 -8.3.2.2 Port-Based Priority Assignment....27 -8.3.2.3 IEEE 802.1p/Q-Based Priority Assignment....28 -8.3.2.4 DSCP-Based Priority Assignment 28 -8.3.2.5 IP Address-Based Priority....28 -8.3.2.6 Reassigned Priority....28 -8.3.2.7 RLDP-Based Priority....28 -8.3.2.8 Packet Priority Selection....29 + - 8.3.2.1 Queue Number Selection + - 8.3.2.2 Port-Based Priority Assignment + - 8.3.2.3 IEEE 802.1p/Q-Based Priority Assignment + - 8.3.2.4 DSCP-Based Priority Assignment + - 8.3.2.5 IP Address-Based Priority + - 8.3.2.6 Reassigned Priority + - 8.3.2.7 RLDP-Based Priority + - 8.3.2.8 Packet Priority Selection -8.4. LOOKUP TABLE FUNCTION ....30 + - 8.4. LOOKUP TABLE FUNCTION -8.4.1. Function Description....30 -8.4.2. Address Search, Learning, and Aging....30 -8.4.3. Lookup Table Definition....31 + - 8.4.1. Function Description + - 8.4.2. Address Search, Learning, and Aging + - 8.4.3. Lookup Table Definition -8.5. STORM FILTER FUNCTION....32 -8.6. INPUT AND OUTPUT DROP FUNCTION 32 -8.7. LED FUNCTION....33 -8.8. ENERGY-EFFICIENT ETHERNET (EEE)....34 -8.9. CABLE DIAGNOSIS 34 + - 8.5. STORM FILTER FUNCTION + - 8.6. INPUT AND OUTPUT DROP FUNCTION + - 8.7. LED FUNCTION + - 8.8. ENERGY-EFFICIENT ETHERNET (EEE) + - 8.9. CABLE DIAGNOSIS -## 9. CHARACTERISTICS....35 +- 9. CHARACTERISTICS -9.1. ELECTRICAL CHARACTERISTICS/MAXIMUM RATINGS ....35 -9.2. OPERATING RANGE....35 -9.3. DC CHARACTERISTICS....35 -9.4. THERMAL CHARACTERISTICS....36 + - 9.1. ELECTRICAL CHARACTERISTICS/MAXIMUM RATINGS + - 9.2. OPERATING RANGE + - 9.3. DC CHARACTERISTICS + - 9.4. THERMAL CHARACTERISTICS -9.4.1. Simulation Conditions 36 -9.4.2. Thermal Characteristics Results ....36 + - 9.4.1. Simulation Conditions + - 9.4.2. Thermal Characteristics Results -9.5. DIGITAL TIMING CHARACTERISTICS ....37 + - 9.5. DIGITAL TIMING CHARACTERISTICS -9.5.1. LED Timing....37 -9.5.2. Reception/Transmission Data Timing of SMI Interface 37 -9.5.3. EEPROM Auto-Load Timing....38 + - 9.5.1. LED Timing + - 9.5.2. Reception/Transmission Data Timing of SMI Interface + - 9.5.3. EEPROM Auto-Load Timing -## 10. MECHANICAL DIMENSIONS....39 +- 10. MECHANICAL DIMENSIONS -10.1. MECHANICAL DIMENSIONS NOTES ....40 + - 10.1. MECHANICAL DIMENSIONS NOTES -## 11. ORDERING INFORMATION....41 +- 11. ORDERING INFORMATION ## List of Tables -TABLE 1. PIN ASSIGNMENTS TABLE ....6 +- TABLE 1. PIN ASSIGNMENTS TABLE -TABLE 2. MEDIA CONNECTION PINS....7 +- TABLE 2. MEDIA CONNECTION PINS -TABLE 3. MISCELLANEOUS PINS ....8 +- TABLE 3. MISCELLANEOUS PINS -TABLE 4. PORT LED PINS....8 +- TABLE 4. PORT LED PINS -TABLE 5. STRAPPING PINS....9 +- TABLE 5. STRAPPING PINS -TABLE 6. REGULATOR PINS....9 +- TABLE 6. REGULATOR PINS -TABLE 7. POWER AND GND PINS ....9 +- TABLE 7. POWER AND GND PINS -TABLE 8. BASIC SMI READ/WRITE CYCLES....18 +- TABLE 8. BASIC SMI READ/WRITE CYCLES -TABLE 9. EXTENDED SMI MANAGEMENT FRAME FORMAT ..... 18 +- TABLE 9. EXTENDED SMI MANAGEMENT FRAME FORMAT -TABLE 10. RESERVED ETHERNET MULTICAST ADDRESSES....19 +- TABLE 10. RESERVED ETHERNET MULTICAST ADDRESSES -TABLE 11. LOOP FRAME FORMAT ....20 +- TABLE 11. LOOP FRAME FORMAT -TABLE 12. CRYSTAL AND OSCILLATOR REQUIREMENTS....22 +- TABLE 12. CRYSTAL AND OSCILLATOR REQUIREMENTS -TABLE 13. VLAN TABLE....23 +- TABLE 13. VLAN TABLE -TABLE 14. VLAN ENTRY ....23 +- TABLE 14. VLAN ENTRY -TABLE 15. L2 TABLE 4-WAY HASH INDEX METHOD ....31 +- TABLE 15. L2 TABLE 4-WAY HASH INDEX METHOD -TABLE 16. ELECTRICAL CHARACTERISTICS/MAXIMUM RATINGS .....35 +- TABLE 16. ELECTRICAL CHARACTERISTICS/MAXIMUM RATINGS -TABLE 17. OPERATING RANGE....35 +- TABLE 17. OPERATING RANGE -TABLE 18. DC CHARACTERISTICS....35 +- TABLE 18. DC CHARACTERISTICS -TABLE 19. PCB DESCRIPTIONS ......36 +- TABLE 19. PCB DESCRIPTIONS -TABLE 20. CONDITION DESCRIPTIONS ....36 +- TABLE 20. CONDITION DESCRIPTIONS -TABLE 21. THERMAL CHARACTERISTICS RESULTS ....36 +- TABLE 21. THERMAL CHARACTERISTICS RESULTS -TABLE 22. LED TIMING....37 +- TABLE 22. LED TIMING -TABLE 23. SMI TIMING....37 +- TABLE 23. SMI TIMING -TABLE 24. EEPROM AUTO-LOAD TIMING CHARACTERISTICS ....38 +- TABLE 24. EEPROM AUTO-LOAD TIMING CHARACTERISTICS -TABLE 25. ORDERING INFORMATION ....41 +- TABLE 25. ORDERING INFORMATION ## List of Figures -FIGURE 1. BLOCK DIAGRAM....4 -FIGURE 2. PIN ASSIGNMENTS ....5 -FIGURE 3. COLLISION-BASED BACKPRESSURE SIGNAL TIMING....10 -FIGURE 4. POWER-ON SEQUENCE....14 -FIGURE 5. RESET 15 -FIGURE 6. START AND STOP DEFINITION....17 -FIGURE 7. OUTPUT ACKNOWLEDGE ....17 -FIGURE 8. RANDOM READ....17 -FIGURE 9. SEQUENTIAL READ ....17 -FIGURE 10. LOOP EXAMPLE ....19 -FIGURE 11. LED AND BUZZER CONTROL SIGNAL FOR LOOP DETECTION ....20 -FIGURE 12. LOOP EXAMPLE 2 ....21 -FIGURE 13. REG. 0.14 LOOPBACK ....21 -FIGURE 14. PACKET-SCHEDULING DIAGRAM....26 -FIGURE 15. RTL8305NBI PRIORITY ASSIGNMENT DIAGRAM....27 -FIGURE 16. BROADCAST INPUT DROP VS. OUTPUT DROP....32 -FIGURE 17. MULTICAST INPUT DROP VS. OUTPUT DROP....32 -FIGURE 18. FLOATING AND PULL-HIGH OF LED PINS FOR LED ....33 -FIGURE 19. RECEPTION DATA TIMING OF SMI INTERFACE....37 -FIGURE 20. TRANSMISSION DATA TIMING OF SMI INTERFACE....37 -FIGURE 21. EEPROM AUTO-LOAD TIMING....38 +- FIGURE 1. BLOCK DIAGRAM +- FIGURE 2. PIN ASSIGNMENTS +- FIGURE 3. COLLISION-BASED BACKPRESSURE SIGNAL TIMING +- FIGURE 4. POWER-ON SEQUENCE +- FIGURE 5. RESET +- FIGURE 6. START AND STOP DEFINITION +- FIGURE 7. OUTPUT ACKNOWLEDGE +- FIGURE 8. RANDOM READ +- FIGURE 9. SEQUENTIAL READ +- FIGURE 10. LOOP EXAMPLE +- FIGURE 11. LED AND BUZZER CONTROL SIGNAL FOR LOOP DETECTION +- FIGURE 12. LOOP EXAMPLE +- FIGURE 13. REG. 0.14 LOOPBACK +- FIGURE 14. PACKET-SCHEDULING DIAGRAM +- FIGURE 15. RTL8305NBI PRIORITY ASSIGNMENT DIAGRAM +- FIGURE 16. BROADCAST INPUT DROP VS. OUTPUT DROP +- FIGURE 17. MULTICAST INPUT DROP VS. OUTPUT DROP +- FIGURE 18. FLOATING AND PULL-HIGH OF LED PINS FOR LED +- FIGURE 19. RECEPTION DATA TIMING OF SMI INTERFACE +- FIGURE 20. TRANSMISSION DATA TIMING OF SMI INTERFACE +- FIGURE 21. EEPROM AUTO-LOAD TIMING ## 1. General Description @@ -251,12 +252,11 @@ In order to accomplish diagnostics in complex network systems, the RTL8305NBI pr The RTL8305NBI supports several advanced QoS functions with four-level priority queues to improve multimedia or real-time networking applications, including: -• Multi-priority assignment -- Differential queue weight -• Port-based rate limitation -- Queue-based rate limitation +- Multi-priority assignment + - Differential queue weight +- Port-based rate limitation + - Queue-based rate limitation -## DRAFT The RTL8305NBI supports 16 VLAN groups. These can be configured as port-based VLANs and/or 802.1Q tag-based VLANs. The RTL8305NBI also supports VLAN learning, with four Independent VLAN Learning (IVL) filtering databases. @@ -270,76 +270,73 @@ To simplify the peripheral power circuit, the RTL8305NBI integrates one LDO regu ## 2. Features -## Basic Switching Functions +**Basic Switching Functions** -## DRAFT -■ 5-port switch controller with memory and transceiver for 10Base-T and 100Base-TX -■ Non-blocking wire-speed reception and transmission and non-head-of-line-blocking forwarding -■ Complies with IEEE 802.3/802.3u auto-negotiation -■ Built-in high efficiency SRAM for packet buffer, with 2K-entry lookup table and two 4-way associative hash algorithms -■ 2048 byte maximum packet length -■ Flow control fully supported -◆ Half duplex: Back pressure flow control -◆ Full duplex: IEEE 802.3x flow control +- 5-port switch controller with memory and transceiver for 10Base-T and 100Base-TX +- Non-blocking wire-speed reception and transmission and non-head-of-line-blocking forwarding +- Complies with IEEE 802.3/802.3u auto-negotiation +- Built-in high efficiency SRAM for packet buffer, with 2K-entry lookup table and two 4-way associative hash algorithms +- 2048 byte maximum packet length +- Flow control fully supported + - Half duplex: Back pressure flow control + - Full duplex: IEEE 802.3x flow control -## Service Quality +**Service Quality** -■ Supports high performance QoS function on each port -◆ Supports 4-level priority queues -◆ Weighted round robin service -◆ Supports strict priority -◆ Input/Output port bandwidth control -◆ Queue-based bandwidth control -◆ 1Q-based, Port-based, DSCP-based, IP address-based, and other types of priority assignments -■ Supports IEEE 802.1p Traffic Remarking +- Supports high performance QoS function on each port + - Supports 4-level priority queues + - Weighted round robin service + - Supports strict priority + - Input/Output port bandwidth control + - Queue-based bandwidth control + - 1Q-based, Port-based, DSCP-based, IP address-based, and other types of priority assignments +- Supports IEEE 802.1p Traffic Remarking -## DRAFT -## Security and Management +**Security and Management** -■ Supports reserved control frame filtering -■ Supports advanced storm filtering -■ Optional EEPROM interface for configuration +- Supports reserved control frame filtering +- Supports advanced storm filtering +- Optional EEPROM interface for configuration -## VLAN Functions +**VLAN Functions** -■ Supports up to 16 VLAN groups -■ Flexible 802.1Q port/tag-based VLAN -■ Supports four IVLs -■ Leaky VLAN for unicast/multicast/broadcast/ARP packets +- Supports up to 16 VLAN groups +- Flexible 802.1Q port/tag-based VLAN +- Supports four IVLs +- Leaky VLAN for unicast/multicast/broadcast/ARP packets -## Power Saving Functions +**Power Saving Functions** -■ Supports Energy-Efficient Ethernet (EEE) function (IEEE 802.3az) -■ Link Down Power Saving Mode +- Supports Energy-Efficient Ethernet (EEE) function (IEEE 802.3az) +- Link Down Power Saving Mode -## Diagnostic Functions +**Diagnostic Functions** -■ Supports hardware loop detection function with LEDs and buzzer to indicate the existence of a loop -■ Supports cable diagnosis (RTCT function) -■ Flexible LED indicators -◆ RTCT status indication -◆ Loop status indication -◆ LEDs blink upon reset for LED diagnostics +- Supports hardware loop detection function with LEDs and buzzer to indicate the existence of a loop +- Supports cable diagnosis (RTCT function) +- Flexible LED indicators + - RTCT status indication + - Loop status indication + - LEDs blink upon reset for LED diagnostics -## Other Features +**Other Features** -■ Optional MDI/MDIX auto crossover for plug-and-play +- Optional MDI/MDIX auto crossover for plug-and-play -■ Physical layer port Polarity Detection and Correction function -■ Robust baseline wander correction for improved 100Base-TX performance -■ 25MHz crystal or 3.3V OSC input +- Physical layer port Polarity Detection and Correction function +- Robust baseline wander correction for improved 100Base-TX performance +- 25MHz crystal or 3.3V OSC input -## DRAFT -■ Single 3.3V power input can be transformed by integrating an LDO regulator to generate 1.0V from 3.3V via a low-cost external diode -■ Low power, 1.0/3.3V, 55nm CMOS technology -■ 48-pin QFN ‘Green’ package +- Single 3.3V power input can be transformed by integrating an LDO regulator to generate 1.0V from 3.3V via a low-cost external diode +- Low power, 1.0/3.3V, 55nm CMOS technology +- 48-pin QFN ‘Green’ package ## 3. System Applications -■ 5-port switch (10Base-T & 100Base-TX) +- 5-port switch (10Base-T & 100Base-TX) ## 4. Block Diagram @@ -351,11 +348,11 @@ To simplify the peripheral power circuit, the RTL8305NBI integrates one LDO regu This diagram illustrates the architecture and data flow of a system architecture, showing interactions between components like a Lookup Table, Packet Buffer, MII_REG/AUTOLOAD, Global Function, LED Control, LDO Regulator, Waveform Shaping, and LED_BLNK_TIME.
-Figure 1. Block Diagram +**Figure 1. Block Diagram** ## 5. Pin Assignments -## 5.1. Pin Assignments Diagram +### 5.1. Pin Assignments Diagram ![](images/bd801eb45eda6fbd1ccdb70573cb87911847e19b5dc867acebccf0258d140cba.jpg) @@ -438,25 +435,74 @@ AVDDL AVDDL
-Figure 2. Pin Assignments +**Figure 2. Pin Assignments** -## 5.2. Package Identification +### 5.2. Package Identification Green package is indicated by the ‘G’ in GXXXX in Figure 2. -## 5.3. Pin Assignments Table +### 5.3. Pin Assignments Table ‘Type’ codes used in the following table: A=Analog, D=Digital, I=Input, O=Output, I/O=Input/Output, I $_{PU}$ =Input Pin with Pull-Up Resistor, I $_{PD}$ =Input Pin with Pull-Down Resistor, G=Ground. -Table 1. Pin Assignments Table +**Table 1. Pin Assignments Table** -
NamePin No.Type
AVDDL1AP
RXIP12AI/O
RXIN13AI/O
TXON14AI/O
TXOP15AI/O
AVDDH6AP
DVDDL7P
AVDDH8P
TXOP29AI/O
TXON210AI/O
RXIN211AI/O
RXIP212AI/O
AVDDL13AP
RXIP314AI/O
RXIN315AI/O
TXON316AI/O
TXOP317AI/O
AVDDH18AP
TXOP419AI/O
TXON420AI/O
RXIN421AI/O
RXIP422AI/O
AVDDL23AP
DVDDL24P
SCL/MDC25I/ $O_{PU}$
+| **Name** | **Pin No.** | **Type** | +| AVDDL | 1 | AP | +| RXIP1 | 2 | AI/O | +| RXIN1 | 3 | AI/O | +| TXON1 | 4 | AI/O | +| TXOP1 | 5 | AI/O | +| AVDDH | 6 | AP | +| DVDDL | 7 | P | +| AVDDH | 8 | P | +| TXOP2 | 9 | AI/O | +| TXON2 | 10 | AI/O | +| RXIN2 | 11 | AI/O | +| RXIP2 | 12 | AI/O | +| AVDDL | 13 | AP | +| RXIP3 | 14 | AI/O | +| RXIN3 | 15 | AI/O | +| TXON3 | 16 | AI/O | +| TXOP3 | 17 | AI/O | +| AVDDH | 18 | AP | +| TXOP4 | 19 | AI/O | +| TXON4 | 20 | AI/O | +| RXIN4 | 21 | AI/O | +| RXIP4 | 22 | AI/O | +| AVDDL | 23 | AP | +| DVDDL | 24 | P | +| SCL/MDC | 25 | I/ $O_{PU}$ | -
NamePin No.Type
SDA/MDIO26 $I/O_{PU}$
RESETB27 $I_{PU}$
LDIND/DIS_LD28 $I/O_{PU}$
P0LED/DIS_EEE29 $I/O_{PD}$
P1LED30 $I/O_{PD}$
DVDDH31P
P2LED/DIS_RST_BLNK32 $I/O_{PD}$
P3LED33 $I/O_{PD}$
P4LED34 $I/O_{PD}$
DVDDL35P
V10OUT36AO
V33IN37AP
AVDDHPLL38AP
XO39AO
XI40AI
AVDDLPLL41AP
IBREF42AO
AVDDL43AP
RXIP044AI/O
RXIN045AI/O
TXON046AI/O
TXOP047AI/O
AVDDH48AP
E-PADE-PADG
+| **Name** | **Pin No.** | **Type** | +| SDA/MDIO | 26 | $I/O_{PU}$ | +| RESETB | 27 | $I_{PU}$ | +| LDIND/DIS_LD | 28 | $I/O_{PU}$ | +| P0LED/DIS_EEE | 29 | $I/O_{PD}$ | +| P1LED | 30 | $I/O_{PD}$ | +| DVDDH | 31 | P | +| P2LED/DIS_RST_BLNK | 32 | $I/O_{PD}$ | +| P3LED | 33 | $I/O_{PD}$ | +| P4LED | 34 | $I/O_{PD}$ | +| DVDDL | 35 | P | +| V10OUT | 36 | AO | +| V33IN | 37 | AP | +| AVDDHPLL | 38 | AP | +| XO | 39 | AO | +| XI | 40 | AI | +| AVDDLPLL | 41 | AP | +| IBREF | 42 | AO | +| AVDDL | 43 | AP | +| RXIP0 | 44 | AI/O | +| RXIN0 | 45 | AI/O | +| TXON0 | 46 | AI/O | +| TXOP0 | 47 | AI/O | +| AVDDH | 48 | AP | +| E-PAD | E-PAD | G | ## 6. Pin Descriptions -## 6.1. Pin Assignment Codes +### 6.1. Pin Assignment Codes I: Input Pin @@ -468,11 +514,9 @@ P: Digital Power Pin G: Digital Ground Pin -$I_{PU}$ : Input Pin With Pull-Up Resistor; -(Typical Value is about 75KΩ) +$I_{PU}$ : Input Pin With Pull-Up Resistor; (Typical Value is about 75KΩ) -$I_{PD}$ : Input Pin With Pull-Down Resistor; -(Typical Value is about 75KΩ) +$I_{PD}$ : Input Pin With Pull-Down Resistor; (Typical Value is about 75KΩ) $I/O_{PU}$ : $I_{PU}$ and $O_{PU}$ @@ -486,75 +530,107 @@ AP: Analog Power Pin AG: Analog Ground Pin -$O_{PU}$ : Output Pin With Pull-Up Resistor; -(Typical Value is about 75KΩ) +$O_{PU}$ : Output Pin With Pull-Up Resistor; (Typical Value is about 75KΩ) -$O_{PD}$ : Output Pin With Pull-Down Resistor; -(Typical Value is about 75KΩ) +$O_{PD}$ : Output Pin With Pull-Down Resistor; (Typical Value is about 75KΩ) $I/O_{PD}: I_{PD}$ and $O_{PD}$ -## 6.2. Media Connection Pins +### 6.2. Media Connection Pins -Table 2. Media Connection Pins +**Table 2. Media Connection Pins** -
Pin NamePin No.TypeDrive (mA)Description
RXIP4/RXIN422, 21AI/O-Differential Receive Data Input.
RXIP3/RXIN314, 15Port0~4 support 10Base-T, 100Base-TX.
RXIP2/RXIN212, 11
RXIP1/RXIN12, 3
RXIP0/RXIN044, 45
TXOP4/TXON419, 20AI/O-Differential Transmit Data Output.
TXOP3/TXON317, 16Port0~4 support 10Base-T, 100Base-TX.
TXOP2/TXON29, 10
TXOP1/TXON15, 4
TXOP0/TXON047, 46
+| **Pin Name** | **Pin No.** | **Type** | **Drive (mA)** | **Description** | +| RXIP4/RXIN4 | 22, 21 | AI/O | - | Differential Receive Data Input. | +| RXIP3/RXIN3 | 14, 15 | | | Port0~4 support 10Base-T, 100Base-TX. | +| RXIP2/RXIN2 | 12, 11 | | | | +| RXIP1/RXIN1 | 2, 3 | | | | +| RXIP0/RXIN0 | 44, 45 | | | | +| TXOP4/TXON4 | 19, 20 | AI/O | - | Differential Transmit Data Output. | +| TXOP3/TXON3 | 17, 16 | | | Port0~4 support 10Base-T, 100Base-TX. | +| TXOP2/TXON2 | 9, 10 | | | | +| TXOP1/TXON1 | 5, 4 | | | | +| TXOP0/TXON0 | 47, 46 | | | | -## 6.3. Miscellaneous Pins +### 6.3. Miscellaneous Pins As the output of the RTL8305NBI is 3.3V, the serial EEPROM and external device must be 3.3V compatible. -Table 3. Miscellaneous Pins +**Table 3. Miscellaneous Pins** -
Pin NamePin No.TypeDrive (mA)Description
LDIND/DIS_LD28I/ $O_{PU}$ 10Loop Indication Used by LED and Buzzer.
SCL/MDC25I/ $O_{PU}$ 4I2C Interface Clock for EEPROM Auto Load when Power On.After power on, this pin is MDC/MDIO Interface Clock for access registers.
SDA/MDIO26I/ $O_{PU}$ 4I2C Interface Data Input/Output for EEPROM Auto Load when Power On.After power on, this pin is MDC/MDIO Interface Data Input/Output for access registers.
RESETB27 $I_{PU}$ -System Pin Reset Input.
XI40AI-25MHz Crystal Clock Input.The clock tolerance is ±50ppm.
XO39AO-25MHz Crystal Clock Output Pin.When the pin of XI is using an oscillator this pin should be floating.
IBREF42AO-Reference Resistor for PHY Bandgap.A 2.49KΩ (1%) resistor should be connected between IBREF and GND.
+| **Pin Name** | **Pin No.** | **Type** | **Drive (mA)** | **Description** | +| LDIND/DIS_LD | 28 | I/ $O_{PU}$ | 10 | Loop Indication Used by LED and Buzzer. | +| SCL/MDC | 25 | I/ $O_{PU}$ | 4 | I2C Interface Clock for EEPROM Auto Load when Power On.After power on, this pin is MDC/MDIO Interface Clock for access registers. | +| SDA/MDIO | 26 | I/ $O_{PU}$ | 4 | I2C Interface Data Input/Output for EEPROM Auto Load when Power On.After power on, this pin is MDC/MDIO Interface Data Input/Output for access registers. | +| RESETB | 27 | $I_{PU}$ | - | System Pin Reset Input. | +| XI | 40 | AI | - | 25MHz Crystal Clock Input.The clock tolerance is ±50ppm. | +| XO | 39 | AO | - | 25MHz Crystal Clock Output Pin.When the pin of XI is using an oscillator this pin should be floating. | +| IBREF | 42 | AO | - | Reference Resistor for PHY Bandgap.A 2.49KΩ (1%) resistor should be connected between IBREF and GND. | -## 6.4. Port LED Pins +### 6.4. Port LED Pins All LED statuses are represented as active-low or high depending on input strapping. Those pins that are dual-function pins are output for LED, or input for strapping. Below are LED descriptions only. -Table 4. Port LED Pins +**Table 4. Port LED Pins** -
Pin NamePin No.TypeDrive (mA)Description
P0LED/DIS_EEE29I/ $O_{PD}$ 10LED for Port0 Status Indication.
P1LED30I/ $O_{PD}$ 10LED for Port1 Status Indication.
P2LED/DIS_RST_BLNK32I/ $O_{PD}$ 10LED for Port2 Status Indication.
P3LED33I/ $O_{PD}$ 10LED for Port3 Status Indication.
P4LED34I/ $O_{PD}$ 10LED for Port4 Status Indication.
+| **Pin Name** | **Pin No.** | **Type** | **Drive (mA)** | **Description** | +| P0LED/DIS_EEE | 29 | I/ $O_{PD}$ | 10 | LED for Port0 Status Indication. | +| P1LED | 30 | I/ $O_{PD}$ | 10 | LED for Port1 Status Indication. | +| P2LED/DIS_RST_BLNK | 32 | I/ $O_{PD}$ | 10 | LED for Port2 Status Indication. | +| P3LED | 33 | I/ $O_{PD}$ | 10 | LED for Port3 Status Indication. | +| P4LED | 34 | I/ $O_{PD}$ | 10 | LED for Port4 Status Indication. | -## 6.5. Strapping Pins +### 6.5. Strapping Pins Pins that are dual function pins are outputs for LED or inputs for strapping. Below are strapping descriptions only. -Table 5. Strapping Pins +**Table 5. Strapping Pins** -
Pin NamePin No.TypeDefaultDescription
DIS_LD/LDIND28I/ $O_{PU}$ -Disable Loop Detection Function.0: Enable1: Disable (default)
DIS_EEE/P0LED29I/ $O_{PD}$ -Disable EEE Function.0: Enable EEE function (default)1: Disable EEE function
DIS_RST_BLNK/P2LED32I/ $O_{PD}$ -Disable LED Power on Blinking.0: Enable (default)1: Disable
+| **Pin Name** | **Pin No.** | **Type** | **Default** | **Description** | +| DIS_LD/LDIND | 28 | I/ $O_{PU}$ | - | Disable Loop Detection Function.0: Enable1: Disable (default) | +| DIS_EEE/P0LED | 29 | I/ $O_{PD}$ | - | Disable EEE Function.0: Enable EEE function (default)1: Disable EEE function | +| DIS_RST_BLNK/P2LED | 32 | I/ $O_{PD}$ | - | Disable LED Power on Blinking.0: Enable (default)1: Disable | -## 6.6. Regulator Pins +### 6.6. Regulator Pins -Table 6. Regulator Pins +**Table 6. Regulator Pins** -
Pin NamePin No.TypeDrive (mA)Description
V10OUT36AO-Switch Regulator 1.0V Output.
V33IN37AP-Switch Regulator 3.3V Input.
+| **Pin Name** | **Pin No.** | **Type** | **Drive (mA)** | **Description** | +| V10OUT | 36 | AO | - | Switch Regulator 1.0V Output. | +| V33IN | 37 | AP | - | Switch Regulator 3.3V Input. | -## 6.7. Power and GND Pins +### 6.7. Power and GND Pins -Table 7. Power and GND Pins +**Table 7. Power and GND Pins** -
Pin NamePin No.TypeDescription
AVDDH6, 8, 18, 48APAnalog Power 3.3V.
AVDDL1, 13, 23, 43APAnalog Power 1.0V.
AVDDHPLL38APPower 3.3V for PLL.
AVDDLPLL41APPower 1.0V for PLL.
DVDDH31PDigital Power 3.3V for IO Pin.
DVDDL7, 24, 35PDigital Power 1.0V for Core Voltage.
GNDE-PADGGround for Whole Chip.
+| **Pin Name** | **Pin No.** | **Type** | **Description** | +| AVDDH | 6, 8, 18, 48 | AP | Analog Power 3.3V. | +| AVDDL | 1, 13, 23, 43 | AP | Analog Power 1.0V. | +| AVDDHPLL | 38 | AP | Power 3.3V for PLL. | +| AVDDLPLL | 41 | AP | Power 1.0V for PLL. | +| DVDDH | 31 | P | Digital Power 3.3V for IO Pin. | +| DVDDL | 7, 24, 35 | P | Digital Power 1.0V for Core Voltage. | +| GND | E-PAD | G | Ground for Whole Chip. | ## 7. Basic Function Descriptions -## 7.1. Switch Core Function Overview +### 7.1. Switch Core Function Overview -## 7.1.1. Flow Control +#### 7.1.1. Flow Control The RTL8305NBI supports IEEE 802.3x full duplex flow control, force mode full duplex flow control, and optional half duplex backpressure. -## 7.1.1.1 IEEE 802.3x Full Duplex Flow Control +##### 7.1.1.1 IEEE 802.3x Full Duplex Flow Control For UTP with auto-negotiation ability, the pause ability of full duplex flow control is enabled by internal registers via SMI on a per-port basis after reset. IEEE 802.3x flow control's ability is auto-negotiated between the remote device and the RTL8305NBI. If the auto-negotiation result of the IEEE 802.3x pause ability is ‘Enabled’, the full duplex 802.3x flow control function is enabled. Otherwise, full duplex IEEE 802.3x flow control function is disabled. -## 7.1.1.2 Half Duplex Back Pressure +##### 7.1.1.2 Half Duplex Back Pressure There are two mechanisms for half duplex backpressure; collision-based or carrier-based. -## Collision-Based Backpressure (Jam Mode) +##### Collision-Based Backpressure (Jam Mode) If the buffer is ready to overflow, this mechanism will force a collision. When the link partner detects this collision, the transmission is rescheduled. @@ -601,13 +677,13 @@ graph LR ```
-Figure 3. Collision-Based Backpressure Signal Timing +**Figure 3. Collision-Based Backpressure Signal Timing** -## Carrier-Based Backpressure (Defer Mode) +##### Carrier-Based Backpressure (Defer Mode) If the buffer is about to overflow, this mechanism will send an 0xAA pattern to defer the other station's transmission. The RTL8305NBI will continuously send the defer signal until the buffer overflow is resolved. -## 7.1.2. Address Search, Learning, and Aging +### 7.1.2. Address Search, Learning, and Aging When a packet is received, the RTL8305NBI will use the destination MAC address and FID to index the 2048-entry lookup table. If the indexed entry is valid, the received packet will be forwarded to the corresponding destination port. Otherwise, the RTL8305NBI will broadcast the packet. This is the 'Address Search'. @@ -615,7 +691,7 @@ The RTL8305NBI then combines the source MAC address and the FID to index the 204 Address aging is used to keep the contents of the address table correct in a dynamic network topology. The lookup engine will update the time stamp information of an entry whenever the corresponding source MAC address appears. An entry will be invalid (aged-out) if its time stamp information is not refreshed by the address learning process during the aging time period. The aging time of the RTL8305NBI is between 200 and 400 seconds. -## 7.1.3. Half Duplex Operation +### 7.1.3. Half Duplex Operation In half duplex mode, the CSMA/CD media access method is the means by which two or more stations share a common transmission medium. To transmit, a station waits (defers) for a quiet period on the medium (that is, no other station is transmitting) and then sends the intended message in bit-serial form. If the message collides with that of another station, then each transmitting station intentionally transmits for an additional predefined period to ensure propagation of the collision throughout the system. The station remains silent for a random amount of time (backoff) before attempting to transmit again. @@ -629,61 +705,61 @@ where: k = min (n, backoffLimit). IEEE 802.3 defines the backoffLimit as 10. -## 7.1.4. InterFrame Gap +### 7.1.4. InterFrame Gap The InterFrame Gap is 9.6μs for 10Mbps Ethernet and 960ns for 100Mbps Fast Ethernet. -## 7.1.5. Illegal Frame +### 7.1.5. Illegal Frame Illegal frames such as CRC error packets, runt packets (length < 64 bytes), and oversize packets (length > maximum length), will be discarded. -## 7.2. Physical Layer Functional Overview +### 7.2. Physical Layer Functional Overview -## 7.2.1. Auto-Negotiation +#### 7.2.1. Auto-Negotiation The RTL8305NBI obtains the states of duplex, speed, and flow control ability for each port through the auto-negotiation mechanism defined in the IEEE 802.3 specifications. During auto-negotiation, each port advertises its ability to its link partner and compares its ability with advertisements received from its link partner. By default, the RTL8305NBI advertises full capabilities (100Full, 100Half, 10Full, 10Half) together with flow control ability. The RTL8305NBI also advertises the Energy Efficient Ethernet (EEE) capability to the link partner. -## 7.2.2. 10Base-T Transmit Function +#### 7.2.2. 10Base-T Transmit Function The output 10Base-T waveform is Manchester-encoded before it is driven onto the network media. The internal filter shapes the driven signals to reduce EMI emissions, eliminating the need for an external filter. -## 7.2.3. 10Base-T Receive Function +#### 7.2.3. 10Base-T Receive Function The Manchester decoder converts the incoming serial stream to NRZ data when the squelch circuit detects the signal level is above squelch level. -## 7.2.4. Link Monitor +#### 7.2.4. Link Monitor The 10Base-T link pulse detection circuit continually monitors the RXIP/RXIN pins for the presence of valid link pulses. Auto-polarity is implemented to correct the detected reverse polarity of RXIP/RXIN signal pairs. -## 7.2.5. 100Base-TX Transmit Function +#### 7.2.5. 100Base-TX Transmit Function The 100Base-TX transmit function performs parallel to serial conversion, 4B/5B coding, scrambling, NRZ/NRZI conversion, and MLT-3 encoding. The 5-bit serial data stream after 4B/5B coding is then scrambled as defined by the TP-PMD Stream Cipher function to flatten the power spectrum energy such that EMI effects are significantly reduced. The scrambled seed is based on PHY addresses and is unique for each port. After scrambling, the bit stream is driven into the network media in the form of MLT-3 signaling. The MLT-3 multi-level signaling technology moves the power spectrum energy from high frequency to low frequency, which further reduces EMI emissions. -## 7.2.6. 100Base-TX Receive Function +#### 7.2.6. 100Base-TX Receive Function The receive path includes a receiver composed of an adaptive equalizer and DC restoration circuits (to compensate for an incoming distorted MLT-3 signal), an MLT-3 to NRZI and NRZI to NRZ converter to convert analog signals to digital bit-stream, and a PLL circuit to clock data bits with minimum bit error rate. A De-scrambler, 5B/4B decoder, and serial-to-parallel conversion circuits are followed by the PLL circuit. Finally, the converted parallel data is fed into the MAC. -## 7.2.7. Power-Down Mode +#### 7.2.7. Power-Down Mode The RTL8305NBI implements power-down mode on a per-port basis. Setting MII Reg.0.11 forces the corresponding port of the RTL8305NBI to enter power-down mode. This disables all transmit/receive functions, except SMI (Serial Management Interface: MDC/MDIO, also known as MII Management Interface). -## 7.2.8. Crossover Detection and Auto Correction +#### 7.2.8. Crossover Detection and Auto Correction During the link setup phase, the RTL8305NBI checks whether it receives active signals on every port in order to determine if a connection can be established. In cases where the receiver data pin pair is connected to the transmitter data pin pair of the peer device and vice versa, the RTL8305NBI automatically changes its configuration and swaps receiver/transmitter data pins as required. If a port is connected to a PC or NIC with MDI-X interface with a crossover cable, the RTL8305NBI will reconfigure the port to ensure proper connection. This replaces the DIP switch commonly used for reconfiguring a port on a hub or switch. Note: IEEE 802.3 compliant forced mode 100M ports with Autoxover have link problems with NWay (Auto-Negotiation) ports. It is recommended to not use Autoxover for forced 100M. -## 7.2.9. Polarity Detection and Correction +#### 7.2.9. Polarity Detection and Correction For better noise immunity and lower interference to ambient devices, the Ethernet electrical signal on a twisted-pair cable is transmitted in differential form. That is, the signal is transmitted on two wires in each direction with inverse polarities (+/-). If wiring on the connector is faulty, or a faulty transformer is used, the two inputs to a transceiver may carry signals with opposite but incorrect polarities. As a direct consequence, the transceiver will not work properly. When the RTL8305NBI operates in 10Base-T mode, it automatically reverses the polarity of its two receiver input pins if it detects that the polarities of the incoming signals on the pins is incorrect. However, this feature is unnecessary when the RTL8305NBI is operating in 100Base-TX mode. -## 7.3. General Function Overview +### 7.3. General Function Overview -## 7.3.1. Power-On Sequence +#### 7.3.1. Power-On Sequence Two power voltage types are required for RTL8305NBI normal operation; 3.3V and 1.0V. The 1.0V is converted from 3.3V via the LDO of the RTL8305NBI. @@ -708,9 +784,9 @@ Two power voltage types are required for RTL8305NBI normal operation; 3.3V and 1 | Te | 3.3 | 3.3 | 1.0 |
-Figure 4. Power-On Sequence +**Figure 4. Power-On Sequence** -## The requirements are: +**The requirements are:** - The time of Ta should be between $500\mu \mathrm{s}$ and $20\mathrm{ms}$ - The sequence of Ta is always less than Tb for the LDO of the RTL8305NBI. In principle, the sequence of Td and Ta/Tb/Tc is also not required. The sequence of Td > 5ms is recommended @@ -721,7 +797,7 @@ Depending on the type of reset, the whole or just part of the RTL8305NBI is init - Hardware reset for the whole chip via pin RESET# or power-on - Soft reset for packet buffer, queue, and MIB counter via register SoftReset -• PHY software reset for each PHY by register reset +- PHY software reset for each PHY by register reset Hardware Reset: Power-on, or pull the RESET# pin low for at least 1μs. The RTL8305NBI resets the whole chip and after all power is ready and the RESET# pin is de-asserted, it gets initial values from pins and serial EEPROM. @@ -730,22 +806,22 @@ Soft Reset: The RTL8305NBI does not reset the LUT, LED circuit, and all register PHY Software Reset: Write bit15 of Reg0 of a PHY as 1. The RTL8305NBI will then reset this PHY. ![](images/4e7f1972851669ee7813932cc0ad1873e0e5703e864a4861aede3708b17ab9f8.jpg) -Figure 5. Reset +**Figure 5. Reset** Some setting values for operation modes are latched from those corresponding mode pins upon hardware reset. ‘Upon reset’ is defined as a short time after the end of a hardware reset. Other advanced configuration parameters may be latched from serial EEPROM. -## 7.3.2. Setup and Configuration +#### 7.3.2. Setup and Configuration The RTL8305NBI can be configured easily and flexibly by: -• Hardware pins upon reset +- Hardware pins upon reset - Optional serial EEPROM upon reset (contact Realtek for detailed EEPROM configuration settings) - Internal registers (including PHY registers for each port and global MAC registers) accessed via SMI (Serial Management Interface: MDC/MDIO, also known as MII Management Interface) There are three methods of configuration: - Only hardware pins for normal switch applications -• Hardware pins and serial EEPROM for advanced switch applications +- Hardware pins and serial EEPROM for advanced switch applications - Hardware pins and internal registers via SMI for applications with processor Two types of pins, each with internal pull-high or pull-low resistors, are used for configuration: @@ -759,31 +835,31 @@ The serial EEPROM shares two pins, SCL/MDC and SDA/MDIO, with SMI, and is option Internal registers can still be accessed after reset via SMI (pin SCL/MDC and SDA/MDIO). Serial EEPROM signals and SMI signals must not exist at the same time. -## 7.3.3. Serial EEPROM Example +#### 7.3.3. Serial EEPROM Example Both the 24LC01/02/04/08/16 and 24C01/02/04/08/16 can be used with the RTL8305NBI. The interface is a 2-wire serial EEPROM interface providing 1K/2K/4K/8K/16K bits of storage space. The EEPROM must be 3.3V compatible. -## 7.3.3.1 EEPROM Device Operation +##### 7.3.3.1 EEPROM Device Operation Clock and Data Transitions: The SDA pin is normally pulled high with an external resistor. Data on the SDA pin may change only during SCL low time periods. Data changes during SCL high periods will indicate a start or stop condition as defined below. The SCL frequency is 200kHz. -## Start Condition +##### Start Condition A high-to-low transition of SDA with SCL high is the start condition and must precede any other command. -## Stop Condition +##### Stop Condition A low-to-high transition of SDA with SCL high is a stop condition. -## Acknowledge +##### Acknowledge All addresses and data are transmitted serially to and from the EEPROM in 8-bit words. The EEPROM sends a zero to acknowledge that it has received each word. This happens during the ninth clock cycle. -## Random Read +##### Random Read A random read requires a ‘dummy’ byte write sequence to load in the data word address. -## Sequential Read +##### Sequential Read For the RTL8305NBI, the sequential reads are initiated by a random address read. After the EEPROM receives a data word, it responds with an acknowledgement. As long as the EEPROM receives an acknowledgement, it will continue to increment the data word address and clock out sequential data words in series. @@ -801,7 +877,7 @@ graph LR ``` -Figure 6. Start and Stop Definition +**Figure 6. Start and Stop Definition** ![](images/5099fe8479487d47f0920c348773ec1a8a10fcf7f3c216e5368948d904f9e870.jpg) @@ -811,7 +887,7 @@ Figure 6. Start and Stop Definition This diagram illustrates the signal processing logic for a digital communication system, showing the relationship between SCL, DATA IN, and DATA OUT signals across a sequence of events. -Figure 7. Output Acknowledge +**Figure 7. Output Acknowledge** ![](images/7e0b5ec52961bb1b0f7b5951ba753a1ad55cb3a81db33f9060a2603822fca44a.jpg) @@ -837,7 +913,7 @@ graph LR ``` -Figure 8. Random Read +**Figure 8. Random Read** ![](images/eb9ba53c18aa9fa48d946018bc43f108cb8756b4610466743ddb7662d3a48e08.jpg) @@ -855,46 +931,64 @@ graph LR ``` -Figure 9. Sequential Read +**Figure 9. Sequential Read** -## 7.3.3.2 EEPROM Size Selection +##### 7.3.3.2 EEPROM Size Selection The RTL8305NBI supports five serial EEPROM sizes —1k bits, 2k bits, 4k bits, 8k bits and 16k bits. Via the auto-download operation, the RTL8305NBI decides the size of the data downloaded to the RTL8305NBI from the EEPROM according to the value of the $2^{nd}$ byte data in the serial EEPROM. If the $2^{nd}$ byte data = 0x01, 0x02, 0x04, 0x08 or 0x16, it means the data size is 1k bits, 2k bits, 4k bits, 8k bits or 16k bits respectively. The value of the $2^{nd}$ byte should accord with the actual EEPROM data size. For example, the value of the $2^{nd}$ byte cannot be ‘0x02’ when the 24(L)C02 is used. -## 7.3.4. SMI +#### 7.3.4. SMI The SMI (Serial Management Interface) is also known as the MII Management Interface, and consists of two signals (MDIO and MDC). It allows external devices with SMI master mode (MDC is output) to control the state of the PHY and internal registers (SMI slave mode: MDC is input). MDC is an input clock for the RTL8305NBI to latch MDIO on its rising edge. The clock can run from DC to 2.5MHz. MDIO is a bi-directional connection used to write data to, or read data from the RTL8305NBI. The PHY address is from 0 to 4. -Table 8. Basic SMI Read/Write Cycles +**Table 8. Basic SMI Read/Write Cycles** -
Preamble (32 bits)Start (2 bits)OP Code (2 bits)PHYAD (5 bits)REGAD (5 bits)Turn Around (2 bits)Data (16 bits)Idle
Read1......10110 $A_4A_3A_2A_1A_0$ $R_4R_3R_2R_1R_0$ Z0 $D_{15}......D_0$ Z*
Write1......10101 $A_4A_3A_2A_1A_0$ $R_4R_3R_2R_1R_0$ 10 $D_{15}......D_0$ Z*
+| | **Preamble (32 bits)** | **Start (2 bits)** | **OP Code (2 bits)** | **PHYAD (5 bits)** | **REGAD (5 bits)** | **Turn Around (2 bits)** | **Data (16 bits)** | **Idle** | +| Read | 1......1 | 01 | 10 | $A_4A_3A_2A_1A_0$ | $R_4R_3R_2R_1R_0$ | Z0 | $D_{15}......D_0$ | Z* | +| Write | 1......1 | 01 | 01 | $A_4A_3A_2A_1A_0$ | $R_4R_3R_2R_1R_0$ | 10 | $D_{15}......D_0$ | Z* | -\*: High-impedance. During idle time MDIO state is determined by an external 1.5KΩ pull-up resistor. +*: High-impedance. During idle time MDIO state is determined by an external 1.5KΩ pull-up resistor. For MDIO Manageable Device (MMD) access, the RTL8305NBI supports the extended SMI format. -Table 9. Extended SMI Management Frame Format +**Table 9. Extended SMI Management Frame Format** -
FramePRESTOPPHYADDEVADTADATAIDLE
Address1...10000AAAAAEEEE10AAAAAAAAAAAAAAAAAAZ
Write1...10001AAAAAEEEE10DDDDDDDDDDDDDDDZ
Read1...10011AAAAAEEEEZ0DDDDDDDDDDDDDDDZ
Post-Read-Increment-Address1...10010AAAAAEEEEZ0DDDDDDDDDDDDDDDZ
+| **Frame** | **PRE** | **ST** | **OP** | **PHYAD** | **DEVAD** | **TA** | **DATA** | **IDLE** | +| Address | 1...1 | 00 | 00 | AAAAA | EEEE | 10 | AAAAAAAAAAAAAAAAAA | Z | +| Write | 1...1 | 00 | 01 | AAAAA | EEEE | 10 | DDDDDDDDDDDDDDD | Z | +| Read | 1...1 | 00 | 11 | AAAAA | EEEE | Z0 | DDDDDDDDDDDDDDD | Z | +| Post-Read-Increment-Address | 1...1 | 00 | 10 | AAAAA | EEEE | Z0 | DDDDDDDDDDDDDDD | Z | To guarantee the first successful SMI transaction after power-on reset, the external device should delay a few moments before issuing the first SMI Read/Write Cycle relative to the rising edge of reset. -## 7.3.5. Head-Of-Line Blocking +#### 7.3.5. Head-Of-Line Blocking The RTL8305NBI incorporates a mechanism to prevent Head-Of-Line blocking problems when flow control is disabled. When the flow control function is disabled, the RTL8305NBI first checks the destination address of the incoming packet. If the destination port is congested, the RTL8305NBI will discard this packet to avoid blocking the next packet, which is going to a non-congested port. -## 7.3.6. Filtering/Forwarding Reserved Control Frame +#### 7.3.6. Filtering/Forwarding Reserved Control Frame The RTL8305NBI supports the ability to forward or drop the frames of the IEEE 802.1 specified reserved Ethernet multicast addresses. -Table 10. Reserved Ethernet Multicast Addresses +**Table 10. Reserved Ethernet Multicast Addresses** B: Broadcast (Search the Look-Up Table) D: Drop -
AssignmentValueAvailable Action
Bridge Group Address01-80-C2-00-00-00D, B (Default)
IEEE Std 802.3, 1988 Edition, Full Duplex PAUSE Operation01-80-C2-00-00-01D (Default), B
IEEE 802.3ad Slow_Protocols-Multicast Address01-80-C2-00-00-02D (Default), B
IEEE 802.1X PAE Address01-80-C2-00-00-03D, B (Default)
Reserved for Future Standards01-80-C2-00-00-04~01-80-C2-00-00-0D, 01-80-C2-00-00-0FD (Default), B
LLDP IEEE Std 802.1AB Link Layer Discovery Protocol Multicast Address01-80-C2-00-00-0ED, B (Default)
All LANs Bridge Management Group Address01-80-C2-00-00-10D (Default), B
Reserved for 01-80-C2-00-00-1x01-80-C2-00-00-11~01-80-C2-00-00-1FD, B (Default)
GMRP Address01-80-C2-00-00-20D (Default), B
GVRP Address01-80-C2-00-00-21D, B (Default)
Reserved for use by Multiple Registration Protocol (MRP) Applications01-80-C2-00-00-22~01-80-C2-00-00-2FD (Default), B
802.1ag PDU CCM/LTM01-80-C2-00-00-31~01-80-C2-00-00-3FD, B (Default)
+| **Assignment** | **Value** | **Available Action** | +| Bridge Group Address | 01-80-C2-00-00-00 | D, B (Default) | +| IEEE Std 802.3, 1988 Edition, Full Duplex PAUSE Operation | 01-80-C2-00-00-01 | D (Default), B | +| IEEE 802.3ad Slow_Protocols-Multicast Address | 01-80-C2-00-00-02 | D (Default), B | +| IEEE 802.1X PAE Address | 01-80-C2-00-00-03 | D, B (Default) | +| Reserved for Future Standards | 01-80-C2-00-00-04~01-80-C2-00-00-0D, 01-80-C2-00-00-0F | D (Default), B | +| LLDP IEEE Std 802.1AB Link Layer Discovery Protocol Multicast Address | 01-80-C2-00-00-0E | D, B (Default) | +| All LANs Bridge Management Group Address | 01-80-C2-00-00-10 | D (Default), B | +| Reserved for 01-80-C2-00-00-1x | 01-80-C2-00-00-11~01-80-C2-00-00-1F | D, B (Default) | +| GMRP Address | 01-80-C2-00-00-20 | D (Default), B | +| GVRP Address | 01-80-C2-00-00-21 | D, B (Default) | +| Reserved for use by Multiple Registration Protocol (MRP) Applications | 01-80-C2-00-00-22~01-80-C2-00-00-2F | D (Default), B | +| 802.1ag PDU CCM/LTM | 01-80-C2-00-00-31~01-80-C2-00-00-3F | D, B (Default) | -## 7.3.7. Loop Detection +#### 7.3.7. Loop Detection Loops should be avoided between switch applications. The simplest loop as shown below results in: 1) Unicast frame duplication; 2) Broadcast frame multiplication; 3) Address table non-convergence. @@ -911,29 +1005,30 @@ Link1 Link2 -Figure 10. Loop Example +**Figure 10. Loop Example** The loop detect function can be enabled/disabled via strapping pin or registers. When the loop detection function is enabled, the RTL8305NBI sends out a broadcast 64-byte loop frame (the frequency is configured by register) and sniffs for the sent loop frame on each port to detect whether there is a network loop (or bridge loop). If a loop is detected, the RTL8305NBI will drive the external LEDs and buzzer alarm. -• The LED driven by the LDIND pin will blink +- The LED driven by the LDIND pin will blink - The LEDs driven by port LED pins (see Table 4, page 8) of the ports on which the network loop is detected will all blink simultaneously - The buzzer driven by the LDIND pin will buzz at the same frequency as the LED blinking Both passive and active buzzers can be supported. The resonant frequency for the passive buzzer is approximately 2kHz. The buzzer and all LEDs will turn on/off simultaneously. In Figure 11, T1 is the turned-off period and T2 is the turned-on period. T1 and T2 are equal and can be configured to 400ms or 800ms. ![](images/6124cfe6c8b70c02b7c7fa66a9a697e10decded4f8828c08287acdbdafd71dbe.jpg) -Figure 11. LED and Buzzer Control Signal for Loop Detection +**Figure 11. LED and Buzzer Control Signal for Loop Detection** Loop status, LED, and buzzer indications can be cleared when one of the following conditions occurs. - Loop frame is not detected in the next loop detection period -• The loop port links down +- The loop port links down The Loop frame length is 64 bytes. Its format is shown below. -Table 11. Loop Frame Format +**Table 11. Loop Frame Format** -
48-bit48-bit16-bit16-bit12-bit4-bit352-bit16-bit
FFFF FFFF FFFFSID88992300000TTL0000CRC
+| **48-bit** | **48-bit** | **16-bit** | **16-bit** | **12-bit** | **4-bit** | **352-bit** | **16-bit** | +| FFFF FFFF FFFF | SID | 8899 | 2300 | 000 | TTL | 0000 | CRC | In order to achieve loop detection, each switch device needs a unique SID (the source MAC address). If an EEPROM is not used, a unique SID should be assigned via SMI after reset. The TTL (Time-To-Live) field is used to avoid a storm triggered by the loop frame. The TTL field in the loop frame will decrease by 1 when it passes through an RTL8305NBI whose MAC address is not equal to the SID of the loop frame. The RTL8305NBI will drop a loop frame in which the TTL is the minimum value (0001 is the minimum value. 0000, meaning 16, is the maximum value). The initial value of the TTL field can be configured via SMI or EEPROM. @@ -956,11 +1051,11 @@ graph TD ``` -Figure 12. Loop Example 2 +**Figure 12. Loop Example 2** -## 7.3.8. Reg.0.14 PHY Digital Loopback Return to Internal +#### 7.3.8. Reg.0.14 PHY Digital Loopback Return to Internal -The digital loopback mode of the PHY (return to internal MAC) may be enabled on a per-port basis by setting MII Reg.0.14 to 1. In digital loopback mode, the TXD of the PHY is transferred directly to the RXD of the PHY, with TXEN changed to CRS\_DV, and returns to the MAC via an internal MII. The data stream coming from the MAC will not egress to the physical medium, and an incoming data stream from the network medium will be blocked in this mode. The packets will be looped back in 10Mbps full duplex or 100Mbps full duplex mode. This function is especially useful for diagnostic purposes. For example, a NIC can be used to send broadcast frames into Port 0 of the RTL8305NBI and set Port 1 to Reg0.14 Loopback. The frame will be looped back to Port 0, so the received packet count can be checked to verify that the switch device is good. In this example, Port 0 can be 10M or 100M, and full or half duplex. +The digital loopback mode of the PHY (return to internal MAC) may be enabled on a per-port basis by setting MII Reg.0.14 to 1. In digital loopback mode, the TXD of the PHY is transferred directly to the RXD of the PHY, with TXEN changed to CRS_DV, and returns to the MAC via an internal MII. The data stream coming from the MAC will not egress to the physical medium, and an incoming data stream from the network medium will be blocked in this mode. The packets will be looped back in 10Mbps full duplex or 100Mbps full duplex mode. This function is especially useful for diagnostic purposes. For example, a NIC can be used to send broadcast frames into Port 0 of the RTL8305NBI and set Port 1 to Reg0.14 Loopback. The frame will be looped back to Port 0, so the received packet count can be checked to verify that the switch device is good. In this example, Port 0 can be 10M or 100M, and full or half duplex. ![](images/ff397eedfa5fc265a971e70c369614c56cad1f5059d5ef1e0eed003169e1ae98.jpg) @@ -974,54 +1069,64 @@ graph LR ``` -Figure 13. Reg. 0.14 Loopback +**Figure 13. Reg. 0.14 Loopback** As the RTL8305NBI only supports digital loopback in full duplex mode, PHY Reg.0.8 for each port will always be kept on 1 when digital loopback is enabled. The digital loopback only functions on broadcast packets (DA=FF-FF-FF-FF-FF-FF). In loopback mode, the link LED of the loopback port should always be ON, and the Speed and Duplex LED combined to reflect the link status (100full/10full) correctly, regardless of what the previous status of this loopback port was. -## 7.3.9. LDO for 1.0V Power Generation +#### 7.3.9. LDO for 1.0V Power Generation The RTL8305NBI can use an internal LDO to generate 1.0V from a 3.3V power supply. This 1.0V is used for the digital core and analog receiver circuits. Do not use the LDO for other chips, even if the rating is enough. Do not connect an inductor (bead) directly between the V10OUT pin and AVDDLPLL pin. This will adversely affect the stability of the 1.0V power to a significant degree. Refer to the reference design for details. -## 7.3.10. Crystal/Oscillator +#### 7.3.10. Crystal/Oscillator When using a crystal, the RTL8305NBI should connect a loading capacitor from each pin of XI and XO to ground. Whether using an oscillator or driving an external 25MHz clock from another device, the external clock should be fed into the XI pin. The following table shows the requirements of the crystal and oscillator. -Table 12. Crystal and Oscillator Requirements +**Table 12. Crystal and Oscillator Requirements** -
Nominal Frequency25.000 MHz
Frequency Tolerance±50ppm Max.
Temperature Characteristics±50ppm in Operating Temperature Range
Equivalent Series Resistance of Crystal50 Ohm Max.
XTALI/OSC Input Clock Jitter Tolerance (in 5KHz to 2.5MHz Range)250ps Max.
Duty Cycle40%~60%
+| **Nominal Frequency** | **25.000 MHz** | +| Frequency Tolerance | ±50ppm Max. | +| Temperature Characteristics | ±50ppm in Operating Temperature Range | +| Equivalent Series Resistance of Crystal | 50 Ohm Max. | +| XTALI/OSC Input Clock Jitter Tolerance (in 5KHz to 2.5MHz Range) | 250ps Max. | +| Duty Cycle | 40%~60% | ## 8. Advanced Function Descriptions -## 8.1. VLAN Function +### 8.1. VLAN Function -## 8.1.1. VLAN Description +#### 8.1.1. VLAN Description The RTL8305NBI supports 16 VLAN groups via the 16-entry VLAN table (see Table 13 and Table 14). These can be configured as port-based VLANs and/or IEEE 802.1Q tag-based VLANs. The RTL8305NBI supports four IVLs, with the mapping information in the VLAN table. The contents of the VLAN table can be configured via SMI or EEPROM. Multiple ingress filtering and egress filtering options provide various VLAN admit rules for the RTL8305NBI. The RTL8305NBI also provides a flexible VLAN tag insert/remove function based on port and VID. -Table 13. VLAN Table +**Table 13. VLAN Table** -
Entry IndexVLAN IDMembershipUNTAG_MSKFID
VLAN Entry 0VLAN ID A[11:0]VLAN ID A membership [4:0]VLAN ID A UNTAG_MSK [4:0]FID[1:0]
VLAN Entry 1VLAN ID B [11:0]VLAN ID B membership [4:0]VLAN ID B UNTAG_MSK [4:0]FID[1:0]
..............................
VLAN Entry 15VLAN ID P [11:0]VLAN ID P membership [4:0]VLAN ID P UNTAG_MSK [4:0]FID[1:0]
+| **Entry Index** | **VLAN ID** | **Membership** | **UNTAG_MSK** | **FID** | +| VLAN Entry 0 | VLAN ID A[11:0] | VLAN ID A membership [4:0] | VLAN ID A UNTAG_MSK [4:0] | FID[1:0] | +| VLAN Entry 1 | VLAN ID B [11:0] | VLAN ID B membership [4:0] | VLAN ID B UNTAG_MSK [4:0] | FID[1:0] | +| ...... | ...... | ...... | ...... | ...... | +| VLAN Entry 15 | VLAN ID P [11:0] | VLAN ID P membership [4:0] | VLAN ID P UNTAG_MSK [4:0] | FID[1:0] | -Table 14. VLAN Entry +**Table 14. VLAN Entry** -
FieldDescriptionBits
VIDThe VLAN ID for Search. -The VID of the ingress packet will be compared with this field.12
MBRVLAN Member Port Set. -If the bit in this field is ‘1’, the corresponding port is a member port of the VLAN specified by the VID field.6
UNTAG SETVLAN Untag Set. -If the bit in this field is ‘1’, egress packets from the corresponding port will be VLAN-untagged.6
PRIORITYVID-Based Priority. -The priority assigned to all ingress packets of the VLAN specified by the VID field.2
FIDThe FID is Used by Lookup Table for IVL Application.12
+| **Field** | **Description** | **Bits** | +| VID | The VLAN ID for Search. The VID of the ingress packet will be compared with this field. | 12 | +| MBR | VLAN Member Port Set. If the bit in this field is ‘1’, the corresponding port is a member port of the VLAN specified by the VID field. | 6 | +| UNTAG SET | VLAN Untag Set. If the bit in this field is ‘1’, egress packets from the corresponding port will be VLAN-untagged. | 6 | +| PRIORITY | VID-Based Priority. The priority assigned to all ingress packets of the VLAN specified by the VID field. | 2 | +| FID | The FID is Used by Lookup Table for IVL Application. | 12 | The main VLAN features of the RTL8305NBI are as follows: -• Supports up to 16 VLAN groups -• Flexible IEEE 802.1Q port/tag-based VLAN +- Supports up to 16 VLAN groups +- Flexible IEEE 802.1Q port/tag-based VLAN - Four IVLs - Leaky VLAN for ARP broadcast/unicast/multicast packets - Leaky inter-VLAN mirror function - VLAN tag Insert/Remove function -## 8.1.2. Port-Based VLAN +#### 8.1.2. Port-Based VLAN The 16 VLAN membership registers designed into the RTL8305NBI provide full flexibility for users to configure the member ports to associate with different VLAN groups in the VLAN table. Each port can join more than one VLAN group. @@ -1031,7 +1136,7 @@ For non-VLAN tagged frames, the RTL8305NBI performs port-based VLAN. The VLAN ID The RTL8305NBI supports Port VID (PVID) for each port and can insert a PVID in the VLAN tag on egress. The PVID in the inserted (or replaced) VLAN tag on egress can indicate the source port of the packet. Using this function, VID information carried in the VLAN tag will be changed to PVID. The RTL8305NBI also provides an option to admit VLAN tagged packets with a specific PVID only. If this function is enabled, it will drop non-tagged packets and packets with an incorrect PVID. -## 8.1.3. IEEE 802.1Q Tagged-VID Based VLAN +#### 8.1.3. IEEE 802.1Q Tagged-VID Based VLAN The RTL8305NBI supports 16 VLAN entries to perform IEEE 802.1Q-tagged VID-based VLAN mapping. The RTL8305NBI uses a 12-bit explicit identifier in the VLAN tag to associate received packets with a VLAN. If the VID of a VLAN-tagged frame does not match any of the 16 VLAN entries, the RTL8305NBI will drop the frame. Otherwise, the RTL8305NBI compares the explicit identifier in the VLAN tag with the 16 VLAN IDs to determine the VLAN association of this packet, and then forwards this packet to the member set of this VLAN. @@ -1039,11 +1144,11 @@ Two VIDs are reserved for special purposes; one of them is all 1's, which is res When ‘802.1Q tag aware VLAN’ is enabled, the RTL8305NBI performs 802.1Q tag-based VLAN mapping for tagged frames, but still performs port-based VLAN mapping for untagged frames. If ‘802.1Q tag aware VLAN’ is disabled, the RTL8305NBI performs only port-based VLAN mapping both on non-tagged and tagged frames. -## 8.1.4. Insert/Remove/Replace Tag +#### 8.1.4. Insert/Remove/Replace Tag The RTL8305NBI supports the VLAN Insertion/Removal/replacing action for each port. The 802.1Q VLAN tags can be inserted, removed, or replaced based on the port's setting. -## 8.1.5. Ingress and Egress Rules +#### 8.1.5. Ingress and Egress Rules The RTL8305NBI provides flexible VLAN ingress and egress rules to permit comprehensive traffic control. The RTL8305NBI can filter packets on ingress according to the tag condition of the packet. For a normalized VLAN application and VLAN translation application, each of the RTL8305NBI ports can be independently configured to: @@ -1063,7 +1168,7 @@ For the egress filter, the RTL8305NBI drops the frame if this frame belongs to a - ‘ARP leaky VLAN’ enables broadcasting of ARP packets to all other ports, ignoring the egress rule - ‘Inter-VLAN mirror function’ enables the inter-VLAN mirror function, ignoring the VLAN member set domain limitation. The default value is ‘Enable the inter-VLAN mirror’ -## 8.2. IEEE 802.1p Remarking Function +### 8.2. IEEE 802.1p Remarking Function The RTL8305NBI provides IEEE 802.1p Remarking ability. Each port can enable or disable IEEE 802.1p Remarking ability. @@ -1073,11 +1178,11 @@ If the port's 1p remarking function is enabled, transmitting VLAN tagged packets When the VLAN tags are inserted to non-tagged packets, the inserted tag's priority will accord with the 1p remarking table, even if the port's 1p remarking function is disabled. When the VLAN tag is replaced on tagged packets and the 1p remarking function is disabled, the VLAN tag's VID will be replaced but the priority will not change. For a VLAN-tagged packet, the VID and 3-bit priority can be replaced by the RTL8305NBI independently. -## 8.3. QoS Function +### 8.3. QoS Function -## 8.3.1. Bandwidth Control +#### 8.3.1. Bandwidth Control -## 8.3.1.1 Output (TX) Bandwidth Control +##### 8.3.1.1 Output (TX) Bandwidth Control The RTL8305NBI supports MIN-MAX packet scheduling. @@ -1104,9 +1209,9 @@ graph LR ``` -Figure 14. Packet-Scheduling Diagram +**Figure 14. Packet-Scheduling Diagram** -## Weighted Round Robin (WRR) +##### Weighted Round Robin (WRR) WRR adds weighting on the basis of Round Robin; for example, assume Q3:Q2:Q1:Q0: 4:3:2:1, then the transmit order will be: @@ -1121,21 +1226,21 @@ WRR guarantees a minimal packet rate for one queue only. If there is strict priority (only in Q2 and Q3) and WRR at the same time, the queue with strict priority has higher priority than WRR. When the scheduler scans queues, queues with strict priority are scanned first, and then the other queues are scanned according to WRR. If there is more than one queue with strict priority, the queue with the bigger QID has higher priority. -## 8.3.1.2 Input (RX) Bandwidth Control +##### 8.3.1.2 Input (RX) Bandwidth Control Input bandwidth control limits the input bandwidth. When input traffic is more than the RX Bandwidth parameter, this port will either send out a ‘pause ON’ frame, or drop the input packet depending on flow control status. The input bandwidth can also be configured from 0kbps to the line rate in steps of 64kbps. -## 8.3.2. Priority Assignment +#### 8.3.2. Priority Assignment Priority assignment specifies the priority of a received packet according to various rules. The RTL8305NBI can recognize the QoS priority information of incoming packets to give a different egress service priority. The RTL8305NBI identifies the priority of packets based on several types of QoS priority information: -• Port-based priority -• IEEE 802.1p/Q VLAN Priority Tag +- Port-based priority +- IEEE 802.1p/Q VLAN Priority Tag - DSCP Priority field - IP Address -• Reassigned priority +- Reassigned priority - RLDP priority Below is a block diagram of the priority assignment. @@ -1166,41 +1271,41 @@ graph LR ``` -Figure 15. RTL8305NBI Priority Assignment Diagram +**Figure 15. RTL8305NBI Priority Assignment Diagram** -## 8.3.2.1 Queue Number Selection +##### 8.3.2.1 Queue Number Selection In the RTL8305NBI, the output queue number can be set. All ports follow a global configuration. The maximum number of output queues per port is 4. After changing the queue number via SMI (Serial Management Interface), the external device must perform a soft reset in order to update the configuration. -## 8.3.2.2 Port-Based Priority Assignment +##### 8.3.2.2 Port-Based Priority Assignment Each physical port is assigned a 2-bit priority level. Packets received from a high-priority port are sent to the high-priority queue of the destination port. Port-based priority can be disabled by register setting. -## 8.3.2.3 IEEE 802.1p/Q-Based Priority Assignment +##### 8.3.2.3 IEEE 802.1p/Q-Based Priority Assignment In IEEE 802.1Q-based priority assignment, when a packet is VLAN-tagged or priority-tagged, the 3-bit priority is specified by tag. When a packet is untagged, the 802.1Q-based priority is assigned to the default 2-bit priority information of a physical port. So, each port must provide a default 2-bit priority (every received packet must be assigned a 2-bit 1Q-Based Priority). When the priority comes from a packet, the 1Q-based priority is acquired by mapping 3-bit tag priority to 2-bit priority though an RTL8305NBI 1Q-based Priority Mapping Table. The 1Q-based priority can be disabled. -## 8.3.2.4 DSCP-Based Priority Assignment +##### 8.3.2.4 DSCP-Based Priority Assignment DSCP (Differentiated Services Code Point)-based priority assignment maps the DSCP of an IP packet to 2-bit priority information through a DSCP to priority table, as DSCP is only in the IP packet. A non-IP packet (such as a Layer 2 frame, ARP, etc) will get a NULL instead of a 2-bit priority. For an IPv6 IP header, DSCP-based priority assignment acquires the DSCP value according to the class of IPv6 header. In the RTL8305NBI, DSCP-based priority assignment provides a DSCP to Priority Table of all DSCP value. If the DSCP of a packet is not matched in the table, the DSCP-based priority is 2'b00, The DSCP-based priority can be disabled by register. -## 8.3.2.5 IP Address-Based Priority +##### 8.3.2.5 IP Address-Based Priority When IP-based priority is enabled, any incoming packets with source or destination IP address equal to the configuration in register IP Priority Address [A] and IP Priority Mask [A], or IP Priority Address [B] and IP Priority Mask [B] will be set to a 2-bit priority. IP priority [A] and IP priority [B] may be enabled or disabled independently. IP address-based priority can be enabled or disabled by the control register. -## 8.3.2.6 Reassigned Priority +##### 8.3.2.6 Reassigned Priority RTL8305NBI can reassign the priority mainly according to the packets' DMAC information. This function is used to differentiate the priority of the Layer 2 control packet, broadcast packet, multicast packet, unicast packet, and so on. -## 8.3.2.7 RLDP-Based Priority +##### 8.3.2.7 RLDP-Based Priority To support the loop detection effectively, the RTL8305NBI provides the RLDP-based priority assignment. When it is enabled, the pre-defined priority will be assigned to all RLDP packets. -## 8.3.2.8 Packet Priority Selection +##### 8.3.2.8 Packet Priority Selection As one received packet may simultaneously support several priority assignment mechanisms, e.g., Port-Based Priority, 1Q-Based Priority, DSCP-Based Priority, it may get several different priority values. @@ -1211,19 +1316,19 @@ As one received packet may simultaneously support several priority assignment me There is a 2-bit register for each of the three types of priority that represent the weight of the priority. The higher value in the register indicates a higher weight for the priority. If more than one of the three types of priority is the same, the final priority will be the one of the three types, whose priority value is greatest. -## Queue Priority Mapping +##### Queue Priority Mapping The 2-bit priority has four numbers; however, every port has at most four output queues, so every port needs a User Priority to Traffic Class Mapping Table to map the priority to QID. A set of Traffic Class Mapping Tables is provided for each port independently. There is a mechanism to prevent a problem caused by mapping the traffic to an unused queue. For example, when a port's queue number is 2, the queue 2 and queue 3 are not used and mapping the traffic to queue 2 or queue 3 will cause the system to crash. In the mechanism, traffic mapped to the unused queue will be forced to the highest used queue (queue 2 in a 3-queue case, queue 1 in a 2-queue case, queue 0 in a 1-queue case). In the example, the traffic mapped to a port's queue 2 or queue 3 will be forwarded to queue 1. -## 8.4. Lookup Table Function +### 8.4. Lookup Table Function -## 8.4.1. Function Description +#### 8.4.1. Function Description -• 2048-entry Lookup Table (LUT) -• 4-way entry for each entry index +- 2048-entry Lookup Table (LUT) +- 4-way entry for each entry index - Supports LRU (Least Recently Used) function for lookup table learning -## 8.4.2. Address Search, Learning, and Aging +#### 8.4.2. Address Search, Learning, and Aging Received packets are forwarded according to the information learned or written into the LUT. When a packet is received, the RTL8305NBI tries to retrieve learned information and assign a forwarding destination port to the packet. @@ -1257,9 +1362,15 @@ MAC15 = Index5 ^ FID0 ^ MAC22 ^ MAC29 ^ MAC36 ^ MAC43 The hashed index key is used to locate a matching LUT entry. There are 4 entries sharing one index key (Table 15). This is called a 4-way hash. It is helpful to minimize address collisions in the address learning process. The address search engine compares the DA packet with the data in 4 entries, from entry 3 to entry 0. The final forwarding destination is abstracted from the first matching entry. If the address search fails to return a matching LUT entry, the packet will be flooded to appropriate ports. -Table 15. L2 Table 4-Way Hash Index Method +**Table 15. L2 Table 4-Way Hash Index Method** -
IndexEntry 0Entry 1Entry 2Entry 3
0x00MAC Addr 0MAC Addr 1MAC Addr 2MAC Addr 3
0x01MAC Addr 4MAC Addr 5MAC Addr 6MAC Addr 7
0x02MAC Addr 8MAC Addr 9MAC Addr 10MAC Addr 11
...............
0x1FEMAC Addr 2040MAC Addr 2041MAC Addr 2042MAC Addr 2043
0x1FFMAC Addr 2044MAC Addr 2045MAC Addr2046MAC Addr 2047
+| **Index** | **Entry 0** | **Entry 1** | **Entry 2** | **Entry 3** | +| 0x00 | MAC Addr 0 | MAC Addr 1 | MAC Addr 2 | MAC Addr 3 | +| 0x01 | MAC Addr 4 | MAC Addr 5 | MAC Addr 6 | MAC Addr 7 | +| 0x02 | MAC Addr 8 | MAC Addr 9 | MAC Addr 10 | MAC Addr 11 | +| ... | ... | ... | ... | ... | +| 0x1FE | MAC Addr 2040 | MAC Addr 2041 | MAC Addr 2042 | MAC Addr 2043 | +| 0x1FF | MAC Addr 2044 | MAC Addr 2045 | MAC Addr2046 | MAC Addr 2047 | Address learning is the gathering process and storing of information from received packets for the future purpose of forwarding frames addressed to the receiving port. The information includes the source MAC address (SA) and the receiving port. As with the hash algorithm, an address search is used in address learning. The SA of the received packet is used to calculate the entry index. The receiving port information and the aging timer of the first matching entry will be updated when an address is learned. If there is no matching entry, the packet's information will be ‘learned’ into the first empty entry. The SA will not be learned when all of the 4 entries are occupied. The address learning process can be disabled on a per-port basis via register setting. @@ -1267,13 +1378,13 @@ For unicast packet learning & search, and multicast packet search, the RTL8305NB Address aging is used to keep the contents of the learned address table updated in a dynamic network topology. The look-up engine will update the aging timer of an entry whenever the corresponding SA appears. An entry will be invalid (aged out) if its aging timer is not refreshed by the address learning process during the aging time period. The aging time of the RTL8305NBI is between 200 and 400 seconds. The RTL8305NBI also supports a fast aging function that is used to age all dynamic entries within 1ms. -## 8.4.3. Lookup Table Definition +#### 8.4.3. Lookup Table Definition In traditional switch learning, if a MAC address hash collision occurs then the later MAC address in the collision will not be learned into the lookup table. The LRU function attempts to resolve this problem. -When Enable LRU = 0b1, then the LRU function is enabled. If the Source MAC address of the incoming packet encounters a hash collision during the learning process and when the 4-way entries are all occupied, then the switch will learn the address in one of the 4-way entries using the LRU aging timer. The criteria for selecting the entry to over-write is comparing via the aging timer and choosing the oldest entry. If the aging timer of the 4 entries are the same, then the entry with the highest Entry\_Address[1:0] value is selected to be over-written. +When Enable LRU = 0b1, then the LRU function is enabled. If the Source MAC address of the incoming packet encounters a hash collision during the learning process and when the 4-way entries are all occupied, then the switch will learn the address in one of the 4-way entries using the LRU aging timer. The criteria for selecting the entry to over-write is comparing via the aging timer and choosing the oldest entry. If the aging timer of the 4 entries are the same, then the entry with the highest Entry_Address[1:0] value is selected to be over-written. -## 8.5. Storm Filter Function +### 8.5. Storm Filter Function The RTL8305NBI can effectively control four-types of broadcast storms; those caused by broadcast packets, multicast packets, unknown multicast packets, and unknown DA unicast packets. @@ -1285,7 +1396,7 @@ An unknown DA unicast packet is a packet whose DA is a unicast address and is no The RTL8305NBI can configure a storm filter rate for these four packet types, and the rate unit can be configured as packet-based or byte-based via registers. The storm filter rate limits the packet forwarding rate to less than the rate threshold. -## 8.6. Input and Output Drop Function +### 8.6. Input and Output Drop Function If some destination ports are blocking, or the buffer is full, the frames to these ports will be dropped. @@ -1330,7 +1441,7 @@ graph LR ``` -Figure 16. Broadcast Input Drop vs. Output Drop +**Figure 16. Broadcast Input Drop vs. Output Drop** 1. Multicast packet from Port 0. 2. Buffer of Port 4 is full, Port 1 is not full. @@ -1365,9 +1476,9 @@ graph LR ``` -Figure 17. Multicast Input Drop vs. Output Drop +**Figure 17. Multicast Input Drop vs. Output Drop** -## 8.7. LED Function +### 8.7. LED Function The RTL8305NBI provides flexible LED functions for diagnostics. The LEDs can be configured to indicate the link information (link, activity, speed, duplex), and collision & loop detection information. @@ -1375,20 +1486,20 @@ The parallel LED for each port indicates the port's link information when loop-d All LED statuses are represented as active-low or high depending on input strapping. -LED\_BLINK\_TIME determines the LED blinking period for activity and collision via register (0: 32ms and 1: 128ms). +LED_BLINK_TIME determines the LED blinking period for activity and collision via register (0: 32ms and 1: 128ms). Some LED pins are dual function pins: input operation for configuration upon reset, and output operation for LED after reset. If the pin input is floating upon reset, the pin output is active high after reset. Otherwise, if the pin input is pulled high upon reset, the pin output is active low after reset. Figure 18 shows example circuits for LEDs. Typical values for pull-down resistors are 10KΩ. ![](images/0adbe7f62e1c38cd50ea0890e6ebd215d7c8805643091907bd9562688f173534.jpg) -Figure 18. Floating and Pull-High of LED Pins for LED +**Figure 18. Floating and Pull-High of LED Pins for LED** -## 8.8. Energy-Efficient Ethernet (EEE) +### 8.8. Energy-Efficient Ethernet (EEE) The RTL8305NBI supports Energy-Efficient Ethernet (EEE) function as defined in IEEE 802.3az. The EEE function implements the Low Power Idle (LPI) mode at 100Mbps operation to save power during periods of low link utilization. In Low Power Idle mode, devices on both sides of the link disable portions of the functionality to lower the power consumption. -At the transmitter side, the RTL8305NBI port 0\~4 can automatically enter or quit LPI mode based on their transmission loading. When a port's EEE function is enabled, the transmission loading is monitored in real time. If the transmission loading is lower than a preset threshold, this port's transmission circuit will enter LPI mode during the idle period. When there are packets to be transmitted, this port wakes up and quits LPI mode. +At the transmitter side, the RTL8305NBI port 0~4 can automatically enter or quit LPI mode based on their transmission loading. When a port's EEE function is enabled, the transmission loading is monitored in real time. If the transmission loading is lower than a preset threshold, this port's transmission circuit will enter LPI mode during the idle period. When there are packets to be transmitted, this port wakes up and quits LPI mode. There are two types of wake-up: @@ -1403,31 +1514,59 @@ The RTL8305NBI also supports EEE at 10Mbps operation by reducing the transmit am The EEE function for each port is enabled by default and can be disabled independently via strapping pin, registers, or EEPROM configurations. -## 8.9. Cable Diagnosis +### 8.9. Cable Diagnosis The RTL8305NBI physical layer transceivers use DSP technology to implement the Realtek Cable Tester (RTCT) feature for cable diagnosis. The RTCT feature can detect short, open, or normal in both differential pair signal runs. ## 9. Characteristics -## 9.1. Electrical Characteristics/Maximum Ratings +### 9.1. Electrical Characteristics/Maximum Ratings WARNING: Maximum ratings are limits beyond which permanent damage may be caused to the device or which may affect device reliability. All voltages are specified reference to GND unless otherwise specified. -Table 16. Electrical Characteristics/Maximum Ratings +**Table 16. Electrical Characteristics/Maximum Ratings** -
ParameterMinMaxUnits
DVDDH, AVDDH, AVDDHPLL Supply Referenced to GNDGND-0.3+3.63V
DVDDL, AVDDL, AVDDLPLL Supply Referenced to GNDGND-0.3+1.10V
+| **Parameter** | **Min** | **Max** | **Units** | +| DVDDH, AVDDH, AVDDHPLL Supply Referenced to GND | GND-0.3 | +3.63 | V | +| DVDDL, AVDDL, AVDDLPLL Supply Referenced to GND | GND-0.3 | +1.10 | V | -## 9.2. Operating Range +### 9.2. Operating Range -Table 17. Operating Range +**Table 17. Operating Range** -
ParameterMinMaxUnits
Storage Temperature-55+150°C
Ambient Operating Temperature (Ta)-40+85°C
3.3V Vcc Supply Voltage Range (DVDDH, AVDDH, AVDDHPLL)3.133.47V
1.0V Vcc Supply Voltage Range (DVDDL, AVDDL, AVDDLPLL)0.951.05V
+| **Parameter** | **Min** | **Max** | **Units** | +| Storage Temperature | -55 | +150 | °C | +| Ambient Operating Temperature (Ta) | -40 | +85 | °C | +| 3.3V Vcc Supply Voltage Range (DVDDH, AVDDH, AVDDHPLL) | 3.13 | 3.47 | V | +| 1.0V Vcc Supply Voltage Range (DVDDL, AVDDL, AVDDLPLL) | 0.95 | 1.05 | V | -## 9.3. DC Characteristics +### 9.3. DC Characteristics -Table 18. DC Characteristics +**Table 18. DC Characteristics** -
ParameterSYMConditionMinTypicalMaxUnits
TTL Input High Voltage $V_{ih}$ -2.0--V
TTL Input Low Voltage $V_{il}$ ---0.8V
TTL Input Current $I_{in}$ --10-10μA
TTL Input Capacitance $C_{in}$ --3-pF
Output High Voltage $V_{oh}$ -2.25--V
Output Low Voltage $V_{ol}$ ---0.4V
Output Three State Leakage Current $|I_{OZ}|$ ---10μA
Power Supply Current for 1.0VIcc10Base-T, idle-31-mA
10Base-T, Peak continuous 100% utilization-32-
100Base-TX, idle-81-
100Base-TX, Peak continuous 100% utilization-81-
Link down-30-
Power Supply Current for 3.3VIcc10Base-T, idle-18-mA
10Base-T, Peak continuous 100% utilization-81-
100Base-TX, idle-95-
100Base-TX, Peak continuous 100% utilization-95-
Link down-18-
Total Power Consumption for All PortsPS10Base-T, idle-89.4-mW
10Base-T, Peak continuous 100% utilization-301.6-
100Base-TX, idle-391.5-
100Base-TX, Peak continuous 100% utilization-393.5-
Link down-89.4-
+| **Parameter** | **SYM** | **Condition** | **Min** | **Typical** | **Max** | **Units** | +| TTL Input High Voltage | $V_{ih}$ | - | 2.0 | - | - | V | +| TTL Input Low Voltage | $V_{il}$ | - | - | - | 0.8 | V | +| TTL Input Current | $I_{in}$ | - | -10 | - | 10 | μA | +| TTL Input Capacitance | $C_{in}$ | - | - | 3 | - | pF | +| Output High Voltage | $V_{oh}$ | - | 2.25 | - | - | V | +| Output Low Voltage | $V_{ol}$ | - | - | - | 0.4 | V | +| Output Three State Leakage Current | $\|I_{OZ}\|$ | - | - | - | 10 | μA | +| Power Supply Current for 1.0V | Icc | 10Base-T, idle | - | 31 | - | mA | +| | | 10Base-T, Peak continuous 100% utilization | - | 32 | - | | +| | | 100Base-TX, idle | - | 81 | - | | +| | | 100Base-TX, Peak continuous 100% utilization | - | 81 | - | | +| | | Link down | - | 30 | - | | +| Power Supply Current for 3.3V | Icc | 10Base-T, idle | - | 18 | - | mA | +| | | 10Base-T, Peak continuous 100% utilization | - | 81 | - | | +| | | 100Base-TX, idle | - | 95 | - | | +| | | 100Base-TX, Peak continuous 100% utilization | - | 95 | - | | +| | | Link down | - | 18 | - | | +| Total Power Consumption for All Ports | PS | 10Base-T, idle | - | 89.4 | - | mW | +| | | 10Base-T, Peak continuous 100% utilization | - | 301.6 | - | | +| | | 100Base-TX, idle | - | 391.5 | - | | +| | | 100Base-TX, Peak continuous 100% utilization | - | 393.5 | - | | +| | | Link down | - | 89.4 | - | | Note: All power supply currents are measured under the following conditions: 1. DVDDL=AVDDL=AVDDHPLL=1.0V; DVDDH=AVDDH=AVDDHPLL=3.3V. @@ -1436,33 +1575,44 @@ Note: All power supply currents are measured under the following conditions: 4. All LEDs are in low-active mode. 5. LDO power is not included. -## 9.4. Thermal Characteristics +### 9.4. Thermal Characteristics -## 9.4.1. Simulation Conditions +##### 9.4.1. Simulation Conditions -Table 19. PCB Descriptions +**Table 19. PCB Descriptions** -
PCBPCB Dimension (L x W)79.7 x 50.8mm
PCB Thickness1.6mm
PCB Top GND Plane Size75%
Number of Cu Layer-PCB1-Layer
+| **PCB** | **PCB Dimension (L x W)** | **79.7 x 50.8mm** | +| | PCB Thickness | 1.6mm | +| | PCB Top GND Plane Size | 75% | +| | Number of Cu Layer-PCB | 1-Layer | -Table 20. Condition Descriptions +**Table 20. Condition Descriptions** -
Input Power0.6 W
Test Board (PCB)1 Layer
Control ConditionAir Flow= 0, 1, 2, 3ms
+| **Input Power** | **0.6 W** | +| Test Board (PCB) | 1 Layer | +| Control Condition | Air Flow= 0, 1, 2, 3ms | -## 9.4.2. Thermal Characteristics Results +##### 9.4.2. Thermal Characteristics Results -Table 21. Thermal Characteristics Results +**Table 21. Thermal Characteristics Results** -
Air Flow (ms)0123
θJA (°C/W)51.645.643.441.7
ψJT (°C/W)0.8---
ψJB (°C/W)24.6---
Max. Junction Temperature125°C
+| **Air Flow (ms)** | **0** | **1** | **2** | **3** | +| θJA (°C/W) | 51.6 | 45.6 | 43.4 | 41.7 | +| ψJT (°C/W) | 0.8 | - | - | - | +| ψJB (°C/W) | 24.6 | - | - | - | +| Max. Junction Temperature | 125°C | | | | -## 9.5. Digital Timing Characteristics +### 9.5. Digital Timing Characteristics -## 9.5.1. LED Timing +##### 9.5.1. LED Timing -Table 22. LED Timing +**Table 22. LED Timing** -
ParameterSYMConditionMinTypicalMaxUnits
LED On TimetLEDonLED Blinking to Indicate Link Information32-128ms
LED Off TimetLEDoffLED Blinking to Indicate Link Information32-128ms
+| **Parameter** | **SYM** | **Condition** | **Min** | **Typical** | **Max** | **Units** | +| LED On Time | tLEDon | LED Blinking to Indicate Link Information | 32 | - | 128 | ms | +| LED Off Time | tLEDoff | LED Blinking to Indicate Link Information | 32 | - | 128 | ms | -## 9.5.2. Reception/Transmission Data Timing of SMI Interface +##### 9.5.2. Reception/Transmission Data Timing of SMI Interface ![](images/7d971618b48b9647493fb7939595c2c8a2f31c4e06308131dc583addd2400177.jpg) @@ -1475,7 +1625,7 @@ TIH MDIO -Figure 19. Reception Data Timing of SMI Interface +**Figure 19. Reception Data Timing of SMI Interface** ![](images/b95afb7d11a0eb43124d7c3287be746dd592e42aa3991a53342b1b722202b104.jpg) @@ -1488,13 +1638,17 @@ MDIO TD -Figure 20. Transmission Data Timing of SMI Interface +**Figure 20. Transmission Data Timing of SMI Interface** -Table 23. SMI Timing +**Table 23. SMI Timing** -
ParameterSYMDescriptionI/OMinTypeMaxUnits
MDC $T_{cyc}$ MDC Clock CycleI400--ns
MDIO Input Setup Time $T_{IS}$ MDIO to MDC Rising Edge Setup TimeI50--ns
MDIO Input Hold Time $T_{IH}$ MDIO to MDC Rising Edge Hold TimeI10--ns
MDIO Output Delay Time $T_{D}$ MDIO to MDC Rising Edge Output DelayO2-10ns
+| **Parameter** | **SYM** | **Description** | **I/O** | **Min** | **Type** | **Max** | **Units** | +| MDC | $T_{cyc}$ | MDC Clock Cycle | I | 400 | - | - | ns | +| MDIO Input Setup Time | $T_{IS}$ | MDIO to MDC Rising Edge Setup Time | I | 50 | - | - | ns | +| MDIO Input Hold Time | $T_{IH}$ | MDIO to MDC Rising Edge Hold Time | I | 10 | - | - | ns | +| MDIO Output Delay Time | $T_{D}$ | MDIO to MDC Rising Edge Output Delay | O | 2 | - | 10 | ns | -## 9.5.3. EEPROM Auto-Load Timing +##### 9.5.3. EEPROM Auto-Load Timing ![](images/b813a873447159e8f9aacb5135706460e80e1c779e67ac3ea33c692d197c3df9.jpg) @@ -1511,28 +1665,46 @@ SDA Data Output Data Output Data Input Data Input -Figure 21. EEPROM Auto-Load Timing +**Figure 21. EEPROM Auto-Load Timing** -Table 24. EEPROM Auto-Load Timing Characteristics +**Table 24. EEPROM Auto-Load Timing Characteristics** -
SymbolDescriptionMinTypicalMaxUnits
t1SCL High Time-2.52-μs
t2SCL Low Time-2.52-μs
t3START Condition Setup Time-2.52-μs
t4START Condition Hold Time-2.52-μs
t5Data In Hold Time0--ns
t6Data In Setup Time100--ns
t7Data Output Hold Time-1.28-μs
t8STOP Condition Setup Time-2.52-μs
+| **Symbol** | **Description** | **Min** | **Typical** | **Max** | **Units** | +| t1 | SCL High Time | - | 2.52 | - | μs | +| t2 | SCL Low Time | - | 2.52 | - | μs | +| t3 | START Condition Setup Time | - | 2.52 | - | μs | +| t4 | START Condition Hold Time | - | 2.52 | - | μs | +| t5 | Data In Hold Time | 0 | - | - | ns | +| t6 | Data In Setup Time | 100 | - | - | ns | +| t7 | Data Output Hold Time | - | 1.28 | - | μs | +| t8 | STOP Condition Setup Time | - | 2.52 | - | μs | ## 10. Mechanical Dimensions ![](images/f292d0b643217da7989de2986c95f0561d2d48ed985fd11b0d171eac4d8d757a.jpg) -10.1. Mechanical Dimensions Notes +### 10.1. Mechanical Dimensions Notes -
SymbolDimension in mmDimension in inch
MinNomMaxMinNomMax
A0.750.851.000.0300.0340.039
A10.000.020.050.0000.0010.002
A30.20 REF0.008 REF
b0.150.200.250.0060.0080.010
D/E6.00BSC0.236BSC
D2/E24.154.44.650.1630.1730.183
e0.40BSC0.016BSC
L0.300.400.500.0120.0160.020
+| **Symbol** | **Dimension in mm** | | | **Dimension in inch** | | | +| | Min | Nom | Max | Min | Nom | Max | +| A | 0.75 | 0.85 | 1.00 | 0.030 | 0.034 | 0.039 | +| A1 | 0.00 | 0.02 | 0.05 | 0.000 | 0.001 | 0.002 | +| A3 | 0.20 REF | | | 0.008 REF | | | +| b | 0.15 | 0.20 | 0.25 | 0.006 | 0.008 | 0.010 | +| D/E | 6.00BSC | | | 0.236BSC | | | +| D2/E2 | 4.15 | 4.4 | 4.65 | 0.163 | 0.173 | 0.183 | +| e | 0.40BSC | | | 0.016BSC | | | +| L | 0.30 | 0.40 | 0.50 | 0.012 | 0.016 | 0.020 | Note 1: CONTROLLING DIMENSION: MILLIMETER (mm). Note 2: REFERENCE DOCUMENT: JEDEC MO-220. ## 11. Ordering Information -Table 25. Ordering Information +**Table 25. Ordering Information** -
Part NumberPackageStatus
RTL8305NBI-CG48-Pin QFN in ‘Green’ Package (RoHS Compliant)MP
+| **Part Number** | **Package** | **Status** | +| RTL8305NBI-CG | 48-Pin QFN in ‘Green’ Package (RoHS Compliant) | MP | Note: See page 5 for package identification. diff --git a/docs/芯片手册/SD2506API-G.md b/docs/芯片手册/SD2506API-G.md index d6e389f8..18cf6a5b 100644 --- a/docs/芯片手册/SD2506API-G.md +++ b/docs/芯片手册/SD2506API-G.md @@ -1,8 +1,8 @@ -## 内置晶振、充电电池、温度补偿的高精度实时时钟 +# SD2506API-G -## SD2506API-G +内置晶振、充电电池、温度补偿的高精度实时时钟 -## 1.概述 +## 1. 概述 SD2506API是一种内置晶振、充电电池、温度补偿及标准 IIC 接口的实时时钟模块,CPU可使用该接口通过 7位地址来寻址读写片内 122字节的数据(包括时间寄存器、报警寄存器、控制寄存器、温度寄存器、电池电量寄存器、70字节的用户 SRAM寄存器及 8 字节的 ID 码寄存器)。 @@ -12,41 +12,40 @@ SD2506API内置充电电池及充电电路,在电池满充的常温情况下内 SD2506API内置 8 字节的 ID,每一颗模块具备唯一的身份识别码。 -SD2506API内置单路定时/报警中断输出,报警中断时间可最长设至 100年;该系列模块可满足对实时时钟模块的各种需要,且管脚与以前的 SD2405兼容,软件部分绝大部分兼容(重要区别在:强烈建议用户的程序在每一次上电时重置 18H 寄存器的值为 82H,用于确保开启充电功能),是在选用高精度实时时钟时的理想选择。 +SD2506API内置单路定时/报警中断输出,报警中断时间可最长设至 100年;该系列模块可满足对实时时钟模块的各种需要,且管脚与以前的 SD2405兼容,软件部分绝大部分兼容(重要区别在:强烈建议用户的程序在每一次上电时重置 18H 寄存器的值为 82H,用于确保开启充电功能),是在选用高精度实时时钟时的理想选择。 -## 2.特性 +## 2. 特性 -低功耗: 0.8μA 典型值(内部电池供电,Ta=25℃)。 -工作电压:2.7V~5.5V,工作温度:-30℃~+80℃。 -标准IIC总线接口方式,最高速度400KHZ(4.5V~5.5V)。 - 年、月、日、星期、时、分、秒的BCD码输入/输出,并可通过独立的地址访问各时间寄存器。 -闰年自动调整功能(从2000年~2099年)。 -可选择12/24小时制式. -. 内置年、月、日、星期、时、分、秒共7字节的报警数据寄存器及1字节的报警允许寄存器。 -内置70字节通用SRAM寄存器可用于存储用户的一般数据。 -三种中断均可选择从INT脚输出,并具有两个中断标志位。 - 内置年、月、日、星期、时、分、秒共7字节的报警数据寄存器及1字节的报警允许寄存器,共有96种组合报警方式,并有单事件报警和周期性报警两种中断输出模式,报警时间最长可设至100年。 -周期性频率中断输出:从4096Hz~1/16Hz……1秒共十四种方波脉冲. +- 低功耗: 0.8μA 典型值(内部电池供电,Ta=25℃)。 +- 工作电压:2.7V~5.5V,工作温度:-30℃~+80℃。 +- 标准IIC总线接口方式,最高速度400KHZ(4.5V~5.5V)。 +- 年、月、日、星期、时、分、秒的BCD码输入/输出,并可通过独立的地址访问各时间寄存器。 +- 闰年自动调整功能(从2000年~2099年)。 +- 可选择12/24小时制式. +- 内置年、月、日、星期、时、分、秒共7字节的报警数据寄存器及1字节的报警允许寄存器。 +- 内置70字节通用SRAM寄存器可用于存储用户的一般数据。 +- 三种中断均可选择从INT脚输出,并具有两个中断标志位。 +- 内置年、月、日、星期、时、分、秒共7字节的报警数据寄存器及1字节的报警允许寄存器,共有96种组合报警方式,并有单事件报警和周期性报警两种中断输出模式,报警时间最长可设至100年。 +- 周期性频率中断输出:从4096Hz~1/16Hz……1秒共十四种方波脉冲. +- 自动重置的三字节共24位的倒计时定时器,可选的2种时钟源(4096Hz、1024Hz)。 +- 内置8bit转换结果的数字温度传感器,为了节省电池电量消耗,设为VDD模式下60S间隔测温一次,电池模式600S间隔测温一次。 +- 内置记录历史高、低温发生时刻及相关温度值的功能;可设置高、低温报警值并从INT脚输出中断。 +- 内置晶振和谐振电容,模块内部通过高精度补偿方法,实现在宽温范围内高精度的计时功能,其中25℃精度<±3.8ppm,即时钟年误差小于2分钟(在25±1℃下)。 +- 内置充电电池及充电电路,累计电池电量超过550mAH,在电池满充的常温情况下内部时钟走时时间在8个月左右,电池使用寿命为5~8年时间。 +- 内置电池电压检测功能,可读取当前电池电压值(三位有效数),设置高低电池报警电压值并从INT脚输出中断。 +- 模块依据不同的电压自动从VDD切换到VBAT或从VBAT切换到VDD。当模块检测到主电源VDD掉到2.4V电压以下且VDD小于VBAT,模块会转为由接在VBAT的后备电池供电;当VDD大于VBAT或VDD大于2.4V,则模块会转为由VDD供电。(内置电源模式指示位PMF,VDD模式时PMF=0,VBAT模式时PMF=1)。 +- 内置总线0.5秒自动复位功能(从Start命令开始计时),保证时钟数据的有效性及可靠性,避免IIC总线挂死问题。 +- 内置三个时钟数据写保护位, 避免对数据的误写操作,可更好地保护数据。 +- 内置软件可控VBAT模式IIC总线通信禁止功能(BATIIC=0,VBAT模式下禁止IIC通信;BATIIC=1,VBAT模式下允许IIC通信.上电默认值BATIIC=0),从而避免在电池供电时CPU对时钟操作所消耗的电池电量,也可避免在VDD上、下电的过程中因CPU的I/O端口所输出的不受控的杂波信号对时钟模块的误写操作,进一步提高时钟模块的可靠性。 +- 在VBAT模式下,模块具有中断输出允许或禁止的功能,可满足在备用电池供电时输出中断的需要。 +- 内置上电复位电路及指示位RTCF,当包括电池在内的所有电源第一次上电时该位置1。 +- 内置电源稳压,内部计时电压可低至2.0V。 +- 内置8字节的ID码,模块出厂之前设定的、全球唯一的身份识别码。 +- 模块在兴威帆的评估板上可通过4KV的群脉冲(EFT)干扰 +- CMOS工艺。 +- 封装形式:8脚的DIP封装。 -自动重置的三字节共24位的倒计时定时器,可选的2种时钟源(4096Hz、1024Hz)。 - 内置8bit转换结果的数字温度传感器,为了节省电池电量消耗,设为VDD模式下60S间隔测温一次,电池模式600S间隔测温一次。 - 内置记录历史高、低温发生时刻及相关温度值的功能;可设置高、低温报警值并从INT脚输出中断。 - 内置晶振和谐振电容,模块内部通过高精度补偿方法,实现在宽温范围内高精度的计时功能,其中25℃精度<±3.8ppm,即时钟年误差小于2分钟(在25±1℃下)。 - 内置充电电池及充电电路,累计电池电量超过550mAH,在电池满充的常温情况下内部时钟走时时间在8个月左右,电池使用寿命为5~8年时间。 - 内置电池电压检测功能,可读取当前电池电压值(三位有效数),设置高低电池报警电压值并从INT脚输出中断。 -模块依据不同的电压自动从VDD切换到VBAT或从VBAT切换到VDD。当模块检测到主电源VDD掉到2.4V电压以下且VDD小于VBAT,模块会转为由接在VBAT的后备电池供电;当VDD大于VBAT或VDD大于2.4V,则模块会转为由VDD供电。(内置电源模式指示位PMF,VDD模式时PMF=0,VBAT模式时PMF=1)。 - 内置总线0.5秒自动复位功能(从Start命令开始计时),保证时钟数据的有效性及可靠性,避免IIC总线挂死问题。 - 内置三个时钟数据写保护位, 避免对数据的误写操作,可更好地保护数据。 - 内置软件可控VBAT模式IIC总线通信禁止功能(BATIIC=0,VBAT模式下禁止IIC通信;BATIIC=1,VBAT模式下允许IIC通信.上电默认值BATIIC=0),从而避免在电池供电时CPU对时钟操作所消耗的电池电量,也可避免在VDD上、下电的过程中因CPU的I/O端口所输出的不受控的杂波信号对时钟模块的误写操作,进一步提高时钟模块的可靠性。 - 在VBAT模式下,模块具有中断输出允许或禁止的功能,可满足在备用电池供电时输出中断的需要。 - 内置上电复位电路及指示位RTCF,当包括电池在内的所有电源第一次上电时该位置1。 -内置电源稳压,内部计时电压可低至2.0V。 -内置8字节的ID码,模块出厂之前设定的、全球唯一的身份识别码。 -模块在兴威帆的评估板上可通过4KV的群脉冲(EFT)干扰 -CMOS工艺。 -封装形式:8脚的DIP封装。 - -## 3.原理框图和管脚定义 +## 3. 原理框图和管脚定义 ![](images/232802d20b9421a02d9d02b17c8f6a991fb908d9c88b558d1187f9bb4857dca5.jpg) @@ -108,19 +107,74 @@ GND 封装形式:DIP-8(引脚宽度 300mil) -表 1 SD2506API 管脚功能表 +**表 1 SD2506API 管脚功能表** -
脚号名称功能特征
1TEST内部电池电压测试脚,该电池通过510K电阻输出到该脚。悬空(不可与其它脚相连,仅用于测试)。
4GND负电源(GND)N沟道开路输出;CMOS输入
5SDA串行数据输入/输出脚,此管脚通常用一电阻上拉至VDD,并与其它漏极开路或集电器开路输出的器件通过线与方式连接。N沟道开路输出,CMOS输入;当模块由内部后备充电电池供电时,该引脚功能被禁止。
6SCL串行时钟输入脚,由于在SCL上升/下降沿处理信号,要特别注意SCL信号的上升/下降升降时间,应严格遵守说明书。与SCL相连的MCU端口最好设置为CMOS输出。CMOS输入,当模块由内部后备充电电池供电时,该引脚功能被禁止。
7INT报警中断输出脚,根据控制寄存器来设置其工作的模式,它可通过重写控制寄存器来禁止。N-沟道开路输出
8VDD正电源2.7V~5.5V
+| 脚号 | 名称 | 功能 | 特征 | +| --- | --- | --- | --- | +| 1 | TEST | 内部电池电压测试脚,该电池通过510K电阻输出到该脚。 | 悬空(不可与其它脚相连,仅用于测试)。 | +| 4 | GND | 负电源(GND) | N沟道开路输出;CMOS输入 | +| 5 | SDA | 串行数据输入/输出脚,此管脚通常用一电阻上拉至VDD,并与其它漏极开路或集电器开路输出的器件通过线与方式连接。 | N沟道开路输出,CMOS输入;当模块由内部后备充电电池供电时,该引脚功能被禁止。 | +| 6 | SCL | 串行时钟输入脚,由于在SCL上升/下降沿处理信号,要特别注意SCL信号的上升/下降升降时间,应严格遵守说明书。与SCL相连的MCU端口最好设置为CMOS输出。 | CMOS输入,当模块由内部后备充电电池供电时,该引脚功能被禁止。 | +| 7 | INT | 报警中断输出脚,根据控制寄存器来设置其工作的模式,它可通过重写控制寄存器来禁止。 | N-沟道开路输出 | +| 8 | VDD | 正电源 | 2.7V~5.5V | ## 4. 基本功能定义 -4.1寄存器列表 +### 4.1 寄存器列表 -
地址寄存器段寄存器名称BIT数值范围(十进制)复位值(二进制)
D7D6D5D4D3D2D1D0
00H实时时钟寄存器秒0S40S20S10S8S4S2S10-59XXXX-XXXX
01H分钟0MN40MN20MN10MN8MN4MN2MN10-59XXXX-XXXX
02H小时12_/240H20P/A_H10H8H4H2H10-23XXXX-XXXX
03H星期00000W4W2W10-6XXXX-XXXX
04H日00D20D10D8D4D2D11-31XXXX-XXXX
05H月000M010M08M04M02M011-12XXXX-XXXX
06H年Y80Y40Y20Y10Y8Y4Y2Y10-99XXXX-XXXX
07H时间报警寄存器秒报警0AS40AS20AS10AS8AS4AS2AS10-590000-0000
08H分钟报警0AMN40AMN20AMN10AMN8AMN4AMN2AMN10-590000-0000
09H小时报警00AH20AP/A_AH10AH8AH4AH2AH10-230000-0000
0AH星期报警0AW6AW5AW4AW3AW2AW1AW0N/A0000-0000
0BH日报警00AD20AD10AD8AD4AD2AD11-310000-0000
0CH月报警000AM010AM08AM04AM02AM011-120000-0000
0DH年报警AY7AY6AY5AY4AY3AY2AY1AY00-990000-0000
0EH报警允许0EAYEAM0EADEAWEAHEAMNEASN/A0000-0000
0FH控制寄存器CTR1WRTC3OSFINTAFINTDFBLFWRTC2PMFRTCFN/A0000-0000
10HCTR2WRTC1IMINTS1INTS0FOBATINTDEINTAEINTFEN/A0000-0000
11HCTR3ARSTF32KTDS1TDS0FS3FS2FS1FS0N/A0000-0000
12H25°C TTF(只读RAM)1ppm/3ppmF6F5F4F3F2F1F0N/A0000-0000
13H倒计时定时器TD7TD6TD5TD4TD3TD2TD1TD00-2550000-0000
14HTD15TD14TD13TD12TD11TD10TD9TD80-2550000-0000
15HTD23TD22TD21TD20TD19TD18TD17TD160-2550000-0000
16H温度寄存器TEMPTM7TM6TM5TM4TM3TM2TM1TMO0-255XXXX-XXXX
17HIIC控制寄存器AGTCBATIIC0------0-2550000-0000
18H充电寄存器CHARGEENCH-----Charge1Charge20-255CHARGE
19H扩展控制寄存器CTR4INTS_E2INTS_E1INTS_E0CONT_BATINTTHEINTTLEINTBHEINTBLE0-2550000_0000
1AHCTR5BAT8_VALSYSOSC_RDYBHFBLF0-2550000_0000
1BH电池电量BAT_VALBAT7_VALBAT6_VALBAT5_VALBAT4_VALBAT3_VALBAT2_VALBAT1_VALBATO_VAL0-2550000_0000
1CH低温报警值TEMP_ALTEMP7_ALTEMP6_ALTEMP5_ALTEMP4_ALTEMP3_ALTEMP2_ALTEMP1_ALTEMPO_AL0-2550000_0000
1DH高温报警值TEMP_AHTEMP7_AHTEMP6_AHTEMP5_AHTEMP4_AHTEMP3_AHTEMP2_AHTEMP1_AHTEMPO_AH0-2550000_0000
1EH历史最低温度TEMP_HIS_LTEMP_L7TEMP_L6TEMP_L5TEMP_L4TEMP_L3TEMP_L2TEMP_L1TEMP_L00-2550111_1111
1FH历史最高温度TEMP_HIS_HTEMP_H7TEMP_H6TEMP_H5TEMP_H4TEMP_H3TEMP_H2TEMP_H1TEMP_H00-2551000_0000
20H历史最低温度发生的时间分钟0-590000_0000
21H小时0-230000_0000
22H星期0-60000_0000
23H日1-310000_0000
24H月1-120000_0000
25H年0-990000_0000
26H历史最高温度发生的时间分钟0-590000_0000
27H小时0-230000_0000
28H星期0-60000_0000
29H日1-310000_0000
2AH月1-120000_0000
2BH年0-990000_0000
2CH~71H用户RAM(70Bytes)BIT7BIT6BIT5BIT4BIT3BIT2BIT1BIT00-255XXXX-XXXX
72H~79HID(只读)(8Bytes)BIT7BIT6BIT5BIT4BIT3BIT2BIT1BIT0N/AXXXX-XXXX
+| 地址 | 寄存器段 | 寄存器名称 | BIT | | | | | | | | 数值范围(十进制) | 复位值(二进制) | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| | | | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | | | +| 00H | 实时时钟寄存器 | 秒 | 0 | S40 | S20 | S10 | S8 | S4 | S2 | S1 | 0-59 | XXXX-XXXX | +| 01H | | 分钟 | 0 | MN40 | MN20 | MN10 | MN8 | MN4 | MN2 | MN1 | 0-59 | XXXX-XXXX | +| 02H | | 小时 | 12_/24 | 0 | H20P/A_ | H10 | H8 | H4 | H2 | H1 | 0-23 | XXXX-XXXX | +| 03H | | 星期 | 0 | 0 | 0 | 0 | 0 | W4 | W2 | W1 | 0-6 | XXXX-XXXX | +| 04H | | 日 | 0 | 0 | D20 | D10 | D8 | D4 | D2 | D1 | 1-31 | XXXX-XXXX | +| 05H | | 月 | 0 | 0 | 0 | M010 | M08 | M04 | M02 | M01 | 1-12 | XXXX-XXXX | +| 06H | | 年 | Y80 | Y40 | Y20 | Y10 | Y8 | Y4 | Y2 | Y1 | 0-99 | XXXX-XXXX | +| 07H | 时间报警寄存器 | 秒报警 | 0 | AS40 | AS20 | AS10 | AS8 | AS4 | AS2 | AS1 | 0-59 | 0000-0000 | +| 08H | | 分钟报警 | 0 | AMN40 | AMN20 | AMN10 | AMN8 | AMN4 | AMN2 | AMN1 | 0-59 | 0000-0000 | +| 09H | | 小时报警 | 0 | 0 | AH20AP/A_ | AH10 | AH8 | AH4 | AH2 | AH1 | 0-23 | 0000-0000 | +| 0AH | | 星期报警 | 0 | AW6 | AW5 | AW4 | AW3 | AW2 | AW1 | AW0 | N/A | 0000-0000 | +| 0BH | | 日报警 | 0 | 0 | AD20 | AD10 | AD8 | AD4 | AD2 | AD1 | 1-31 | 0000-0000 | +| 0CH | | 月报警 | 0 | 0 | 0 | AM010 | AM08 | AM04 | AM02 | AM01 | 1-12 | 0000-0000 | +| 0DH | | 年报警 | AY7 | AY6 | AY5 | AY4 | AY3 | AY2 | AY1 | AY0 | 0-99 | 0000-0000 | +| 0EH | | 报警允许 | 0 | EAY | EAM0 | EAD | EAW | EAH | EAMN | EAS | N/A | 0000-0000 | +| 0FH | 控制寄存器 | CTR1 | WRTC3 | OSF | INTAF | INTDF | BLF | WRTC2 | PMF | RTCF | N/A | 0000-0000 | +| 10H | | CTR2 | WRTC1 | IM | INTS1 | INTS0 | FOBAT | INTDE | INTAE | INTFE | N/A | 0000-0000 | +| 11H | | CTR3 | ARST | F32K | TDS1 | TDS0 | FS3 | FS2 | FS1 | FS0 | N/A | 0000-0000 | +| 12H | | 25°C TTF(只读RAM) | 1ppm/3ppm | F6 | F5 | F4 | F3 | F2 | F1 | F0 | N/A | 0000-0000 | +| 13H | | 倒计时定时器 | TD7 | TD6 | TD5 | TD4 | TD3 | TD2 | TD1 | TD0 | 0-255 | 0000-0000 | +| 14H | | | TD15 | TD14 | TD13 | TD12 | TD11 | TD10 | TD9 | TD8 | 0-255 | 0000-0000 | +| 15H | | | TD23 | TD22 | TD21 | TD20 | TD19 | TD18 | TD17 | TD16 | 0-255 | 0000-0000 | +| 16H | 温度寄存器 | TEMP | TM7 | TM6 | TM5 | TM4 | TM3 | TM2 | TM1 | TMO | 0-255 | XXXX-XXXX | +| 17H | IIC控制寄存器 | AGTC | BATIIC | 0 | - | - | - | - | - | - | 0-255 | 0000-0000 | +| 18H | 充电寄存器 | CHARGE | ENCH | - | - | - | - | - | Charge1 | Charge2 | 0-255 | CHARGE | +| 19H | 扩展控制寄存器 | CTR4 | INTS_E2 | INTS_E1 | INTS_E0 | CONT_BAT | INTTHE | INTTLE | INTBHE | INTBLE | 0-255 | 0000_0000 | +| 1AH | | CTR5 | BAT8_VAL | SYS | OSC_RDY | | | | BHF | BLF | 0-255 | 0000_0000 | +| 1BH | 电池电量 | BAT_VAL | BAT7_VAL | BAT6_VAL | BAT5_VAL | BAT4_VAL | BAT3_VAL | BAT2_VAL | BAT1_VAL | BATO_VAL | 0-255 | 0000_0000 | +| 1CH | 低温报警值 | TEMP_AL | TEMP7_AL | TEMP6_AL | TEMP5_AL | TEMP4_AL | TEMP3_AL | TEMP2_AL | TEMP1_AL | TEMPO_AL | 0-255 | 0000_0000 | +| 1DH | 高温报警值 | TEMP_AH | TEMP7_AH | TEMP6_AH | TEMP5_AH | TEMP4_AH | TEMP3_AH | TEMP2_AH | TEMP1_AH | TEMPO_AH | 0-255 | 0000_0000 | +| 1EH | 历史最低温度 | TEMP_HIS_L | TEMP_L7 | TEMP_L6 | TEMP_L5 | TEMP_L4 | TEMP_L3 | TEMP_L2 | TEMP_L1 | TEMP_L0 | 0-255 | 0111_1111 | +| 1FH | 历史最高温度 | TEMP_HIS_H | TEMP_H7 | TEMP_H6 | TEMP_H5 | TEMP_H4 | TEMP_H3 | TEMP_H2 | TEMP_H1 | TEMP_H0 | 0-255 | 1000_0000 | +| 20H | 历史最低温度发生的时间 | 分钟 | | | | | | | | | 0-59 | 0000_0000 | +| 21H | | 小时 | | | | | | | | | 0-23 | 0000_0000 | +| 22H | | 星期 | | | | | | | | | 0-6 | 0000_0000 | +| 23H | | 日 | | | | | | | | | 1-31 | 0000_0000 | +| 24H | | 月 | | | | | | | | | 1-12 | 0000_0000 | +| 25H | | 年 | | | | | | | | | 0-99 | 0000_0000 | +| 26H | 历史最高温度发生的时间 | 分钟 | | | | | | | | | 0-59 | 0000_0000 | +| 27H | | 小时 | | | | | | | | | 0-23 | 0000_0000 | +| 28H | | 星期 | | | | | | | | | 0-6 | 0000_0000 | +| 29H | | 日 | | | | | | | | | 1-31 | 0000_0000 | +| 2AH | | 月 | | | | | | | | | 1-12 | 0000_0000 | +| 2BH | | 年 | | | | | | | | | 0-99 | 0000_0000 | +| 2CH~71H | 用户RAM | (70Bytes) | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 | 0-255 | XXXX-XXXX | +| 72H~79H | ID(只读) | (8Bytes) | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 | N/A | XXXX-XXXX | 注: $" \mathbb { X } ^ { 3 1 }$ 表示随机值,可以为 0或者 1。 -## 4.2 实时时钟数据寄存器 (00H~06H) +### 4.2 实时时钟数据寄存器 (00H~06H) 实时时钟数据寄存器是 7字节的存储器,它以 BCD码方式存贮包括年、月、日、星期、时、分、秒的数据。 @@ -148,7 +202,20 @@ GND 见下表:(位 H20H10H8H4H2H1) -
24小时显示系统12小时显示系统24小时显示系统12小时显示系统
0012(AM12)1232(PM12)
0101(AM1)1321(PM1)
0202(AM2)1422(PM2)
0303(AM3)1523(PM3)
0404(AM4)1624(PM4)
0505(AM5)1725(PM5)
0606(AM6)1826(PM6)
0707(AM7)1927(PM7)
0808(AM8)2028(PM8)
0909(AM9)2129(PM9)
1010(AM10)2230(PM10)
1111(AM11)2331(PM11)
+| 24小时显示系统 | 12小时显示系统 | 24小时显示系统 | 12小时显示系统 | +| --- | --- | --- | --- | +| 00 | 12(AM12) | 12 | 32(PM12) | +| 01 | 01(AM1) | 13 | 21(PM1) | +| 02 | 02(AM2) | 14 | 22(PM2) | +| 03 | 03(AM3) | 15 | 23(PM3) | +| 04 | 04(AM4) | 16 | 24(PM4) | +| 05 | 05(AM5) | 17 | 25(PM5) | +| 06 | 06(AM6) | 18 | 26(PM6) | +| 07 | 07(AM7) | 19 | 27(PM7) | +| 08 | 08(AM8) | 20 | 28(PM8) | +| 09 | 09(AM9) | 21 | 29(PM9) | +| 10 | 10(AM10) | 22 | 30(PM10) | +| 11 | 11(AM11) | 23 | 31(PM11) | 注意: 当读取小时数据时,要屏蔽掉小时的最高位12\_/24,否则在24小时制时会因为12\_/24=1而显示不对。 @@ -164,17 +231,19 @@ GND 1.在上电复位时,芯片内部不对实时时钟数据寄存器作清零或置位处理。 2.当写实时时间数据时 $\mathrm { ( 0 0 H \widetilde { \Omega } 0 6 H ) }$ ,不可以单独对 7 个时间数据中的某一位进行写操作,否则可能会引起时间数据的错误进位,所以要修改其中某一个数据, 应一次性写入全部 7 个实时时钟数据。 -3. 当芯片收到读实时时钟数据命令,则所有实时时钟数据被锁存(时钟走时并不受影 +3. 当芯片收到读实时时钟数据命令,则所有实时时钟数据被锁存(时钟走时并不受影响),此功能可以避免时间数据的错读现象。 -响),此功能可以避免时间数据的错读现象。 - -## 4.3 时间中断(07H\~15H 地址) +### 4.3 时间中断(07H~15H 地址) SD2506API 有 3 种不同时间报警中断,它们由控制寄存器 CTR2(10H)中的位 INTAE、INTFE、INTDE 位来使能: $\$ 123,456$ -
序号中断允许位(1=允许, 0=禁止)中断名中断标志位(1=有中断, 0=无中断)
1INTAE报警中断INTAF
2INTFE频率中断无
3INTDE倒计时中断INTDF
+| 序号 | 中断允许位(1=允许, 0=禁止) | 中断名 | 中断标志位(1=有中断, 0=无中断) | +| --- | --- | --- | --- | +| 1 | INTAE | 报警中断 | INTAF | +| 2 | INTFE | 频率中断 | 无 | +| 3 | INTDE | 倒计时中断 | INTDF | 当报警中断产生时,置中断标志位 INTAF 为 1;当倒计时中断产生时, 置中断标志位INTDF为 1;频率中断没有标志位,标志位被置 1后,需要手动清除。 @@ -182,15 +251,23 @@ $\$ 123,456$ INT脚中断输出选通表 -
序号INTS1INTS0描述
000电量报警(见4.4)
101报警中断输出
210频率中断输出
311倒计时中断输出
+| 序号 | INTS1 | INTS0 | 描述 | +| --- | --- | --- | --- | +| 0 | 0 | 0 | 电量报警(见4.4) | +| 1 | 0 | 1 | 报警中断输出 | +| 2 | 1 | 0 | 频率中断输出 | +| 3 | 1 | 1 | 倒计时中断输出 | -## (1)报警中断 +#### (1) 报警中断 -当 INTAE=1时报警中断被允许,报警中断何时发生由时间报警寄存器(07H\~0EH)来确定。这其中 07H\~0DH依次用于存放报警时间的秒、分钟、小时、星期、日、月、年数据,除小时报警数据寄存器的最高位始终为”0” 、星期位的定义不同以外,其它的格式与实时时钟寄存器相同。 +当 INTAE=1时报警中断被允许,报警中断何时发生由时间报警寄存器(07H~0EH)来确定。这其中 07H~0DH依次用于存放报警时间的秒、分钟、小时、星期、日、月、年数据,除小时报警数据寄存器的最高位始终为”0” 、星期位的定义不同以外,其它的格式与实时时钟寄存器相同。 0EH为时间报警允许寄存器,如下: -
BITD7D6D5D4D3D2D1D0
位名(值)0EAYEAMOEADEAWEAHEAMNEAS
报警允许-年(ODH)月(OCH)日(OBH)星期(OAH)小时(09H)分钟(08H)秒(07H)
+| BIT | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 位名(值) | 0 | EAY | EAMO | EAD | EAW | EAH | EAMN | EAS | +| 报警允许 | - | 年(ODH) | 月(OCH) | 日(OBH) | 星期(OAH) | 小时(09H) | 分钟(08H) | 秒(07H) | 注:1=允许,0=禁止。 @@ -199,13 +276,14 @@ INT脚中断输出选通表 特别: 1.当日报警与星期报警均被允许即 EAD=EAW=1时,只有日报警有效,而星期报警无效,所以时间报警中断共有 96种组合方式。 -2.星期报警寄存器的数据格式与实时时钟数据星期的格式不同,星期报警寄存器的位AW6、AW5、AW4、AW3、AW2、AW1、AW0分别对应星期六、星期五、星期四、星期三、星期二、 - -星期一、星期日,并可多位置 1,例如 AW6,AW1=1,其它位为 0,则对应在星期六,星期一会有报警。 +2.星期报警寄存器的数据格式与实时时钟数据星期的格式不同,星期报警寄存器的位AW6、AW5、AW4、AW3、AW2、AW1、AW0分别对应星期六、星期五、星期四、星期三、星期二、星期一、星期日,并可多位置 1,例如 AW6,AW1=1,其它位为 0,则对应在星期六,星期一会有报警。 每一次对时间报警允许寄存器的写入都会清 INTAF 为"0",当设置 INTS1=0、INTS0=1时,即允许报警中断从 INT脚输出。该报警中断有两种模式,即单事件报警和周期性报警, 模式的选定由控制寄存器 2中的报警中断模式位 IM的值来定: -
IM位报警中断模式INT脚
0单事件报警输出低电平直至INTAF位清零
1周期性报警输出低电平有效、宽度为250ms的周期性脉冲直至中断允许位清零
+| IM位 | 报警中断模式 | INT脚 | +| --- | --- | --- | +| 0 | 单事件报警 | 输出低电平直至INTAF位清零 | +| 1 | 周期性报警 | 输出低电平有效、宽度为250ms的周期性脉冲直至中断允许位清零 | 为清除报警中断,可通过写操作将控制寄存器 1 的 INTAF 位置”0”,但当 ARST位置为”1”,则在控制寄存器 1 被读取时,INTAF位会自动清零。 @@ -229,7 +307,7 @@ INT 4.设寄存器 0EH=0111 0100B, 年报警寄存器 0DH=08h,月报警寄存器 0CH=08h,日报警寄存器 0BH=08h,小时报警寄存器 09H=20h,位 INTAE=1、IM=0、INTS1=0、INTS0=1、12\_/24=1 ,则到 2008 年 8 月 8 日 20 点 0 分 0 秒时,INT 脚会输出低电平。此后如清零 INTAF,则 INT脚从低电平变成高电平。 -(2)频率中断 +#### (2) 频率中断 当 INTFE=1时频率中断被允许, INTFE=0 时频率中断被禁止。 @@ -237,19 +315,38 @@ INT INT脚输出频率中断由控制寄存器 3 中的 FS3、FS2、FS1、FS0位来选择确定: -
频率(Hz)FS3FS2FS1FS0
00000
40960010
10240011
640100
320101
160110
80111
41000
21001
11010
1/21011
1/41100
1/81101
1/161110
1秒1111
+| 频率(Hz) | FS3 | FS2 | FS1 | FS0 | +| --- | --- | --- | --- | --- | +| 0 | 0 | 0 | 0 | 0 | +| 4096 | 0 | 0 | 1 | 0 | +| 1024 | 0 | 0 | 1 | 1 | +| 64 | 0 | 1 | 0 | 0 | +| 32 | 0 | 1 | 0 | 1 | +| 16 | 0 | 1 | 1 | 0 | +| 8 | 0 | 1 | 1 | 1 | +| 4 | 1 | 0 | 0 | 0 | +| 2 | 1 | 0 | 0 | 1 | +| 1 | 1 | 0 | 1 | 0 | +| 1/2 | 1 | 0 | 1 | 1 | +| 1/4 | 1 | 1 | 0 | 0 | +| 1/8 | 1 | 1 | 0 | 1 | +| 1/16 | 1 | 1 | 1 | 0 | +| 1秒 | 1 | 1 | 1 | 1 | 注:除了 1 秒外,INT 脚输出的频率中断均是由 32768Hz 晶体振荡电路整形及分频得到的;1 秒频率中断是指输出 500ms 低电平、500ms 高电平的方波,其低电平的下降沿与秒进位同步; 在温度补偿时内部数字调整寄存器起作用时,1秒和 1Hz的时长是不相同的。 例如:需要 INT 脚输出 1Hz 信号时,可以通过设置位 INTS1=1、INTS0=0、INTFE=1、FS3-FS2-FS1-FS0=1010 得到。 -## (1)倒计时中断 +#### (1) 倒计时中断 与倒计时中断相关的寄存器是三字节 24bit 的倒数定时器 13H、14H 和 15H, 当控制寄存器 CTR2 中的位 INTDE=1时倒计时中断被允许。 倒数定时器的频率源由控制寄存器 CTR2中的位 TDS1、TDS0来选定; -
TDS1TDS0定时器频率源
004096Hz
011024Hz
+| TDS1 | TDS0 | 定时器频率源 | +| --- | --- | --- | +| 0 | 0 | 4096Hz | +| 0 | 1 | 1024Hz | 当 INTDE=1 且倒数定时器写入一个 24 位自动重置的二进制数后, 倒数定时器会按照TDS1、TDS0选定的频率时间来减一。每次当三字节倒数定时器全为零时,会置倒计时中断的标志位(控制寄存器 1 中的位 INTDF)为 1。 @@ -259,21 +356,32 @@ INT脚输出频率中断由控制寄存器 3 中的 FS3、FS2、FS1、FS0位来 特别的:当重新配置倒计时中断时,需要复位倒计时计数器,即置 INTDE=0,然后再置INTDE=1,才可以启用新的倒计时中断。 -## 4.4 电池控制寄存器 +### 4.4 电池控制寄存器 -## 4.4.1 IIC 控制寄存器 AGTC(17H) +#### 4.4.1 IIC 控制寄存器 AGTC(17H) -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
AGTCBATIIC0------0-255
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| AGTC | BATIIC | 0 | - | - | - | - | - | - | 0-255 | BATIIC: BATIIC=0,VBAT 模式下禁止 IIC 通信;BATIIC=1,VBAT 模式下允许 IIC 通信,该寄存器位上电默认值为 0;其它位赋值均须为 0. -## 4.4.2 扩展控制寄存器 CRT4(19H): +#### 4.4.2 扩展控制寄存器 CRT4(19H) -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
CTR4INTS_E2INTS_E1INTS_E0CONT_BATINTTHEINTTLEINTBHEINTBLE0000_0000
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| CTR4 | INTS_E2 | INTS_E1 | INTS_E0 | CONT_BAT | INTTHE | INTTLE | INTBHE | INTBLE | 0000_0000 | 当寄存器 10H 中的{INTS1,INTS0}=00 时,INT 引脚输出扩展以下功能: -
INTS_E2INTS_E1INTS_E0功能描述
000禁止输出,高阻态
001-
010-
011电池低压报警
100电池高压报警
其他禁止输出,高阻态
+| INTS_E2 | INTS_E1 | INTS_E0 | 功能描述 | +| --- | --- | --- | --- | +| 0 | 0 | 0 | 禁止输出,高阻态 | +| 0 | 0 | 1 | - | +| 0 | 1 | 0 | - | +| 0 | 1 | 1 | 电池低压报警 | +| 1 | 0 | 0 | 电池高压报警 | +| 其他 | | | 禁止输出,高阻态 | VDD模式下,芯片电池电量测量周期为 60S,从每分钟的 3S 开始测量; VBAT模式下,芯片电池电量测量周期为 600S,从分钟的个位为 0 的 3S 时开始测量。 电池电压测量结果存放在寄存器 1AH[7]和 VBAT\_VAL 中。(下同) 例如:1AH=80H,1BH(VBAT\_VAL)=30H,则芯片 Vbat 脚外接电池的电压=130H=304D=3.04V。 @@ -291,9 +399,11 @@ INTBLE:电池低压报警使能位(电池报警电压值为 2.20V,精度± 2. Bit4位默认值为 0;该位赋值均须为 0。 -## 4.4.3 寄存器 CTR5(1AH) +#### 4.4.3 寄存器 CTR5(1AH) -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
CTR5BAT8_VAL---THFTLFBHFBLF0000_0000
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| CTR5 | BAT8_VAL | - | - | - | THF | TLF | BHF | BLF | 0000_0000 | BAT8\_VAL:电池测量结果的最高位 @@ -303,9 +413,11 @@ BLF:电池电压欠压标志位(与 0FH寄存器的 BLF 位完全相同, BHF、BLF 不能通过软件来设置或清除;当 INT 选择为BHF、BLF 输出时, 中断脚 INT的状态和BHF、BLF 状态一致。 -## 4.4.4 电池电量低八位寄存器(1BH) +#### 4.4.4 电池电量低八位寄存器(1BH) -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
BAT_VLBAT7_VLBAT6_VLBAT5_VLBAT4_VLBAT3_VLBAT2_VLBAT1_VLBATO_VL0000_0000
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| BAT_VL | BAT7_VL | BAT6_VL | BAT5_VL | BAT4_VL | BAT3_VL | BAT2_VL | BAT1_VL | BATO_VL | 0000_0000 | 电池测量值,若禁止电池测量,则读出的数据为 0,该寄存器只读。 @@ -313,17 +425,19 @@ BHF、BLF 不能通过软件来设置或清除;当 INT 选择为BHF、BLF 输 如 1AH[7]=1,VBAT\_VAL=35H,则当前所测的电池电量=135H=309(十进制)=3.09V。 -## 4.5 温度控制寄存器 +### 4.5 温度控制寄存器 -## 4.5.1 温度寄存器 TEMP(16H) +#### 4.5.1 温度寄存器 TEMP(16H) -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
TEMPTM7TM6TM5TM4TM3TM2TM1TMO0-255
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TEMP | TM7 | TM6 | TM5 | TM4 | TM3 | TM2 | TM1 | TMO | 0-255 | 最高位 TM7 为符号位,举例:TEMP=10H,温度为 16℃,TEMP=FEH,温度为-2℃。 在 VDD模式下 60S间隔自动测温一次,电池模式 600S 间隔测温一次,温度值在 0S 时自动更新。 -## 寄存器(19H)和寄存器 CTR5(1AH)的温度相关位:INTTHE、INTTLE、THF、TLF。 +寄存器(19H)和寄存器 CTR5(1AH)的温度相关位:INTTHE、INTTLE、THF、TLF。 INTTLE: 低温报警使能位。INTTLE=1并且芯片测量的温度低于或等于 TEMP\_AL寄存器设定的值时,芯片发出低温报警,置低温报警位 CTR5 中的 TLF 为 1;直到下次测量的温度值高于 TEMP\_AL 寄存器设定的值,TLF 变为 0。若 INTTLE=0,禁止低温报警,TEMP\_AL 所设定的值没有意义,可以作为通用的 SRAM来使用,在这种情况下 TLF一直为 0。 @@ -333,47 +447,60 @@ THF : 高 温 报 警 标 志 位 , 当 寄 存 器 10H 中 的 {INTS1,INTS0 TLF : 低 温 报 警 标 志 位 , 当 寄 存 器 10H 中 的 {INTS1,INTS0}=00 且 19H 中 的{INTS\_E2,INTS\_E1,INTS\_E0}=001 时,INT 脚输出低温报警中断。 -## 4.5.2 寄存器 1CH +#### 4.5.2 寄存器 1CH -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
TEMP_ALTEMP7_ALTEMP6_ALTEMP5_ALTEMP4_ALTEMP3_ALTEMP2_ALTEMP1_ALTEMP0_AL0000_0000
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TEMP_AL | TEMP7_AL | TEMP6_AL | TEMP5_AL | TEMP4_AL | TEMP3_AL | TEMP2_AL | TEMP1_AL | TEMP0_AL | 0000_0000 | 低温报警的温度值,低于或等于此温度开始报警。 -## 4.5.3 寄存器 1DH +#### 4.5.3 寄存器 1DH -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
TEMP_AHTEMP7_AHTEMP6_AHTEMP5_AHTEMP4_AHTEMP3_AHTEMP2_AHTEMP1_AHTEMPO_AH0000_0000
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TEMP_AH | TEMP7_AH | TEMP6_AH | TEMP5_AH | TEMP4_AH | TEMP3_AH | TEMP2_AH | TEMP1_AH | TEMPO_AH | 0000_0000 | 高温报警的温度值,高于或等于此温度开始报警。 -## 4.5.4 寄存器 1EH +#### 4.5.4 寄存器 1EH -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
TEMP_HIS_LTEMP_L7TEMP_L6TEMP_L5TEMP_L4TEMP_L3TEMP_L2TEMP_L1TEMP_L00111_1111
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TEMP_HIS_L | TEMP_L7 | TEMP_L6 | TEMP_L5 | TEMP_L4 | TEMP_L3 | TEMP_L2 | TEMP_L1 | TEMP_L0 | 0111_1111 | 历史低温数据值,Bit7 为符号位。 -## 4.5.5 寄存器 1FH +#### 4.5.5 寄存器 1FH -
名称Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0复位值
TEMP_HIS_HTEMP_H7TEMP_H6TEMP_H5TEMP_H4TEMP_H3TEMP_H2TEMP_H1TEMP_H01000_0000
+| 名称 | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | 复位值 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TEMP_HIS_H | TEMP_H7 | TEMP_H6 | TEMP_H5 | TEMP_H4 | TEMP_H3 | TEMP_H2 | TEMP_H1 | TEMP_H0 | 1000_0000 | 历史高温数据值,Bit7 为符号位。 -## 4.5.6 寄存器 20H~25H +#### 4.5.6 寄存器 20H~25H 分别记录发生最低温的历史时间:年、月、日、星期、小时、分钟。 -## 4.5.7 寄存器 26H~2BH +#### 4.5.7 寄存器 26H~2BH 分别记录发生最高温的历史时间:年、月、日、星期、小时、分钟。 -## 4.6 用户 RAM(2CH\~71H):70 字节的用户数据 RAM +### 4.6 用户 RAM(2CH~71H):70 字节的用户数据 RAM -## 4.7 ID码(72H\~79H):8 字节的模块身份识别码,包含生产日期、内部批号、内部序号等 +### 4.7 ID码(72H~79H):8 字节的模块身份识别码,包含生产日期、内部批号、内部序号等 -
ID码地址72H73H74H75H76H77H78H79H
说明生产年份:00~99生产月份:1~12生产日份:1~31生产机台编号四位生产工单号:如A394.工单内序号:0000~9999
+| ID码地址 | 72H | 73H | 74H | 75H | 76H | 77H | 78H | 79H | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 说明 | 生产年份:00~99 | 生产月份:1~12 | 生产日份:1~31 | 生产机台编号 | 四位生产工单号:如A394. | | 工单内序号:0000~9999 | | -## 4.8 充电选择寄存器(18H): +### 4.8 充电选择寄存器(18H) -
Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0
EN_ChargeCharge1Charge0
0-----11
+| Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| EN_Charge | | | | | | Charge1 | Charge0 | +| 0 | - | - | - | - | - | 1 | 1 | EN\_Charge:0 禁止充电功能;1允许充电功能;复位值为 0,即禁止充电功能。 @@ -389,9 +516,7 @@ Charge1 Charge0 描述 对于该寄存器,SD2506API出厂均配置成 82H.为了避免在停电时间过长导致电池电量耗光、18H的值被改变,强烈建议用户的程序在每一次上电时重置 18H寄存器的值为 82H。(充电功能开启后,模块会增加大约 80uA 左右的 VDD工作电流。) -WRTC1、WRTC2、WRTC3 位: 寄存器(00H\~71H)写允许位。即 WRTC1=1、WRTC2=1、WRTC3=1时写允许.注意置位有先后顺序,先置 WRTC1 为 1,后置 WRTC2、WRTC3 为 1;当 WRTC1=0、 - -WRTC2=0、WRTC3=0 时则写禁止,同样置位有先后顺序,先置 WRTC2、WRTC3 为 0,后置WRTC1为 0。当写禁止时,除了以上三位可以写以外,从 00H 到 79H所有的寄存器均不可以写。写禁止并不影响读操作。 +WRTC1、WRTC2、WRTC3 位: 寄存器(00H~71H)写允许位。即 WRTC1=1、WRTC2=1、WRTC3=1时写允许.注意置位有先后顺序,先置 WRTC1 为 1,后置 WRTC2、WRTC3 为 1;当 WRTC1=0、WRTC2=0、WRTC3=0 时则写禁止,同样置位有先后顺序,先置 WRTC2、WRTC3 为 0,后置WRTC1为 0。当写禁止时,除了以上三位可以写以外,从 00H 到 79H所有的寄存器均不可以写。写禁止并不影响读操作。 特别的:当写允许时,如需要赋值与写保护位相关的寄存器 0FH、10H,则要注意对应的位WRTC1、WRTC2、WRTC3 的赋值,这三个位只能为 1 而不可为 0,否则会造成写禁止而数据写不进相应的寄存器.又因为 0F 的寄存器其它位均为标志位,所以建议写允许时赋值 0F 寄存器的值可以定为 FFH,写禁止时赋值 0F 寄存器的值可以定为 7BH。 @@ -413,17 +538,17 @@ PMF:电源模式标志位,当电源模式为 VDD 模式时 PMF=0; 当电源模 F32K:32K 输出控制位---32K=0,允许输出;32K=1,禁止输出;默认值为 0。 -## 5.串行 IIC 接口 +## 5. 串行 IIC 接口 -## 5.1 SD2506API 通过两线式 IIC串行接口方式接收各种命令并读写数据。两线式串行 IIC接口方式描述如下: +### 5.1 SD2506API 通过两线式 IIC串行接口方式接收各种命令并读写数据。两线式串行 IIC接口方式描述如下: 两线式串行 IIC接口方式描述如下: -## (1)开始条件 +#### (1)开始条件 当 SCL处于高电平时,SDA 由高电平变成低电平构成一个开始条件,对 SD2506API 的所有操作均必须由开始条件开始。 -## (2)停止条件 +#### (2)停止条件 当 SCL处于高电平时,SDA 由低电平变成高电平构成一个停止条件,对 SD2506API 的所有操作均停止,系统进入待机状态。 @@ -442,7 +567,7 @@ stop 实时时钟串行接口 -## (3)数据传输 +#### (3)数据传输 当 SCL为低电平,且 SDA线电平变化时,则数据由 CPU传输给 SD2506API(高位在前、低位在后,下同);当 SCL为高电平,且 SDA电平保持不变时,则 CPU读取 SD2506API 发送来的数据:当 SCL为高电平,且 SDA电平变化时,SD2506API收到一个开始或停止条件。 @@ -459,7 +584,7 @@ SDA 实时时钟数据传输时序 -## (4)确认 +#### (4)确认 数据传输以 8 位序列进行。SD2506API 在第九个时钟周期时将 SDA置位为低电平,即送出一个确认信号(Acknowledge bit,以下简称“ACK”),表明数据已经被其收到。 @@ -481,27 +606,33 @@ tdh 实时时钟确认信号 -## 5.2 数据/指令传输格式 +### 5.2 数据/指令传输格式 当 CPU 发出开始条件与实时时钟建立连接后, CPU 首先通过 SDA 总线连续输出 7 位器件地址和 1位读/写指令来唤醒 SD2506API。 -## (1) 器件代码: +#### (1) 器件代码: -
Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0
0110010R/W
+| Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 0 | 1 | 1 | 0 | 0 | 1 | 0 | R/W | -其中高 7 位 BIT7\~BIT1 称“器件代码”,它代表实时时钟的器件地址,固定为“0110010”;BIT0 为读/写位,”1”为读操作,”0”为写操作。 +其中高 7 位 BIT7~BIT1 称“器件代码”,它代表实时时钟的器件地址,固定为“0110010”;BIT0 为读/写位,"1"为读操作,"0"为写操作。 -## (2) 数据传输格式 +#### (2) 数据传输格式 在数据发送/接收时停止信号到来时,将结束其数据传输,同时内部 5 位地址归零(注:内部 5 位地址的缺省值为 00000B)。如果只有开始信号,而没有结束信号,接着重新产生起始信号,则还要重新设置器件代码(在传输方向需要改变时,就用这种传输方式,如下面的读数据方式 1)。 主设备向从设备写入数据过程如下图: -
S01100100A数据A数据AP
寻址字节写
+| S | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | A | 数据 | A | 数据 | A | P | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| | 寻址字节 | | | | | | | 写 | | | | | | | 主设备向从设备直接读取数据过程如下图: -
S01100101A数据A数据A_P
寻址字节读
+| S | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | A | 数据 | A | 数据 | A_ | P | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| | 寻址字节 | | | | | | | 读 | | | | | | | 数据传输时改变其传输方向过程图: @@ -518,43 +649,42 @@ S 起始信号 P 停止信号 Sr 重新起始信号 通知从设备以结束读数据 -(3)SD2506API 数据传输的写模式 +#### (3)SD2506API 数据传输的写模式 -先送 7 位器件地址(0110010),第 8 位送入写命令(“0”), 第 9 位是 SD2506API 的响应位(ACK),SD2506API 进入写状态。 +先送 7 位器件地址(0110010),第 8 位送入写命令("0"), 第 9 位是 SD2506API 的响应位(ACK),SD2506API 进入写状态。 -接下来一个字节的前 8 位确定 SD2506API 的内部地址(00H\~79H), , 第 9 位是 SD2506API的响应位。 +接下来一个字节的前 8 位确定 SD2506API 的内部地址(00H~79H), , 第 9 位是 SD2506API的响应位。 开始写数据,每写完1个字节的数据之后,都经过1位的响应信号才能写下1字节的数据;如果要结束写数据过程,则在 ACK 后送出停止命令即可。 SD2506API 写数据示例(向 14H,15H 地址写数据): -
S01100100A00010100A数据A数据AP
寻址字节写设置14H内部地址14H15H
+| S | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | A | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | A | 数据 | A | 数据 | A | P | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |--- | +| | 寻址字节 | | | | | | | 写 | | 设置14H内部地址 | | | | | | | | | 14H | | 15H | | | ![](images/0f33608a662c2eb50849d4282cd88c7b2d68111caefee4430b8d17f282499e46.jpg) 特别注意: -1.除了 WRTC1、WRTC2、WRTC3 三个写允许位,对寄存器(00H\~71H)的写操作必须确认模块处于写允状态,否则写无效。 +1.除了 WRTC1、WRTC2、WRTC3 三个写允许位,对寄存器(00H~71H)的写操作必须确认模块处于写允状态,否则写无效。 2.写时间同步:每次对实时时间秒寄存器的写操作时,当秒数据的 8个 bit完全写入并收到 ACK信号后,就会对秒以下的内部计数器清零,使时间同步。 3. 从当前地址开始,每次读写完一个字节地址自动加 1。 4.如果写入的时间数据不存在,则不改写相对应的时间寄存器的值。 5.为了提高数据的可靠性,当写完成后,应将模块置于写禁止状态。(参见 5.7) -(4)SD2506API 数据传输的读模式 +#### (4)SD2506API 数据传输的读模式 SD2506API 有两种读数据方法: I)读方法 1:从指定的内部地址中读取数据。 -. 与写模式的前两步一样。 +1.与写模式的前两步一样。 +2.重新发出开始命令以改变两线接口数据传输方向。 +3.再送 7 位器件地址(0110010),第 8 位送入读命令("1"), 第 9 位是 SD2506API 的响应位(ACK),SD2506API 进入读状态。 +4.开始读数据,每读完1个字节的数据之后,CPU都要送出1位的响应信号(ACK,低电平)才能读下1字节的数据;如果想要结束读数据过程,则CPU要送出1位的响应信号(ACK_,高电平), ACK_后送出停止命令即可。 -. 重新发出开始命令以改变两线接口数据传输方向。 - -. 再送 7 位器件地址(0110010),第 8 位送入读命令(“1”), 第 9 位是 SD2506API 的响应位(ACK),SD2506API 进入读状态。 - -. 开始读数据,每读完1个字节的数据之后,CPU都要送出1位的响应信号(ACK,低电平)才能读下1字节的数据;如果想要结束读数据过程,则CPU要送出1位的响应信号(ACK\_,高电平), ACK\_后送出停止命令即可。 - -SD2506API 读数据方法 1 示例(从 7H\~9H 地址读取数据): +SD2506API 读数据方法 1 示例(从 7H~9H 地址读取数据): ![](images/7bf6b3c945980e17b4e2859485f424d2cc287acc86a7967499ad56b0c4eb16ce.jpg) @@ -572,25 +702,22 @@ S 起始信号 P 停止信号 Sr 重新起始信号 II)读方法 2:直接读取数据(从内部地址 00H开始) -. 开始信号后,先送 7 位器件地址(0110010),第 8 位送入读命令(“1”), 第 9 位是SD2506API 的响应位(ACK),SD2506API 进入读状态。 - -. 每读完 1 个字节的数据之后,CPU 都要送出 1 位的响应信号(ACK,低电平)才能读下 1字节的数据;如果想要结束读数据过程,则CPU要送出1位的响应信号(ACK\_,高电平), ACK\_后送出停止命令即可。 +1.开始信号后,先送 7 位器件地址(0110010),第 8 位送入读命令("1"), 第 9 位是SD2506API 的响应位(ACK),SD2506API 进入读状态。 +2.每读完 1 个字节的数据之后,CPU 都要送出 1 位的响应信号(ACK,低电平)才能读下 1字节的数据;如果想要结束读数据过程,则CPU要送出1位的响应信号(ACK_,高电平), ACK_后送出停止命令即可。 SD2506API 读数据方法 2 示例(从 00H 地址开始读取数据): ![](images/3648bb8b1603ba26ab34070418559365f9df547a1dc36cf7879a3f4d489d46d1.jpg) -(5)SD2506API在特殊条件下的数据传输 +#### (5)SD2506API在特殊条件下的数据传输 为了保证读写数据的有效性,SD2506API 的两线通信开始到结束仅在此 0.5S 秒之内,如此可避免总线挂死的现象。 -因此在 SD2506API 中,IIC 通信方式会在第一个开始信号(START)到来之后的 0.5 秒之内自动终止本次通信。所以,要注意: 从开始信号进行读/写数据,直到停止信号, 读/写操作 - -过程必须在 0.5秒之内完成。 +因此在 SD2506API 中,IIC 通信方式会在第一个开始信号(START)到来之后的 0.5 秒之内自动终止本次通信。所以,要注意: 从开始信号进行读/写数据,直到停止信号, 读/写操作过程必须在 0.5秒之内完成。 特别:在进入 VBAT 模式后 IC会禁止 IIC总线的通信。 -## 6.备用电源切换电路 +## 6. 备用电源切换电路 SD2506API具有后备电池自动切换功能:当模块检测到主电源 VDD掉到后备电池电压以下且 VDD小于 2.4V时自动转为由内部的后备电池供电,模块进入后备电池 VBAT 供电模式;当 VDD大于内部电池电压或 VDD大于 2.4V,则模块会转为由 VDD供电,模块进入 VDD供电模式。(内置电源模式指示位 PMF,VDD 模式时 PMF=0,VBAT 模式时 PMF=1)。 @@ -598,22 +725,22 @@ SD2506API具有后备电池自动切换功能:当模块检测到主电源 VDD SD2506API 有一个实时时钟失效位(RTCF),用于检测总电源失效。它在器件丢失所有电源(VDD 和后备电池)之后使用户可以确定器件是否已上电。 -## 7.内部电池充电电路 +## 7. 内部电池充电电路 SD2506API 内置电池充电电路,当 VDD 电压正常时,内部充电自动进行直至电池充满。(内部充电过程不需要用户干预,会自动完成)。 内部充电电池一次充满时的电量为 5.5mAh,在外电源掉电的常温情况下时钟走时时间在 8 个月左右。电池可以满充 100 次(注:满充的定义是指将电池从 0V 充到 3.0V),常温下等效总电量为 550mAh;如果从电池电量的 90%开始充电,则电池的充电此数为 1000次。 -## 特别提示: +### 特别提示: 1.为了保证充电功能的正常运行,用户的程序须在每一次上电时重置 18H 寄存器的值为 82H。 2.对内置充电电池的时钟模块不上电充电时间不能超过 8 个月,不要等电池的电耗光后再去充电,否则会减少电池的使用寿命。 -## 8.上电复位 +## 8. 上电复位 模块内部具有上电复位电路,当所有电源包括内部电池完全掉电再上电时会复位内部寄存器,复位操作对内部部分寄存器进行置初值但不包括实时时钟数据寄存器、通用 RAM。 -## 9.使用说明 +## 9. 使用说明 1.为了防止电路噪声问题,请在此模块的旁边放置两个旁路电容,分别是 0.1uF 电容和10uF 电容。 2.为了防止干扰,在 PCB 制作时请保证模块底部无大电流信号通过,最好能铺地。 @@ -639,30 +766,64 @@ GND SDA 3 5 10K 10K -## 11.极限参数 +## 11. 极限参数 -VDD 、VBAT 、SCL、SDA 和 INT 引脚上的电压(相对于地)……… -0.5V 至 7.0V引线温度(焊接,5秒)…… 350℃ +- VDD 、VBAT 、SCL、SDA 和 INT 引脚上的电压(相对于地)……… -0.5V 至 7.0V +- 引线温度(焊接,5秒)…… 350℃ 注: 1.因模块内置电池本体的表面温度不能超过 85℃,本产品不可以采用回流焊或波峰焊方式,最好采用烙铁焊接方式。 2.强度超出所列的极限参数可能导致器件的永久性损坏。这些仅仅是极限参数,并不意味着在极限条件下或在任何其它超出推荐工作条件所示参数的情况下器件能有效地工作。延长在极限参数条件上的工作时间会影响器件的可靠性。 -## 12.直流特性 +## 12. 直流特性 -
符号参数条件最小值典型值最大值单位备注
$V_{DD}$ 主电源2.75.5V
$I_{DD1}$ 电源电流 $V_{DD}=5V$ 80uA2mA1
$V_{DD}=3.3V$ 80uA2mA
$I_{BAT}$ 电池供电电流 $V_{BAT}=3V$ 800nA
$I_{L1}$ SCL的输入漏电流100nA
$I_{LO}$ SDA上的I/O泄漏电流100nA
$V_{BATHYS}$ VBAT和VDD之间切换的迟滞电压300mV
INT/SDAVOLINT/SDA低电平输出电压 $V_{DD}=5V$ $I_{OL}=0.5mA$ 0.10.20.3V
+| 符号 | 参数 | 条件 | 最小值 | 典型值 | 最大值 | 单位 | 备注 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| $V_{DD}$ | 主电源 | | 2.7 | | 5.5 | V | | +| $I_{DD1}$ | 电源电流 | $V_{DD}=5V$ | 80uA | | 2mA | | 1 | +| | | $V_{DD}=3.3V$ | 80uA | | 2mA | | | +| $I_{BAT}$ | 电池供电电流 | $V_{BAT}=3V$ | | 800 | | nA | | +| $I_{L1}$ | SCL的输入漏电流 | | | 100 | | nA | | +| $I_{LO}$ | SDA上的I/O泄漏电流 | | | 100 | | nA | | +| $V_{BATHYS}$ | VBAT和VDD之间切换的迟滞电压 | | | 300 | | mV | | +| INT/SDAVOL | INT/SDA低电平输出电压 | $V_{DD}=5V$ $I_{OL}=0.5mA$ | 0.1 | 0.2 | 0.3 | V | | Note1:此时最大电流 2mA主要是充电电流.当内部电池电压为 0V时,充电电流最大。当充电功能打开时,会增加 80uA的功耗。 -## 13.掉电时序(温度:-30℃至+80℃) +## 13. 掉电时序(温度:-30℃至+80℃) -
符号参数条件最小值典型值最大值单位注意
$V_{DD\ br}$ $V_{DD}$ 下电速率10V/ms
+| 符号 | 参数 | 条件 | 最小值 | 典型值 | 最大值 | 单位 | 注意 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| $V_{DD\ br}$ | $V_{DD}$ 下电速率 | | | | 10 | V/ms | | -## 14.交流特性 +## 14. 交流特性 -
符号参数条件标准模式(fSCL=100kHz)快速模式(fSCL I=400kHz)单位
最小值典型值最大值最小值典型值最大值
fSCLSCL 频率100400kHz
VILSDA 和 SCL 低电平输入电压-0.30.3×VDD-0.30.3×VDDV
VIHSDA 和 SCL 高电平输入电压0.7×VDDVDD+0.30.7×VDDVDD+0.3V
VhysSDA 和 SCL 施密特触发输入滞后0.05×VDD0.05×VDDV
VOL低电平输出电压SDA 输出低电平状态下,吸收 2mA 电流时的电压0.40.4V
CpinSDA 和 SCL引脚电容TA=25°C f=1MHzVDD=5V VIN=0V VOUT=0V1010pF
tINSDA 和 SCL 输入端的脉冲宽度抑制时间10050ns
tAASCL 下降沿到 SDA 输出数据有效SCL 下降到 0.3×VDD,直到 SDA 不在 0.3×VDD至 0.7×VDD区间。900900ns
tBUF总线在 STOP 和 START之间的空闲时间SDA 在 STOP 条件下上升到 0.7×VDD,在开始条件下下降到 0.7×VDD。47001300ns
tLOW时钟低电平时间在 0.3×VDD处测量47001300ns
tHIGH时钟高电平时间在 0.7×VDD处测量4000600ns
tSU:STAIIC 启动信号的建立时间SCL 上升到 0.7×VDD至 SDA 下降沿到 0.7×VDD4700600ns
tHD:STAIIC 停止信号的保持时间在 SDA 下降沿到 0.3×VDD, SCL 下降沿到 0.7×VDD4000600ns
tSU:DAT输入数据的建立时间从 SDA 不在 0.3×VDD至 0.7×VDD范围,到 SCL 上升沿的 0.3×VDD250100ns
tHD:DAT输入数据保持时间SCL 下降沿的 0.3×VDD,到 SDA 在 0.3×VDD至 0.7×VDD区间00ns
tSU:STO停止条件的建立时间从 SCL 上升沿经过 0.7×VDD,到 SDA上升沿 0.3×VDD4000600ns
tDH数据输出的保持时间从 SCL 下降沿 0.7×VDD,到 SDA 0.3×VDD至 0.7×VDD区间00ns
$t_{R}$ SDA 和 SCL 的上升时间 $0.3 \times V_{DD}$ 至 $0.7 \times V_{DD}$ 区间1000300ns
$t_{F}$ SDA 和 SCL 的下降时间 $0.3 \times V_{DD}$ 至 $0.7 \times V_{DD}$ 区间300300ns
+| 符号 | 参数 | 条件 | 标准模式(fSCL=100kHz) | | | 快速模式(fSCL I=400kHz) | | | 单位 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| | | | 最小值 | 典型值 | 最大值 | 最小值 | 典型值 | 最大值 | | +| fSCL | SCL 频率 | | | | 100 | | | 400 | kHz | +| VIL | SDA 和 SCL 低电平输入电压 | | -0.3 | | 0.3×VDD | -0.3 | | 0.3×VDD | V | +| VIH | SDA 和 SCL 高电平输入电压 | | 0.7×VDD | | VDD+0.3 | 0.7×VDD | | VDD+0.3 | V | +| Vhys | SDA 和 SCL 施密特触发输入滞后 | | 0.05×VDD | | | 0.05×VDD | | | V | +| VOL | 低电平输出电压 | SDA 输出低电平状态下,吸收 2mA 电流时的电压 | | 0.4 | | | 0.4 | | V | +| Cpin | SDA 和 SCL引脚电容 | TA=25°C f=1MHzVDD=5V VIN=0V VOUT=0V | | | 10 | | | 10 | pF | +| tIN | SDA 和 SCL 输入端的脉冲宽度抑制时间 | | | | 100 | | | 50 | ns | +| tAA | SCL 下降沿到 SDA 输出数据有效 | SCL 下降到 0.3×VDD,直到 SDA 不在 0.3×VDD至 0.7×VDD区间。 | | | 900 | | | 900 | ns | +| tBUF | 总线在 STOP 和 START之间的空闲时间 | SDA 在 STOP 条件下上升到 0.7×VDD,在开始条件下下降到 0.7×VDD。 | 4700 | | | 1300 | | | ns | +| tLOW | 时钟低电平时间 | 在 0.3×VDD处测量 | 4700 | | | 1300 | | | ns | +| tHIGH | 时钟高电平时间 | 在 0.7×VDD处测量 | 4000 | | | 600 | | | ns | +| tSU:STA | IIC 启动信号的建立时间 | SCL 上升到 0.7×VDD至 SDA 下降沿到 0.7×VDD | 4700 | | | 600 | | | ns | +| tHD:STA | IIC 停止信号的保持时间 | 在 SDA 下降沿到 0.3×VDD, SCL 下降沿到 0.7×VDD | 4000 | | | 600 | | | ns | +| tSU:DAT | 输入数据的建立时间 | 从 SDA 不在 0.3×VDD至 0.7×VDD范围,到 SCL 上升沿的 0.3×VDD | 250 | | | 100 | | | ns | +| tHD:DAT | 输入数据保持时间 | SCL 下降沿的 0.3×VDD,到 SDA 在 0.3×VDD至 0.7×VDD区间 | 0 | | | 0 | | | ns | +| tSU:STO | 停止条件的建立时间 | 从 SCL 上升沿经过 0.7×VDD,到 SDA上升沿 0.3×VDD | 4000 | | | 600 | | | ns | +| tDH | 数据输出的保持时间 | 从 SCL 下降沿 0.7×VDD,到 SDA 0.3×VDD至 0.7×VDD区间 | 0 | | | 0 | | | ns | +| $t_{R}$ | SDA 和 SCL 的上升时间 | $0.3 \times V_{DD}$ 至 $0.7 \times V_{DD}$ 区间 | | | 1000 | | | 300 | ns | +| $t_{F}$ | SDA 和 SCL 的下降时间 | $0.3 \times V_{DD}$ 至 $0.7 \times V_{DD}$ 区间 | | | 300 | | | 300 | ns | IIC Timing Chart + ![](images/28e2f0df5c853c92033967c174cdd1cf7055764c95077034acfde0e7e1c3edae.jpg)
@@ -685,7 +846,7 @@ tAA tDH
-15.频率误差&温度关系曲线(与没有温补功能的时钟模块进行对比): +## 15. 频率误差&温度关系曲线(与没有温补功能的时钟模块进行对比): ![](images/465951ede3196713718e34db1b6885a5627c50aa0ed981246efb5dedca30d8e9.jpg) @@ -709,7 +870,7 @@ tDH | 80 | ~-18 | -140 | -16.SD2506API充放电曲线(图中电压均是指内部电池电压) +## 16. SD2506API充放电曲线(图中电压均是指内部电池电压) ![](images/c8865d0318313374ef1721bb3a1bb64adf2fd418e65c8db9bd35ca5b6dc6a2ca.jpg) @@ -750,11 +911,11 @@ tDH SD2506API 放电曲线 -## 17.模块顶部字符说明 +## 17. 模块顶部字符说明 ![](images/3669a9ab2b6d709e7e4d35a4ae9188009fa344f4b60844441ff055efa7eccf1c.jpg) -## 18.封装尺寸(DIP8,单位:mm;尺寸误差±0.2mm,特别说明除外。) +## 18. 封装尺寸(DIP8,单位:mm;尺寸误差±0.2mm,特别说明除外。) ![](images/41580fc9cac8fc2a6b4cfef8dad118f1b596f5326d67ff0ef958611e4b3b1ae0.jpg) @@ -774,7 +935,7 @@ SD2506API 放电曲线 SD2506 封装尺寸(管脚尺寸:0.5\*0.3mm,管脚公差±0.05mm) -##  编后语 +## 编后语 感谢您阅读本资料。由于经验和水平的欠缺,本文难免有错误和遗漏。如果您在使用过程中发现错误或不恰当的地方,请拨打电话:0755-83246178 或请 E-mail:support@whwave.com.cn,我们将尽快予以答复。 @@ -796,18 +957,22 @@ SD2506 封装尺寸(管脚尺寸:0.5\*0.3mm,管脚公差±0.05mm) 深圳市兴威帆电子技术有限公司 -
版本号修改时间修改记录修改人
V2.02025.08.29修改倒计时部分CHENMY
V2.12026.02.03修改封装尺寸部分CHENMY
+| 版本号 | 修改时间 | 修改记录 | 修改人 | +| --- | --- | --- | --- | +| V2.0 | 2025.08.29 | 修改倒计时部分 | CHENMY | +| V2.1 | 2026.02.03 | 修改封装尺寸部分 | CHENMY | -附录: +## 附录: -## 一. SD2506系列应用中硬件注意事项: +### 一. SD2506系列应用中硬件注意事项: -(1) 对 SD2506 及 MCU 的电源\~地之间加 104 电容去高频。 -(2) 对SD2506及MCU所在的板数字电源\~地的输入端加220uF以上的电解及104电容去除电源扰动. -(3) 为了防止干扰,在 PCB 制作时请保证模块底部无大电流信号通过,最好能铺地. -(4) 对MCU的复位端尽量采用可靠的复位方式,而摒弃阻容复位或直接连到VDD的方式.以下推荐廉价的三极管复位方式,当然用电压检测器或专用复位电路就更好啦. +- (1) 对 SD2506 及 MCU 的电源\~地之间加 104 电容去高频。 +- (2) 对SD2506及MCU所在的板数字电源\~地的输入端加220uF以上的电解及104电容去除电源扰动. +- (3) 为了防止干扰,在 PCB 制作时请保证模块底部无大电流信号通过,最好能铺地. +- (4) 对MCU的复位端尽量采用可靠的复位方式,而摒弃阻容复位或直接连到VDD的方式.以下推荐廉价的三极管复位方式,当然用电压检测器或专用复位电路就更好啦. ![](images/4a911065bebf54af10afdb0752802a3b875abcff3cc81619e5b9403ea2fcc977.jpg) + 图中 R2,R3 为分压电阻,其选值可用以下公式: Vbe/Vrst=R2/(R2+R3) @@ -820,31 +985,25 @@ C1为延时电容 D1为延时电容的放电二极管,可选用. -## 二. SD2506系列应用中软件注意事项 +### 二. SD2506系列应用中软件注意事项 -1) 软件上电开始做一个几百毫秒的延时,等 MCU端口稳定后再去进行读写操作。 -2) 时钟最多半秒才读一次:这样做主要目的是考虑到时间数据的改变需要 1S,频繁去读的话是在做无用功,不但会加大电源功耗,而且会引入干扰。 -3) IIC 总线”START”里:在置 SDA 为高后要再判断 SDA 是否为高,即 SDA 是否被箝位为低,否则等 SDA 线 -置高。在写命令字时:要判断ACK 是否正常,否则退出 +1.软件上电开始做一个几百毫秒的延时,等 MCU端口稳定后再去进行读写操作。 +2.时钟最多半秒才读一次:这样做主要目的是考虑到时间数据的改变需要 1S,频繁去读的话是在做无用功,不但会加大电源功耗,而且会引入干扰。 +3.IIC 总线"START"里:在置 SDA 为高后要再判断 SDA 是否为高,即 SDA 是否被箝位为低,否则等 SDA 线置高。在写命令字时:要判断ACK 是否正常,否则退出 +4.IIC 总线"STOP"里:在置 SDA 为高后要再判断 SDA 是否为高,即 SDA 是否被箝位为低,否则多置 SCL 脉冲让 SDA 线释放为高.在写命令字时:要判断ACK 是否正常,否则退出,不要再进行下面的操作。 -4)IIC 总线”STOP”里:在置 SDA 为高后要再判断 SDA 是否为高,即 SDA 是否被箝位为低,否则多置 SCL 脉 +### 三. 有关 SD2506 的一些问题回答 -冲让 SDA 线释放为高.在写命令字时:要判断ACK 是否正常,否则退出,不要再进行下面的操 - -作。 - -## 三. 有关 SD2506 的一些问题回答 - -## (1) 时钟精度: +#### (1) 时钟精度: SD2506 的精度指标为常温 25 度下±3.8ppm,即年误差少于 2 分钟. -出厂之前对每一只 SD2506 都用超高精度的仪器和严格的程序做了校准,所以能保证精度.有人说他拿到 SD2506 的精度好像不准,一般问题出在参考时钟上,通常可用电信 12117台作基准来验证,但如用电脑那就差远了,因为电脑的时钟精度一般都不高,通常大于 30ppm.最好用网络校时软件. +出厂之前对每一只 SD2506 都用超高精度的仪器和严格的程序做了校准,所以能保证精度.有人说他拿到 SD2506 的精度好像不准,一般问题出在参考时钟上,通常可用电信 12117台作基准来验证,但如用电脑那就差远了,因为电脑的时钟精度一般都不高,通常大于 30ppm.最好用网络校时软件。 手机安卓版网络/GPS校时软件: https://www.whwave.com.cn/filedownload/420894 -## (2) 温度对精度的影响: +#### (2) 温度对精度的影响: SD2506时钟具有温度补偿功能。 diff --git a/docs/问题记录/远程显示延迟分析.md b/docs/问题记录/远程显示延迟分析.md new file mode 100644 index 00000000..38ca5c5d --- /dev/null +++ b/docs/问题记录/远程显示延迟分析.md @@ -0,0 +1,436 @@ +# 远程显示(RemoDispBus)延迟分析报告 + +> **模块名称**:远程显示链路(`ui_net_sink` → `net_socket` → CH395F → DTU-RemoteLCD) +> **文档目的**:针对"远端 LCD 连 MCU 有明显延迟感、连 PC 模拟器没有"这一现象,做**静态全链路分析**: +> 拆解延迟预算、列出候选原因(按可能性排序并给出代码级证据)、给出可执行的排查流程与逐因对策。 +> **日期**:2026-09-22 +> **状态**:**只读分析,未修改任何代码**;本文所有行号基于当前工作副本,改动后需复核。 +> **对照实验事实**:同一份共享服务端 `Drivers/BSP/UI/ui_net_sink.c` + 同一个 DTU-RemoteLCD 客户端, +> 连模拟器(Winsock)流畅、连 MCU(CH395F)有明显延迟 → 差异全部落在 `net_*` 的 **MCU 后端**, +> 协议与客户端实现可视为已排除。 + +--- + +## 0. 摘要 + +**这不是"网线慢",而是 MCU 侧 CH395F 通道上的结构性开销 + 芯片重传粒度放大。** + +"模拟器没有延迟"这个对照实验的证明力有限:它只证明了**协议与客户端没问题**, +**没有**证明链路/芯片层没问题——因为模拟器根本不走 CH395F、不走 SPI、不走 netMsgQueue、 +不走 FreeRTOS 调度,Winsock 一次 `send()` 就把 3209 字节交给 PC 协议栈连续发出。 + +### 原因排序 + +| # | 原因 | 量级 | 可能性 | 关键证据 | +|---|---|---|---|---| +| **D1** | CH395F TCP 重传周期 **500ms** —— 一次丢包 = 整帧多等半秒 | **500ms ~ 4s** | 高 | `net_socket.c:143-144`(8 次 × 500ms,总窗口 4s);客户端注释自证"整帧分 4 块耗时 **0.5~3 秒**属正常"(`remo_disp_server.py:165-174`),0.5/1.0/1.5s 台阶正是 500ms 整数倍 | +| **D2** | 整帧 3209B + CH395F **1KB 发送 FIFO 停止等待** —— 每帧固定 4 次串行分块握手 | 15~30ms 基线,弱网放大 | 高(基线) | `NET_SEND_CHUNK_MAX = CH395F_SEND_BUF_SIZE_BYTES = 1KB`(`net_socket.c:916-921`、`ch395f.h:119-129`);`net_send_locked` 每块写完必须等 `SENBUF_FREE`(`:955-985`) | +| **D3** | **netMsgQueue 头阻塞**(既有审计 **E-02**):lftpd/对时的阻塞 recv 在 netTask 上下文空转 | **最长 30s 全网停摆** | 高(间歇性秒级卡顿的首要嫌疑) | `net_socket.c:357` 一次只取一条消息 + `:1019-1067` 阻塞 recv 在 netTask 内空转;触发源 `lftpd_inet.c:106-109`、`time_sync.c:84`(均 `flags=0`) | +| **D4** | PC 端 TCP **延迟确认(40~200ms)** 与"每段等 ACK"的停止等待发送端交互 | 40~200ms/块 | 中高 | 客户端注释已记录;CH395F 发送缓冲仅 1×MSS,无法让 2 段同飞骗过延迟确认 | +| **D5** | uiTask 节拍:10ms 循环 + **每拍只 recv 一次 64B** + step 在 poll **之前** | 10~30ms + 多等一拍 100ms | 中 | `ui_task.c:91-95`;`ui_net_sink.c:441`(`RM_RX_BUF = 64`,`:38`) | +| **D6** | 阻塞式日志在关键路径:`fputc` 逐字节 `HAL_UART_Transmit` @115200 | **每行 ~5ms**(阻塞调用任务) | 中(确定的基线税) | `Src/usart.c:479-484`;`net_socket.c:1025` 每次收数据都 `DBG_INFO` 一行;`dbg_cfg.h:28/40` `DBG_ENABLE` + 时间戳均开 | +| **D7** | 物理链路:双工不匹配 / 网线 / Wi-Fi / ARP | 不可预估,直接喂给 D1 | 中 | `net_task.c:100` **强制** 100M 全双工;若交换机自协商 → 双工不匹配是丢包经典成因 | +| **D8** | 客户端固定成本:100ms 拉取节拍(**按键后不立即取帧**)+ 1bpp→PNG→base64→WebSocket | 100ms + 数 ms | 确定的基线 | `remo_disp_server.py:588-647`(`_refresh_stop.wait(0.1)`);`:736-741` `on_key` 只发键不触发取帧 | +| **D9** | LVGL 全量渲染(`LV_DEF_REFR_PERIOD=20ms`、`RENDER_MODE_FULL`)+ 每次 flush 整帧 memcpy,uiTask 为 **BelowNormal** 易被抢占 | 单次渲染数 ms~数十 ms | 中低(待实测) | `ui_port.c:126-127/80-86`;`lv_conf.h:11/22`;任务优先级见 `AGENTS.md` 任务表 | +| **D10** | 断线重连路径含 `osDelay(500)`(netTask 上下文,最多 3 次) | 0.5 ~ 1.5s | 中(仅断线/重连时) | `net_socket.c:1098-1105` | + +### 一句话行动建议 + +先做 §3 的 **Step 1(客户端三点打点)+ Step 2(Wireshark 抓包)**,半天内不改固件即可定性; +随后落 **P0 两项一行级改动**:`set_retrans_period(500) → 200`(治 D1 症状)+ +删 `net_socket.c:1025` 的 per-recv 日志(治 D6)。根治方向是 **差量/RLE + 帧版本号**(治 D2,连带把 D1 的影响面压到几乎为零)。 + +--- + +## 1. 链路与延迟预算 + +### 1.1 一次"按键 → 看到画面"的完整旅程 + +```mermaid +sequenceDiagram + participant B as 浏览器 + participant F as Flask/SocketIO
refresh_loop + participant C as CH395F + participant N as netTask + participant U as uiTask + + B->>F: 按键 (socketio "key") + F->>C: KEY 帧 10B(on_key 不触发取帧) + Note over F: D8 最多等 100ms 才轮到下一次 fetch_screen + F->>C: LCDMEM 请求 10B + C-->>N: RECV 中断 → net_poll + Note over N: D3 netMsgQueue 头阻塞窗口 + U->>N: net_recv(64B)(队列 + TaskNotify) + Note over U: D5 最多 10ms 相位 + LVGL 渲染占时 + N-->>U: 按键注入 UI + Note over U: D9 下一拍才渲染(step 在 poll 之前) + U->>N: net_send 3209B + loop D2 4 次串行分块 + N->>C: WRITE_SEND_BUF ≤1KB(SPI DMA ≈1.6ms/KB) + C-->>N: SENBUF_FREE 中断 + end + Note over C: D1 丢一段 → 重传 +500ms(D7 供包) + C-->>F: 4 × 1024B 段 + Note over F: D4 Windows 延迟确认 40~200ms + F->>B: PNG → base64 → socketio → canvas +``` + +### 1.2 各段耗时(健康值 vs 异常值) + +| 段 | 位置 | 健康耗时 | 异常时的耗时 | 备注 | +|---|---|---|---|---| +| 客户端拉取节拍 | `remo_disp_server.py:647` | ≤100ms | ≤100ms(固定) | 按键后不提前取帧 | +| 线路 RTT(请求) | 以太网 | ~0.1ms | 1~50ms(Wi-Fi) | — | +| CH395F → netTask 事件 | `net_task.c:212-214` | ≤1ms | 数秒(D3) | 主循环 `osDelay(1)` | +| uiTask 拾取请求 | `ui_task.c:91-95` | ≤10ms | 数十 ms(D5/D9) | 每拍只 recv 一次 | +| 队列往返(每次 `net_*`) | `net_socket.c:1190-1290` | ~1-2ms | 数秒(D3) | 投递 + TaskNotify,2 次任务切换 | +| 收数据日志 | `net_socket.c:1025` → `Src/usart.c:479` | ~5ms | ~5ms(固定税) | 阻塞在 netTask 内 | +| 取帧 + 组帧 | `ui_net_sink.c:223-226` | ~0.1ms(2×3200B memcpy) | — | 见 D2-方案⑤ | +| 4 次分块发送 | `net_socket.c:955-985` | 15~25ms | **每 chunk 上限 1s**(`NET_SEND_TIMEOUT_MS`)→ 4s | SPI 5.25Mbit/s ≈1.6ms/KB | +| 分段传输 + ACK | 线路 + PC | ~0.5ms | **500ms/1s/1.5s 台阶**(D1)、40~200ms/块(D4) | 决定性判据 | +| PNG + base64 + WS + canvas | `remo_disp_server.py:510-617` | 数 ms | 数 ms(固定) | 双端相同 | +| **合计(典型)** | | **≈130~170ms** | **0.5s ~ 30s** | 模拟器合计 ≈105~115ms | + +### 1.3 关键量化常量 + +| 常量 | 值 | 来源 | +|---|---|---| +| 屏幕 / 帧大小 | 160×160 **1bpp** = 3200 B | `ui_net_sink.c:33-35` | +| LCDMEM 应答 wire 长度 | 4(头) + 4(偏移) + 3200(位图) + 1(CRC) = **3209 B** | `ui_net_sink.c:226` + `remo_send` | +| TCP MSS | 1024 | `ch395f.h:119`、`net_socket.c:140` | +| CH395F 每 socket 发送缓冲 | 2 块 × 512B = **1KB** | `ch395f.h:122-128` | +| `NET_SEND_CHUNK_MAX` | **1KB**(受发送缓冲与 DMA 载荷 2048 双重钳制) | `net_socket.c:916-921` | +| LCDMEM 一帧分块数 / 分段数 | **4**(1024×3 + 137) | 3209 ÷ 1024 | +| CH395F 内部 RAM | 48 块 × 512B = 24KB,**8 socket 均分 6 块** | `net_socket.c:196-202` | +| TCP 重传 | **8 次 × 500ms = 4s 窗口** | `net_socket.c:143-144`;手册 §5.17/§5.18 默认 12 次/500ms | +| SPI2 速率 | APB1 42MHz ÷ 8 ≈ **5.25 Mbit/s** → 1KB ≈ **1.6ms** | `Src/spi.c:84`、`.ioc APB1CLKDivider=DIV4`;分频 8 为 PCB 约束(`AGENTS.md`) | +| 发送等待超时 | 1s(`NET_SEND_TIMEOUT_MS`) | `net_config.h:32-34` | +| 接收超时 | 30s(`NET_RECV_TIMEOUT_MS`) | `net_config.h:36-38` | +| 调试串口 | USART1 115200,**逐字节阻塞发送** | `Src/usart.c:479-484` | +| LVGL 刷新周期 / 色深 | 20ms / 1bpp(`RENDER_MODE_FULL`) | `lv_conf.h:11/22`、`ui_port.c:126-127` | + +--- + +## 2. 原因清单(机制 / 证据 / 判据 / 对策) + +### D1 CH395F TCP 重传周期 500ms —— 秒级延迟的放大器 ★首要嫌疑 + +- **机制**:CH395F 的 TCP 重传是**固定周期**(非指数退避的 RTT 自适应)。工程显式设为 + `ch395f_set_retrans_count(8)` + `ch395f_set_retrans_period(500)` + (`Drivers/BSP/NET/net_socket.c:142-144`)。任何一段 1024 字节数据在链路上丢失, + 就要等满 500ms 才重发;两段的代价是 1s,依此类推,最坏 4s 后判发送失败。 + 一帧 LCDMEM 应答是 4 个段 → 丢包概率随段数线性上升。 +- **证据**:客户端源码已把这一现象写进注释—— + "整帧 LCDMEM(3209 字节) 分 4 块到达耗时 **0.5~3 秒以上属正常现象**" + (`../DTU-RemtoeLCD/remo_disp_server.py:165-174`,客户端为**与本仓库同级**的独立工程)。 + 观测到的延迟若呈 0.5/1.0/1.5s 台阶, + 就是 500ms 粒度的指纹。 +- **判据**:Wireshark `tcp.port==7003 && tcp.analysis.retransmission`,看是否有重传、 + 以及相邻重传的间隔是否 ≈500ms。 +- **对策**:见 §4-S1(调小重传周期)+ §4-S2(把一帧压到 1~2 段,从根本上减少需要重传的数据)。 + +### D2 整帧 3209B × 1KB 发送 FIFO 停止等待 + +- **机制**:CH395F 发送缓冲只有 2 块 = 1KB(`ch395f.h:122-128`),且手册明确 + "**单片机向 Socket 发送缓冲区写入一次数据后,必须等到产生 `SINT_STAT_SENBUF_FREE` + 中断后才可以写入下一次数据**"(`docs/芯片手册/CH395F.md:568/623`)。 + `net_send_locked` 因此把 3209B 切成 4 块,每块"SPI 写 → 置 `send_ready=0` → 死循环 + `net_poll()` 等中断"(`net_socket.c:955-985`)。期间 **netTask 全程占用**, + 既不能处理其它队列消息,也不能处理其它 socket 事件。 +- **叠加成本**:每次 `WRITE_SEND_BUF` 是 1KB SPI DMA(≈1.6ms @5.25Mbit/s, + `ch395f.c:1117-1147`,内部忙等 `s_spi2_dma_done`,`ch395f.c:89-135`); + 组帧路径还有两次 3200B `memcpy`(`ui_net_sink.c:223` 取帧 + `:114` 组帧)。 +- **基线代价**:约 15~25ms/帧;**常驻带宽 ≈32KB/s**(客户端 10Hz 拉取,画面没变也照发)。 +- **判据**:§3-Step4 实验 A(把应答临时改成 100B,延迟若立刻正常 → 确诊本项)。 +- **对策**:§4-S2(差量/RLE + 版本号 + 由拉改推)、§4-S4(提高发送块配额与 MSS)、 + §4-S5(少拷一次,顺带省 3.2KB RAM)。 + +### D3 netMsgQueue 头阻塞(= 既有审计 **E-02**) + +- **机制**:所有跨任务 `net_*` 操作都排队到 netTask 串行执行 + (`net_socket.c:1190-1337` 投递 + `xTaskNotifyWait` 阻塞等回执), + 而 `net_process_messages` **一次只取一条消息并处理到底**(`:357`)。 + 只要有任何一个消费者发起 `flags=0` 的阻塞 recv,netTask 就会在 + `net_recv_locked` 的等待循环里空转最长 **30s**(`:1019-1067`, + `NET_RECV_TIMEOUT_MS=30000`),此时 **uiTask 每 10ms 的轮询、accept、其它服务的收发全部排在队里**。 +- **触发源**:`lftpd_inet.c:106-109`(FTP 控制连接 `read_line` 阻塞 recv—— + **FTP 客户端连上后空闲 >30s 即触发,属常见工况**)、`time_sync.c:84`。 + `ui_net_sink` 自己是正确的 `NET_MSG_DONTWAIT` 模式,所以它既是受害者也不是触发者。 +- **特征**:**间歇性整屏冻结数秒~数十秒**,与"是否在动 FTP/对时"相关。 +- **判据**:卡顿发生时检查是否有 FTP 会话挂着(或临时停掉 ftpTask/timeSyncTask 做对照,§3-Step4-C)。 +- **对策**:§4-S6(阻塞 recv 改 DONTWAIT 分片 + 调用方重投;超时按服务分档)。 + +### D4 PC 端 TCP 延迟确认(Delayed ACK) + +- **机制**:Windows 对**满 MSS** 段默认延迟确认(最多 200ms,通常 40~200ms,等待捎带)。 + 它每收到 2 个满 MSS 段才立即 ACK。而 CH395F 发送缓冲只有 1×MSS, + **无法把两个段同时放上线**,于是每一段都成了"新的第一段" → 每一段都吃满延迟确认。 +- **判据**:Wireshark `tcp.analysis.ack_rtt`;或看 4 个 1024 段之间的间隔是否 ≈40~200ms + (正常应 <1ms)。 +- **对策**:§4-S2(一帧压到 1 段后自然免疫);必要时客户端机器关延迟确认 + (注册表 `TcpDelayedAcknowledge=0`,机器级副作用需评估),或参考客户端跑 Linux + `TCP_QUICKACK`。 + +### D5 uiTask 节拍与"每拍只收一次 64B" + +- **机制**:主循环顺序是 `ui_port_step(10)` → `ui_net_sink_poll()` → `osDelay(10)` + (`App/task/ui_task.c:91-95`)。两个后果: + 1. **按键注入晚一拍**:KEY 是在 poll 阶段注入的,而 LVGL 的 `lv_timer_handler()` + 已经在本轮开头跑完 → 按键要到下一轮(≤10ms 后)才被读到并渲染。 + 2. **每轮只做一次 `net_recv`,上限 64B**(`ui_net_sink.c:441`,`RM_RX_BUF=64`): + 排空不彻底,突发多帧时跨多拍才吃完。 +- **判据**:uiTask 循环打点(实测循环周期);§3-Step4-D(把 poll 挪到 step 之前对照)。 +- **对策**:§4-S7(poll 前置 + while 排空)。 + +### D6 阻塞式日志躺在关键路径上 + +- **机制**:`fputc` = `HAL_UART_Transmit(&huart1, &ch, 1, HAL_MAX_DELAY)` + (`Src/usart.c:479-484`),逐字节阻塞;`DBG_ENABLE` 与 `APP_TIMESTAMP_ENABLE` 均开 + (`dbg_cfg.h:28/40`),带时间戳一行 ≈55~70 字符 → **约 5ms 阻塞在调用任务内**。 + `net_recv_locked` **每次收到数据都打印一行** + `DBG_INFO("recv sock%d len=%d", ...)`(`net_socket.c:1025`)→ 每帧固定给 netTask 加 ~5ms, + 且这段时间 CH395F 完全没人轮询。 +- **判据**:临时注释该行后复测(这本身也是 P0 快改项之一)。 +- **对策**:§4-P0-b(删/降级该行)+ §4-S8(`fputc` 改环形缓冲 + TX 中断/DMA)。 + +### D7 物理链路与协商("网络延迟"最字面的一种) + +- **机制**:`net_stack_bring_up` **强制** `ch395f_set_phy(CH395F_PHY_100M_FULL)` + (`App/task/net_task.c:100`)。若交换机/网卡端口处于自协商,会出现**双工不匹配** + (一端强制全双工、一端半双工)→ 冲突与静默丢包 → 直接喂给 D1,表现为"莫名秒级卡顿"。 + 另外 PC 在 Wi-Fi 上会引入抖动与丢包;ARP 表项失效也会造成首包停顿 + (CH395F 默认 ARP 重传 3 次 × 1s,手册 §5.42)。 +- **判据**: + 1. 开机日志核对 `PHY: 100M FULL`(`net_task.c:117-122`)——若显示 `10M HALF`/`DISCONNECT`,本项即确诊; + 2. 抓包看重传分布; + 3. 用 `ch395f_get_unreach_ipp()`(`ch395f.c:1326-1331`)看是否有解析不到的目标 IP; + 4. **拓扑对照**:PC 有线直连设备、不经交换机、远离 Wi-Fi,再测。 +- **对策**:§4-S3。 + +### D8 客户端固定成本(双端相同,但决定"下限") + +- **机制**:刷新线程"取帧 → 编码 → 推送 → `wait(0.1)`"串行 + (`remo_disp_server.py:588-647`),因此**有效刷新率 = 1 / (fetch 耗时 + 100ms)**; + 若 fetch = 0.5s,刷新率直接掉到 2fps,"延迟感"主要来自这里。 + `on_key` 只 `send_key`、**不触发取帧**(`:736-741`)→ 按键平均多等 50ms、最多 100ms。 + 1bpp → PIL PNG → base64 → SocketIO → 浏览器解码,是一串纯 CPU 开销(3200B 帧其实不需要 PNG)。 +- **判据**:§3-Step1 打点即可分离出"客户端等待"与"网络等待";§3-Step4-B 用裸 TCP 客户端对照。 +- **对策**:§4-S9。 + +### D9 LVGL 全量渲染与任务优先级 + +- **机制**:`RENDER_MODE_FULL`(`ui_port.c:126-127`)+ `LV_DEF_REFR_PERIOD=20ms` + (`lv_conf.h:22`)→ 任何 invalidate 都重画整屏 160×160;flush 回调无条件整帧 + `memcpy` 3200B(`ui_port.c:80-86`)。uiTask 优先级 **BelowNormal**(`AGENTS.md` 任务表), + 而 defaultTask(Normal,syslog 唯一写者,写 NAND)与 netTask/ftpTask/timeSyncTask(Normal) + 都会抢占它 → `ui_net_sink_poll()` 的实际调用间隔可能远大于 10ms。 +- **判据**:uiTask 循环打点统计实际周期分布;待机页(时钟每秒刷新)与菜单页对比。 +- **对策**:§4-S7;若实测渲染是瓶颈,再考虑降低刷新率/按需 invalidate(改前先核 RAM)。 + +### D10 断线重连路径含 netTask 内 `osDelay(500)` + +- **机制**:`net_relisten_locked` 里 `ch395f_open_socket` 失败时 + `ch395f_close_socket()` + `osDelay(500)`,最多重试 3 次(`net_socket.c:1098-1105`)。 + 这**在 netTask 上下文**执行 → 期间全网停摆 0.5~1.5s。 + 客户端侧连续 5 次拉取失败会主动断开重连(`remo_disp_server.py:597-644`), + 于是"卡顿 → 判失败 → 重连 → 更卡"可能互相放大。 +- **判据**:日志中 `sock0 LISTEN TIMEOUT` / `open socket` 失败 / 客户端 `device_disconnected`。 +- **对策**:§4-S6(改非阻塞重试)。 + +### 相关既有缺陷(会掩盖/放大延迟,建议一并处理) + +| 编号 | 位置 | 与延迟的关系 | +|---|---|---| +| **E-02** | `错误码传播审计.md` §3.2 | 即本文 D3,已立案 | +| **E-04** | `ui_net_sink.c:117` | `remo_send` 丢弃 `net_send` 返回值 → 弱网**短写静默截断**(net 层只打 `short send`,`:990`),客户端 CRC 不过 → 断连 → 触发 D10 → 观感是"卡完直接黑屏/卡住" | +| **E-33/E-34** | `ch395f.c:89-135/181-210` | DMA 失败 void 化无错误通道;字节级命令 `HAL_MAX_DELAY` → 一次 SPI 故障可能让 netTask 永久挂死(无看门狗) | + +--- + +## 3. 排查流程 + +### Step 0 —— 先分类(决定后面查哪一支) + +| 观测现象 | 首要指向 | 跳到 | +|---|---|---| +| 每帧都**稳定**慢 ≈150~300ms | 结构性基线(D2/D5/D6/D8/D9) | Step 1 + Step 4-A | +| **间歇**卡,且 ≈500ms 的整数倍 | **D1** 丢包重传 | Step 2 + Step 4-A | +| **间歇**整屏冻结数秒~数十秒 | **D3** 队列头阻塞 / D10 重连 | Step 3 + Step 4-C | +| 只在开 FTP / 对时后卡 | D3 + 单连接 socket 争用 | Step 4-C | +| 换网口/换线/换 PC 就好 | D7 | Step 2 + 拓扑对照 | + +### Step 1 —— 客户端三点打点(成本最低,最先做) + +在 `../DTU-RemtoeLCD/remo_disp_server.py::fetch_screen` 记录 **t0 请求发出 / t1 收到首字节 / +t2 整帧收齐**,每帧输出一行:`req2first=..ms total=..ms bytes=..`。 + +判读规则(最有决定性的一步): + +| 结果 | 结论 | 下一步 | +|---|---|---| +| `req2first` 大(>50ms) | 设备迟迟不开口:uiTask 拾取慢 / 队列头阻塞 / CH395F 收包慢 | Step 3、Step 4-C/D | +| `req2first` 小但 `total` 大 | 尾块传输慢:1KB 分块 + 重传 + 对端 ACK | Step 2 | +| `total` 落在 500/1000/1500ms | **D1 确诊** | §4-P0-a | +| `total` 稳定 ≈20~40ms 但观感仍慢 | 瓶颈在客户端 100ms 节拍/编码(D8) | §4-S9 | + +### Step 2 —— PC 抓包 `tcp.port == 7003` + +| 过滤器 / 观察项 | 判读 | 对应 | +|---|---|---| +| `tcp.analysis.retransmission` | 有重传 & 间隔 ≈500ms → **D1** | S1 / S3 | +| 4 个 1024B 段的间隔 | 正常 <1ms;≈40~200ms → 被对端 ACK 门控(D4) | S2 / S9 | +| 请求 → 响应首包间隔 | >50ms → 设备侧调度慢(D3/D5/D6) | Step 3 / Step 4 | +| `tcp.analysis.zero_window` / `window_full` | CH395F 接收窗口仅 2KB 被打满 | 收路径核查 | +| `tcp.analysis.out_of_order` / `dupack` | 链路丢包/乱序 | S3 | +| 客户端 `sendall` 是否阻塞 | 请求发不出去 = 设备窗口小/未读 | 收路径核查 | + +### Step 3 —— MCU 侧打点(serial_tool 抓开机+运行日志) + +> ⚠️ **先关掉 `net_socket.c:1025` 的 per-recv 日志再打点**,否则 ~5ms/行的阻塞日志 +> 就在被测路径上,会自欺欺人(见 D6)。 +> ⚠️ 按 `AGENTS.md` 铁律:**新增字符串字面量必须纯 ASCII**。 + +```powershell +& '.venv\Scripts\python.exe' tools/serial_tool/serial_tool.py open +& '.venv\Scripts\python.exe' tools/serial_tool/serial_tool.py clear +# 烧录/复位(采集必须先于烧录) +& '.venv\Scripts\python.exe' tools/serial_tool/serial_tool.py wait-idle --timeout 90 --settle-ms 8000 --max-ms 45000 +``` + +打点位置与建议指标(用 `HAL_GetTick()` 或 DWT CYCCNT): + +| 位置 | 指标 | +|---|---| +| `net_send_locked`(`net_socket.c:938`) | 每 chunk 等待 ms + 整次总 ms + `sent/len`(已有 `short send` 日志可先看) | +| `ui_net_sink_poll`(`ui_net_sink.c:406`) | `net_recv` 返回 → `net_send` 返回的 ms | +| `ui_task` 循环(`ui_task.c:91`) | 实测循环周期(`osDelay(10)` 之外多花了多少)→ 反推 D5/D9 | +| `net_process_messages`(`net_socket.c:357`) | 单条消息处理耗时 + 队列积压深度 → **直接量化 D3** | +| 开机日志 | `PHY: 100M FULL` 是否成立(D7) | + +### Step 4 —— 四个隔离实验(二分定位) + +| # | 实验 | 做法 | 结论 | +|---|---|---|---| +| **A** | **缩小响应**(★最有决定性) | 临时让 `CMD_LCDMEM` 只回 ~100B(而非 3204B) | 延迟立刻正常 → 确诊 D2(及被 D1/D4 放大);仍慢 → 瓶颈在请求/调度路径(D3/D5/D6) | +| **B** | **裸 TCP 客户端** | 30 行 Python:直连 7003,循环发 LCDMEM 并计时往返(不走 Flask/WebSocket/PNG) | 变快 → D8;不变 → 设备侧 | +| **C** | **关掉 FTP/对时** | 临时只保留 :7003 服务 | 变快 → **D3**(E-02 头阻塞)/ 单连接 socket 争用 | +| **D** | **poll 前置** | `ui_task.c` 循环改成先 `ui_net_sink_poll()` 再 `ui_port_step(10)` | 变快 → D5 | + +> 实验 A/B/D 是**临时验证手段**;正式修法以 §4 为准,勿把临时改动提交。 + +--- + +## 4. 解决方案 + +### P0 —— 一行级、低风险、先做(需 `@build` 0 错误 0 警告) + +| 编号 | 改动 | 治 | 预期 / 代价 | +|---|---|---|---| +| **P0-a** | `net_socket.c:144` `ch395f_set_retrans_period(500)` → **`200`** | D1 症状 | 单次丢包代价 500ms→200ms(体感卡顿降一档)。代价:总窗口 8×200=1.6s,链路真断时判失败更快(对远显是有利的)。手册 §5.18 允许 ≤1000ms,**最小值未标注,需台架确认 200 被接受**(可用 `GET_CMD_STATUS` 校验) | +| **P0-b** | 删除/降级 `net_socket.c:1025` 的 per-recv `DBG_INFO` | D6 | 每帧省 ~5ms netTask 阻塞;顺带减少 syslog 噪声与 NAND 写入 | + +### S1 —— 重传与超时参数分档 + +- `NET_SEND_TIMEOUT_MS=1000` / `NET_RECV_TIMEOUT_MS=30000`(`net_config.h:32-38`) + 目前是**全局一刀切**:对远显,30s 阻塞是灾难;对 FTP,1s 发送超时可能偏紧。 + 建议按服务分离(`ui_net_sink` 用短超时 + 非阻塞,FTP 保留长超时)。 + +### S2 —— 差量 / RLE + 帧版本号 + 由拉改推(**根治,收益最大**) + +| 手段 | 做法 | 效果 | +|---|---|---| +| **差量 + RLE** | `ui_flush_cb`(`ui_port.c:80-86`)中与上一帧比对,记录变化矩形/行程;协议新增 `CMD_LCDMEM_DIFF`(保留旧 `LCDMEM` 全帧以兼容) | 1bpp 菜单典型变化 **<100B** → 单段一帧 → 延迟 ≈ 一个 RTT;D1 丢包影响面从 4 段降到 1 段 | +| **帧版本号** | 设备维护 `frame_seq`,客户端请求带上自己看到的 seq;未变则回 `NOCHANGE`(~10B) | 稳态带宽从 **32KB/s → ≈0**,把通道留给"真变化帧" | +| **由拉改推** | flush 后设备主动推 UNSOL 帧,客户端不必等 100ms 节拍 | 按键响应从"≤100ms + 取帧"降到"一个 RTT" | +| 兼容 | 新命令走同一帧格式与 CRC;旧客户端仍可用 `LCDMEM` 全帧 | 双端共用 `ui_net_sink.c` 不变 | + +### S3 —— 物理链路与协商 + +1. 核对开机日志 `PHY: 100M FULL`(`net_task.c:117-122`)。 +2. 二选一:交换机端口也**强制 100M 全双工**(与 `net_task.c:100` 对齐), + 或让 CH395F 走自协商(需评估既有约束后改动)。 +3. 换网线 / 换交换机端口 / **PC 有线直连**做对照;PC 不要在 Wi-Fi 上测。 +4. 用 `ch395f_get_unreach_ipp()` 观测不可达 IP(ARP 侧线索)。 + +### S4 —— 提高发送块配额与 MSS + +- 现状:48 块 × 512B 均分给 8 个 socket(`net_socket.c:196-202`),**实际只用到 3 个** + (:21 FTP / :8888 对时 / :7003 远显)。 +- 改法:按**实际使用数**分配,例如 3 个 socket 各 16 块(接收 3KB + 发送 3KB), + MSS 提到 1460 → 3209B 从 4 chunk/4 段降到 **1~2 chunk/段**。 +- **约束**: + - `NET_SEND_CHUNK_MAX` 仍受 SPI DMA 载荷 2048B 二次钳制(`net_socket.c:916-921`), + 单块实际 ≤2048B; + - **Trap 20 铁律**:单次写长度 **必须 ≤ 发送缓冲**,否则写穿 FIFO 静默丢数据 + (`docs/问题记录/CH395F_Trap_Records.md` Trap 20); + - `net_init` 的分配是按 `i * blocks_per_socket` 硬算 8 个(`:196-202`),改分配策略需同步 + `net_bind` 里的重复分配路径(`:518-521`); + - 改前先读 `docs/问题记录/CH395F_Trap_Records.md`,改后跑 CH395F 测试套件 Phase 8(100KB 吞吐)。 + +### S5 —— 去掉一次 3200B 拷贝(顺带省 RAM) + +`g_tx[3209]`(`ui_net_sink.c:40`)与 `g_lcdmem_data[3204]`(`:41`)各占一份, +LCDMEM 路径是"帧缓冲 → `g_lcdmem_data+4` → `g_tx+4`"两次 memcpy。 +让 `g_tx` 直接承接位图(取帧直接写到 `g_tx + 8`)可省一次 3200B 拷贝 + **约 3.2KB 常驻 RAM** +(该缓冲已被 `docs/内存管理/RAM使用分析与优化方案.md` §1 列为 6.4KB 常驻大户)。 +**注意**:改 RAM 占用前先按 `AGENTS.md` 跑 `scripts/ram_report.py` 核算。 + +### S6 —— 消除 netTask 头阻塞(= 修 E-02,顺带修 D10) + +1. `lftpd_inet.c:106-109`、`time_sync.c:84` 的 `flags=0` 阻塞 recv → + **`NET_MSG_DONTWAIT` 分片 + 调用方重投 + 总超时**(`ui_net_sink` 已是正确范式)。 +2. `net_relisten_locked` 的 `osDelay(500)`(`net_socket.c:1104`)改为**非阻塞重试** + (置状态位,交主循环下轮重试),别在 netTask 里睡。 +3. 顺带修 **E-04**:`ui_net_sink.c:117` 校验 `net_send` 返回值 `== len`, + 短写则 `DBG_ERROR` + `net_close(g_client)` 触发客户端重连补帧(让截断可观测、可自愈)。 + +### S7 —— uiTask 节拍与收包排空 + +- `ui_task.c:91-95` 改为**先 `ui_net_sink_poll()` 再 `ui_port_step(10)`**(省掉按键的一整拍)。 +- `ui_net_sink_poll` 内改成 **while 排空**:一轮里连续 `net_recv` 直到 `WOULDBLOCK`, + 而不是每轮只收一次 64B;必要时把 `RM_RX_BUF`(`ui_net_sink.c:38`)与请求帧长对齐。 +- 若 Step 3 实测渲染是瓶颈:降低 `LV_DEF_REFR_PERIOD` 的无效刷新、按需 `invalidate`, + 或把 sink 轮询从渲染中解耦(渲染前后各排空一次收包)。 + **改栈/缓冲前先核 RAM 与 `scripts/stack_report.py`。** + +### S8 —— 日志改非阻塞 + +`Src/usart.c:479-484` 的 `fputc` 从"逐字节阻塞 `HAL_UART_Transmit`"改为 +**环形缓冲 + TX 中断/DMA 发送**(关键路径上只写环形缓冲)。 +收益:所有任务的日志成本从 ~5ms/行降到 µs 级;风险:需处理缓冲溢出丢弃策略与 +`HardFault` 路径的同步冲刷。 + +### S9 —— 客户端侧(DTU-RemoteLCD) + +1. **按键后立即取帧**:`on_key`(`:736-741`)里发完键直接触发一次 `fetch_screen`, + 不等 100ms tick(省平均 50ms、最多 100ms)。 +2. **不传 PNG**:直接把 1bpp 位图(或差量)发给前端,用 canvas 绘制, + 省掉 PIL 编码 + base64 + 浏览器 PNG 解码。 +3. **自适应节拍**:把固定 `wait(0.1)` 改为"上一帧耗时补偿"式调度,避免慢帧叠加。 +4. **关闭 Windows 延迟确认**(可选,机器级副作用需评估): + `TcpDelayedAcknowledge=0`;或参考客户端跑 Linux + `TCP_QUICKACK`。 +5. **拓扑纪律**:验证用 PC 一律**有线**、与设备同交换机或直连。 + +--- + +## 5. 相关文档 + +| 主题 | 位置 | +|---|---| +| RemoDispBus 协议与接收设计 | `docs/UI 设计/RemoDispBus协议与接收设计说明.md` | +| UI 设计架构(离屏渲染 + 推流) | `docs/UI 设计/UI_设计架构.md` | +| BSD Socket API 与双端后端 | `docs/BSD_Socket_API_使用指南.md` | +| CH395F 陷阱史(Trap 20 发送分块等) | `docs/问题记录/CH395F_Trap_Records.md` | +| 错误码传播审计(**E-02 / E-04 / E-33 / E-34**) | `docs/问题记录/错误码传播审计.md` | +| CH395F 测试套件(吞吐 Phase 8) | `docs/测试说明/CH395F_Test_Guide.md` | +| RAM 预算(`ui_net_sink` 6.4KB 常驻) | `docs/内存管理/RAM使用分析与优化方案.md` | +| 串口助手(抓日志排查) | `docs/测试说明/串口助手使用指南.md` | +| 远端显示客户端(**与本仓库同级的独立工程**,目录名含历史拼写 `Remtoe`) | `../DTU-RemtoeLCD/remo_disp_server.py`、`remo_disp_ui.html` | +| 芯片手册(§5.17/§5.18 重传、§5.42 ARP、SENBUF_FREE 语义) | `docs/芯片手册/CH395F.md` | + +--- + +## 6. 变更记录 + +| 日期 | 变更 | +|---|---| +| 2026-09-22 | 创建:远程显示延迟静态全链路分析。拆解一帧旅程与延迟预算(典型 ≈130~170ms,异常 0.5s~30s);列 D1~D10 十项候选原因并给代码级证据与判据(首要嫌疑 = CH395F 重传周期 500ms `net_socket.c:143-144`,其次 = 3209B 整帧 × 1KB 发送 FIFO 停止等待);给出 Step 0~4 排查流程(分类 → 客户端三点打点 → Wireshark → MCU 打点 → 四个隔离实验)与逐因对策(P0 两项一行级改动 + S1~S9);关联既有审计 E-02/E-04/E-33/E-34。**未修改任何代码。** | diff --git a/docs/项目启动顺序管理.md b/docs/项目启动顺序管理.md index 5211aea1..9e869f3f 100644 --- a/docs/项目启动顺序管理.md +++ b/docs/项目启动顺序管理.md @@ -45,15 +45,6 @@ > 上图:**左 = 生产者(set)· 中 = 事件组 `sys_evtHandle` · 右 = 消费者(wait)**;任务卡与事件位对齐同一行,`bit1 → ftpTask` 的额外依赖用 **All 汇合点**(`osFlagsWaitAll`)表达。 > 源码 `tools/arch_diagram/gen_barrier_svg.py`(改数据重跑即可),PNG 预览本地渲染、不入库。 -### 为什么用 Event Flags 而非全局变量 -| 维度 | 全局标志 `g_net_ready` | Event Flags | -|------|----------------------|-------------| -| 等待方式 | 忙等轮询(浪费 tick) | 阻塞等待(零占用) | -| 多依赖组合 | 不支持 | `osFlagsWaitAll` / `osFlagsWaitAny` | -| 类型/作用域 | 裸 `volatile`,易误用 | 内核对象,API 受控 | -| 清空语义 | 手动维护 | 默认**消费**(唤醒即清);**就绪位必须 `osFlagsNoClear`**,只有命令位才用消费语义 | -| 调试可见性 | 不可观测 | 可在 IDE 观测事件组值 | - --- ## 3. 方案设计 diff --git a/docs/项目审计改进清单.md b/docs/项目审计改进清单.md index 07957f63..7e4179ae 100644 --- a/docs/项目审计改进清单.md +++ b/docs/项目审计改进清单.md @@ -1,38 +1,25 @@ -# 项目审计改进清单 +# 项目审计改进清单(待办) - **审计日期**:2026-09-15(**2026-09-18 补充**:《项目审查与优化建议报告》新增 P0-1,当日修复) - **范围**:全部项目自有代码(App/、Drivers/BSP/、Src/ USER CODE、MDK-ARM/、test/、tools/、scripts/、.opencode/、docs/ 一致性);第三方库(Lib/、Middlewares/、CMSIS/HAL、lftpd 内部、LVGL)仅查项目侧误用 - **方法**:静态逐文件走读 + pre-commit 全库基线(cppcheck/规范/乱码,全绿)+ 三份 trap 文档交叉核对 - **严重度**:P0 = 确认功能 bug | P1 = 疑似 bug 或健壮性风险 | P2 = 改进 | P3 = 卫生 - **置信度**:high = 代码级确证 | medium = 依赖外部条件(手册/硬件/产品定义) +- **删改**:2026-09-21 —— 已完成条目删除,仅保留待办;已修项见文末「已完成(勿重复报)」;各保留条目均于 2026-09-21 对照源码复核现态 ## 结论总览 -| 级别 | 数量 | 说明 | +| 级别 | 剩余 | 说明 | |---|---|---| -| P0 | 1 | ✅ P0-1 已修复(2026-09-18):配置引擎临时缓冲走 C 库 `malloc`,而 MicroLIB C 堆仅 512 B → 真机配置加载/保存必然失败(模拟器走宿主堆,双端表现分裂) | -| P1 | 10 | 6 项已修复(P1-2/3/4/5/6,2026-09-15;P1-9,2026-09-16);4 项未修:P1-1 ADC / P1-7 MAC 顺序 / P1-8 重建恢复 / P1-10 测试固件 RAM 溢出 | -| P2 | 16 | 健壮性/错误传播/文档失真 | -| P3 | 13 | 卫生项,可随其他修改顺手处理 | +| P0 | 0 | P0-1 已修复(2026-09-18),仅剩真机 `@test` 验证(见「待验证」) | +| P1 | 4 | P1-1 ADC / P1-7 MAC 顺序 / P1-8 重建恢复 / P1-10 测试固件构建验证 | +| P2 | 13 | 健壮性/错误传播/文档失真:P2-1/2/3/4/5/7/8/9/10/11/12/13/15 | +| P3 | 11 | 卫生项:P3-1~8/11/12/13,可随其他修改顺手处理 | **Top 3(建议优先)**: 1. **P1-1 ADC 采样数据路径在产品构建中死亡** —— 上电后仅采 1 次即永久停止,DTU 核心采集路径实际不工作(若产品定义为持续采集,应升 P0)。 -2. **P1-2 墙钟 49.7 天回绕** —— 无人值守部署数月后时间回跳,日志时间戳/RTC 全乱,且自检代码是死的、无法发现。✅ 2026-09-15 已修复(30 天惰性再基准化 + 错误契约 + 哨兵修正) -3. **P1-3/P1-5 配置并发与缺失轮询需一并修** —— UI 保存在 uiTask 直接调 cfg_file,与 netTask 的 load/poll 共享无保护静态态;补上缺失的 `cfg_file_poll` 前必须先做任务串行化,否则会放大竞态。✅ 2026-09-15 已修复(配置引擎属主 = defaultTask,P1-3/P1-5 同一变更落地) - ---- - -## P0 - -### P0-1 配置引擎临时缓冲用 C 库 malloc(MicroLIB C 堆仅 512 B)→ 真机配置加载/保存必然失败 ✅ 已修复(2026-09-18) -- **位置**:`App/cfg_file.c` 修复前 `:485`(读缓冲 4,097B)/ `:522`(模板输出 5,120B)/ `:1038`(外科保存 Pass1 副本 ≤4,097B)/ `:1050`(保存输出 5,120B);根因参数在 `MDK-ARM/startup_stm32f407xx.s:43` `Heap_Size EQU 0x200` -- **证据**(静态三重,可独立复核):① `.map`(2026-09-16)`0x2001e878 - 0x00000200 Zero RW HEAP` 且 `__heap_limit = 0x2001ea78` → **C 库堆总量 = 0x200 = 512 B**;② 交叉引用 `cfg_file.o(i.s_load_file)` / `(i.s_write_template)` / `(i.cfg_file_save)` `refers to malloc.o(i.malloc)`,请求量 4,097 / 5,120 B,且全仓库无 `pvPortMalloc` retarget;③ `.lnp` 带 `--library_type=microlib`,MicroLIB 堆为固定区不可外扩。调用链:`Src/freertos.c:234 net_cfg_load()` → `Drivers/BSP/NETCFG/net_cfg.c:146 cfg_file_load()` → `s_load_file()` → `malloc`。 -- **影响**:真机上配置文件**读不进也写不出**——上电加载回落工厂默认(192.168.1.100/24)、首启出厂模板生成失败、UI 保存与外科保存全部失败;日志签名 `read: malloc 4097B FAIL` / `template: malloc 5120B FAIL` / `load: file read FAIL, factory defaults kept`。**PC 模拟器走宿主 CRT 堆(MB 级)完全正常** → 双端行为分裂,双端 0 错 0 警也照不出来(`--cfgtest` PASS 亦不能证伪)。`test/verify_log.py` Forbidden 含 `FAIL`,一次 `@test` 即可捕获。 -- **修复**(2026-09-18 实际实施):① `cfg_file_store` 后端新增 `cfg_store_alloc` / `cfg_store_free` / `cfg_store_heap_free` 三件套——MCU 端 = `pvPortMalloc`/`vPortFree`(取 FreeRTOS 堆 49,152B,与 `lftpd_io.c` 既有模式一致),模拟器端 = `malloc`/`free`;共享核心 `cfg_file.c` 内 C 库分配符号清零,双端仍可同源编译;② 新增编译期堆峰值预算 `CFG_HEAP_BUDGET = 10,240B`(typedef 负数组实现,ARMCC 5 C99 无 `_Static_assert`)——放大 `CFG_FILE_MAX_LEN`/`CFG_OUT_MAX` 会先编译失败,而不是退化为真机运行期失败;③ 四条失败路径(加载读失败 / 模板写失败 / 保存分配失败 / 保存写盘失败)补 `syslog_post(SYSLOG_SELFTEST, …)`,失败进自检记录页而非只留一行串口;`load: OK` 行附带 `heap free` 实测值;④ 文件头"属主任务"注释 netTask → defaultTask(P1-3 遗留漂移)。 -- **未采用方案**:静态缓冲(4,097 + 5,120 = 9,217B,当时 SRAM1 仅余 4,488B 放不下,须先做 CCM 迁移);调大 MicroLIB `Heap_Size`/加 `--heap`(只是把"必然失败"变成"够大就侥幸",且该堆无碎片与水位观测)。**流式读写(免整文件驻留内存,+≈1.2KB 即可彻底摆脱暂态堆)列为后续改进项**,见《项目审查与优化建议报告》§4.4.1。 -- **验证**:MCU `@build` 0 错 0 警;map 复核 `malloc.o` 不再参与链接、`Removing startup_stm32f407xx.o(HEAP), (512 bytes)` → 静态区 126,584 → **126,064 B**(余 4,488 → **5,008 B**),并新增 `cfg_file.o → cfg_file_store_mcu.o(cfg_store_alloc) → heap_4.o(pvPortMalloc)`;模拟器重建 0 警 + `lvgl_sim.exe --cfgtest` **PASS**(加载 523B → 外部编辑沉降重载 → 外科保存注释/值无损)。**待补**:真机 `@test` 一次,确认 `load: OK` 出现且 Forbidden `FAIL` 零命中。 -- **附带发现(工具链陷阱)**:`App/cfg_file.c` 带 UTF-8 BOM(内含中文文档头字符串字面量);编辑工具剥掉 BOM 后 ARMCC 按 CP936 解码 → `s_doc_header` 报 4 条 `#870-D invalid multibyte character sequence`。已按 HEAD 的 BOM 状态复原。**改含中文字面量的文件后必须核对 BOM 未被剥**(`@build` 的 #870-D 是唯一暴露点)。 -- **置信度**:high(失败机制 = 链接图 + 工具链堆尺寸双重静态确证;真机正向确认待 `@test`)。 +2. **P1-8 网络重建失败无恢复** —— 一次 PHY/芯片抖动 → 网络永久不可用,运行日志无任何痕迹,现场无法定位。 +3. **P1-10 CH395F 测试固件构建闭环** —— 预算基线已重建、测试缓冲已瘦身(产品镜像余量 4.4KB → ~49KB),剩 7 配置构建实测 + `@test`。 --- @@ -45,41 +32,6 @@ - **修复建议**:周期 CONVST 重触发(定时器或任务)+ 数据消费任务;若基线版本有意不实现采集,应删除启动调用并以 `APP_ADC_ENABLE` 真实门控。 - **置信度**:high(代码级确证);P0/P1 定级取决于产品定义(需确认,见"待确认")。 -### P1-2 墙钟 49.7 天回绕 + 时钟自检死代码 ✅ 已修复(2026-09-15) -- **位置**:`App/util/sys_clock.c` -- **证据**:`sys_clock_get()` 以 `(HAL_GetTick() - s_base_tick) / 1000` 计秒——32 位 tick 49.7 天回绕,无周期基准刷新;`sys_clock_init()` 恒返回 0,`app_main` 中"时钟芯片异常"自检分支永不可达。 -- **影响**:连续运行 49.7 天后墙钟整体回退,`/log` 时间戳、FatFS 文件时间、RTC 同步基准全部错乱;时钟芯片真实故障也永远不会被自检发现。 -- **修复**(2026-09-15 实际实施):`sys_clock_get` 内 **30 天惰性再基准化**(自我参照、零 I2C、亚秒余数结转、无损;不周期重读 RTC——重锚会吸收 RTC 漂移);`sys_clock_init` 检查 `sd2506_get_time` 返回值 + 写回复验,失败降级为编译期默认时间基准并返回 -1(自检复活);顺带修正"未初始化"哨兵判断对正 int64 永不成立的缺陷。详见 `docs/系统墙钟时间维护说明.md` §3/§6/§7/§9/§12。构建时新发现:本机 ARMCC 按 CP936 解读无 BOM 源文件,字符串字面量含奇数个汉字会吞掉结束引号致编译失败(本次 13 汉字日志串触发)→ 日志串改 ASCII 并沉淀为 AGENTS.md 编码铁律。 -- **置信度**:high。 - -### P1-3 UI 保存路径与 netTask 的 cfg_file 跨任务竞态 ✅ 已修复(2026-09-15) -- **位置**:`Drivers/BSP/UI/ui_page_net_cfg.c:312`(`nc_key_cb` 在 **uiTask 上下文**直接调 `net_cfg_save()` → `cfg_file_save()`) -- **证据**:cfg_file 引擎为单任务静态态设计(解析表/模板缓冲无跨任务保护);netTask(优先级 Normal > uiTask BelowNormal)可在 uiTask 保存中途抢占,执行 reconfig 的 `net_cfg_load` 或(补齐 P1-5 后)周期 `cfg_file_poll`。 -- **影响**:第二次保存落入 reconfig 启动窗口(约前 100ms 的读文件+解析)时,两任务读写同一静态解析态 → 配置内容损坏/丢失。当前窗口小(需用户恰好在重建初期再按保存);**补齐 P1-5 的 poll 后窗口变为常态化**(poll 每 ~4s 一次 vs 保存耗时数百 ms 至秒级)。 -- **修复**(2026-09-15 实际实施,属主 = **defaultTask**):① UI 保存改跨任务交接——`net_cfg_save_request()`(uiTask:置"待保存"单字节标志 + `SYS_EVT_CFG_SAVE` 事件唤醒,微秒级、不触碰引擎)→ defaultTask `net_cfg_save_run()` 执行写盘,成功触发"立即应用"(`SYS_EVT_NET_RECONFIG` → netTask 重建);交接状态 ENTER/WAIT/OK/ERR 供保存行显示,写盘期间的新请求重位置位不丢失;② `net_stack_bring_up` 不再调 `net_cfg_load`——加载移入 defaultTask 的 `STORAGE_READY` 屏障之前(屏障语义 = 卷挂载 + 配置就绪),netTask 只读 RAM 副本;③ 引擎全部调用(load/save/poll)收敛 defaultTask 单任务串行 → 静态态无交错可能,竞态在结构上消除(不加锁:锁只会把"写坏"换成"网络心跳卡死")。模拟器主循环兼任属主(回调模式,零 CMSIS 依赖)。`@build` 0 错 0 警 + UI 五步门禁全绿。 -- **置信度**:medium(引擎静态态共享 + 抢占窗口确证;触发需特定时序)。 - -### P1-4 lftpd 写路径对 net_send 返回 0 死循环 ✅ 已修复(2026-09-15) -- **位置**:`Drivers/BSP/NET/lftpd/lftpd_inet.c`(`lftpd_inet_write` / `write_string`) -- **证据**:循环体 `p += n; remaining -= n;` 无 `n == 0` 分支;`net_send` 超时(`NET_SEND_TIMEOUT_MS=1000`)且未写入任何字节时返回 0。 -- **影响**:对端停滞/链路劣化 → ftpTask 在写循环中空转永不返回 → FTP 整体死亡,只能重启。 -- **修复**(2026-09-15 实际实施):`lftpd_inet_write` / `lftpd_inet_write_string` 循环均改为 `n <= 0` → 返回 -1(零进展超时 = 对端未消费数据,判为致命);拆除走调用方既有路径:`send_file` 收到 <0 → 关文件报错 → 命令层回 450(会话保留);`handle_control_channel` 欢迎语路径非零即拆连接;未检查返回值的响应最迟由下一次 `read_line`(≤ `NET_RECV_TIMEOUT_MS`)或代次变化终止会话——任务不再死锁。`@build` 0 错 0 警。 -- **置信度**:high。 - -### P1-5 MCU 端缺失 cfg_file_poll(文档承诺的自动重载不存在)✅ 已修复(2026-09-15) -- **位置**:`App/task/net_task.c`(主循环);对照 `simulator/main.c`(唯一调用点) -- **证据**:`cfg_file_poll()` 仅 PC 模拟器调用;`App/net_cfg.c:157` 注释、`docs/网络配置说明.md:82`、`docs/项目架构总览.md:152` 均承诺"FTP 外部修改 4s 沉降后自动重载,无需重启"。 -- **影响**:真机上经 FTP 修改 `/config.ini` 必须重启才生效——文档承诺的功能在 MCU 端缺失。 -- **修复**(2026-09-15 实际实施):defaultTask 主循环加 **2s 周期** `cfg_file_poll()`(与模拟器同节拍;4s 沉降去抖 → 检出后重载 + 变更回调 → `SYS_EVT_NET_RECONFIG` → netTask 重建网络,免重启生效)——与 P1-3 同一变更落地:poll 与 UI 保存同在 defaultTask 串行执行,互不交错。 -- **置信度**:high。 - -### P1-6 netTask 双重创建 ✅ 已修复(2026-09-15) -- **位置**:`Src/freertos.c:162`(CubeMX 区无条件创建)+ `:176`(USER CODE,`#ifdef ENABLE_NET_LAYER_TESTS` 再创建一次) -- **证据**:NET 层测试构建下两个 netTask 并发轮询同一 SPI/消息队列;硬件层测试模式下 netTask 仍被创建且持续 `net_poll`,与"testTask 独占 CH395F"的设计矛盾。 -- **修复**(2026-09-15 实际实施,基础线程策略):① 删除 `freertos.c` USER CODE 区冗余的 netTask 创建(生成区实例 = 唯一基础线程,全构建存在);② `net_task_func` 入口加 `ENABLE_HW_LAYER_TESTS` 自挂起守卫(不 bring_up、不轮询、不广播 NET_READY)→ 硬件层模式 testTask 独占 CH395F,设计承诺成真。彻底方案(`.ioc` 移除 + 条件创建)经用户拍板未采用:网络线程是基础线程、恒创建,测试模式仅"不运行"(硬件层测试构建仍占 4KB 栈,接受该成本)。 -- **验证**:产品 / CH395F-HW(Phase2) 构建 0 错 0 警;守卫零 RAM 增量(126592B = 基线 126584B + 8B `g_test_stats`);A/B 对照(HEAD vs 修复后)确证 NET 层配置的两处缺陷(P1-9/P1-10)均为存量、与本次变更无关。 -- **置信度**:high。 - ### P1-7 MAC 写入顺序违反 trap 文档 - **位置**:`Drivers/BSP/NET/net_socket.c` `net_init()`:`ch395f_init()` → `ch395f_set_mac_addr()` - **证据**:`docs/问题记录/CH395F_Trap_Records.md` 规定初始化顺序 SET_MAC → SET_IP → INIT_CH395 → SET_PHY;现码把 MAC 放在 INIT 之后。 @@ -94,20 +46,12 @@ - **修复建议**:失败写 syslog 自检记录 + LED 指示 + 周期重试(如 30s 一次),不得自驻留。 - **置信度**:high。 -### P1-9 NET 层测试固件编译断裂(s_rx_buf 使用先于声明)✅ 已修复(2026-09-16) -- **位置**:`test/net_test_task.c:60`(`phase6_run` 内引用)vs `:431`(声明) -- **证据**:`s_rx_buf`/`s_tx_buf`(各 32KB)声明在文件后段,`phase6_run` 在 60 行先使用 → ARMCC `#20: identifier "s_rx_buf" is undefined`;文件诞生即此顺序(2026-08 `88160a5`),NET 层配置逾两周不可编译;A/B 对照(HEAD vs P1-6 修复后)报错相同,与 P1-6 变更无关。 -- **影响**:全部 NET 层测试构建(Phase 6~11)无法编译;P1-6 的 NET 模式运行验证与 CH395F NET 套件回归被阻塞。 -- **修复**(2026-09-16 实际实施):① 两个 static 声明与其依赖的 Phase 8 方向配置块(`PHASE8_DIR_*` / `PHASE8_DIRECTION` / `PHASE8_FILE_SIZE`)整体前移到文件首部(`TEST_BUF_SIZE` 之后、Phase 6 首次使用之前)——方向配置与声明的相对顺序不变,各方向(ECHO/SEND/RECV)的缓冲门控语义不变;② 顺带修复同类伴随缺陷:`phase9_run` 亦使用 `s_rx_buf` 而原声明条件遗漏 Phase 9(PHASE9-only 配置仍会 `#20` 断裂)→ `s_rx_buf` 条件补 `defined(ENABLE_PHASE9_TESTS)`,文件头注释同步更正。 -- **验证**(2026-09-16,7 配置构建复验,ARMCC V5.06u7 增量构建):① 产品构建 0 错 0 警,ZI=125952B 与审计基线完全一致(产品配置零静态增量);② PHASE7-only 0 错 0 警且链接成功(该配置无 32KB 静态缓冲,全链绿色);③ PHASE6 / PHASE9 / PHASE10 / PHASE8-ECHO / PHASE8-SEND / PHASE8-RECV 六配置 `net_test_task.c` 编译阶段均 0 错 0 警(#20 消除;ECHO 声明双缓冲、SEND 仅 `s_tx_buf`、RECV 仅 `s_rx_buf`,方向门控逐一实测正确);③ 中各配置链接阶段报 `L6406E/L6407E` scatter 溢出——即修复建议预判的 P1-10 暴露(32KB 缓冲超出 4.4KB 静态余量),与本次修复无关,留待 P1-10 处置。 -- **置信度**:high(构建实测复现;修复后 7 配置构建复验通过)。 - -### P1-10 测试固件静态 RAM 预算溢出(128KB scatter vs 产品镜像 123.6KB) +### P1-10 测试固件静态 RAM 预算溢出(基线已重建、测试缓冲已瘦身,剩构建实测闭环) - **位置**:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct`(`RW_IRAM1 0x20000000 0x00020000` = 128KB;芯片 192KB 的上部 64KB 有意未映射,克隆芯片兼容性考量) -- **证据**:产品构建静态镜像 = RW 632B + ZI 125952B = **123.6KB / 128KB**(余量仅 4.4KB);HW 全阶段固件的 PHASE5 三缓冲(3×4093B ≈ 12.3KB)→ 超 8.7KB(`L6406E` 实测复现);NET 固件的 32KB `s_rx_buf` → 超约 28KB。产品镜像自 2026-09 起 UI 功能增长(runlog/selftest 页、配置引擎、日志引擎等)已吃掉静态预算。 -- **影响**:CH395F 套件(HW 全阶段 / NET 层)均不可构建,对应 @test 闭环中断;`docs/内存管理/` 旧表数字与实际失真,预算基线需重建。 -- **修复建议**:先全量实测静态/堆建立新基线(`scripts/ram_report.py` + FreeRTOS 堆统计);再按序选择:测试缓冲瘦身(PHASE5 分块、`TEST_BUF_SIZE` 下调)、产品镜像压缩(UI 缓冲/任务栈,含 P2-13 testTask 门控省 4KB);**不建议**扩 scatter 到 192KB(破坏既有克隆芯片兼容决策)。 -- **置信度**:high(构建实测 + scatter 文件交叉核对)。 +- **历史**(2026-09-15 审计 / 09-16 复现):产品构建静态镜像曾 = 123.6KB/128KB(余量仅 4.4KB);HW 全阶段固件的 PHASE5 三缓冲(3×4093B)→ 超 8.7KB(`L6406E` 实测);NET 固件的 32KB `s_rx_buf` → 超约 28KB;`docs/内存管理/` 旧表数字与实际失真。 +- **进展**(2026-09-21,已复核):① 预算基线重建(`docs/内存管理/RAM使用分析与优化方案.md` v1.2):FreeRTOS 堆迁 CCM(RW_CCM 49,184/65,536 B),任务栈(合计 38,912B)改由 CCM 堆分配、不再占主 SRAM;产品构建 RW_IRAM1 = **82,048/131,072 B(62.6%,余 ~49KB)**;② NET 层测试 32KB `s_rx_buf`/`s_tx_buf` 已移除,改小局部缓冲(`rx_buf[300]`/`rx_buf[64]`);③ STORAGE 套件固件 2026-09-20/21 实测可构建可运行(含 deep call chain、破坏性 GD5F 运行)。 +- **剩余**:① CH395F 套件(`TEST_SUITE_CH395F`,HW 全阶段 / NET 层 7 配置)实际构建确认链接通过——PHASE5 三静态缓冲 3×4093B 仍在,理论余量足够但未实测;② `@test` 闭环;③ **不建议**扩 scatter 到 192KB(破坏既有克隆芯片兼容决策)。 +- **置信度**:high(基线数据来自 2026-09-21 map;测试固件构建未复验)。 --- @@ -115,22 +59,19 @@ | # | 位置 | 问题 | 修复建议 | 置信度 | |---|---|---|---|---| -| P2-1 | `App/cfg_file.c` | 受管键行 >127 字符时外科保存产生重复键行(值静默截断,旧行残留) | 行长超限时拒绝保存并报错,或整卷重写 | high | -| P2-2 | `Drivers/BSP/CH395F/ch395f.c` | DMA 超时/abort 后不向调用方传播错误(调用方当成功继续 → 静默数据损坏) | 超时路径置错误标志并经返回值/查询接口上报 | high | -| P2-3 | `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c` `gd5f_bbt_save` | 丢弃 program_load/program_exec 返回值:编程失败仍推进 BBT 版本号并报成功 | 检查返回值,失败不推进版本 | high | +| P2-1 | `App/cfg_file.c` | 受管键行 >127 字符时外科保存产生重复键行(值静默截断,旧行残留;Pass1 匹配仍受 `CFG_LINE_TEST_MAX=127` 限制) | 行长超限时拒绝保存并报错,或整卷重写 | high | +| P2-2 | `Drivers/BSP/CH395F/ch395f.c` | `ch395f_spi_dma_transfer` 为 void:DMA 超时/abort 后不向调用方传播错误(调用方当成功继续 → 静默数据损坏) | 超时路径置错误标志并经返回值/查询接口上报 | high | +| P2-3 | `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c` `gd5f_bbt_save` | 丢弃 program_load/program_exec/wait_busy 返回值:编程失败仍推进 BBT 版本号并报成功 | 检查返回值,失败不推进版本 | high | | P2-4 | `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` `dhara_nand_read` | 忽略 `gd5f_read_from_cache` 返回值(DMA 超时 → 返回成功 + 垃圾数据) | 检查返回值并向上报错 | high | -| P2-5 | `Drivers/BSP/SD2506/sd2506.c` | 负温度解码 `(int8_t)(val \| 0xF0)` → 所有负温读成 -1 | `-(int8_t)(val & 0x7F)` | high | -| P2-6 | `Drivers/BSP/SD2506/sd2506.c` `sd2506_init` | 未使能 WRTC 即写 CTR1(ARST),上电写保护可能静默丢弃配置 | 先开 WRTC 再写 CTR1(或按手册确认写保护默认态) | medium | +| P2-5 | `Drivers/BSP/SD2506/sd2506.c` | 负温度解码 `(int8_t)(val \| 0xF0)` → 所有负温读成错误值(符号-绝对值编码未取补码) | `-(int8_t)(val & 0x7F)` | high | | P2-7 | `Drivers/BSP/NET/net_select.c` `check_socket_readable` | LISTENING 视为恒可读 → 单连接模式下对监听 socket select(readfds) 永久立即返回(当前无调用方,潜在地雷) | LISTENING 仅在写集合/异常集合计数可读,或明确文档化 | high | -| P2-8 | `App/time_sync.c` + `Drivers/BSP/NET/net_config.h` | `NET_RECV_TIMEOUT_MS=30000`:静默对端使同步任务卡 30s 才恢复 | 同步收包单独用短超时(如 3s),与通用收发超时分离 | high | -| P2-9 | `App/log/sys_log_mcu.c` | 挂载时 mkdir 失败 → `s_fs_ready` 不置位,drain 永久 no-op 无重试 | 周期重试挂载/建目录 | medium | +| P2-8 | `App/time_sync.c` + `Drivers/BSP/NET/net_config.h` | `NET_RECV_TIMEOUT_MS=30000`;且 `time_sync.c:84` 的 `net_recv` 现传 timeout=0(**无限等待**)——静默对端使同步任务永久卡死 | 同步收包单独用短超时(如 3s),与通用收发超时分离 | high | +| P2-9 | `App/log/sys_log_mcu.c` | 挂载时 mkdir 失败 → `s_fs_ready` 不置位,drain 永久 no-op 无重试(`syslog_mcu_mounted` 仅调用一次) | 周期重试挂载/建目录 | medium | | P2-10 | `App/log/sys_log_mcu.c` | 容量满 unlink 与 UI 读记录(FA_READ)存在竞态窗口 | unlink 前等待读者释放,或双文件轮转 | medium | | P2-11 | `App/task/net_task.c` | RECONFIG 事件用 `osEventFlagsGet`+Clear,非原子(检查与清除间可丢事件) | 改 `osEventFlagsWait` 原子获取 | high | | P2-12 | `App/app_cfg.h` | `APP_NET_ENABLE`/`APP_ADC_ENABLE`/`APP_RS485_ENABLE` 全仓库无 .c 引用,纯装饰 | 真实接入模块门控,或从文档/注释中删除"开关"说法 | high | -| P2-13 | `Src/freertos.c:168` | testTask 无条件创建,产品构建空转占 4KB 栈 | 按 `test_config.h` 门控创建 | high | -| P2-14 | `MDK-ARM/build.bat` | ① 成功标记子串匹配:`"10 Error(s), 0 Warning(s)"` 也命中 → 10/20/30… 错误误判 BUILD_OK;② UV4 未运行(路径错/换机)时陈旧 `build_log.txt` 残留成功标记 → 假 PASS | 标记加行前缀(`- 0 Error(s)`);调用 UV4 前先删除旧日志 | high | -| P2-15 | `MDK-ARM/download.bat` | 同 P2-14②:陈旧 "Application running" 掩盖失败烧录 | 调用前先删旧日志 | high | -| P2-16 | `AGENTS.md` 任务栈表 | 表格值全部为实际的 4 倍(表:2048/4096/4096/4096/16384/2048w;`freertos.c` 实际:512/1024/1024/1024/4096/512w,合计 32KB 而非 114KB)——直接扭曲待处理的内存预算基线 | 按 `Src/freertos.c:64-100` 实际值更新表格 | high | +| P2-13 | `Src/freertos.c:183` | testTask 无条件创建,产品构建空转占 2048 words(8KB,CCM 堆,2026-09-21 口径) | 按 `test_config.h` 门控创建 | high | +| P2-15 | `MDK-ARM/download.bat` | 调用 UV4 前不删旧 `download_log.txt`:陈旧 "Application running" 掩盖失败烧录 | 调用前先删旧日志 | high | --- @@ -146,14 +87,44 @@ | P3-6 | `Drivers/BSP/CH395F/ch395f.c` `ch395f_get_socket_status` | `p_status` 无 NULL 检查 | | P3-7 | `test/test.txt` | 0 字节空文件(删除或补内容) | | P3-8 | `.opencode/command/build.md` | 退出码契约写 "2+ = 编译错误",`build.bat` 实际只有 0/1 两档(与 AGENTS.md 一致的是后者) | -| P3-9 | `docs/测试说明/FTP_Test_Guide.md` | 仍描述旧 7s 魔法延时(代码已改为事件屏障) | -| P3-10 | `docs/测试说明/CH395F_Test_Guide.md` | "硬件层模式 netTask 不创建"表述过期(line 87/90/126/146/239 共 5 处,与代码矛盾)✅ 已修复(2026-09-15:改写为"netTask 自挂起(创建但不运行)",与 P1-6 守卫一致) | | P3-11 | `App/task/adc_task.c` / `rs485_task.c` | 空桩无注释说明原因(建议注明"预留,未启用") | | P3-12 | `App/time_sync.c` | `len >= 8` 严格判定:TIME 包被 TCP 拆两段时拒绝处理(可容忍,增强为累积到 8 字节再判) | | P3-13 | `test/ch395f_test_task.c` `test_parse_ip` | 门控为 `ENABLE_HW_LAYER_TESTS`,实际引用仅在 Phase 2/5 → 单选阶段(如仅 Phase 1)时 `#177-D` 未引用函数告警(门控应改为 `PHASE2/PHASE5` 析取) | --- +## 待验证 + +- **P0-1**(2026-09-18 修复:cfg_file C 库 `malloc` → `cfg_store_alloc/free/heap_free`,MCU 端 = FreeRTOS 堆,`CFG_HEAP_BUDGET=10,240B` 编译期预算 + 四条失败路径自检):真机 `@test` 一次,确认 `load: OK` 出现且 Forbidden `FAIL` 零命中(双端构建/`--cfgtest` 已绿;模拟器宿主堆照不出真机问题,真机正向确认是最后一道门)。 + +--- + +## 待确认(需手册/硬件/产品定义,暂不直接改码) + +1. TPAFE5160 是否有自动扫描模式(决定 P1-1 定 P0 还是 P1)。 +2. CH395F 手册 §9.2.1:`INIT_CH395` 是否锁存 MAC 寄存器(决定 P1-7 修复紧迫度)。 +3. `APP_ADC_ENABLE` / `APP_RS485_ENABLE` 是否应在产品构建真实门控模块(关联 P1-1、P2-12、P3-11)。 +4. RS-485 PD0 DE 的半双工方向语义需硬件实测确认(本次仅静态审查)。 + +## 内存预算(支撑 `待处理内容.md`) + +现行权威数据 = `docs/内存管理/RAM使用分析与优化方案.md` v1.2(2026-09-21):任务栈在 CCM 堆(defaultTask 1024w / netTask 1024w / ftpTask 1024w / testTask 2048w / uiTask 4096w / timeSyncTask 512w);产品构建 RW_IRAM1 82,048/131,072 B(62.6%)+ RW_CCM 49,184/65,536 B(75.0%);实测堆 `free=8,784 / min_ever=4,848` B;台账工具 `scripts/ram_report.py` + `scripts/stack_report.py`。本清单旧栈表/旧余量数字作废,勿引用。 + +## 已完成(勿重复报) + +- **P0-1**(2026-09-18):配置引擎堆分配走 `cfg_store_alloc`(MCU=FreeRTOS 堆),`malloc.o` 退出链接,静态区 126,584 → 126,064 B;真机 `@test` 见「待验证」。 +- **P1-2**(2026-09-15):墙钟 49.7 天回绕 → 30 天惰性再基准化 + init 失败降级返回 -1(自检复活)+ 哨兵修正。 +- **P1-3**(2026-09-15):cfg_file 跨任务竞态 → 引擎属主 = defaultTask,UI 保存跨任务交接(`SYS_EVT_CFG_SAVE`)。 +- **P1-4**(2026-09-15):lftpd 写路径 `n <= 0` → -1,零进展超时判致命,ftpTask 不再空转。 +- **P1-5**(2026-09-15):defaultTask 2s 周期 `cfg_file_poll`(FTP 改配置免重启重载)。 +- **P1-6**(2026-09-15):netTask 基础线程唯一实例 + 硬件层测试入口自挂起守卫(testTask 独占 CH395F)。 +- **P1-9**(2026-09-16):测试固件 `s_rx_buf`/`s_tx_buf` 声明前移 + Phase 9 方向门控补全(7 配置编译阶段复验通过)。 +- **P2-6**:SD2506 上电后不写 CTR1(按手册确认写禁止默认态、删除无效 0x7B 写;ARST 位实位于 CTR3 D7),所有受保护寄存器写均在 WRTC 窗口内。 +- **P2-14**:`build.bat` 调 UV4 前先删旧日志(UV4 未运行 → `BUILD_FAIL: no build log`)+ `error:` 诊断检查 + `0 Error(s), 0 Warning(s)` 成功标记。 +- **P2-16**(2026-09-21):AGENTS.md 任务栈表已更新为 `freertos.c` 实际值(1024/1024/1024/2048/4096/512w)。 +- **P3-9**(2026-09-21):`FTP_Test_Guide.md` 启动时序改写为事件屏障(含 `osFlagsNoClear` 教训 §8.6),旧 7s 魔法延时表述已删。 +- **P3-10**(2026-09-15):`CH395F_Test_Guide.md` 改写为"netTask 自挂起(创建但不运行)",与 P1-6 守卫一致。 + ## 已核验无问题(勿重复报) - `App/util/ringbuf.c`、`App/util/crc.c`、`App/task/ui_task.c`:结构正确。 @@ -162,26 +133,3 @@ - FatFS 卷归属:产品构建单卷拥有者 = defaultTask 的 `f_mount` 实例,lftpd 复用不另 mount;`FF_FS_REENTRANT=1` ✓。 - CH395F trap 修复在位:Trap 01(listen 顺序)、Trap 10(xTaskNotify)、Trap 11(单连接 accept)、Trap 20(send 分块 `NET_SEND_CHUNK_MAX`)均核验存在。 - 宿主侧:`test/verify_log.py`、`test/run_boot_test.py`、`test/time_sync_server.py`(伪 epoch 与时区处理注释完备)、`tools/menu_gen/gen_menu.py`(自带校验:拒 Tab/缩进非整倍数/节点 >255)、`tools/serial_tool/`(近期已验证)均健康。 - -## 待确认(需手册/硬件/产品定义,暂不直接改码) - -1. TPAFE5160 是否有自动扫描模式(决定 P1-1 定 P0 还是 P1)。 -2. CH395F 手册 §9.2.1:`INIT_CH395` 是否锁存 MAC 寄存器(决定 P1-7 修复紧迫度)。 -3. `APP_ADC_ENABLE` / `APP_RS485_ENABLE` 是否应在产品构建真实门控模块(关联 P1-1、P2-12、P3-11)。 -4. RS-485 PD0 DE 的半双工方向语义需硬件实测确认(本次仅静态审查)。 - -## 内存预算证据(支撑 `待处理内容.md`) - -实测任务栈(`Src/freertos.c:64-100`)合计 **32KB**(AGENTS.md 旧表 114KB 为 4 倍失真值,见 P2-16): - -| 任务 | 栈 | 备注 | -|---|---|---| -| defaultTask | 512w (2KB) | FatFS 挂载 + 配置加载/保存/轮询(引擎属主)+ LED | -| netTask | 1024w (4KB) | 含 CH395F SPI 收发路径 | -| ftpTask | 1024w (4KB) | lftpd | -| testTask | 1024w (4KB) | **产品构建空转 → 门控后可省 4KB**(P2-13) | -| uiTask | 4096w (16KB) | 最大栈,需实测高水位后再谈压缩 | -| timeSyncTask | 512w (2KB) | | - -静态缓冲(已核验):uiTask 侧 2×`UI_FB_SIZE`=6.4KB 帧缓冲(`ui_port.c:16-18`)+ net_sink 组帧/应答/接收缓冲 ~6.3KB(`ui_net_sink.c:40-42`)。 -其余(CH395F DMA 缓冲、FatFS/dhara 页缓冲、netMsgQueue、lftpd 缓冲)建议用 `scripts/ram_report.py` + FreeRTOS heap 统计实测后并入预算表,勿沿用旧表数字。 diff --git a/simulator/CMakeLists.txt b/simulator/CMakeLists.txt index e4bda8af..55f06de2 100644 --- a/simulator/CMakeLists.txt +++ b/simulator/CMakeLists.txt @@ -52,6 +52,9 @@ include_directories( ${UI_DIR} # ui_menu.h, ui_font_cn_12.h ${NET_DIR} # net_socket.h / net_types.h(net_* API 双端共享核心) ${NETCFG_DIR} # net_cfg.h(网络配置,双端共享核心) + ${PROJECT_ROOT}/test # test_config.h / net_latency_test.h(共享核心 + # ui_net_sink.c、net_socket.c 引含;探针默认关闭, + # 声明宿主安全;2026-09-21 修复模拟器构建断链) ${PROJECT_ROOT}/App # cfg_file.h / cfg_file_store.h(通用配置文件引擎,双端共享核心) ${PROJECT_ROOT}/App/log # sys_log.h(运行/自检记录引擎,双端共享核心) ${PROJECT_ROOT}/Drivers/BSP # dbg_log.h(使用 simulator 的 dbg_cfg.h 配置) @@ -70,6 +73,7 @@ set(SDL2_LIB_X64 "${SDL2_DIR}/lib/x64") set(SIM_SOURCES main.c sim/sim_port.c + sim/sim_latency_stub.c # 探针符号宿主空实现(ui_net_sink.c 引用,见文件头注释) ) # UI 源文件(与 MCU 端共用,直接编译) diff --git a/simulator/sim/sim_latency_stub.c b/simulator/sim/sim_latency_stub.c new file mode 100644 index 00000000..52d064a6 --- /dev/null +++ b/simulator/sim/sim_latency_stub.c @@ -0,0 +1,49 @@ +/* + * 模块名称:sim_latency_stub + * 模块功能:链路时延探针(net_latency_test.h)的宿主空实现——探针打点站点位于 + * 双端共享核心(ui_net_sink.c,TEST_LATENCY_PROBE 使能时编译), + * MCU 端符号由 test/net_latency_test.c 提供(DWT CYCCNT 时基, + * MCU 专属、宿主不可编译);模拟器构建在此以空操作满足链接, + * 不计时、不出报告(时延测量属台架能力,模拟器无意义) + * 适用平台:PC 模拟器(仅 simulator 构建编译本文件) + * 作者:王建锋 + * 创建日期:2026-09-21 + * 修改记录: + * 2026-09-21 王建锋 初版:修复模拟器链接断链(ui_net_sink.c 探针引用 + * lat_mark/lat_mark_us/lat_probe_add_seg 无宿主定义) + */ + +#include "net_latency_test.h" + +/* + * 函数功能:打点标记(空操作,字段清零保证可辨识) + * 入口参数:p - 打点标记容器 + * 返回值:无 + */ +void lat_mark(lat_tmark_t *p) { + if (p != NULL) { + p->cyc = 0u; + p->tick = 0u; + } +} + +/* + * 函数功能:打点差值(恒 0:模拟器不测时延) + * 入口参数:p - 打点标记容器 + * 返回值:恒 0(微秒) uint32_t + */ +uint32_t lat_mark_us(const lat_tmark_t *p) { + (void)p; + return 0u; +} + +/* + * 函数功能:按段名追加样本(空操作) + * 入口参数:seg - 段名 const char * + * us - 样本值(微秒) uint32_t + * 返回值:无 + */ +void lat_probe_add_seg(const char *seg, uint32_t us) { + (void)seg; + (void)us; +} diff --git a/test/ch395f_test.h b/test/ch395f_test.h index 69af1263..80ab4515 100644 --- a/test/ch395f_test.h +++ b/test/ch395f_test.h @@ -9,7 +9,7 @@ * 修改记录: * 2026-07-22 全面重构为单连接模式,增加大文件传输、边界条件等测试 * - * === 测试阶段一览(硬件层 1~5 → NET 层 6~11,严格顺序执行) === + * === 测试阶段一览(硬件层 1~5/12 → NET 层 6~11/13/14,严格顺序执行) === * Phase | 层 | 描述 * ---------|----------|----------------------------------------------- * Phase 1 | 硬件层 | 独立运行:寄存器读写 + SPI 命令路径验证 @@ -23,6 +23,10 @@ * Phase 9 | NET 层 | testTask:Select/Poll I/O 多路复用 * Phase 10 | NET 层 | testTask:边界条件测试(零长度、DMA 边界、缓冲溢出) * Phase 11 | NET 层 | testTask:KeepAlive 超时断开测试 + * Phase 12 | 硬件层 | 独立运行:SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC) + * Phase 13 | NET 层 | testTask:芯片 + 队列时延(L3~L7/L10,需 PC peer,独立运行) + * Phase 14 | NET 层 | testTask:生产路径时延(L8~L11 + 模拟器 A/B,需 PC peer)〔M3 规划〕 + * 时延分段定义/判定基线/执行流程:docs/测试说明/链路延迟测试说明.md */ #ifndef CH395F_TEST_H @@ -45,7 +49,7 @@ extern "C" { * Phase 4 (DHCP) 会改变 IP,必须独立运行; * 两层同时启用会在下方触发 #error。 */ -/* ---- 硬件层(阶段 1~5)---- */ +/* ---- 硬件层(阶段 1~5/12)---- */ //#define ENABLE_PHASE1_TESTS /* [硬] 寄存器读写 + SPI 命令路径 */ //#define ENABLE_PHASE2_TESTS /* [硬] TCP Client 收发 + 关闭重连 */ //#define ENABLE_PHASE3_TESTS /* [硬] UDP Server Echo */ @@ -58,16 +62,22 @@ extern "C" { //#define ENABLE_PHASE9_TESTS /* [网] Select/Poll I/O 多路复用 */ //#define ENABLE_PHASE10_TESTS /* [网] 边界条件测试 */ //#define ENABLE_PHASE11_TESTS /* [网] KeepAlive 超时断开测试 */ +/* ---- 链路时延基线(M1;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */ +//#define ENABLE_PHASE12_TESTS /* [硬] SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC)—— M1 已完成,基线见说明文档 §4.1 */ +/* ---- 链路时延(M2/M3;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */ +//#define ENABLE_PHASE13_TESTS /* [网] 芯片 + 队列时延(L3~L7/L10,需 PC peer)—— M2 已完成,基线见说明文档 §4.2 */ +#define ENABLE_PHASE14_TESTS /* [网] 生产路径时延(L8~L11 + 模拟器 Δ)—— M3,需 peer --mode remo(:7003) */ #if defined(ENABLE_PHASE1_TESTS) || defined(ENABLE_PHASE2_TESTS) || \ defined(ENABLE_PHASE3_TESTS) || defined(ENABLE_PHASE4_TESTS) || \ - defined(ENABLE_PHASE5_TESTS) + defined(ENABLE_PHASE5_TESTS) || defined(ENABLE_PHASE12_TESTS) #define ENABLE_HW_LAYER_TESTS 1 #endif #if defined(ENABLE_PHASE6_TESTS) || defined(ENABLE_PHASE7_TESTS) || \ defined(ENABLE_PHASE8_TESTS) || defined(ENABLE_PHASE9_TESTS) || \ - defined(ENABLE_PHASE10_TESTS) || defined(ENABLE_PHASE11_TESTS) + defined(ENABLE_PHASE10_TESTS) || defined(ENABLE_PHASE11_TESTS) || \ + defined(ENABLE_PHASE13_TESTS) || defined(ENABLE_PHASE14_TESTS) #define ENABLE_NET_LAYER_TESTS 1 #endif diff --git a/test/ch395f_test_task.c b/test/ch395f_test_task.c index c92ecf17..32bcbe09 100644 --- a/test/ch395f_test_task.c +++ b/test/ch395f_test_task.c @@ -13,6 +13,7 @@ #include "ch395f_test_task.h" #include "ch395f_test.h" #include "net_test_task.h" +#include "net_latency_test.h" #include "cmsis_os.h" #include "net_task.h" #include "net_socket.h" @@ -49,8 +50,10 @@ test_stats_t g_test_stats; * =================================================================== */ /* 解析点分十进制 IP 字符串为数组(避免引入 sscanf 库)。 - * 仅硬件层阶段(1~5)使用,NET 层无需此辅助。 */ -#if defined(ENABLE_HW_LAYER_TESTS) + * 仅硬件层 Phase 2(TCP Client 目标 IP)与 Phase 5(大文件目标 IP)使用; + * 守卫精确到使用方,避免只启用其他 HW 阶段时产生 #177-D + * (2026-09-22:仅启用 Phase 12 构建实证暴露此潜在警告)。 */ +#if defined(ENABLE_PHASE2_TESTS) || defined(ENABLE_PHASE5_TESTS) static void test_parse_ip(const char *str, uint8_t *ip_arr) { uint32_t val; @@ -68,7 +71,7 @@ static void test_parse_ip(const char *str, uint8_t *ip_arr) } } } -#endif /* ENABLE_HW_LAYER_TESTS */ +#endif /* ENABLE_PHASE2_TESTS || ENABLE_PHASE5_TESTS */ #ifdef ENABLE_PHASE1_TESTS static void phase1_run(void) { @@ -1377,6 +1380,11 @@ void ch395f_test_run(void) #endif #ifdef ENABLE_PHASE5_TESTS phase5_run(); +#endif +#ifdef ENABLE_PHASE12_TESTS + /* Phase 12(链路时延基线 L1/L2,M1):独立于 Phase 1~5 的 SPI 时延 + * 采样,放硬件层阶段末尾执行(说明文档:链路延迟测试说明.md) */ + net_latency_phase12_run(); #endif } #else diff --git a/test/ftp_regression.py b/test/ftp_regression.py new file mode 100644 index 00000000..d01d046e --- /dev/null +++ b/test/ftp_regression.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""FTP 数据连接回归测试(D20 验证 + 日后回归) + +背景(2026-09-22 台架实测 D20):同一 FTP 会话内**第二个数据连接必失败** +(425 Can't open data connection)。根因是单连接 Socket 复用时的"陈旧 DISCONNECT" +误判:上一条数据连接的 FIN/ACK 在 Socket 已复用为新连接之后才上报,新连接被 +net 层当作"刚建立就断开"释放。修复见 net_socket.c handle_disconnect_event 的 +陈旧事件甄别(回读芯片 TCP 状态,仍 ESTABLISHED 则丢弃该中断)。 + +本脚本在一个会话里连续做多次数据连接(LIST/NLST/STOR/RETR),并做大文件往返 +内容校验;任一环节失败即退出码 1。 + +用法: + & '.venv\\Scripts\\python.exe' test/ftp_regression.py --host 192.168.1.100 +""" + +import argparse +import io +import sys +import time +from ftplib import FTP + +SMALL = b"D20-small-" + b"A" * 1017 # 1024B +BIG = bytes(((i * 7 + 3) & 0xFF) for i in range(32768)) # 32KB 模式数据 + + +def main(argv=None): + ap = argparse.ArgumentParser(description="FTP data-connection regression (D20)") + ap.add_argument("--host", default="192.168.1.100", help="device IP") + ap.add_argument("--port", type=int, default=21, help="FTP control port") + ap.add_argument("--timeout", type=int, default=20, help="socket timeout (s)") + args = ap.parse_args(argv) + + results = [] + + def step(name, fn): + """执行一步并记录结果;不抛异常,便于一次跑完全部步骤。""" + t0 = time.time() + try: + extra = fn() + dt = time.time() - t0 + results.append((name, True, dt)) + print(" %-16s OK %6.2fs %s" % (name, dt, extra if extra is not None else "")) + return True + except Exception as e: # noqa: BLE001 + dt = time.time() - t0 + results.append((name, False, dt)) + print(" %-16s FAIL %6.2fs %s %s" % (name, dt, type(e).__name__, e)) + return False + + f = FTP() + print("[ftp] connecting %s:%d ..." % (args.host, args.port)) + f.connect(args.host, args.port, timeout=args.timeout) + f.login("anonymous", "x@x") + print("[ftp] login ok: %s" % f.getwelcome()) + + # 同一会话内连续 8 次数据连接(含两次大文件)—— D20 的失败点是第 2 次 + step("LIST #1", lambda: "%d lines" % len(f.nlst())) + step("STOR #2 1KB", lambda: f.storbinary("STOR d20s.tmp", io.BytesIO(SMALL)) or "226") + + got = bytearray() + step("RETR #3 1KB", lambda: (f.retrbinary("RETR d20s.tmp", got.extend), "226")[1]) + if bytes(got) != SMALL: + print(" !! RETR #3 content mismatch (%d bytes)" % len(got)) + results.append(("RETR#3-content", False, 0.0)) + else: + results.append(("RETR#3-content", True, 0.0)) + + step("LIST #4", lambda: "%d lines" % len(f.nlst())) + step("STOR #5 32KB", lambda: f.storbinary("STOR d20b.tmp", io.BytesIO(BIG)) or "226") + + big = bytearray() + step("RETR #6 32KB", lambda: (f.retrbinary("RETR d20b.tmp", big.extend), "226")[1]) + if bytes(big) != BIG: + print(" !! RETR #6 content mismatch (%d/%d bytes)" % (len(big), len(BIG))) + results.append(("RETR#6-content", False, 0.0)) + else: + results.append(("RETR#6-content", True, 0.0)) + + step("LIST #7", lambda: "%d lines" % len(f.nlst())) + step("cleanup", lambda: (f.delete("d20s.tmp"), f.delete("d20b.tmp"), "deleted")[2]) + step("QUIT", lambda: f.quit() or "221") + + ok = sum(1 for _, good, _ in results if good) + print("[ftp] %d/%d steps passed" % (ok, len(results))) + return 0 if ok == len(results) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test/latency_peer.py b/test/latency_peer.py new file mode 100644 index 00000000..46db6291 --- /dev/null +++ b/test/latency_peer.py @@ -0,0 +1,836 @@ +#!/usr/bin/env python3 +"""latency_peer.py - Link latency test peer for Phase 13 (M2). + +Server-driven protocol: the MCU (testTask, port 8080) sends one operation +byte; the peer executes the corresponding traffic pattern. See +docs/测试说明/链路延迟测试说明.md §2.5 for the full protocol spec. + +Ops (MCU -> peer): + 0x01 L5 recv flood : peer sends 50 x 100B (5ms apart) + 0x02 L5 send drain : peer drains 50 x 100B + 0x03 L6 : peer sends 10B request, expects 10B echo (timed) + 0x04 L7 : peer sends 9B LCDMEM request, expects 3209B frame + 0x05 L3 send drain : peer drains 32 x 4096B + 0xFF done : session complete + +The peer records host-side (L4-side) timing: first byte / frame total / +per-batch arrival curve / >=500ms spikes (TCP retransmission fingerprint). +Both the serial log and this JSON carry HOST timestamps -> direct correlation. + +Usage: + python latency_peer.py --host 192.168.1.100 --port 8080 --json m2_net.json + python latency_peer.py --mode net --ftp-stress ... (E-02 head-of-line repro) + +Pure stdlib (socket/json/time/argparse/threading). ASCII output only. +""" + +import argparse +import json +import socket +import sys +import threading +import time + +# --- Protocol constants (mirror test/net_latency_test.c Phase 13 defines) --- +HELLO = b"\x5a\xa5\x58\x01" +OP_L5_RECV = 0x01 +OP_L5_SEND = 0x02 +OP_L6 = 0x03 +OP_L7 = 0x04 +OP_L3 = 0x05 +OP_DONE = 0xFF + +L5_CHUNKS = 20 # 2026-09-22 run1: 50 chunks saturated the chip RX buffer + # (window close -> retransmit re-delivery -> RST); 20 @ 50ms + # = 1KB/s quiet baseline (see 链路延迟测试说明.md §8) +L5_LEN = 100 +L6_SIZE = 10 +L7_REQ_LEN = 9 +L7_FRAME_LEN = 3209 +L3_SENDS = 16 # mirror firmware LAT_P13_L3_SENDS (run3: 32->16, 64 chunk samples) +L3_LEN = 4096 + +SPIKE_MS = 500.0 # retransmission granularity fingerprint (D1) + +# --- Production RemoDispBus constants (mirror drivers/BSP/UI/ui_net_sink.h) --- +REMO_TAG_CLIENT = 0xAA +REMO_TAG_DEVICE = 0xBB +REMO_CMD_KEEP = 0 +REMO_CMD_INIT = 1 +REMO_CMD_KEY = 2 +REMO_CMD_LCDMEM = 3 +REMO_MEMSIZE = 3200 # 160x160 1bpp +REMO_LCDMEM_FRAME = 4 + 4 + REMO_MEMSIZE + 1 # 3209 (header + offset + bitmap + CRC) +REMO_CYCLE_DEADLINE_S = 45.0 # per-cycle recv deadline (E-02 stress: up to 30s) + + +def now_ns(): + return time.perf_counter_ns() + + +def iso_now(): + return time.strftime("%Y-%m-%d %H:%M:%S") + + +def recv_exact(sock, n, deadline_ns): + buf = bytearray() + while len(buf) < n: + left = deadline_ns - now_ns() + if left <= 0: + raise TimeoutError("recv_exact deadline (%d/%d bytes)" % (len(buf), n)) + try: + sock.settimeout(max(left, 1) / 1e9 / 5.0) + d = sock.recv(n - len(buf)) + except socket.timeout: + continue + if not d: + raise ConnectionError("peer connection closed") + buf.extend(d) + return bytes(buf) + + +def l7_request(seq=0): + """Production-shaped 9B LCDMEM request (ui_net_sink.c remo_handle): + [TAG 0xAA][CMD 0x03][LEN=4 BE][offset u32 BE = 0][CRC = XOR of the 4 offset bytes]. + LEN is the DATA-segment length (4), NOT the requested bitmap size; the offset + field must be 0 (device only serves full-frame requests).""" + data = bytes([0, 0, 0, 0]) + crc = 0 + for b in data: + crc ^= b + return bytes([0xAA, 0x03, 0x00, 0x04]) + data + bytes([crc]) + + +def l7_frame_ok(data): + """Validate the 3209B production LCDMEM response (ui_net_sink.c remo_send): + [TAG 0xBB][CMD 0x03][LEN=3204 BE][offset u32 = 0][1bpp bitmap 3200B] + [CRC = XOR of the 3204 data bytes] -> 4 + 3204 + 1 = 3209B.""" + if len(data) != L7_FRAME_LEN: + return False + if data[0] != 0xBB or data[1] != 0x03 or data[2] != 0x0C or data[3] != 0x84: + return False + payload = data[4:4 + 3204] + crc = 0 + for b in payload: + crc ^= b + if data[4 + 3204] != crc: + return False + if payload[:4] != bytes(4): + return False + for i in range(3200): + if payload[4 + i] != (i & 0xFF): + return False + return True + + +def pattern(n, seed=0): + return bytes((seed + i) & 0xFF for i in range(n)) + + +# ================= RemoDispBus production client (M3, :7003) ================= + +def xor_bytes(data): + crc = 0 + for b in data: + crc ^= b + return crc + + +def remo_request(cmd, data=b""): + """Client->device frame: [0xAA][CMD][LEN_H][LEN_L][DATA][CRC = XOR(DATA)].""" + body = bytes(data) + return (bytes([REMO_TAG_CLIENT, cmd, (len(body) >> 8) & 0xFF, len(body) & 0xFF]) + + body + bytes([xor_bytes(body)])) + + +def remo_lcdmem_request(): + """9B full-frame LCDMEM request (offset 0) - what the production client sends.""" + return remo_request(REMO_CMD_LCDMEM, bytes(4)) + + +def recv_upto(sock, want, deadline_ns): + """Bounded read: never consumes more than `want` bytes (no over-read of a + following frame/op byte - the run10 lesson).""" + left = deadline_ns - now_ns() + if left <= 0: + raise TimeoutError("frame deadline") + try: + sock.settimeout(max(left, 1) / 1e9 / 5.0) + return sock.recv(want) + except socket.timeout: + return b"" + + +def read_remo_frame(sock, deadline_ns, ack_probe=False): + """Read exactly one device frame; returns tag/cmd/data/CRC + arrival curve. + + The first read is bounded to 8B so the segment-arrival curve stays + fine-grained; subsequent reads are bounded by the remaining frame length + (device never pipelines: one response per request). + + ack_probe=True: as soon as the frame is known to be incomplete, send one + byte the device parser discards (0x00, not TAG 0xAA). The resulting TCP + segment carries a piggybacked ACK for the data already received, which + discriminates "sender holds the last partial segment waiting for an ACK + (Nagle-style)" from "chip-internal fixed timer" - without any packet capture.""" + buf = bytearray() + batches = [] + first_ns = None + total = None + while True: + want = (8 - len(buf)) if total is None else (total - len(buf)) + if want <= 0: + break + d = recv_upto(sock, min(want, 4096), deadline_ns) + if not d: + if total is not None: + raise ConnectionError("frame truncated (%d/%d bytes)" % (len(buf), total)) + continue # header still incomplete: wait until deadline + t = now_ns() + if first_ns is None: + first_ns = t + buf.extend(d) + batches.append([t, len(d)]) + if total is None and len(buf) >= 4: + total = 4 + (((buf[2] << 8) | buf[3])) + 1 + if ack_probe and total is not None and len(buf) < total: + try: + sock.sendall(b"\x00") # parser-safe: discarded while waiting for TAG 0xAA + except OSError: + pass + data = bytes(buf[4:total - 1]) + crc = buf[total - 1] + return {"tag": buf[0], "cmd": buf[1], "data": data, "total": total, + "crc_ok": crc == xor_bytes(data), "first_ns": first_ns, "batches": batches} + + +def enable_immediate_ack(sock): + """Windows: disable delayed ACK on this socket (SIO_TCP_SET_ACK_FREQUENCY=1). + + Root-cause fix for the ~50ms tail: the CH395F (MSS=1024) holds the final + sub-MSS segment of a 3209B frame per Nagle until the peer ACKs; Windows' + delayed ACK defers that ACK ~50ms. Setting ACK frequency=1 makes every + segment ACKed immediately -> tail 52ms -> ~4ms (measured). Returns True on + success. Other platforms: no-op (Linux uses TCP_QUICKACK per recv).""" + if sys.platform != "win32": + return False + try: + import ctypes + ws2 = ctypes.WinDLL("ws2_32", use_last_error=True) + WSAIoctl = ws2.WSAIoctl + WSAIoctl.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint32, + ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), + ctypes.c_void_p, ctypes.c_void_p] + WSAIoctl.restype = ctypes.c_int + val = ctypes.c_uint32(1) # 1 = ACK every segment + ret = ctypes.c_uint32(0) + rc = WSAIoctl(ctypes.c_void_p(sock.fileno()), 0x98000017, + ctypes.byref(val), 4, None, 0, ctypes.byref(ret), None, None) + return rc == 0 + except (OSError, AttributeError): + return False + + +def remo_bitmap_nontrivial(payload): + """Same 'non-trivial frame' criterion as simulator/tests/check_remo_sink.py: + the 1bpp bitmap must not be a single repeated byte (blank/broken frame).""" + bmp = payload[4:4 + REMO_MEMSIZE] + return len(set(bmp)) > 2 + + +class RemoPeer: + """Production-path peer (M3): drives L8/L9 against :7003 (device) or the + simulator, measuring the full client-observed frame cycle.""" + + def __init__(self, host, port, wait_s, json_path, samples, cadence_ms, + stress_from, nodelay, reconnect_rounds=1, ack_probe=False, ack_fast=False): + self.host = host + self.port = port + self.wait_s = wait_s + self.json_path = json_path + self.samples = samples + self.cadence_s = cadence_ms / 1000.0 + self.stress_from = stress_from # cycle index where FTP stress starts + self.nodelay = nodelay + self.reconnect_rounds = max(1, reconnect_rounds) + self.ack_probe = ack_probe # in-band ACK-release experiment (tail diagnosis) + self.ack_fast = ack_fast # Windows immediate-ACK socket option + self.ack_fast_ok = None + self.sock = None + self.t0_wall = None + self.t0_ns = 0 + self.connect_ms = None + self.reconnect_ms = None + self.reconnect_list = [] + self.init_rtt_ms = None + self.geom = {} + self.frames = [] + + # --- connection --- + def connect_once(self): + t = now_ns() + s = socket.create_connection((self.host, self.port), timeout=5.0) + if self.nodelay: + s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + if self.ack_fast: + self.ack_fast_ok = enable_immediate_ack(s) + print("[peer] immediate-ACK (SIO_TCP_SET_ACK_FREQUENCY=1): %s" + % ("applied" if self.ack_fast_ok else "not available")) + self.sock = s + return (now_ns() - t) / 1e6 + + def connect_with_retry(self): + t_end = time.time() + self.wait_s + last = None + while time.time() < t_end: + try: + return self.connect_once() + except OSError as e: + last = e + time.sleep(1.0) + raise ConnectionError("cannot connect to %s:%d within %ds (%s)" % + (self.host, self.port, self.wait_s, last)) + + def init_roundtrip(self, deadline_s=5.0): + """INIT request -> geometry reply; returns round-trip ms.""" + t = now_ns() + self.sock.sendall(remo_request(REMO_CMD_INIT)) + f = read_remo_frame(self.sock, t + int(deadline_s * 1e9)) + ms = (now_ns() - t) / 1e6 + if f["cmd"] == REMO_CMD_INIT and len(f["data"]) >= 6: + # 8B (fixed): [W u16][H u16][MEMSIZE u32 BE]; 6B = 旧固件(u32 被截半 → 0) + self.geom = {"w": (f["data"][0] << 8) | f["data"][1], + "h": (f["data"][2] << 8) | f["data"][3], + "framebytes": int.from_bytes(f["data"][4:8], "big") + if len(f["data"]) >= 8 + else ((f["data"][4] << 8) | f["data"][5]), + "payload_len": len(f["data"])} + return ms + + # --- FTP head-of-line stress (E-02) --- + 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 (NET_RECV_TIMEOUT_MS) + -> every queued message (incl. the LCDMEM response) waits behind it (E-02).""" + if not start_evt.wait(120.0): + sys.stdout.write("[peer] ftp stress: window never opened\n") + return + try: + s = socket.create_connection((self.host, 21), timeout=10.0) + s.settimeout(10.0) + s.sendall(b"USER latency\r\n") + reply = b"" + while b"\r\n" not in reply: + d = s.recv(64) + if not d: + break + reply += d + sys.stdout.write("[peer] ftp stress armed (331: %r)\n" % reply[:40]) + stop_evt.wait(hold_s) + try: + s.sendall(b"QUIT\r\n") + except OSError: + pass + s.close() + sys.stdout.write("[peer] ftp stress released\n") + except OSError as e: + sys.stdout.write("[peer] ftp stress FAILED: %s\n" % e) + + # --- session --- + def one_session(self): + self.t0_wall = iso_now() + self.t0_ns = now_ns() + + self.connect_ms = self.connect_with_retry() + print("[peer] connected %s:%d in %.1f ms" % (self.host, self.port, self.connect_ms)) + self.init_rtt_ms = self.init_roundtrip() + print("[peer] INIT rtt=%.1f ms geom=%s" % (self.init_rtt_ms, self.geom)) + + stress_start = threading.Event() + stress_stop = threading.Event() + stress_thread = None + if self.stress_from is not None: + # hold long enough to cover the remaining cycles (+15s margin) + hold_s = self.cadence_s * max(1, self.samples - self.stress_from) + 15.0 + stress_thread = threading.Thread( + target=self.ftp_stress_worker, + args=(stress_start, stress_stop, hold_s), + daemon=True) + stress_thread.start() + + for i in range(self.samples): + if self.stress_from is not None and i == self.stress_from: + print("[peer] === FTP stress ON (E-02 head-of-line) at cycle %d ===" % i) + stress_start.set() + time.sleep(0.5) # let the FTP half-login block netTask + + stressed = (self.stress_from is not None and i >= self.stress_from) + t = now_ns() + self.sock.sendall(remo_lcdmem_request()) + f = read_remo_frame(self.sock, t + int(REMO_CYCLE_DEADLINE_S * 1e9), + ack_probe=self.ack_probe) + t_end = now_ns() + + first_ms = (f["first_ns"] - t) / 1e6 + total_ms = (t_end - t) / 1e6 + tail_ms = (f["batches"][-1][0] - f["batches"][0][0]) / 1e6 if len(f["batches"]) > 1 else 0.0 + rec = { + "cycle": i, + "stress": stressed, + "first_ms": round(first_ms, 3), + "total_ms": round(total_ms, 3), + "tail_lag_ms": round(tail_ms, 3), + "batches": [[round((b[0] - t) / 1e6, 3), b[1]] for b in f["batches"]], + "bytes": f["total"], + "tag": f["tag"], + "cmd": f["cmd"], + "crc_ok": bool(f["crc_ok"]), + "nontrivial": bool(f["cmd"] == REMO_CMD_LCDMEM + and len(f["data"]) == 4 + REMO_MEMSIZE + and remo_bitmap_nontrivial(f["data"])), + } + self.frames.append(rec) + if (i % 10) == 0 or stressed: + print("[peer] cycle %3d %-7s first=%7.1f total=%8.1f ms batches=%d %s" + % (i, "STRESS" if stressed else "quiet", first_ms, total_ms, + len(f["batches"]), + "OK" if (rec["crc_ok"] and rec["nontrivial"]) else "BAD")) + if i + 1 < self.samples: + time.sleep(self.cadence_s) + + if stress_thread is not None: + stress_stop.set() + + self.dump_json() + + # --- L9: close + reconnect rounds (each close drives the device auto-relisten + # probe; each reconnect measures connect + INIT round trip) --- + for r in range(self.reconnect_rounds): + self.sock.close() + print("[peer] round %d/%d: closed; reconnect in 1.5s (device relisten window) ..." + % (r + 1, self.reconnect_rounds)) + time.sleep(1.5) + t = now_ns() + self.connect_once() + self.init_rtt_ms = self.init_roundtrip() + ms = (now_ns() - t) / 1e6 + self.reconnect_list.append(round(ms, 3)) + self.reconnect_ms = ms + print("[peer] round %d: reconnect + INIT = %.1f ms" % (r + 1, ms)) + + rep = self.dump_json() + self.print_summary(rep) + self.sock.close() + + # --- reporting --- + def _split(self, key, stressed): + return [f[key] for f in self.frames if f["stress"] is stressed] + + def _spikes(self, vals): + return sum(1 for x in vals if x >= SPIKE_MS) + + def dump_json(self, error=None): + all_total = [f["total_ms"] for f in self.frames] + all_first = [f["first_ms"] for f in self.frames] + all_tail = [f["tail_lag_ms"] for f in self.frames] + quiet_total = self._split("total_ms", False) + stress_total = self._split("total_ms", True) + rep = { + "mode": "remo", + "host": self.host, + "port": self.port, + "ts_start": self.t0_wall, + "ts_end": iso_now(), + "duration_s": round((now_ns() - self.t0_ns) / 1e9, 2), + "samples": self.samples, + "cadence_ms": round(self.cadence_s * 1000.0, 1), + "tcp_nodelay": bool(self.nodelay), + "immediate_ack": (None if not self.ack_fast else bool(self.ack_fast_ok)), + "in_band_ack_probe": bool(self.ack_probe), + "stress_from": self.stress_from, + "connect_ms": None if self.connect_ms is None else round(self.connect_ms, 3), + "reconnect_ms": None if self.reconnect_ms is None else round(self.reconnect_ms, 3), + "reconnect_list_ms": self.reconnect_list, + "reconnect_stat": stat_block(self.reconnect_list), + "init": {"rtt_ms": None if self.init_rtt_ms is None else round(self.init_rtt_ms, 3), + "geom": self.geom}, + "cycle_all": stat_block(all_total, self._spikes(all_total)), + "cycle_quiet": stat_block(quiet_total, self._spikes(quiet_total)), + "cycle_stress": stat_block(stress_total, self._spikes(stress_total)), + "first_byte_all": stat_block(all_first, self._spikes(all_first)), + "tail_lag_all": stat_block(all_tail, self._spikes(all_tail)), + "crc": {"ok": sum(1 for f in self.frames if f["crc_ok"]), + "fail": sum(1 for f in self.frames if not f["crc_ok"])}, + "bitmap": {"nontrivial_ok": sum(1 for f in self.frames if f["nontrivial"]), + "nontrivial_fail": sum(1 for f in self.frames if not f["nontrivial"])}, + "frames": self.frames, + } + if error: + rep["error"] = error + if self.json_path: + with open(self.json_path, "w", encoding="utf-8") as fh: + json.dump(rep, fh, ensure_ascii=False, indent=1) + print("[peer] JSON report -> %s%s" % (self.json_path, + " (PARTIAL - error)" if error else "")) + return rep + + def print_summary(self, rep): + print("=== latency_peer summary (M3 / Phase 14, production RemoDispBus) ===") + print("host=%s:%d samples=%d cadence=%.0fms nodelay=%d duration=%.1fs" + % (self.host, self.port, rep["samples"], rep["cadence_ms"], + int(rep["tcp_nodelay"]), rep["duration_s"])) + print("connect=%s ms reconnect+INIT=%s ms geom=%s" + % (rep["connect_ms"], rep["reconnect_ms"], rep["init"]["geom"])) + for key in ("cycle_all", "cycle_quiet", "cycle_stress", + "first_byte_all", "tail_lag_all"): + b = rep[key] + if b.get("n"): + print("%-15s n=%-4d min=%9.1f med=%9.1f p95=%9.1f max=%9.1f ms spikes=%d" + % (key, b["n"], b["min_ms"], b["med_ms"], b["p95_ms"], b["max_ms"], + b.get("spikes500", 0))) + else: + print("%-15s (no samples)" % key) + print("crc ok/fail = %d/%d bitmap nontrivial ok/fail = %d/%d" + % (rep["crc"]["ok"], rep["crc"]["fail"], + rep["bitmap"]["nontrivial_ok"], rep["bitmap"]["nontrivial_fail"])) + + def run(self): + try: + self.one_session() + except (ConnectionError, TimeoutError, RuntimeError, OSError) as e: + self.dump_json(error="%s: %s" % (type(e).__name__, e)) + raise + + +def percentile(sorted_vals, pct): + if not sorted_vals: + return None + idx = (len(sorted_vals) * pct) // 100 + if idx >= len(sorted_vals): + idx = len(sorted_vals) - 1 + return sorted_vals[idx] + + +def stat_block(ms_vals, spikes=0): + if not ms_vals: + return {"n": 0} + s = sorted(ms_vals) + return { + "n": len(s), + "min_ms": round(s[0], 3), + "med_ms": round(s[len(s) // 2], 3), + "p95_ms": round(percentile(s, 95), 3), + "max_ms": round(s[-1], 3), + "spikes500": spikes, + } + + +def fresh_reports(): + """Per-session measurement containers (reset on each retry attempt).""" + return { + "l6": {"samples": [], "echo_ok": 0, "echo_fail": 0}, + "l7": {"frames": [], "first_ms": [], "total_ms": [], "spikes500": 0, + "crc_ok": 0, "crc_fail": 0, "batches_per_frame": []}, + "l5_recv": {"bytes": 0, "window_ms": None}, + "l5_send": {"bytes": 0, "window_ms": None}, + "l3": {"bytes": 0, "window_ms": None}, + } + + +class Peer: + def __init__(self, host, port, wait_s, json_path, ftp_stress): + self.host = host + self.port = port + self.wait_s = wait_s + self.connect_wait_s = wait_s # per-attempt connect budget (retries shrink it) + self.json_path = json_path + self.ftp_stress = ftp_stress + self.sock = None + self.t0_wall = None + self.t0_ns = 0 + self.n_ops = 0 + self.reports = fresh_reports() + + def connect_with_retry(self): + t_end = time.time() + self.connect_wait_s + while time.time() < t_end: + try: + s = socket.create_connection((self.host, self.port), timeout=5.0) + s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + self.sock = s + return + except OSError: + time.sleep(1.0) + raise ConnectionError("cannot connect to %s:%d within %ds" % + (self.host, self.port, self.wait_s)) + + def ftp_stress_worker(self, start_evt, stop_evt): + """Hold an idle FTP login: lftpd read_line -> blocking net_recv(0) in + netTask (30s timeout) -> all queued messages wait behind it (E-02).""" + try: + s = socket.create_connection((self.host, 21), timeout=10.0) + s.settimeout(10.0) + s.sendall(b"USER latency\r\n") + reply = b"" + while b"\r\n" not in reply: + d = s.recv(64) + if not d: + break + reply += d + start_evt.set() # L5 window starts under stress + sys.stdout.write("[peer] ftp stress armed (331: %r)\n" % reply[:40]) + stop_evt.wait(35.0) # > 30s: netTask read times out (full freeze) + try: + s.sendall(b"QUIT\r\n") + except OSError: + pass + s.close() + sys.stdout.write("[peer] ftp stress released\n") + except OSError as e: + sys.stdout.write("[peer] ftp stress FAILED: %s\n" % e) + start_evt.set() + + def dump_json(self, error=None): + """Build the report dict and write it (idempotent; error => partial).""" + rep = { + "mode": "net", + "host": self.host, + "port": self.port, + "ts_start": self.t0_wall, + "ts_end": iso_now(), + "duration_s": round((now_ns() - self.t0_ns) / 1e9, 2), + "ops_seen": self.n_ops, + "ftp_stress": self.ftp_stress, + "seg_l6": stat_block(self.reports["l6"]["samples"], 0) + | {"echo_ok": self.reports["l6"]["echo_ok"], "echo_fail": self.reports["l6"]["echo_fail"]}, + "seg_l7_first_byte": stat_block(self.reports["l7"]["first_ms"]), + "seg_l7_total": stat_block(self.reports["l7"]["total_ms"], self.reports["l7"]["spikes500"]), + "l7_crc": {"ok": self.reports["l7"]["crc_ok"], "fail": self.reports["l7"]["crc_fail"], + "batches_per_frame": sorted(set(self.reports["l7"]["batches_per_frame"]))}, + "l5_recv": self.reports["l5_recv"], + "l5_send": self.reports["l5_send"], + "l3_drain": self.reports["l3"], + "frames_l7": self.reports["l7"]["frames"], + } + if error: + rep["error"] = error + if self.json_path: + with open(self.json_path, "w", encoding="utf-8") as f: + json.dump(rep, f, indent=1, ensure_ascii=True) + print("[peer] JSON report -> %s%s" % (self.json_path, + " (PARTIAL - error)" if error else "")) + return rep + + def run(self, max_attempts=3): + """整会话重试:芯片对空闲连接自行 RST(MCU 栈慢于芯片握手完成时,连接 + 建立后 15~30s 无流量即被芯片超时断开,2026-09-22 台架实证)→ 重连 + + MCU 端 re-HELLO 重启 = 闭环。重试时缩短 connect 预算(设备已在运行)。""" + last_err = None + for attempt in range(1, max_attempts + 1): + self.t0_wall = iso_now() + self.t0_ns = now_ns() + self.n_ops = 0 + self.reports = fresh_reports() + self.connect_wait_s = self.wait_s if attempt == 1 else min(self.wait_s, 60) + try: + self.one_session() + return + except (ConnectionError, TimeoutError, RuntimeError, OSError) as e: + last_err = e + self.close_quiet() + if attempt < max_attempts: + print("[peer] session attempt %d/%d failed: %s" % (attempt, max_attempts, e)) + print("[peer] reconnect in 2s (MCU auto-relistens; re-HELLO restarts) ...") + time.sleep(2.0) + raise RuntimeError("all %d session attempts failed (last: %s)" % (max_attempts, last_err)) + + def close_quiet(self): + try: + if self.sock is not None: + self.sock.close() + except OSError: + pass + self.sock = None + + def one_session(self): + t0_wall = self.t0_wall + t0_ns = self.t0_ns + print("[peer] connecting %s:%d (wait up to %ds) ..." % (self.host, self.port, self.connect_wait_s)) + self.connect_with_retry() + print("[peer] connected at %s" % iso_now()) + + l5_start_evt = threading.Event() + l5_stop_evt = threading.Event() + if self.ftp_stress: + th = threading.Thread(target=self.ftp_stress_worker, + args=(l5_start_evt, l5_stop_evt), daemon=True) + th.start() + + self.sock.sendall(HELLO) + print("[peer] HELLO sent, awaiting MCU ops ...") + + l5_window_open = False + l5_t0 = 0 + l3_t0 = 0 + while True: + op = recv_exact(self.sock, 1, t0_ns + 300 * 1000 * 1000000) + op = op[0] + self.n_ops += 1 + if op == OP_DONE: + print("[peer] op=done after %d ops" % self.n_ops) + break + + if op == OP_L5_RECV: + if self.ftp_stress: + l5_start_evt.wait(60.0) # start window only under full stress + l5_window_open = True + l5_t0 = now_ns() + for i in range(L5_CHUNKS): + self.sock.sendall(pattern(L5_LEN, seed=i)) + time.sleep(0.05) # 50ms: keep 1KB/s below MCU drain rate + self.reports["l5_recv"]["window_ms"] = (now_ns() - l5_t0) / 1e6 + self.reports["l5_recv"]["bytes"] = L5_CHUNKS * L5_LEN + l5_stop_evt.set() + + elif op == OP_L5_SEND: + t = now_ns() + data = recv_exact(self.sock, L5_CHUNKS * L5_LEN, t + 120 * 1000 * 1000000) + self.reports["l5_send"]["bytes"] = len(data) + self.reports["l5_send"]["window_ms"] = (now_ns() - t) / 1e6 + + elif op == OP_L6: + t0 = now_ns() + req = pattern(L6_SIZE, seed=self.n_ops) + self.sock.sendall(req) + resp = recv_exact(self.sock, L6_SIZE, t0 + 40 * 1000 * 1000000) + ms = (now_ns() - t0) / 1e6 + self.reports["l6"]["samples"].append(ms) + if resp == req: + self.reports["l6"]["echo_ok"] += 1 + else: + self.reports["l6"]["echo_fail"] += 1 + + elif op == OP_L7: + t_send = now_ns() + self.sock.sendall(l7_request(self.n_ops)) + got = bytearray() + batches = [] + first_ns = None + while len(got) < L7_FRAME_LEN: + # bounded read: never consume the next op byte (ASCII 0x04) that + # follows the frame in the stream (run10: unbounded recv ate it -> + # 3210-byte "frame" -> length check failed -> peer closed) + d = self.sock.recv(min(4096, L7_FRAME_LEN - len(got))) + if not d: + raise ConnectionError("L7 frame truncated (%d/%d)" % + (len(got), L7_FRAME_LEN)) + if first_ns is None: + first_ns = now_ns() + got.extend(d) + batches.append([round((now_ns() - t_send) / 1e6, 3), len(d)]) + t_end = now_ns() + first_ms = (first_ns - t_send) / 1e6 if first_ns else None + total_ms = (t_end - t_send) / 1e6 + # inter-batch arrival gaps + spikes = 0 + for k in range(1, len(batches)): + if batches[k][0] - batches[k - 1][0] >= SPIKE_MS: + spikes += 1 + self.reports["l7"]["first_ms"].append(first_ms if first_ms is not None else 0.0) + self.reports["l7"]["total_ms"].append(total_ms) + self.reports["l7"]["spikes500"] += spikes + self.reports["l7"]["batches_per_frame"].append(len(batches)) + if l7_frame_ok(bytes(got)): + self.reports["l7"]["crc_ok"] += 1 + else: + self.reports["l7"]["crc_fail"] += 1 + self.reports["l7"]["frames"].append( + {"first_ms": round(first_ms, 3) if first_ms is not None else None, + "total_ms": round(total_ms, 3), "batches": batches}) + + elif op == OP_L3: + l3_t0 = now_ns() + recv_exact(self.sock, L3_SENDS * L3_LEN, l3_t0 + 120 * 1000 * 1000000) + self.reports["l3"]["bytes"] = L3_SENDS * L3_LEN + self.reports["l3"]["window_ms"] = (now_ns() - l3_t0) / 1e6 + + else: + raise RuntimeError("unknown op byte 0x%02X (protocol desync)" % op) + + # --- JSON report (also used on the error path: partial data kept) --- + rep = self.dump_json() + + print("=== latency_peer summary (M2 / Phase 13) ===") + print("ops=%d duration=%.1fs ftp_stress=%d" % (self.n_ops, rep["duration_s"], int(self.ftp_stress))) + for key in ("seg_l6", "seg_l7_first_byte", "seg_l7_total"): + b = rep[key] + if b.get("n"): + print("%-18s n=%-4d min=%9.1f med=%9.1f p95=%9.1f max=%9.1f ms spikes=%d" + % (key, b["n"], b["min_ms"], b["med_ms"], b["p95_ms"], b["max_ms"], + b.get("spikes500", 0))) + else: + print("%-18s (no samples)" % key) + print("l7 crc ok/fail = %d/%d batches/frame = %s" + % (rep["l7_crc"]["ok"], rep["l7_crc"]["fail"], rep["l7_crc"]["batches_per_frame"])) + self.sock.close() + + +def main(argv=None): + ap = argparse.ArgumentParser( + description="link latency test peer - M2 Phase 13 (net) / M3 Phase 14 (remo)") + ap.add_argument("--mode", default="net", choices=["net", "remo"], + help="net = Phase 13 test protocol :8080; " + "remo = production RemoDispBus client :7003 (Phase 14 / simulator)") + ap.add_argument("--host", default="192.168.1.100", help="device IP") + ap.add_argument("--port", type=int, default=None, + help="listen port (default: 8080 for net, 7003 for remo)") + ap.add_argument("--wait", type=int, default=180, + help="max seconds to retry connect (device boots after flash)") + ap.add_argument("--json", default=None, help="write JSON report to this path") + ap.add_argument("--ftp-stress", action="store_true", + help="hold idle FTP login (E-02 head-of-line repro)") + ap.add_argument("--samples", type=int, default=50, + help="remo mode: LCDMEM frame cycles to measure (default 50)") + ap.add_argument("--cadence-ms", type=int, default=100, + help="remo mode: client poll cadence in ms (production client = 100)") + ap.add_argument("--stress-from", type=int, default=None, + help="remo mode: cycle index where FTP stress starts " + "(default with --ftp-stress: half of --samples)") + ap.add_argument("--nodelay", action="store_true", + help="remo mode: set TCP_NODELAY on the client socket") + ap.add_argument("--reconnect-rounds", type=int, default=1, + help="remo mode: close+reconnect rounds at the end (each one drives " + "the device auto-relisten probe; M3 uses 3)") + ap.add_argument("--ack-probe", action="store_true", + help="remo mode: when a frame is still incomplete, send one parser-safe " + "byte so the TCP segment piggybacks an ACK (diagnoses whether the " + "~50ms tail waits for an ACK; no packet capture needed)") + ap.add_argument("--ack-fast", action="store_true", + help="remo mode: Windows only - disable delayed ACK on this socket " + "(SIO_TCP_SET_ACK_FREQUENCY=1); removes the ~50ms frame tail") + args = ap.parse_args(argv) + + if args.mode == "remo": + port = args.port if args.port else 7003 + stress_from = args.stress_from + if args.ftp_stress and stress_from is None: + stress_from = max(1, args.samples // 2) + peer = RemoPeer(args.host, port, args.wait, args.json, args.samples, + args.cadence_ms, stress_from, args.nodelay, + args.reconnect_rounds, args.ack_probe, args.ack_fast) + else: + peer = Peer(args.host, args.port or 8080, args.wait, args.json, args.ftp_stress) + try: + peer.run() + return 0 + except (ConnectionError, TimeoutError, RuntimeError, OSError) as e: + print("[peer] ERROR: %s" % e) + try: + peer.dump_json(error=str(e)) # partial report: keep measured data + except OSError as e2: + print("[peer] (JSON write failed: %s)" % e2) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test/net_latency_test.c b/test/net_latency_test.c new file mode 100644 index 00000000..fd58b080 --- /dev/null +++ b/test/net_latency_test.c @@ -0,0 +1,1344 @@ +/****************************************************************************** + * 模块名称:CH395F 链路延迟测试(实现) + * 模块功能:DWT CYCCNT 微秒时基 + 采样统计(min/med/p95/max/尖峰)+ [LAT] + * 机器可读报告与 verdict;Phase 12(M1):SPI 字节命令 / DMA 时延 + * 基线(L1/L2,硬件层模式,无需 PC)。分段定义与判定基线见 + * docs/测试说明/链路延迟测试说明.md。 + * 适用平台:STM32F4 系列(CH395F) + * 作者:王建锋 + * 创建日期:2026-09-22 + * 修改记录: + * 2026-09-22 王建锋 初版:时基 + 统计/报告 + Phase 12(L1/L2) + * + * 构建形态: + * - 产品构建:三个 Phase 宏均未定义 → 本文件除 include 外全部不参与编译; + * - 测试构建(TEST_SUITE_CH395F):按所选 Phase 编译对应代码。 + * 所有字符串字面量纯 ASCII([LAT] 报告行),中文仅注释。 + ******************************************************************************/ + +#include + +#include "net_latency_test.h" +#include "ch395f_test.h" /* TEST_CHECK / TEST_REPORT / g_test_stats / Phase 宏 */ +#include "ch395f.h" /* 驱动公开 API(L1 字节命令 / L2 DMA) */ +#include "net_socket.h" /* net_send / net_recv / NET_MSG_DONTWAIT(Phase 13) */ +#include "test_config.h" /* TEST_LATENCY_PROBE(探针编译开关) */ +#include "main.h" /* CoreDebug / DWT / SystemCoreClock / HAL_GetTick */ + +#define DBG_TAG "[LAT_TEST]" +#include "dbg_log.h" + +#if defined(ENABLE_PHASE12_TESTS) || \ + defined(ENABLE_PHASE13_TESTS) || \ + defined(ENABLE_PHASE14_TESTS) + +/* ============================================================ + * DWT CYCCNT 微秒时基(测试专用) + * + * 工程内首个 DWT 用户(sys_clock 为纯 HAL tick,无 us 能力)。 + * 32 位计数 168MHz 下回绕周期 25.17ms:M1 各段(<5ms)与 M2 + * 调用方侧测量(单次 <25ms)用单次读数差即可;跨 25ms 的段 + * (L4/L8 整帧与重传尖峰)由 PC 端 peer 计时(说明文档 §2.3)。 + * 读取 DWT->CYCCNT 无共享状态,任意任务上下文安全。 + * ============================================================ */ + +static int s_dwt_ready = 0; + +void lat_time_init(void) +{ + if (s_dwt_ready != 0) { + return; + } + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; /* 使能 DWT 的前置条件 */ + DWT->CYCCNT = 0U; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + s_dwt_ready = 1; +} + +#ifdef ENABLE_PHASE12_TESTS +/* Phase 12 专用静态助手(仅该阶段引用;守卫防止单阶段构建的 #177-D) */ +static uint32_t lat_cycles(void) +{ + return DWT->CYCCNT; +} +#endif /* ENABLE_PHASE12_TESTS */ + +static uint32_t lat_us_from_cycles(uint32_t cycles) +{ + /* 换算用 SystemCoreClock(不写死频率),64 位中间量防溢出 */ + return (uint32_t)(((uint64_t)cycles * 1000000UL) / (uint64_t)SystemCoreClock); +} + +#ifdef ENABLE_PHASE12_TESTS +static uint32_t lat_elapsed_us(uint32_t c_start) +{ + /* 有效区间:单次测量 < 25.17ms(32 位回绕周期),M1 各段均满足 */ + return lat_us_from_cycles(DWT->CYCCNT - c_start); +} +#endif /* ENABLE_PHASE12_TESTS */ + +/* 公开读数接口(net_socket.c 的 L3 探针经此访问 DWT,避免直接碰寄存器) */ +uint32_t lat_cycles_now(void) +{ + return DWT->CYCCNT; +} + +uint32_t lat_cycles_diff_us(uint32_t c_start) +{ + return lat_us_from_cycles(DWT->CYCCNT - c_start); +} + +/* M3 探针计时标记:DWT(亚微秒) + tick(>25ms 回绕兜底),见头文件说明 */ +void lat_mark(lat_tmark_t *p) +{ + p->cyc = DWT->CYCCNT; + p->tick = HAL_GetTick(); +} + +uint32_t lat_mark_us(const lat_tmark_t *p) +{ + uint32_t dt_ms = HAL_GetTick() - p->tick; + + /* 跨度 ≥20ms:DWT 可能已回绕(25.17ms)→ 用 tick 兜底(1ms 分辨率) */ + if (dt_ms >= 20U) { + return dt_ms * 1000U; + } + return lat_us_from_cycles(DWT->CYCCNT - p->cyc); +} + +/* ============================================================ + * 采样统计(静态数组,零 C 库堆) + * ============================================================ */ + +static uint32_t s_lat_samples[LAT_MAX_SAMPLES]; /* 原始样本(按段复用) */ +static uint32_t s_lat_sort[LAT_MAX_SAMPLES]; /* 排序工作区(lat_stat_finish) */ + +void lat_stat_reset(lat_stats_t *p) +{ + p->n = 0U; + p->spikes = 0U; + p->mn = 0xFFFFFFFFU; /* 空集哨兵 */ + p->med = 0U; + p->p95 = 0U; + p->mx = 0U; +} + +void lat_stat_add(lat_stats_t *p, uint32_t us, uint32_t spike_th_us) +{ + if (p->n < (uint16_t)LAT_MAX_SAMPLES) { + s_lat_samples[p->n] = us; + } + p->n++; + if (us < p->mn) { + p->mn = us; + } + if (us > p->mx) { + p->mx = us; + } + if ((spike_th_us != 0U) && (us >= spike_th_us)) { + p->spikes++; + } +} + +void lat_stat_finish(lat_stats_t *p) +{ + uint32_t n = (p->n < (uint32_t)LAT_MAX_SAMPLES) ? (uint32_t)p->n + : (uint32_t)LAT_MAX_SAMPLES; + uint32_t i, j; + uint32_t idx; + + for (i = 0U; i < n; i++) { + s_lat_sort[i] = s_lat_samples[i]; + } + /* 插入排序(n<=128,O(n^2) 可接受;零堆) */ + for (i = 1U; i < n; i++) { + uint32_t key = s_lat_sort[i]; + j = i; + while ((j > 0U) && (s_lat_sort[j - 1U] > key)) { + s_lat_sort[j] = s_lat_sort[j - 1U]; + j--; + } + s_lat_sort[j] = key; + } + p->med = (n > 0U) ? s_lat_sort[n / 2U] : 0U; + idx = (n * 95U) / 100U; + if (idx >= n) { + idx = (n > 0U) ? (n - 1U) : 0U; + } + p->p95 = (n > 0U) ? s_lat_sort[idx] : 0U; +} + +void lat_stat_report(const char *p_seg, const lat_stats_t *p, + uint32_t warn_med_us, uint32_t fail_med_us, + uint16_t max_spikes, int *p_verdict) +{ + int v = LAT_VERDICT_OK; + + DBG_INFO("[LAT] seg=%s n=%u min=%lu med=%lu p95=%lu max=%lu us spikes=%u", + p_seg, (unsigned)p->n, + (unsigned long)p->mn, (unsigned long)p->med, + (unsigned long)p->p95, (unsigned long)p->mx, + (unsigned)p->spikes); + + if (p->n == 0U) { + v = LAT_VERDICT_FAIL; /* 空集 = 测量未发生,判 FAIL */ + } else if (p->med > fail_med_us) { + v = LAT_VERDICT_FAIL; + } else if (p->spikes > max_spikes) { + v = LAT_VERDICT_FAIL; + } else if (p->med > warn_med_us) { + v = LAT_VERDICT_WARN; /* 超 warn 未超 fail:计 PASS,提示重锁阈值 */ + } + + DBG_INFO("[LAT] verdict=%s seg=%s (med=%lums warn<=%lu crit<=%lu spikes=%u/%u)", + (v == LAT_VERDICT_OK) ? "OK" : ((v == LAT_VERDICT_WARN) ? "WARN" : "FAIL"), + p_seg, (unsigned long)p->med, + (unsigned long)warn_med_us, (unsigned long)fail_med_us, + (unsigned)p->spikes, (unsigned)max_spikes); + + if (p_verdict != NULL) { + *p_verdict = v; + } +} + +/* ============================================================ + * TEST_LATENCY_PROBE 探针(M2/M3) + * + * netTask 上下文(net_socket.c 发送分块循环)向 testTask 报告段内 + * 打点(L3 chunk_wait 等调用方侧无法测量的段)。 + * 并发模型:单写者(netTask)/单读者(testTask); + * - s_probe_n 为 32 位(Cortex-M4 单字读写原子,无锁) + * - 样本写入 → n++ 的顺序保证读者看到一致的 (n, samples[0..n)) + * - disarm 后 netTask 最多再补 1 个在飞样本(统计已定稿,忽略) + * 探针用独立样本/排序数组,不与 lat_stat_* 的工作区共享。 + * ============================================================ */ +#if defined(TEST_LATENCY_PROBE) + +static char s_probe_seg[24]; +static uint32_t s_probe_max; +static volatile uint32_t s_probe_n; /* 32 位原子:netTask 写 / testTask 读 */ +static uint32_t s_probe_spike_th; +static uint32_t s_probe_spikes; +static uint32_t s_probe_mn; +static uint32_t s_probe_mx; +static uint8_t s_probe_armed; +static uint32_t s_probe_samples[LAT_MAX_SAMPLES]; +static uint32_t s_probe_sort[LAT_MAX_SAMPLES]; +static lat_stats_t s_probe_stats; + +uint8_t lat_probe_armed(void) +{ + return s_probe_armed; +} + +void lat_probe_arm(const char *seg, uint32_t max_n, uint32_t spike_th_us) +{ + uint32_t i; + + if (max_n > (uint32_t)LAT_MAX_SAMPLES) { + max_n = (uint32_t)LAT_MAX_SAMPLES; + } + s_probe_max = max_n; + s_probe_n = 0U; + s_probe_spikes = 0U; + s_probe_mn = 0xFFFFFFFFU; + s_probe_mx = 0U; + s_probe_spike_th = spike_th_us; + s_probe_armed = 0U; /* 最后置位:arm 完成前 netTask 不写入 */ + for (i = 0U; i < sizeof(s_probe_seg) - 1U; i++) { + s_probe_seg[i] = seg[i]; + if (s_probe_seg[i] == '\0') { + break; + } + } + s_probe_seg[i] = '\0'; + s_probe_armed = 1U; +} + +void lat_probe_add(uint32_t us) +{ + if (s_probe_armed == 0U) { + return; + } + if (s_probe_n >= s_probe_max) { + return; + } + s_probe_samples[s_probe_n] = us; + if (us < s_probe_mn) { + s_probe_mn = us; + } + if (us > s_probe_mx) { + s_probe_mx = us; + } + if ((s_probe_spike_th != 0U) && (us >= s_probe_spike_th)) { + s_probe_spikes++; + } + s_probe_n++; +} + +uint32_t lat_probe_count(void) +{ + return s_probe_n; +} + +/* 段名比较(避免为一处探针引入 依赖) */ +static int lat_probe_seg_is(const char *seg) +{ + uint32_t i; + + for (i = 0U; i < sizeof(s_probe_seg); i++) { + if (s_probe_seg[i] != seg[i]) { + return 0; + } + if (seg[i] == '\0') { + return 1; + } + } + return 0; +} + +void lat_probe_add_seg(const char *seg, uint32_t us) +{ + if (s_probe_armed == 0U) { + return; + } + if (lat_probe_seg_is(seg) == 0) { + return; /* 当前窗口不是本段 → 丢弃(防跨段污染) */ + } + lat_probe_add(us); +} + +void lat_probe_disarm(void) +{ + uint32_t n = s_probe_n; + uint32_t i, j; + uint32_t idx; + + s_probe_armed = 0U; + + for (i = 0U; i < n; i++) { + s_probe_sort[i] = s_probe_samples[i]; + } + for (i = 1U; i < n; i++) { + uint32_t key = s_probe_sort[i]; + j = i; + while ((j > 0U) && (s_probe_sort[j - 1U] > key)) { + s_probe_sort[j] = s_probe_sort[j - 1U]; + j--; + } + s_probe_sort[j] = key; + } + s_probe_stats.n = (uint16_t)n; + s_probe_stats.mn = (n > 0U) ? s_probe_mn : 0U; + s_probe_stats.mx = s_probe_mx; + s_probe_stats.med = (n > 0U) ? s_probe_sort[n / 2U] : 0U; + idx = (n * 95U) / 100U; + if (idx >= n) { + idx = (n > 0U) ? (n - 1U) : 0U; + } + s_probe_stats.p95 = (n > 0U) ? s_probe_sort[idx] : 0U; + s_probe_stats.spikes = (uint16_t)s_probe_spikes; +} + +const lat_stats_t *lat_probe_stats(void) +{ + return &s_probe_stats; +} + +#endif /* TEST_LATENCY_PROBE */ + +/* ============================================================ + * Phase 12 — SPI 时延基线(L1/L2,硬件层模式,无需 PC) + * + * 测量对象(见 链路延迟测试说明.md §1): + * L1 SPI 字节命令 —— 3 个代表性命令的完整 SPI 事务时长 + * (公开函数内部含 spi_begin/spi_end,即一次完整事务) + * L2 SPI DMA —— ch395f_read_recv_buf(N) 完整 DMA 事务 + * (读方向:MCU 主发时钟,芯片侧输出内容不影响传输时长; + * 硬件层模式 socket 未打开,读方向不改芯片任何状态, + * 保持非侵入。写方向 DMA 机制相同(s_spi2_dma_tx_buf), + * 由 Phase 13(M2)发送分块握手覆盖) + * + * 前置:ch395f_test_run() 硬件层分支已完成 net_stack_bring_up() + * 并独占 CH395F(无 net_poll 并发 SPI 事务)。 + * 初始阈值:静态推算值,台架首跑后重锁(说明文档 §4.1/§8)。 + * ============================================================ */ + +#ifdef ENABLE_PHASE12_TESTS +#define LAT_P12_CMD_N 100U /* L1 每命令样本数 */ +#define LAT_P12_DMA_N 100U /* L2 每长度档样本数 */ +#define LAT_P12_DMA_LEN_MAX 2048U /* L2 单发上限 = CH395F_SPI_DMA_MAX_PAYLOAD */ + +/* L1 阈值(us)—— 2026-09-22 台架首跑后重锁(实测 med 10~15us; + * warn ≈ 2×med,crit ≈ 2×warn,见 链路延迟测试说明.md §4.1) */ +#define LAT_P12_CMD_WARN_US 40U +#define LAT_P12_CMD_FAIL_US 100U + +static const char *const a_p12_cmd_seg[3] = { + "spi_cmd_getver", /* GET_IC_VER:1 写 + 1 读 */ + "spi_cmd_chkexist", /* CHECK_EXIST:1 写 + 1 读(回显取反自检) */ + "spi_cmd_gint16", /* GET_GLOB_INT_STATUS:1 写 + 2 读(读即清) */ +}; + +static const uint16_t a_p12_dma_len[4] = { 4U, 64U, 1024U, 2048U }; +static const char *const a_p12_dma_seg[4] = { + "spi_dma_4", "spi_dma_64", "spi_dma_1024", "spi_dma_2048" +}; +/* 2026-09-22 台架首跑后重锁(实测 med 20/114/1611/3208us; + * warn ≈ 2×med,crit ≈ 2×warn) */ +static const uint32_t a_p12_dma_warn[4] = { 40U, 250U, 3200U, 6400U }; +static const uint32_t a_p12_dma_fail[4] = { 100U, 600U, 6400U, 12800U }; +#endif /* ENABLE_PHASE12_TESTS */ + +#ifdef ENABLE_PHASE12_TESTS + +void net_latency_phase12_run(void) +{ + lat_stats_t st; + static uint8_t s_dma_buf[LAT_P12_DMA_LEN_MAX]; /* DMA 读输出缓冲(静态,零堆) */ + int v; + int v_all = LAT_VERDICT_OK; + uint32_t i; + uint32_t k; + + /* Phase 12 为硬件层运行的最后阶段:重置全局测试统计不影响 + * 前序阶段已打印的 TEST_REPORT,使 Phase 12 计数独立清晰 */ + memset(&g_test_stats, 0U, sizeof(g_test_stats)); + + DBG_INFO("=== CH395F Phase 12 Tests (link latency baseline, L1/L2) ==="); + + lat_time_init(); + DBG_INFO("[LAT] run_id=%lu phase=12", (unsigned long)HAL_GetTick()); + /* 时基校准行:台架可用逻辑分析仪核对 us/cycle 换算 */ + DBG_INFO("[LAT] cal core_clk=%lu us_per_1024cyc=%lu", + (unsigned long)SystemCoreClock, + (unsigned long)(((uint64_t)1024U * 1000000UL) / (uint64_t)SystemCoreClock)); + + /* ---- 前置:芯片存活(无芯片则无法测量,判 FAIL 跳过) ---- */ + { + ch395f_status_t ret = ch395f_check_exist(); + if (ret != CH395F_ERR_SUCCESS) { + DBG_ERROR("[LAT] chip not detected (0x%02X), Phase 12 skipped", + (uint8_t)ret); + TEST_CHECK(0, "P12 pre: ch395f_check_exist"); + DBG_INFO("[LAT] summary verdict=FAIL"); + DBG_INFO("[LAT] report complete"); + TEST_REPORT("Phase 12"); + return; + } + } + + memset(s_dma_buf, 0U, sizeof(s_dma_buf)); + + /* ---- L1:SPI 字节命令时延(3 个代表性命令 × 100) ---- */ + for (i = 0U; i < 3U; i++) { + lat_stat_reset(&st); + for (k = 0U; k < LAT_P12_CMD_N; k++) { + uint32_t c0 = lat_cycles(); + switch (i) { + case 0U: + (void)ch395f_get_version(); + break; + case 1U: + (void)ch395f_check_exist(); + break; + default: + (void)ch395f_get_glob_int_status_all(); + break; + } + lat_stat_add(&st, lat_elapsed_us(c0), 0U); + } + lat_stat_finish(&st); + lat_stat_report(a_p12_cmd_seg[i], &st, + LAT_P12_CMD_WARN_US, LAT_P12_CMD_FAIL_US, 0U, &v); + if (v > v_all) { + v_all = v; + } + } + + /* ---- L2:SPI DMA 时延(4 档长度 × 100) ---- */ + for (i = 0U; i < 4U; i++) { + lat_stat_reset(&st); + for (k = 0U; k < LAT_P12_DMA_N; k++) { + uint32_t c0 = lat_cycles(); + (void)ch395f_read_recv_buf(0U, s_dma_buf, a_p12_dma_len[i]); + lat_stat_add(&st, lat_elapsed_us(c0), 0U); + } + lat_stat_finish(&st); + lat_stat_report(a_p12_dma_seg[i], &st, + a_p12_dma_warn[i], a_p12_dma_fail[i], 0U, &v); + if (v > v_all) { + v_all = v; + } + } + + /* ---- 汇总 ---- */ + { + const char *p_name = (v_all == LAT_VERDICT_OK) ? "OK" + : ((v_all == LAT_VERDICT_WARN) ? "WARN" : "FAIL"); + DBG_INFO("[LAT] summary verdict=%s", p_name); + } + /* WARN 计 PASS(提示重锁阈值);FAIL 才使套件失败 */ + TEST_CHECK(v_all != LAT_VERDICT_FAIL, + "P12 latency baseline (verdict=%s)", + (v_all == LAT_VERDICT_OK) ? "OK" + : ((v_all == LAT_VERDICT_WARN) ? "WARN" : "FAIL")); + DBG_INFO("[LAT] report complete"); + TEST_REPORT("Phase 12"); +} + +#else /* ENABLE_PHASE12_TESTS */ + +void net_latency_phase12_run(void) +{ + /* 本构建未选中 Phase 12:空实现(符号保留,行为不参与) */ +} + +#endif /* ENABLE_PHASE12_TESTS */ + +#ifdef ENABLE_PHASE13_TESTS + +/* ==================================================================== + * Phase 13 — 芯片 + 队列时延(L3~L7/L10,需 PC peer,M2) + * + * 由 net_layer_test_main() 监听循环(8080 端口,~10ms 节拍)事件驱动。 + * 服务器驱动协议:MCU 发操作字节,peer(test/latency_peer.py --mode net) + * 执行对应流量;段完成后 MCU 打 [LAT] 报告行并推进下一操作。 + * + * 段定义(说明文档 §1): + * L5 queue_rt_recv / queue_rt_send —— 调用方视角 net_recv/net_send + * 全程(入队 + netTask 调度 + 处理 + 通知),静默基线档; + * 压力档 = peer --ftp-stress(lftpd 阻塞读占住 netTask → E-02 复现) + * L6 req2first_mcu —— 请求收齐 → 应答发送完成(MCU 侧,含队列+1 chunk) + * L7 fullframe_mcu —— 9B LCDMEM 请求 → 3209B 全帧发送完成 + * (netTask 内 4 次分块停止等待,= 生产路径发送段) + * L3 chunk_wait —— netTask 内"写发送缓冲 → SENBUF_FREE"等待 + * (调用方不可见,经 TEST_LATENCY_PROBE 探针,见 net_socket.c) + * L10 logtax_line —— 一行 60 字符 printf(fputc 逐字节 @115200) + * + * 执行顺序(2026-09-22 run3 后重排):L5 → L3 → L10 → L6 → L7 —— + * 失步敏感的小帧 RX 协议段(L6/L7)放最后,先锁住抗失步段的基线。 + * 对端侧(L4 线上传输/整帧到达/尖峰)由 peer JSON 报告,按主机时间戳关联。 + * + * 阈值:初始值,台架首跑后重锁(说明文档 §4.2)。 + * 独立运行:与其他 Phase 共用 8080 socket,混跑会污染帧测量。 + * ==================================================================== */ + +#define LAT_P13_SAMPLES 10U /* L6/L7 每段样本数(2026-09-22 run3 实证:L6 50 轮 + * 小交换后芯片 TCP 失步 → TX 停滞 + 重复递交 9B 碎片 + * → 降至 10,先取干净基线再量化失步边界) */ +#define LAT_P13_L5_CHUNKS 20U /* L5 flood 块数(2026-09-22 run1 实证:50×100B@5ms + * 达 20KB/s 饱和芯片接收缓冲 → 窗口关闭重传 → + * 重复递交 11884/5000B → 对端 RST;降至 1KB/s + * 静默档,见 链路延迟测试说明.md §8 run1 记录) */ +#define LAT_P13_L5_CHUNK_LEN 100U +#define LAT_P13_L3_SENDS 16U /* 每次 4KB = 4 chunk → 16×4 = 64 探针样本 + * (run3 后从 32 减半:控制持续 TX 总量) */ +#define LAT_P13_L3_SEND_LEN 4096U +#define LAT_P13_L3_SAMPLES (LAT_P13_L3_SENDS * 4U) +#define LAT_P13_L6_SIZE 10U +#define LAT_P13_L7_REQ_LEN 9U +#define LAT_P13_FRAME_LEN 3209U /* 4B 头 + 3204B 数据段(偏移u32+位图3200B) + 1B CRC */ +#define LAT_P13_RECV_LEN 256U +#define LAT_P13_TIMEOUT_MS 240000U /* 整体会话超时(压力档含 30s 冻结) */ +#define LAT_P13_SAMPLE_T_MS 35000U /* 单样本等待超时(覆盖一个 30s 冻结) */ + +/* 初始阈值(us):warn ≈ 2~3×预期,crit 覆盖硬失败(DMA/发送超时 1s 必超) */ +#define LAT_P13_QRT_WARN_US 10000U +#define LAT_P13_QRT_CRIT_US 100000U +#define LAT_P13_R2F_WARN_US 20000U +#define LAT_P13_R2F_CRIT_US 200000U +#define LAT_P13_FF_WARN_US 50000U +#define LAT_P13_FF_CRIT_US 500000U +#define LAT_P13_CW_WARN_US 10000U +#define LAT_P13_CW_CRIT_US 500000U +#define LAT_P13_CW_SPIKE_US 500000U /* ≥500ms = 重传粒度指纹(D1) */ +#define LAT_P13_LT_WARN_US 10000U +#define LAT_P13_LT_CRIT_US 25000U + +/* 操作字节(MCU → peer) */ +#define LAT_P13_OP_L5_RECV 0x01U +#define LAT_P13_OP_L5_SEND 0x02U +#define LAT_P13_OP_L6 0x03U +#define LAT_P13_OP_L7 0x04U +#define LAT_P13_OP_L3 0x05U +#define LAT_P13_OP_DONE 0xFFU + +/* HELLO 魔术字(peer → MCU,4 字节) */ +static const uint8_t a_p13_hello[4] = { 0x5AU, 0xA5U, 0x58U, 0x01U }; + +enum { + P13_ST_HELLO = 0, + P13_ST_L5_RECV, + P13_ST_L5_SEND, + P13_ST_L6, + P13_ST_L7, + P13_ST_L3, + P13_ST_L10, + P13_ST_DONE, + P13_ST_ABORT +}; + +static uint8_t s_p13_state = P13_ST_HELLO; +static uint8_t s_p13_done = 0U; /* 会话完成/中止:后续调用空操作 */ +static uint8_t s_p13_resync_done = 0U; /* abort 后首次重同步只执行一次 */ +static uint8_t s_p13_clean_done = 0U; /* 会话干净完成(全段 OK + 已发 0xFF) */ +static uint32_t s_p13_resync_tick = 0U; /* 最近一次(重)监听的时刻(周期自愈用) */ +static uint8_t s_p13_hello_n = 0U; +static int s_p13_verdict_all = LAT_VERDICT_OK; +static int s_p13_seg_force_fail = 0; /* 当前段出现短写/错误 → 强制 FAIL */ +static uint32_t s_p13_start_tick; +static uint32_t s_p13_step_tick; /* L6/L7 单样本等待计时 */ +static uint32_t s_p13_stage; +static uint32_t s_p13_rx_bytes; +static uint16_t s_p13_l6rx_n; +static uint16_t s_p13_l7rx_n; +static lat_stats_t s_p13_st; + +static uint8_t s_p13_rx[LAT_P13_RECV_LEN]; +static uint8_t s_p13_tx[LAT_P13_L3_SEND_LEN]; /* L5/L3 flood 载荷(模式预填) */ +static uint8_t s_p13_frame[LAT_P13_FRAME_LEN]; /* L7 应答帧(建一次) */ +static uint8_t s_p13_l6rx[LAT_P13_L6_SIZE]; +static uint8_t s_p13_l7rx[LAT_P13_L7_REQ_LEN]; + +/* 构造 L7 应答帧(严格照生产 RemoDispBus 帧格式,ui_net_sink.c remo_send): + * [TAG=0xBB][CMD=0x03][LEN=3204 大端][DATA = 偏移u32(0) + 1bpp 位图 3200B][CRC] + * CRC = 仅对 DATA 段(3204B)逐字节异或;整帧 = 4 + 3204 + 1 = 3209B */ +static void p13_build_frame(void) +{ + uint32_t i; + uint8_t crc = 0U; + + s_p13_frame[0] = 0xBBU; + s_p13_frame[1] = 0x03U; + s_p13_frame[2] = 0x0CU; /* 3204 = 0x0C84,大端高字节 */ + s_p13_frame[3] = 0x84U; + s_p13_frame[4] = 0U; /* 数据段首 4B = 起始偏移,大端 0 */ + s_p13_frame[5] = 0U; + s_p13_frame[6] = 0U; + s_p13_frame[7] = 0U; + for (i = 0U; i < 3200U; i++) { + s_p13_frame[8U + i] = (uint8_t)(i & 0xFFU); + } + for (i = 0U; i < 4U + 3200U; i++) { + crc ^= s_p13_frame[4U + i]; /* CRC 覆盖数据段(索引 4..3207) */ + } + s_p13_frame[8U + 3200U] = crc; /* 索引 3208 = 帧尾 CRC */ +} + +static uint8_t p13_send_op(int32_t sockfd, uint8_t op) +{ + int n = net_send(sockfd, &op, 1, 0); + + if (n != 1) { + /* 协议操作字节发送失败 = 与 peer 的状态机同步永久丢失(run9 实证: + * op 静默丢失 → peer 空等 300s 超时 → 整段会话作废)→ 必须可见 */ + DBG_INFO("[LAT] note: op 0x%02X send failed n=%d errno=%d", + op, n, net_get_errno()); + } + return (n == 1) ? 1U : 0U; +} + +/* 当前段收尾:报告 + verdict 累计 + 统计容器复位 */ +static void p13_report_seg(const char *p_seg, uint32_t warn_us, uint32_t crit_us, + uint16_t max_spikes) +{ + int v = LAT_VERDICT_OK; + + if (s_p13_seg_force_fail != 0) { + v = LAT_VERDICT_FAIL; + } + lat_stat_finish(&s_p13_st); + lat_stat_report(p_seg, &s_p13_st, warn_us, crit_us, max_spikes, &v); + if (s_p13_seg_force_fail != 0) { + DBG_INFO("[LAT] note: %s has send error/short write -> FAIL", p_seg); + } + if (v > s_p13_verdict_all) { + s_p13_verdict_all = v; + } + s_p13_seg_force_fail = 0; + lat_stat_reset(&s_p13_st); +} + +/* 会话中止:尽力报告进行中段的 partial 统计 + FAIL 汇总(标记链不断) */ +static void p13_abort(const char *p_why) +{ + const char *p_name = "FAIL"; + + if (s_p13_stage > 0U) { + lat_stat_finish(&s_p13_st); + DBG_INFO("[LAT] seg=partial n=%u min=%lu med=%lu p95=%lu max=%lu us (interrupted)", + (unsigned)s_p13_st.n, (unsigned long)s_p13_st.mn, + (unsigned long)s_p13_st.med, (unsigned long)s_p13_st.p95, + (unsigned long)s_p13_st.mx); + s_p13_verdict_all = LAT_VERDICT_FAIL; + } +#if defined(TEST_LATENCY_PROBE) + if (lat_probe_armed() != 0U) { + const lat_stats_t *p_ps; + + lat_probe_disarm(); + p_ps = lat_probe_stats(); + DBG_INFO("[LAT] seg=chunk_wait n=%u min=%lu med=%lu p95=%lu max=%lu us spikes=%u (partial, interrupted)", + (unsigned)p_ps->n, (unsigned long)p_ps->mn, + (unsigned long)p_ps->med, (unsigned long)p_ps->p95, + (unsigned long)p_ps->mx, (unsigned)p_ps->spikes); + s_p13_verdict_all = LAT_VERDICT_FAIL; + } +#endif + DBG_ERROR("[LAT] phase13 abort: %s", p_why); + TEST_CHECK(0, "P13 abort: %s", p_why); + DBG_INFO("[LAT] summary verdict=%s", p_name); + DBG_INFO("[LAT] report complete"); + TEST_REPORT("Phase 13"); + s_p13_done = 1U; + s_p13_state = P13_ST_ABORT; +} + +/* HELLO 确认后的公共入口(首连与重启共用):复位会话进度 → 发 op 0x01 进 L5 */ +static void p13_on_hello(int32_t sockfd) +{ + s_p13_hello_n = 0U; + DBG_INFO("[LAT] peer session start"); + lat_stat_reset(&s_p13_st); + s_p13_stage = 0U; + s_p13_rx_bytes = 0U; + s_p13_state = P13_ST_L5_RECV; + (void)p13_send_op(sockfd, LAT_P13_OP_L5_RECV); +} + +/* 会话重启初始化(abort 后 peer 重连再 HELLO 时调用): + * 2026-09-22 run6 实证:芯片对空闲连接自行超时发 RST(MCU 网络栈慢于 + * 芯片握手完成时,连接建立后 15~30s 内无流量即被 RST)→ peer 重连 + + * 本重启 = 闭环,避免一次慢启动毁掉整轮测试 */ +static void p13_reinit(void) +{ +#if defined(TEST_LATENCY_PROBE) + if (lat_probe_armed() != 0U) { + lat_probe_disarm(); + } +#endif + s_p13_state = P13_ST_HELLO; /* 由 p13_on_hello 覆盖为 L5_RECV */ + s_p13_stage = 0U; + s_p13_rx_bytes = 0U; + s_p13_l6rx_n = 0U; + s_p13_l7rx_n = 0U; + s_p13_step_tick = HAL_GetTick(); + s_p13_start_tick = HAL_GetTick(); /* 整体 240s 预算按重启重新计时 */ + s_p13_seg_force_fail = 0; + s_p13_verdict_all = LAT_VERDICT_OK; + s_p13_done = 0U; + s_p13_resync_done = 0U; + s_p13_clean_done = 0U; + s_p13_resync_tick = HAL_GetTick(); + s_p13_hello_n = 0U; + lat_stat_reset(&s_p13_st); +} + +/* 会话是否活动(供 net_test_task 的监听循环判断:活动期跳过 net_accept, + * 连接由芯片 CONNECT 事件建立 + 本模块 HELLO/重连轮询接管,run8 实证: + * 10ms 一次 accept 与协议消息争抢 netTask 队列并产生大量陈旧通知噪声) */ +int net_latency_phase13_busy(void) +{ +#ifdef ENABLE_PHASE13_TESTS + return (s_p13_done == 0U || s_p13_state == (uint8_t)P13_ST_ABORT) ? 1 : 0; +#else + return 0; +#endif +} + +void net_latency_phase13_run(int32_t sockfd) +{ +#ifdef ENABLE_PHASE13_TESTS + int32_t n; + uint32_t i; + uint32_t c0; + uint32_t us; + + if (s_p13_done != 0U) { + if (s_p13_state != P13_ST_ABORT) { + return; /* 正常完成/DONE:会话终态,不再处理 */ + } + /* 重同步(run7~run9 实证):peer 断连后芯片 Socket 去向不确定 + * (静默回收回监听 / RST 关闭 / 监听器丢失)→ 进 abort 时做一次 + * 分状态重同步 + 等待期周期性自愈: + * ESTABLISHED(陈旧)→ 先只修 MCU 侧状态(run8:close 会摧毁芯片 + * 侧健康监听器 → 连接永拒),再无条件芯片侧重监听(open+listen + * 含 Trap 09 重试;run9:一次性 net_listen 落 Trap 09 窗口失败 + * 且被忽略 → 监听器永久丢失); + * CLOSED → 芯片命令重新 LISTEN; + * LISTENING → 10s 无新连接则再重监听一次(自愈静默丢失的监听器; + * 重监听监听窗口中断 ~1s,peer 重连重试可吸收)。 */ + if (s_p13_resync_done == 0U) { + int st = net_sock_state(sockfd); + + s_p13_resync_done = 1U; + s_p13_resync_tick = HAL_GetTick(); + if (st == NET_SOCK_STATE_ESTABLISHED) { + DBG_INFO("[LAT] note: stale ESTABLISHED on abort -> resync + relisten"); + (void)net_resync_listen(sockfd); + (void)net_relisten(sockfd); + } else if (st == NET_SOCK_STATE_CLOSED) { + DBG_INFO("[LAT] note: CLOSED on abort -> re-listen via chip"); + (void)net_relisten(sockfd); + } + } else if (s_p13_clean_done == 0U) { + /* 周期自愈:10s 无新连接(状态仍 LISTENING)→ 再重监听一次; + * 新连接到来时状态已转 ESTABLISHED,本分支自然不再触发。 + * 干净完成(已发 0xFF、peer 正常退出)后不再自愈——否则每 10s + * 拆建一次监听器(无谓 SPI 开销 + 日志噪声 + 台架永不判空闲, + * run12 实证),此时候只需静候下一次 re-HELLO。 */ + uint32_t now = HAL_GetTick(); + if (net_sock_state(sockfd) == NET_SOCK_STATE_LISTENING && + (uint32_t)(now - s_p13_resync_tick) >= 10000U) { + s_p13_resync_tick = now; + DBG_INFO("[LAT] note: no peer in 10s -> relisten (self-heal)"); + (void)net_relisten(sockfd); + } + } + n = net_recv(sockfd, s_p13_rx, (int)sizeof(s_p13_rx), NET_MSG_DONTWAIT); + if (n > 0) { + for (i = 0U; i + 4U <= (uint32_t)n; i++) { + if (s_p13_rx[i] == 0x5AU && s_p13_rx[i + 1U] == 0xA5U && + s_p13_rx[i + 2U] == 0x58U && s_p13_rx[i + 3U] == 0x01U) { + p13_reinit(); + DBG_INFO("[LAT] peer re-HELLO -> session restart"); + p13_on_hello(sockfd); /* HELLO 已消耗 → 直接进 L5 并回 op */ + break; + } + } + } + /* 重启等待总超时(abort 后再给 120s):无 peer 则转终态,停止轮询 */ + if (s_p13_done != 0U && + (HAL_GetTick() - s_p13_start_tick) > + (uint32_t)((uint32_t)LAT_P13_TIMEOUT_MS + 120000U)) { + s_p13_state = P13_ST_DONE; + } + return; + } + + /* ---- 会话起点:时基/帧/载荷一次性准备 ---- */ + if (s_p13_state == P13_ST_HELLO && s_p13_start_tick == 0U) { + s_p13_start_tick = HAL_GetTick(); + lat_time_init(); + p13_build_frame(); + for (i = 0U; i < LAT_P13_L3_SEND_LEN; i++) { + s_p13_tx[i] = (uint8_t)(i & 0xFFU); + } + DBG_INFO("=== CH395F Phase 13 Tests (chip+queue latency, L3~L7/L10) ==="); + DBG_INFO("[LAT] run_id=%lu phase=13 cal core_clk=%lu us_per_1024cyc=%lu", + (unsigned long)HAL_GetTick(), + (unsigned long)SystemCoreClock, + (unsigned long)(((uint64_t)1024U * 1000000UL) / (uint64_t)SystemCoreClock)); + DBG_INFO("[LAT] waiting for peer HELLO (port 8080, latency_peer.py --mode net)"); + } + + if ((HAL_GetTick() - s_p13_start_tick) > LAT_P13_TIMEOUT_MS) { + p13_abort("overall timeout"); + return; + } + + switch (s_p13_state) { + + /* ---- HELLO:4 字节魔术字(容忍杂散字节:失配即从头对齐) ---- */ + case P13_ST_HELLO: { + n = net_recv(sockfd, s_p13_rx, (int)sizeof(s_p13_rx), NET_MSG_DONTWAIT); + if (n == 0) { + p13_abort("peer connection closed (EOF)"); + return; + } + if (n > 0) { + for (i = 0U; (int32_t)i < n; i++) { + uint8_t b = s_p13_rx[i]; + if (s_p13_hello_n == 0U && b != a_p13_hello[0U]) { + continue; + } + if (b != a_p13_hello[s_p13_hello_n]) { + s_p13_hello_n = 0U; + continue; + } + s_p13_hello_n++; + if (s_p13_hello_n == 4U) { + p13_on_hello(sockfd); + } + } + } + /* n < 0: DONTWAIT 空轮询(WOULDBLOCK = 暂无数据)= 正常态,继续等待 */ + break; + } + + /* ---- L5 静默基线:peer flood 50×100B,MCU 逐次 DONTWAIT 收取 ---- + * 仅对"收到数据"的调用计时(队列往返 + netTask SPI 读);空轮询不采样 */ + case P13_ST_L5_RECV: { + c0 = lat_cycles_now(); + n = net_recv(sockfd, s_p13_rx, (int)sizeof(s_p13_rx), NET_MSG_DONTWAIT); + us = lat_cycles_diff_us(c0); + if (n == 0) { + p13_abort("peer connection closed (L5 recv)"); + return; + } + if (n > 0) { + lat_stat_add(&s_p13_st, us, 0U); + s_p13_rx_bytes += (uint32_t)n; + } + /* 重复递交守卫(run1 实证缺陷 D11):芯片接收缓冲饱和后窗口 + * 关闭/重开会把重传段重复递交(run1:5000B 送达 11884B,+138%)→ + * 超容差即判 FAIL 并落 partial 报告(段内数据仍记录) */ + if (s_p13_rx_bytes > + (LAT_P13_L5_CHUNKS * LAT_P13_L5_CHUNK_LEN) + (2U * LAT_P13_RECV_LEN)) { + p13_abort("L5 duplicate delivery (CH395F TCP retransmit re-delivery)"); + return; + } + if (s_p13_rx_bytes >= (LAT_P13_L5_CHUNKS * LAT_P13_L5_CHUNK_LEN)) { + p13_report_seg("queue_rt_recv", LAT_P13_QRT_WARN_US, LAT_P13_QRT_CRIT_US, 0U); + s_p13_state = P13_ST_L5_SEND; + s_p13_stage = 0U; + lat_stat_reset(&s_p13_st); + if (p13_send_op(sockfd, LAT_P13_OP_L5_SEND) == 0U) { + p13_abort("L5->L5SEND op send failed (peer desync)"); + return; + } + } + break; + } + + /* ---- L5 静默基线:MCU 阻塞 net_send 50×100B(调用即含队列往返全程) ---- + * 阻塞调用经 xTaskNotifyWait 让出 → netTask(Normal) 自然抢占,无额外延迟 */ + case P13_ST_L5_SEND: { + while (s_p13_stage < LAT_P13_L5_CHUNKS) { + c0 = lat_cycles_now(); + n = net_send(sockfd, s_p13_tx, (int32_t)LAT_P13_L5_CHUNK_LEN, 0); + us = lat_cycles_diff_us(c0); + if (n != (int32_t)LAT_P13_L5_CHUNK_LEN) { + s_p13_seg_force_fail = 1; +#if defined(TEST_LATENCY_PROBE) + DBG_INFO("[LAT] L5 send unexpected n=%d stage=%u", + (int)n, (unsigned)s_p13_stage); +#endif + } + lat_stat_add(&s_p13_st, us, 0U); + s_p13_stage++; + } + p13_report_seg("queue_rt_send", LAT_P13_QRT_WARN_US, LAT_P13_QRT_CRIT_US, 0U); + /* run3 重排:先 L3(纯 TX flood,1s 超时保护下不挂死)+ L10(本地), + * 后 L6/L7(RX 协议段;run3 实证芯片 TCP 对小帧交换易失步 → 靠后并降样本数) */ + s_p13_state = P13_ST_L3; + s_p13_stage = 0U; + break; + } + + /* ---- L6:每样本一轮 op→10B 请求→10B 回显(MCU 侧:收齐→发出) ---- */ + case P13_ST_L6: { + n = net_recv(sockfd, s_p13_l6rx + s_p13_l6rx_n, + (int)(LAT_P13_L6_SIZE - s_p13_l6rx_n), NET_MSG_DONTWAIT); + if (n == 0) { + p13_abort("peer connection closed (L6)"); + return; + } + if (n > 0) { + s_p13_l6rx_n += (uint16_t)n; + } + if (s_p13_l6rx_n >= LAT_P13_L6_SIZE) { + c0 = lat_cycles_now(); + n = net_send(sockfd, s_p13_l6rx, (int32_t)LAT_P13_L6_SIZE, 0); /* 回显 */ + us = lat_cycles_diff_us(c0); + if (n != (int32_t)LAT_P13_L6_SIZE) { + s_p13_seg_force_fail = 1; +#if defined(TEST_LATENCY_PROBE) + DBG_INFO("[LAT] L6 echo unexpected n=%d stage=%u", + (int)n, (unsigned)s_p13_stage); +#endif + } + lat_stat_add(&s_p13_st, us, 0U); + s_p13_stage++; + if (s_p13_stage >= LAT_P13_SAMPLES) { + p13_report_seg("req2first_mcu", LAT_P13_R2F_WARN_US, LAT_P13_R2F_CRIT_US, 0U); + s_p13_state = P13_ST_L7; + s_p13_stage = 0U; + s_p13_l7rx_n = 0U; + s_p13_step_tick = HAL_GetTick(); + (void)p13_send_op(sockfd, LAT_P13_OP_L7); + } else { + s_p13_l6rx_n = 0U; + s_p13_step_tick = HAL_GetTick(); + (void)p13_send_op(sockfd, LAT_P13_OP_L6); + } + } else if ((HAL_GetTick() - s_p13_step_tick) > LAT_P13_SAMPLE_T_MS) { + p13_abort("L6 peer timeout"); + return; + } + break; + } + + /* ---- L7:9B LCDMEM 请求 → 3209B 全帧(netTask 内 4 分块停止等待) ---- */ + case P13_ST_L7: { + n = net_recv(sockfd, s_p13_l7rx + s_p13_l7rx_n, + (int)(LAT_P13_L7_REQ_LEN - s_p13_l7rx_n), NET_MSG_DONTWAIT); + if (n == 0) { + p13_abort("peer connection closed (L7)"); + return; + } + if (n > 0) { + s_p13_l7rx_n += (uint16_t)n; + } + if (s_p13_l7rx_n >= LAT_P13_L7_REQ_LEN) { + /* 校验请求帧(严格照生产解析器 ui_net_sink.c remo_handle 的语义): + * [TAG=0xAA][CMD=0x03][LEN=4 大端][偏移u32 大端=0][CRC=XOR(数据段 4B)] + * 注意 LEN 是"数据段长度"(4),非请求的位图字节数;CRC 只覆盖数据段 */ + { + uint8_t crc = 0U; + uint32_t len16; + for (i = 4U; i < 8U; i++) { + crc ^= s_p13_l7rx[i]; + } + len16 = ((uint32_t)s_p13_l7rx[2U] << 8) | (uint32_t)s_p13_l7rx[3U]; + if (s_p13_l7rx[0U] != 0xAAU || s_p13_l7rx[1U] != 0x03U || + len16 != 4U || crc != s_p13_l7rx[8U] || + s_p13_l7rx[4U] != 0U || s_p13_l7rx[5U] != 0U || + s_p13_l7rx[6U] != 0U || s_p13_l7rx[7U] != 0U) { + p13_abort("L7 bad request frame"); + return; + } + } + c0 = lat_cycles_now(); + n = net_send(sockfd, s_p13_frame, (int32_t)LAT_P13_FRAME_LEN, 0); + us = lat_cycles_diff_us(c0); + if (n != (int32_t)LAT_P13_FRAME_LEN) { + s_p13_seg_force_fail = 1; + } + lat_stat_add(&s_p13_st, us, 0U); + s_p13_stage++; + if (s_p13_stage >= LAT_P13_SAMPLES) { + p13_report_seg("fullframe_mcu", LAT_P13_FF_WARN_US, LAT_P13_FF_CRIT_US, 0U); + /* L7 为末段(run3 重排后的最终顺序:L5→L3→L10→L6→L7→DONE); + * 此处若回跳 L3 会形成 L3→L10→L6→L7 无限循环、永不下发 0xFF, + * peer 只能等 300s 超时(run11 实证) */ + s_p13_state = P13_ST_DONE; + s_p13_stage = 0U; + } else { + s_p13_l7rx_n = 0U; + s_p13_step_tick = HAL_GetTick(); + (void)p13_send_op(sockfd, LAT_P13_OP_L7); + } + } else if ((HAL_GetTick() - s_p13_step_tick) > LAT_P13_SAMPLE_T_MS) { + p13_abort("L7 peer timeout"); + return; + } + break; + } + +#if defined(TEST_LATENCY_PROBE) + /* ---- L3:探针武装 + 32×4KB 发送 flood(= 128 chunk 样本) ---- + * chunk_wait 在 netTask 内测量(写发送缓冲 → SENBUF_FREE), + * 调用方侧不可见 → 必须经 net_socket.c 探针(TEST_LATENCY_PROBE) */ + case P13_ST_L3: { + if (s_p13_stage == 0U) { + (void)p13_send_op(sockfd, LAT_P13_OP_L3); + lat_probe_arm("chunk_wait", (uint32_t)LAT_P13_L3_SAMPLES, LAT_P13_CW_SPIKE_US); + } + while (s_p13_stage < LAT_P13_L3_SENDS) { + n = net_send(sockfd, s_p13_tx, (int32_t)LAT_P13_L3_SEND_LEN, 0); + if (n != (int32_t)LAT_P13_L3_SEND_LEN) { + s_p13_seg_force_fail = 1; +#if defined(TEST_LATENCY_PROBE) + DBG_INFO("[LAT] L3 send unexpected n=%d stage=%u", + (int)n, (unsigned)s_p13_stage); +#endif + } + s_p13_stage++; + } + /* 阻塞发送均"芯片确认本块全部 chunk 完成(SEND_OK)"才返回 → 此刻探针 + * 样本已定稿;芯片 TX 失步时以短写记录(1s 超时保护,不会挂死调用方) */ + { + int v = LAT_VERDICT_OK; + const lat_stats_t *p_ps; + + lat_probe_disarm(); + p_ps = lat_probe_stats(); + if (s_p13_seg_force_fail != 0) { + v = LAT_VERDICT_FAIL; + } + lat_stat_report("chunk_wait", p_ps, LAT_P13_CW_WARN_US, + LAT_P13_CW_CRIT_US, 0U, &v); + if (s_p13_seg_force_fail != 0) { + DBG_INFO("[LAT] note: chunk_wait has send error/short write -> FAIL"); + } + if (v > s_p13_verdict_all) { + s_p13_verdict_all = v; + } + if (s_p13_seg_force_fail != 0) { + DBG_INFO("[LAT] note: chunk_wait partial n=%u/%u (chip TX stall suspected)", + (unsigned)p_ps->n, (unsigned)LAT_P13_L3_SAMPLES); + } + s_p13_seg_force_fail = 0; + s_p13_state = P13_ST_L10; + s_p13_stage = 0U; + } + break; + } +#else /* TEST_LATENCY_PROBE */ + case P13_ST_L3: { + /* 探针未编译(未选 Phase 13/14 之外的组合):跳过本段 */ + DBG_INFO("[LAT] skip chunk_wait (probe not compiled)"); + s_p13_state = P13_ST_L10; + s_p13_stage = 0U; + break; + } +#endif /* TEST_LATENCY_PROBE */ + + /* ---- L10:一行 60 字符 printf 的耗时(fputc 逐字节 @115200,D6 量化) ---- */ + case P13_ST_L10: { + static char s_line[64]; + if (s_p13_stage == 0U) { + snprintf(s_line, sizeof(s_line), "LAT logtax line: 0123456789 0123456789 012345"); + lat_stat_reset(&s_p13_st); + } + while (s_p13_stage < LAT_P13_SAMPLES) { + c0 = lat_cycles_now(); + printf("%s\n", s_line); + lat_stat_add(&s_p13_st, lat_cycles_diff_us(c0), 0U); + s_p13_stage++; + } + p13_report_seg("logtax_line", LAT_P13_LT_WARN_US, LAT_P13_LT_CRIT_US, 0U); + /* L6 前清空 socket 残留(重传/重复递交字节,run1 实证 6884B、run3 实证 9B 碎片): + * 残留会与 L6/L7 的请求帧错位混读 → 协议失步。此窗口对端静默 + * (对端只在收到操作字节后才发包),DONTWAIT 抽干即安全。 */ + { + int32_t d; + uint32_t drained = 0U; + uint32_t t0 = HAL_GetTick(); + while (drained < 65536U && (HAL_GetTick() - t0) < 5000U) { + d = net_recv(sockfd, s_p13_rx, (int)sizeof(s_p13_rx), NET_MSG_DONTWAIT); + if (d <= 0) { + break; /* d==0 = 已排空/EOF;d<0 = 空轮询(WOULDBLOCK) */ + } + drained += (uint32_t)d; + } + if (drained > 0U) { + DBG_INFO("[LAT] note: drained %u residual bytes before L6 (retransmit/duplicate)", + (unsigned)drained); + } + } + s_p13_state = P13_ST_L6; + s_p13_stage = 0U; + s_p13_l6rx_n = 0U; + s_p13_step_tick = HAL_GetTick(); + (void)p13_send_op(sockfd, LAT_P13_OP_L6); + break; + } + + case P13_ST_DONE: { + const char *p_name; + + (void)p13_send_op(sockfd, LAT_P13_OP_DONE); + p_name = (s_p13_verdict_all == LAT_VERDICT_OK) ? "OK" + : ((s_p13_verdict_all == LAT_VERDICT_WARN) ? "WARN" : "FAIL"); + DBG_INFO("[LAT] summary verdict=%s", p_name); + TEST_CHECK(s_p13_verdict_all != LAT_VERDICT_FAIL, + "P13 latency baseline (verdict=%s)", p_name); + DBG_INFO("[LAT] report complete"); + TEST_REPORT("Phase 13"); + s_p13_clean_done = 1U; /* 已下发 0xFF:peer 正常收尾,停止周期自愈 */ + s_p13_done = 1U; + s_p13_state = P13_ST_ABORT; + break; + } + + default: + break; + } +#else /* ENABLE_PHASE13_TESTS */ + (void)sockfd; +#endif /* ENABLE_PHASE13_TESTS */ +} + +#endif /* ENABLE_PHASE13_TESTS */ + +#ifdef ENABLE_PHASE14_TESTS + +/* ==================================================================== + * Phase 14 — 生产路径时延(L8~L11,M3) + * + * 与 Phase 13 的关键区别:**不占用测试 socket**,测的是真实生产路径 + * (uiTask + ui_net_sink :7003 + 真实 LVGL 取帧),由 PC 端 + * `latency_peer.py --mode remo --port 7003` 驱动。 + * + * 探针是全局单窗口 → 三个窗口必须串行: + * ui_loop uiTask 主循环实测周期(本地,不需要 peer,120 样本) + * prod_cycle_mcu ui_net_sink 处理一帧 LCDMEM 的 MCU 净成本 + * (取 1bpp 位图 + 组帧 + net_send 队列/芯片;不含 10ms 拾取拍) + * relisten peer 断开后自动重监听耗时(量化 D10:Trap 09 重试含 osDelay(500)) + * + * 端口分工:peer 走 :7003(生产 socket,uiTask 持有),本 Phase 不碰 :8080。 + * 段阈值(首轮 M3 实测后按 §4.2 重锁): + * ui_loop warn 25ms / crit 50ms + * prod_cycle warn 20ms / crit 200ms + * relisten warn 50ms / crit 500ms(spike ≥500ms = D10 指纹) + * ==================================================================== */ + +#define LAT_P14_UI_SAMPLES 120U +#define LAT_P14_PROD_SAMPLES 50U +#define LAT_P14_REL_SAMPLES 3U /* = peer --reconnect-rounds(每次 close 触发一次 auto-relisten) */ +#define LAT_P14_UI_WIN_MS 20000U +#define LAT_P14_PROD_WIN_MS 90000U +#define LAT_P14_REL_WIN_MS 60000U +#define LAT_P14_TIMEOUT_MS 240000U + +#define LAT_P14_UI_WARN_US 25000U +#define LAT_P14_UI_CRIT_US 50000U +#define LAT_P14_PROD_WARN_US 20000U +#define LAT_P14_PROD_CRIT_US 200000U +#define LAT_P14_REL_WARN_US 100000U /* D10 修复后实测 89ms(重试间隔 30ms + 芯片沉降); + * 原 50ms 阈值对应"旧实现 588ms"已不适用 */ +#define LAT_P14_REL_CRIT_US 500000U + +enum { + P14_ST_UI_LOOP = 0, + P14_ST_PROD, + P14_ST_RELISTEN, + P14_ST_DONE +}; + +static uint8_t s_p14_done = 0U; +static uint8_t s_p14_banner = 0U; +#if defined(TEST_LATENCY_PROBE) +static uint8_t s_p14_state = (uint8_t)P14_ST_UI_LOOP; +static uint8_t s_p14_win_open = 0U; +static int s_p14_verdict_all = LAT_VERDICT_OK; +static uint32_t s_p14_start_tick; +static uint32_t s_p14_win_tick; +#endif + +int net_latency_phase14_busy(void) +{ + return (s_p14_done == 0U) ? 1 : 0; +} + +#if defined(TEST_LATENCY_PROBE) +/* 收尾一个窗口:disarm → 取统计 → 报告 → verdict 累计 */ +static void p14_close_seg(const char *p_seg, uint32_t warn_us, uint32_t crit_us, + uint16_t max_spikes) +{ + const lat_stats_t *p_st; + int v = LAT_VERDICT_OK; + + lat_probe_disarm(); + p_st = lat_probe_stats(); + if (p_st->n == 0U) { + DBG_INFO("[LAT] note: %s window got 0 samples (peer missing/timeout)", p_seg); + v = LAT_VERDICT_FAIL; + } else if (p_st->med == 0U) { + /* 中位数为 0 = 计时器未使能/探针未生效(真实处理不可能 0µs)。 + * 2026-09-22 run16 实证:本 Phase 曾漏调 lat_time_init() → DWT CYCCNT + * 恒 0 → 三段全 0 却输出"verdict=OK"的假绿 → 必须判无效。 */ + DBG_ERROR("[LAT] note: %s med=0us -> probe timer not armed (result invalid)", p_seg); + v = LAT_VERDICT_FAIL; + } else { + lat_stat_report(p_seg, p_st, warn_us, crit_us, max_spikes, &v); + } + if (v > s_p14_verdict_all) { + s_p14_verdict_all = v; + } +} +#endif /* TEST_LATENCY_PROBE */ + +void net_latency_phase14_run(void) +{ +#if defined(TEST_LATENCY_PROBE) + if (s_p14_done != 0U) { + return; + } + + /* 一次性横幅 + 操作提示 */ + if (s_p14_banner == 0U) { + s_p14_banner = 1U; + s_p14_start_tick = HAL_GetTick(); + lat_time_init(); /* DWT CYCCNT 使能(漏调则所有样本恒 0) */ + DBG_INFO("=== CH395F Phase 14 Tests (prod-path latency, L8~L11) ==="); + DBG_INFO("[LAT] run_id=%lu phase=14 cal core_clk=%lu us_per_1024cyc=%lu", + (unsigned long)s_p14_start_tick, + (unsigned long)SystemCoreClock, + (unsigned long)(((uint64_t)1024U * 1000000UL) / (uint64_t)SystemCoreClock)); + DBG_INFO("[LAT] prod path: uiTask + ui_net_sink :7003 " + "(run latency_peer.py --mode remo --port 7003)"); + } + + /* 整体超时保护:避免 peer 缺席时无限等窗口 */ + if ((HAL_GetTick() - s_p14_start_tick) > LAT_P14_TIMEOUT_MS) { + DBG_ERROR("[LAT] phase14 abort: overall timeout"); + s_p14_done = 1U; + return; + } + + switch (s_p14_state) { + case P14_ST_UI_LOOP: + if (s_p14_win_open == 0U) { + lat_probe_arm("ui_loop", LAT_P14_UI_SAMPLES, 0U); + s_p14_win_tick = HAL_GetTick(); + s_p14_win_open = 1U; + break; + } + if ((lat_probe_count() >= LAT_P14_UI_SAMPLES) || + ((HAL_GetTick() - s_p14_win_tick) > LAT_P14_UI_WIN_MS)) { + p14_close_seg("ui_loop", LAT_P14_UI_WARN_US, LAT_P14_UI_CRIT_US, 0U); + s_p14_state = (uint8_t)P14_ST_PROD; + s_p14_win_open = 0U; + } + break; + + case P14_ST_PROD: + if (s_p14_win_open == 0U) { + lat_probe_arm("prod_cycle_mcu", LAT_P14_PROD_SAMPLES, 500000U); + s_p14_win_tick = HAL_GetTick(); + s_p14_win_open = 1U; + break; + } + if ((lat_probe_count() >= LAT_P14_PROD_SAMPLES) || + ((HAL_GetTick() - s_p14_win_tick) > LAT_P14_PROD_WIN_MS)) { + p14_close_seg("prod_cycle_mcu", LAT_P14_PROD_WARN_US, LAT_P14_PROD_CRIT_US, 0U); + s_p14_state = (uint8_t)P14_ST_RELISTEN; + s_p14_win_open = 0U; + } + break; + + case P14_ST_RELISTEN: + if (s_p14_win_open == 0U) { + lat_probe_arm("relisten", LAT_P14_REL_SAMPLES, LAT_P14_REL_CRIT_US); + s_p14_win_tick = HAL_GetTick(); + s_p14_win_open = 1U; + break; + } + if ((lat_probe_count() >= LAT_P14_REL_SAMPLES) || + ((HAL_GetTick() - s_p14_win_tick) > LAT_P14_REL_WIN_MS)) { + p14_close_seg("relisten", LAT_P14_REL_WARN_US, LAT_P14_REL_CRIT_US, 1U); + s_p14_state = (uint8_t)P14_ST_DONE; + s_p14_win_open = 0U; + } + break; + + case P14_ST_DONE: + default: { + const char *p_name = (s_p14_verdict_all == LAT_VERDICT_OK) ? "OK" + : ((s_p14_verdict_all == LAT_VERDICT_WARN) ? "WARN" : "FAIL"); + + DBG_INFO("[LAT] summary verdict=%s", p_name); + TEST_CHECK(s_p14_verdict_all != LAT_VERDICT_FAIL, + "P14 prod-path latency baseline (verdict=%s)", p_name); + DBG_INFO("[LAT] report complete"); + TEST_REPORT("Phase 14"); + s_p14_done = 1U; + break; + } + } +#else /* TEST_LATENCY_PROBE */ + if (s_p14_banner == 0U) { + s_p14_banner = 1U; + DBG_INFO("[LAT] phase14 skipped: TEST_LATENCY_PROBE not compiled"); + s_p14_done = 1U; + } +#endif /* TEST_LATENCY_PROBE */ +} + +#endif /* ENABLE_PHASE14_TESTS */ + +#endif /* ENABLE_PHASE12/13/14 任一使能 */ diff --git a/test/net_latency_test.h b/test/net_latency_test.h new file mode 100644 index 00000000..ff5471f1 --- /dev/null +++ b/test/net_latency_test.h @@ -0,0 +1,228 @@ +/****************************************************************************** + * 模块名称:CH395F 链路延迟测试 + * 模块功能:链路分段时延测量的微秒时基(DWT CYCCNT)、采样统计 + * (min/med/p95/max + 尖峰计数)、机器可读 [LAT] 报告输出与判定。 + * Phase 12(硬件层,M1):SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC); + * Phase 13/14(NET 层,M2/M3 规划):芯片发送握手 / 队列往返 / + * 生产路径时延。分段定义、判定基线与执行流程见 + * docs/测试说明/链路延迟测试说明.md。 + * 适用平台:STM32F4 系列(CH395F) + * 作者:王建锋 + * 创建日期:2026-09-22 + * 修改记录: + * 2026-09-22 王建锋 初版:时基 + 统计/报告 + Phase 12(L1/L2 SPI 时延基线) + ******************************************************************************/ + +#ifndef NET_LATENCY_TEST_H +#define NET_LATENCY_TEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * === 链路时延测试阶段一览(见 链路延迟测试说明.md §2.2) === + * Phase 12 [硬件层] SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC) —— M1(已完成) + * Phase 13 [NET 层] 芯片 + 队列时延(L3~L7/L10,需 PC peer) —— M2(已实现) + * Phase 14 [NET 层] 生产路径时延(L8~L11 + 模拟器 A/B,需 PC peer)—— M3(规划) + * + * 时基说明:DWT CYCCNT 为 32 位,168MHz 下回绕周期 25.17ms。 + * M1 各段(<5ms)与 M2 调用方侧测量(单次 <25ms)用单次读数差即可; + * 跨 25ms 的段(L4/L8 整帧与重传尖峰)由 PC 端 peer 计时。 + * + * Phase 13 协议(服务器驱动:MCU 发操作字节,peer 执行;详见说明文档 §2.5): + * HELLO = 5A A5 58 01(peer → MCU,4 字节) + * 操作字节(MCU → peer):0x01=L5 recv flood / 0x02=L5 send flood / + * 0x03=L6 单请求 / 0x04=L7 LCDMEM 全帧 / 0x05=L3 send flood / 0xFF=完成 + * Phase 13 需**独立运行**(同 Phase 4 约定):与其他 Phase 共用 8080 端口 + * 的 socket,混跑会污染帧测量。 + */ + +/* verdict 三态(lat_stat_report 输出,调用方据此汇总) */ +#define LAT_VERDICT_OK 0 +#define LAT_VERDICT_WARN 1 +#define LAT_VERDICT_FAIL 2 + +/* + * 采样保存上限:静态数组容量(自研代码禁用 C 库堆,见 AGENTS.md)。 + * n 可超过该值(min/max/spikes 仍对全量累计;med/p95 按已保存样本计算)。 + */ +#define LAT_MAX_SAMPLES 128 + +/* + * 采样统计容器。 + * n - 总样本数(可 > LAT_MAX_SAMPLES) + * mn/mx - 全量 min/max(us) + * med - 中位数(us,lat_stat_finish 后有效) + * p95 - 95 分位(us,lat_stat_finish 后有效) + * spikes - 达到尖峰阈值的样本数(spike_th_us=0 表示本段不统计) + */ +typedef struct { + uint16_t n; + uint32_t mn; + uint32_t med; + uint32_t p95; + uint32_t mx; + uint16_t spikes; +} lat_stats_t; + +/* + * 函数功能:使能 DWT CYCCNT 周期计数器时基(幂等) + * 入口参数:无 + * 返回值:无 + * 限定条件:任何测量前调用一次即可;仅测试构建路径会执行(产品构建不调用) + * 函数说明:CoreDebug->DEMCR(TRCENA) + DWT->CYCCNT 清零 + CYCCNTENA 置位 + */ +void lat_time_init(void); + +/* + * 函数功能:重置统计容器(mn 置 0xFFFFFFFF,空集可辨识) + * 入口参数:p - 统计容器 + */ +void lat_stat_reset(lat_stats_t *p); + +/* + * 函数功能:追加一个时延样本(微秒) + * 入口参数:p - 统计容器 + * us - 本样本测量时长(微秒) + * spike_th_us - 尖峰阈值(us);0 表示本段不统计尖峰 + */ +void lat_stat_add(lat_stats_t *p, uint32_t us, uint32_t spike_th_us); + +/* + * 函数功能:采样结束后排序计算 med/p95(内部静态工作区,零堆) + * 入口参数:p - 统计容器(lat_stat_add 全部追加完成后调用) + */ +void lat_stat_finish(lat_stats_t *p); + +/* + * 函数功能:输出 [LAT] 报告行并给出本段 verdict + * 入口参数:p_seg - 段名(纯 ASCII 标识,如 "spi_dma_1024") + * p - 统计容器(须先 lat_stat_finish) + * warn_med_us - WARN 阈值:med 超过 → WARN(重锁阈值提示,计 PASS) + * fail_med_us - FAIL 阈值:med 超过 → FAIL + * max_spikes - 尖峰数上限:spikes 超过 → FAIL + * p_verdict - 输出:本段 verdict(LAT_VERDICT_*) + * 返回值:无 + * 函数说明:报告行格式见 链路延迟测试说明.md §2.4;字符串字面量纯 ASCII + */ +void lat_stat_report(const char *p_seg, const lat_stats_t *p, + uint32_t warn_med_us, uint32_t fail_med_us, + uint16_t max_spikes, int *p_verdict); + +/* + * 函数功能:Phase 12 入口(硬件层:SPI 字节命令 / DMA 时延基线,L1/L2,无需 PC) + * 入口参数:无 + * 返回值:无 + * 限定条件:由 ch395f_test_run() 硬件层分支调用(net_stack_bring_up 完成后 + * testTask 独占 CH395F,无 SPI 并发);仅台架执行 + * 函数说明:内部先做 check_exist 存活自检,失败即判 FAIL 并跳过; + * 套件未选中本 Phase 时函数体为空(产品构建可编译、不执行) + */ +void net_latency_phase12_run(void); + +/* + * 函数功能:Phase 13 入口(NET 层:芯片 + 队列时延 L3~L7/L10,需 PC peer) + * 入口参数:sockfd - 测试监听 socket(连接后的数据通道 fd,8080 端口) + * 返回值:无 + * 限定条件:由 net_layer_test_main() 监听循环每 ~10ms 调用一次(事件驱动 + * 状态机);需 test/latency_peer.py --mode net 配合;须独立运行 + * 函数说明:服务器驱动协议(见文件头协议注释);会话完成后 s_done 置位, + * 后续调用为空操作;套件未选中本 Phase 时函数体为空 + */ +void net_latency_phase13_run(int32_t sockfd); + +/* + * 函数功能:Phase 13 会话是否活动(HELLO 等待/进行中/abort 重连等待) + * 返回值:1 - 活动(监听循环应跳过 net_accept,由本模块轮询接管) + * 0 - 非活动(正常完成/终态,恢复常规 accept 等待) + * 限定条件:Phase 13 未启用时恒返回 0 + */ +int net_latency_phase13_busy(void); + +/* + * 函数功能:Phase 14 入口(NET 层:生产路径时延 L8~L11,真实 uiTask + :7003) + * 入口参数:无(本 Phase 不占用测试 socket,测量生产 socket 的服务端行为) + * 返回值:无 + * 限定条件:由 net_layer_test_main() 循环每 ~10ms 调用一次;需 + * test/latency_peer.py --mode remo --port 7003 配合;须单独使能 + * (与 Phase 13 同启时两者都会跑,M3 台架建议只开 Phase 14) + * 函数说明:状态机顺序 = ui_loop(本地节拍窗口)→ prod_cycle_mcu(等 peer + * 发满 N 帧 LCDMEM)→ relisten(等 peer 断开重连)→ DONE; + * 各窗口通过 TEST_LATENCY_PROBE 采集(uiTask/netTask 侧打点); + * 未定义 TEST_LATENCY_PROBE 时只打印跳过说明;套件未选中时函数体为空 + */ +void net_latency_phase14_run(void); + +/* + * 函数功能:Phase 14 是否活动(活动期监听循环应让位,保证 10ms 轮询节拍) + * 返回值:1 - 活动;0 - 非活动/未启用 + */ +int net_latency_phase14_busy(void); + +/* + * === TEST_LATENCY_PROBE 探针接口(M2/M3)=== + * + * 用途:netTask 上下文(net_socket.c 发送分块循环)向 testTask 报告的 + * 段内打点。单写者(netTask)/单读者(testTask),32 位变量原子 + * 读写,无锁。仅在 TEST_LATENCY_PROBE 定义(测试构建)时编译。 + */ + +/* 当前是否处于采样窗口(netTask 侧高频查询,须极廉价) */ +uint8_t lat_probe_armed(void); + +/* testTask 侧开一个采样窗口:seg 段名、max_n 样本上限、spike_th_us 尖峰阈值 */ +void lat_probe_arm(const char *seg, uint32_t max_n, uint32_t spike_th_us); + +/* netTask 侧追加一个样本(us);未武装或已满 → 无操作 */ +void lat_probe_add(uint32_t us); + +/* + * 函数功能:**按段名**追加样本(多打点站点共存时唯一安全的写法) + * 入口参数:seg - 本打点站点所属段名(须与 lat_probe_arm 的 seg 完全一致) + * us - 样本值(us) + * 返回值:无 + * 函数说明:探针是**全局单窗口**:窗口武装成 A 段时,B 段站点的 lat_probe_add + * 会把 B 的样本灌进 A 的窗口(2026-09-22 run17 实证:Phase 14 三段 + * 全被 uiTask 循环样本填满,prod/relisten 都读出 ~11ms 的 ui_loop 值)。 + * 只有段名匹配才落样本 → 各站点互不污染。多站点构建**一律**用本接口。 + */ +void lat_probe_add_seg(const char *seg, uint32_t us); + +/* 已采集样本数(32 位原子读) */ +uint32_t lat_probe_count(void); + +/* 关闭采样窗口(此后的 lat_probe_add 无操作;在飞样本最多超 1 个) */ +void lat_probe_disarm(void); + +/* testTask 侧取窗口统计(disarm 后调用;内部 lat_stat_finish 已含 med/p95) */ +const lat_stats_t *lat_probe_stats(void); + +/* + * 函数功能:DWT CYCCNT 读数 / 差值换算(供 net_socket.c 探针使用) + * 入口参数:c_start - 起始读数(lat_cycles_now 返回值) + * 返回值:当前读数(us 计数域)/ 自 c_start 起的微秒数(<25.17ms 有效) + */ +uint32_t lat_cycles_now(void); +uint32_t lat_cycles_diff_us(uint32_t c_start); + +/* ---- M3 探针计时标记(可跨 >25ms 区间的安全计时) ---- + * DWT CYCCNT 为 32 位 @168MHz → 单次差值超过 **25.17ms 即回绕**,会把 30ms 读成 + * 4.8ms 这类"静默错误小值"(M1 各段均 <25ms 时无此问题,M3 的压力档/重监听会超)。 + * lat_mark/lat_mark_us 同时记 DWT 与 HAL tick,跨度 ≥20ms 时自动改用 tick 兜底 + * (分辨率降为 1ms,量级正确);<20ms 用 DWT(亚微秒精度)。 */ +typedef struct { + uint32_t cyc; /* DWT->CYCCNT 起始值 */ + uint32_t tick; /* HAL_GetTick() 起始值 */ +} lat_tmark_t; + +void lat_mark(lat_tmark_t *p); +uint32_t lat_mark_us(const lat_tmark_t *p); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_LATENCY_TEST_H */ diff --git a/test/net_test_task.c b/test/net_test_task.c index 8ce92346..556fcaa6 100644 --- a/test/net_test_task.c +++ b/test/net_test_task.c @@ -16,6 +16,7 @@ #include "ch395f_test.h" #include "ch395f_test_task.h" #include "net_test_task.h" +#include "net_latency_test.h" #define DBG_TAG "[NET_TEST]" #include "dbg_log.h" @@ -867,7 +868,14 @@ void net_layer_test_main(void) * 断开后自动重新 listen。 */ for (;;) { - if (!phase_active) { + if (!phase_active +#ifdef ENABLE_PHASE13_TESTS + && net_latency_phase13_busy() == 0 +#endif +#ifdef ENABLE_PHASE14_TESTS + && net_latency_phase14_busy() == 0 +#endif + ) { int32_t acc = net_accept(sockfd, (struct net_sockaddr *)&client_addr, &addrlen); if (acc < 0) { if (accept_retry % 100 == 0) { @@ -907,6 +915,18 @@ void net_layer_test_main(void) #ifdef ENABLE_PHASE11_TESTS phase11_run(sockfd); #endif +#ifdef ENABLE_PHASE13_TESTS + /* Phase 13(链路时延 L3~L7/L10,M2):独立运行,勿与其他 Phase 混选; + * 注意此处**不**重置 phase_active —— 保持 10ms 节拍(时延测量对 + * 循环周期敏感,Phase 8 式 accept 重入的 100ms 退避会污染样本) */ + net_latency_phase13_run(sockfd); +#endif +#ifdef ENABLE_PHASE14_TESTS + /* Phase 14(生产路径时延 L8~L11,M3):不占测试 socket —— 测量真实 + * uiTask + ui_net_sink(:7003) 生产路径,由 peer --mode remo 驱动; + * 本调用只是探针窗口状态机(arm/disarm/report),同样保持 10ms 节拍 */ + net_latency_phase14_run(); +#endif /* * 单连接模式:连接断开由 net 层自动重监听(auto_relisten), diff --git a/test/test_config.h b/test/test_config.h index 3282ed89..560b5bc9 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -22,8 +22,13 @@ extern "C" { #include -/* === 选择当前构建的测试套件(三选一,或都注释 = 正常产品构建)=== */ -//#define TEST_SUITE_CH395F +/* === 选择当前构建的测试套件(三选一,或都注释 = 正常产品构建)=== + * 2026-09-22:M2(链路时延 Phase 13,NET 层,需 PC peer)交付 —— + * 当前启用 CH395F 套件 + Phase 13(独立运行,勿与其他 Phase 混选)。 + * 执行:先起 test/latency_peer.py --mode net --port 8080,再 @test 烧录。 + * 恢复产品构建 = 注释掉下面两行(TEST_SUITE_CH395F 与 ch395f_test.h 的 + * ENABLE_PHASE13_TESTS)。详见 docs/测试说明/链路延迟测试说明.md §3.2。 */ +#define TEST_SUITE_CH395F //#define TEST_SUITE_STORAGE //#define TEST_SUITE_GD5F //#define TEST_SUITE_RTC @@ -91,6 +96,16 @@ extern "C" { #define TEST_SUITE_NAME "NONE" #endif +/* + * 链路时延探针(M2/M3):在 net_socket.c 发送分块循环内定义 L3(chunk_wait) + * 测量钩子(netTask 上下文);产品构建未定义 → 钩子代码编译消失(零开销)。 + * 依赖 ch395f_test.h 的 Phase 宏(上方 TEST_SUITE_CH395F 块内已包含)。 + */ +#if defined(TEST_SUITE_CH395F) && \ + (defined(ENABLE_PHASE13_TESTS) || defined(ENABLE_PHASE14_TESTS)) +#define TEST_LATENCY_PROBE 1 +#endif + #ifdef __cplusplus } #endif diff --git a/待处理内容.md b/待处理内容.md deleted file mode 100644 index 5b9bcfd9..00000000 --- a/待处理内容.md +++ /dev/null @@ -1,3 +0,0 @@ -* 严格的内存使用情况排查,现在所有任务的内存都是往大的给的,导致内存不够用,后面要仔细排查内存的预算情况。 -* [2026-09-15 实测] 静态 RAM 预算:scatter 仅映射 **128KB**(`0x20000000`,芯片 192KB 的上部 64KB 未映射——克隆芯片兼容性决策,勿擅自扩);产品构建静态镜像 **123.6KB**(RW 632B + ZI 125952B,含 FreeRTOS 堆),余量仅 **4.4KB** → 测试固件溢出:CH395F HW 全阶段固件超 8.7KB(PHASE5 三缓冲 12.3KB)、NET 层固件超 ~28KB(`s_rx_buf` 32KB),`L6406E` 构建实测(审计清单 P1-10)。跑 CH395F 套件前须先解决 RAM 预算(全量实测新基线 + 测试缓冲瘦身 + 产品镜像压缩)。**[2026-09-18 P0-1 后新基线]** 产品镜像 RW 624B + ZI 125440B = **126064B(123.1KB)**,余量 **5008B(4.9KB)**——比 09-15 基线多 512B,来源是配置引擎弃用 C 库 `malloc` 后 MicroLIB 的 512B `HEAP` 段被链接器整段移除(map:`Removing startup_stm32f407xx.o(HEAP), (512 bytes)`);两处测试固件溢出量随之各减约 0.5KB,结论不变。 -* [2026-09-21 已解决] FTP 服务(端口 21)不监听:根因是就绪屏障位被消费(ftpTask 的 WaitAll 永不成立),已由 `1abb3d2` 修复;复测端口 21 返回 `220 Service ready`,上传/下载正常。