refactor(编号): 链路时延段号 L1~L11 → 权威 ASCII 段名(含 C 宏名与 peer 变量)
批次六余账之一。批次 3b 当时只改了 net_latency_test.c 的日志串与注释,本笔收掉剩下 的:标识符、peer 脚本、其余四个 board 文件。 - 段名映射(权威表在 scripts/archive/id_gate/编号治理历史.md §10):SPI_CMD / SPI_DMA / CHUNK_WAIT / TO_PC / QUEUE_RT / ECHO_RT / FRAME_RT / PROD_CYCLE / RELISTEN / LOGTAX / UI_LOOP。C 侧纯 ASCII,文档侧沿用中文段名。 - C 标识符 29 条规则 86 token:LAT_P13_OP_L5_RECV → LAT_P13_OP_QUEUE_RT_RECV、 P13_ST_L3 → P13_ST_CHUNK_WAIT、LAT_P13_L3_SAMPLES → LAT_P13_CHUNK_WAIT_SAMPLES, 并把文件内既有的 CW / QRT / LT 三个缩写前缀一并展开成同一套词,不留两套词汇。 - REQ2FIRST / FULLFRAME **不并入** FRAME_RT:它们是整帧往返段内两个不同测量 (报告标签本就叫 req2first_mcu / fullframe_mcu),并入会撞名并丢掉区分度。 - peer(test/host/latency_peer.py)42 条标识符 + 7 个小写局部量 + 操作表整表重排。 **JSON 输出键 l5_recv / seg_l6 / frames_l7 等 48 token 刻意不改**——它们是 docs/测试说明/m2_data/ 五份冻结实测数据的 schema,改名等于让代码与归档证据 漂移;边界与理由写进归档 §10.3,是**有记录的例外**不是漏网。 - 注释 23 处;顺带修一处上一批路径残留:peer 被写成 test/latency_peer.py,实际 在 test/host/ 下,三处注释改对。 - 站点:test/board 111 + Drivers/BSP/CH395F/ch395f.h 1 + peer 46(无词边界口径)。 - 零代码生成变化:@build 改前/改后各一次,均 0 错 0 警且 Program Size 完全相同 (Code=236456 RO=28124 RW=1876 ZI=137084)。被改名的标识符全落在 ENABLE_PHASE13_TESTS 区间内 → 另开 TEST_SUITE_CH395F + PHASE13 组合编一次 (0 错 0 警),套件开关事后无条件还原、sha256 与还原前一致,再重建产品构建。 - 等价证明:剥注释 + 套登记表后与 HEAD 逐字节相同(not_equivalent=0)。 - 禁改一字未动:console_log 的 "L1\r\n"(测试数据)、lftpd 与 ftp_capture 的 "215 UNIX Type: L8"(FTP 应答断言)。
This commit is contained in:
+58
-58
@@ -6,14 +6,14 @@ byte; the peer executes the corresponding traffic pattern. See
|
||||
docs/测试说明/链路延迟测试说明.md §4.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
|
||||
0x01 QUEUE_RT recv flood : peer sends 50 x 100B (5ms apart)
|
||||
0x02 QUEUE_RT send drain : peer drains 50 x 100B
|
||||
0x03 ECHO_RT : peer sends 10B request, expects 10B echo (timed)
|
||||
0x04 FRAME_RT : peer sends 9B LCDMEM request, expects 3209B frame
|
||||
0x05 CHUNK_WAIT send drain : peer drains 32 x 4096B
|
||||
0xFF done : session complete
|
||||
|
||||
The peer records host-side (L4-side) timing: first byte / frame total /
|
||||
The peer records host-side (TO_PC-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.
|
||||
|
||||
@@ -33,22 +33,22 @@ import time
|
||||
|
||||
# --- Protocol constants (mirror test/board/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_QUEUE_RT_RECV = 0x01
|
||||
OP_QUEUE_RT_SEND = 0x02
|
||||
OP_ECHO_RT = 0x03
|
||||
OP_FRAME_RT = 0x04
|
||||
OP_CHUNK_WAIT = 0x05
|
||||
OP_DONE = 0xFF
|
||||
|
||||
L5_CHUNKS = 20 # 2026-09-22 run1: 50 chunks saturated the chip RX buffer
|
||||
QUEUE_RT_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 §6.2)
|
||||
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
|
||||
QUEUE_RT_CHUNK_LEN = 100
|
||||
ECHO_RT_SIZE = 10
|
||||
FRAME_RT_REQ_LEN = 9
|
||||
FRAME_RT_FRAME_LEN = 3209
|
||||
CHUNK_WAIT_SENDS = 16 # mirror firmware LAT_P13_CHUNK_WAIT_SENDS (run3: 32->16, 64 chunk samples)
|
||||
CHUNK_WAIT_LEN = 4096
|
||||
|
||||
SPIKE_MS = 500.0 # retransmission granularity fingerprint (D1)
|
||||
|
||||
@@ -89,7 +89,7 @@ def recv_exact(sock, n, deadline_ns):
|
||||
return bytes(buf)
|
||||
|
||||
|
||||
def l7_request(seq=0):
|
||||
def frame_rt_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
|
||||
@@ -101,11 +101,11 @@ def l7_request(seq=0):
|
||||
return bytes([0xAA, 0x03, 0x00, 0x04]) + data + bytes([crc])
|
||||
|
||||
|
||||
def l7_frame_ok(data):
|
||||
def frame_rt_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:
|
||||
if len(data) != FRAME_RT_FRAME_LEN:
|
||||
return False
|
||||
if data[0] != 0xBB or data[1] != 0x03 or data[2] != 0x0C or data[3] != 0x84:
|
||||
return False
|
||||
@@ -239,7 +239,7 @@ def remo_bitmap_nontrivial(payload):
|
||||
|
||||
|
||||
class RemoPeer:
|
||||
"""Production-path peer (M3): drives L8/L9 against :7003 (device) or the
|
||||
"""Production-path peer (M3): drives PROD_CYCLE/RELISTEN 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,
|
||||
@@ -312,7 +312,7 @@ class RemoPeer:
|
||||
"""Wait for the stress window to open, then hold an idle FTP login:
|
||||
lftpd read_line -> blocking net_recv(0) inside netTask (old 30s default
|
||||
budget NET_RECV_TIMEOUT_MS, removed in self-heal phase 1.5; today the wait is
|
||||
an L3 pending recv -> this scenario must NOT freeze the queue anymore)
|
||||
a CHUNK_WAIT pending recv -> this scenario must NOT freeze the queue anymore)
|
||||
-> every queued message (incl. the LCDMEM response) waits behind it."""
|
||||
if not start_evt.wait(120.0):
|
||||
sys.stdout.write("[peer] ftp stress: window never opened\n")
|
||||
@@ -405,7 +405,7 @@ class RemoPeer:
|
||||
|
||||
self.dump_json()
|
||||
|
||||
# --- L9: close + reconnect rounds (each close drives the device auto-relisten
|
||||
# --- RELISTEN: 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()
|
||||
@@ -579,7 +579,7 @@ class Peer:
|
||||
if not d:
|
||||
break
|
||||
reply += d
|
||||
start_evt.set() # L5 window starts under stress
|
||||
start_evt.set() # QUEUE_RT 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:
|
||||
@@ -661,19 +661,19 @@ class Peer:
|
||||
self.connect_with_retry()
|
||||
print("[peer] connected at %s" % iso_now())
|
||||
|
||||
l5_start_evt = threading.Event()
|
||||
l5_stop_evt = threading.Event()
|
||||
queue_rt_start_evt = threading.Event()
|
||||
queue_rt_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)
|
||||
args=(queue_rt_start_evt, queue_rt_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
|
||||
queue_rt_window_open = False
|
||||
queue_rt_t0 = 0
|
||||
chunk_wait_t0 = 0
|
||||
while True:
|
||||
op = recv_exact(self.sock, 1, t0_ns + 300 * 1000 * 1000000)
|
||||
op = op[0]
|
||||
@@ -682,29 +682,29 @@ class Peer:
|
||||
print("[peer] op=done after %d ops" % self.n_ops)
|
||||
break
|
||||
|
||||
if op == OP_L5_RECV:
|
||||
if op == OP_QUEUE_RT_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))
|
||||
queue_rt_start_evt.wait(60.0) # start window only under full stress
|
||||
queue_rt_window_open = True
|
||||
queue_rt_t0 = now_ns()
|
||||
for i in range(QUEUE_RT_CHUNKS):
|
||||
self.sock.sendall(pattern(QUEUE_RT_CHUNK_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()
|
||||
self.reports["l5_recv"]["window_ms"] = (now_ns() - queue_rt_t0) / 1e6
|
||||
self.reports["l5_recv"]["bytes"] = QUEUE_RT_CHUNKS * QUEUE_RT_CHUNK_LEN
|
||||
queue_rt_stop_evt.set()
|
||||
|
||||
elif op == OP_L5_SEND:
|
||||
elif op == OP_QUEUE_RT_SEND:
|
||||
t = now_ns()
|
||||
data = recv_exact(self.sock, L5_CHUNKS * L5_LEN, t + 120 * 1000 * 1000000)
|
||||
data = recv_exact(self.sock, QUEUE_RT_CHUNKS * QUEUE_RT_CHUNK_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:
|
||||
elif op == OP_ECHO_RT:
|
||||
t0 = now_ns()
|
||||
req = pattern(L6_SIZE, seed=self.n_ops)
|
||||
req = pattern(ECHO_RT_SIZE, seed=self.n_ops)
|
||||
self.sock.sendall(req)
|
||||
resp = recv_exact(self.sock, L6_SIZE, t0 + 40 * 1000 * 1000000)
|
||||
resp = recv_exact(self.sock, ECHO_RT_SIZE, t0 + 40 * 1000 * 1000000)
|
||||
ms = (now_ns() - t0) / 1e6
|
||||
self.reports["l6"]["samples"].append(ms)
|
||||
if resp == req:
|
||||
@@ -712,20 +712,20 @@ class Peer:
|
||||
else:
|
||||
self.reports["l6"]["echo_fail"] += 1
|
||||
|
||||
elif op == OP_L7:
|
||||
elif op == OP_FRAME_RT:
|
||||
t_send = now_ns()
|
||||
self.sock.sendall(l7_request(self.n_ops))
|
||||
self.sock.sendall(frame_rt_request(self.n_ops))
|
||||
got = bytearray()
|
||||
batches = []
|
||||
first_ns = None
|
||||
while len(got) < L7_FRAME_LEN:
|
||||
while len(got) < FRAME_RT_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)))
|
||||
d = self.sock.recv(min(4096, FRAME_RT_FRAME_LEN - len(got)))
|
||||
if not d:
|
||||
raise ConnectionError("L7 frame truncated (%d/%d)" %
|
||||
(len(got), L7_FRAME_LEN))
|
||||
raise ConnectionError("FRAME_RT frame truncated (%d/%d)" %
|
||||
(len(got), FRAME_RT_FRAME_LEN))
|
||||
if first_ns is None:
|
||||
first_ns = now_ns()
|
||||
got.extend(d)
|
||||
@@ -742,7 +742,7 @@ class Peer:
|
||||
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)):
|
||||
if frame_rt_ok(bytes(got)):
|
||||
self.reports["l7"]["crc_ok"] += 1
|
||||
else:
|
||||
self.reports["l7"]["crc_fail"] += 1
|
||||
@@ -750,11 +750,11 @@ class Peer:
|
||||
{"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
|
||||
elif op == OP_CHUNK_WAIT:
|
||||
chunk_wait_t0 = now_ns()
|
||||
recv_exact(self.sock, CHUNK_WAIT_SENDS * CHUNK_WAIT_LEN, chunk_wait_t0 + 120 * 1000 * 1000000)
|
||||
self.reports["l3"]["bytes"] = CHUNK_WAIT_SENDS * CHUNK_WAIT_LEN
|
||||
self.reports["l3"]["window_ms"] = (now_ns() - chunk_wait_t0) / 1e6
|
||||
|
||||
else:
|
||||
raise RuntimeError("unknown op byte 0x%02X (protocol desync)" % op)
|
||||
|
||||
Reference in New Issue
Block a user