firmwareloop 0.1.7__tar.gz → 0.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/PKG-INFO +4 -4
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/README.md +3 -3
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/firmwareloop.egg-info/PKG-INFO +4 -4
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/pyproject.toml +1 -1
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/__init__.py +1 -1
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/fw_mcp_server.py +7 -7
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/lib/ndtdbg.py +83 -42
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/ndtdbg_bridge.ps1 +171 -2
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/setup-agent-mcp.ps1 +2 -2
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/LICENSE +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/firmwareloop.egg-info/SOURCES.txt +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/firmwareloop.egg-info/dependency_links.txt +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/firmwareloop.egg-info/entry_points.txt +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/firmwareloop.egg-info/requires.txt +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/firmwareloop.egg-info/top_level.txt +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/setup.cfg +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/acceptance-scenario.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/build.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/can.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/check-qoder-mcp.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/common/build-backends.psm1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/common/fw.psm1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/common/uart_probe.py +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/doctor.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/flash.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/instrument_cli.py +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/lib/__init__.py +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/lib/instruments.py +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/logic_capture.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/logic_decode.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/ndtdbg_cli.py +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/reset.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/sigrok_csv_to_fwloop.py +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/test-backends.ps1 +0 -0
- {firmwareloop-0.1.7 → firmwareloop-0.1.8}/tools/test.ps1 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firmwareloop
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)
|
|
5
5
|
Author-email: WeilinZhang <19523330249@163.com>
|
|
6
6
|
License: MIT
|
|
@@ -363,7 +363,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
363
363
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
364
364
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
365
365
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
366
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.
|
|
366
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.8)
|
|
367
367
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
368
368
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
369
369
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -373,7 +373,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
373
373
|
|
|
374
374
|
---
|
|
375
375
|
|
|
376
|
-
## 能力验证状态(v0.1.
|
|
376
|
+
## 能力验证状态(v0.1.8)
|
|
377
377
|
|
|
378
378
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
379
379
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -394,7 +394,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
394
394
|
| Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
|
|
395
395
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
396
396
|
| NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
|
|
397
|
-
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) |
|
|
397
|
+
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass, ~22s;双闸门 Fail-Closed) |
|
|
398
398
|
|
|
399
399
|
---
|
|
400
400
|
|
|
@@ -335,7 +335,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
335
335
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
336
336
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
337
337
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
338
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.
|
|
338
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.8)
|
|
339
339
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
340
340
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
341
341
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -345,7 +345,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
345
345
|
|
|
346
346
|
---
|
|
347
347
|
|
|
348
|
-
## 能力验证状态(v0.1.
|
|
348
|
+
## 能力验证状态(v0.1.8)
|
|
349
349
|
|
|
350
350
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
351
351
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -366,7 +366,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
366
366
|
| Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
|
|
367
367
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
368
368
|
| NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
|
|
369
|
-
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) |
|
|
369
|
+
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass, ~22s;双闸门 Fail-Closed) |
|
|
370
370
|
|
|
371
371
|
---
|
|
372
372
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firmwareloop
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)
|
|
5
5
|
Author-email: WeilinZhang <19523330249@163.com>
|
|
6
6
|
License: MIT
|
|
@@ -363,7 +363,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
363
363
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
364
364
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
365
365
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
366
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.
|
|
366
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.8)
|
|
367
367
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
368
368
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
369
369
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -373,7 +373,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
373
373
|
|
|
374
374
|
---
|
|
375
375
|
|
|
376
|
-
## 能力验证状态(v0.1.
|
|
376
|
+
## 能力验证状态(v0.1.8)
|
|
377
377
|
|
|
378
378
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
379
379
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -394,7 +394,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
394
394
|
| Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
|
|
395
395
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
396
396
|
| NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
|
|
397
|
-
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) |
|
|
397
|
+
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass, ~22s;双闸门 Fail-Closed) |
|
|
398
398
|
|
|
399
399
|
---
|
|
400
400
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.1.
|
|
3
|
+
fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.1.8).
|
|
4
4
|
|
|
5
5
|
Exposes high-level firmware engineering and hardware management tools to AI Coding
|
|
6
6
|
Agents (Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, Cursor) via the Model Context
|
|
@@ -1487,7 +1487,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
1487
1487
|
},
|
|
1488
1488
|
"serverInfo": {
|
|
1489
1489
|
"name": "firmwareloop",
|
|
1490
|
-
"version": "0.1.
|
|
1490
|
+
"version": "0.1.8"
|
|
1491
1491
|
}
|
|
1492
1492
|
}
|
|
1493
1493
|
}
|
|
@@ -1579,7 +1579,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
1579
1579
|
def handle_cli_update() -> int:
|
|
1580
1580
|
"""Handle `firmwareloop update` command."""
|
|
1581
1581
|
print("============================================================")
|
|
1582
|
-
print(" FirmwareLoop Auto-Updater (v0.1.
|
|
1582
|
+
print(" FirmwareLoop Auto-Updater (v0.1.8)")
|
|
1583
1583
|
print("============================================================")
|
|
1584
1584
|
|
|
1585
1585
|
is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
|
|
@@ -1849,7 +1849,7 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
|
|
|
1849
1849
|
def handle_cli_setup() -> int:
|
|
1850
1850
|
"""Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, and CodeBuddy IDE."""
|
|
1851
1851
|
print("============================================================")
|
|
1852
|
-
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.1.
|
|
1852
|
+
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.1.8)")
|
|
1853
1853
|
print("============================================================")
|
|
1854
1854
|
print("")
|
|
1855
1855
|
|
|
@@ -1974,7 +1974,7 @@ def handle_cli_setup() -> int:
|
|
|
1974
1974
|
def handle_cli_init() -> int:
|
|
1975
1975
|
"""Handle `firmwareloop init` command."""
|
|
1976
1976
|
print("============================================================")
|
|
1977
|
-
print(" FirmwareLoop Multi-Agent Project Initializer (v0.1.
|
|
1977
|
+
print(" FirmwareLoop Multi-Agent Project Initializer (v0.1.8)")
|
|
1978
1978
|
print("============================================================")
|
|
1979
1979
|
cwd = os.getcwd()
|
|
1980
1980
|
print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
|
|
@@ -1995,7 +1995,7 @@ def handle_cli_init() -> int:
|
|
|
1995
1995
|
|
|
1996
1996
|
|
|
1997
1997
|
def print_cli_help() -> None:
|
|
1998
|
-
print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.1.
|
|
1998
|
+
print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.1.8)
|
|
1999
1999
|
|
|
2000
2000
|
Usage:
|
|
2001
2001
|
fwloop [command] (or: firmwareloop [command])
|
|
@@ -2035,7 +2035,7 @@ def main() -> None:
|
|
|
2035
2035
|
sys.exit(2)
|
|
2036
2036
|
sys.exit(handle_cli_ndtdebugtool(args[1]))
|
|
2037
2037
|
elif cmd in ["version", "-v", "--version"]:
|
|
2038
|
-
print("FirmwareLoop v0.1.
|
|
2038
|
+
print("FirmwareLoop v0.1.8")
|
|
2039
2039
|
sys.exit(0)
|
|
2040
2040
|
elif cmd in ["help", "-h", "--help"]:
|
|
2041
2041
|
print_cli_help()
|
|
@@ -217,6 +217,64 @@ class NdtBoxIo:
|
|
|
217
217
|
data = self._bridge("raw", WriteHex=write.hex(), ReadLen=read_len)
|
|
218
218
|
return bytes.fromhex(data["hex"].replace(" ", "")) if read_len > 0 else None
|
|
219
219
|
|
|
220
|
+
def _bridge_pipe(self, action: str, payload_text: str, timeout_ms: int | None) -> dict:
|
|
221
|
+
"""把 JSON 文本经 stdin 送入桥的批量 action, 单进程执行。
|
|
222
|
+
|
|
223
|
+
#progress 进度行由桥写 stdout (非 JSON), 本方法转发到本进程 stderr。
|
|
224
|
+
末行为 JSON: {ok:false,...} -> 转 NdtBoxError (Fail-Closed); 成功返回 data。
|
|
225
|
+
"""
|
|
226
|
+
import sys
|
|
227
|
+
args = [PS32, "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
|
|
228
|
+
"-File", self.bridge_ps1, "-Action", action,
|
|
229
|
+
"-DllDir", self.dll_dir, "-SlaveAddr", str(self.slave_addr),
|
|
230
|
+
"-Speed", str(self.speed)]
|
|
231
|
+
to = self.timeout_ms if timeout_ms is None else timeout_ms
|
|
232
|
+
try:
|
|
233
|
+
proc = subprocess.run(args, input=payload_text.encode("utf-8"), capture_output=True,
|
|
234
|
+
timeout=to / 1000.0, check=False)
|
|
235
|
+
except subprocess.TimeoutExpired:
|
|
236
|
+
raise NdtBoxError("INSTRUMENT_TIMEOUT", f"桥 {action} 执行超时 >{to}ms", None)
|
|
237
|
+
out = (proc.stdout or b"").decode("utf-8", errors="replace")
|
|
238
|
+
lines = [ln for ln in out.splitlines() if ln]
|
|
239
|
+
for ln in lines[:-1]:
|
|
240
|
+
if ln.startswith("#progress"):
|
|
241
|
+
print(ln, file=sys.stderr, flush=True)
|
|
242
|
+
if not lines:
|
|
243
|
+
tail = (proc.stderr or b"").decode("utf-8", errors="replace").strip()[-400:]
|
|
244
|
+
raise NdtBoxError("UNKNOWN_ERROR", f"桥 {action} 无输出 (exit={proc.returncode})", tail or None)
|
|
245
|
+
try:
|
|
246
|
+
result = json.loads(lines[-1])
|
|
247
|
+
except json.JSONDecodeError as exc:
|
|
248
|
+
raise NdtBoxError("UNKNOWN_ERROR", f"桥 {action} 输出非 JSON", out[-400:]) from exc
|
|
249
|
+
if not result.get("ok"):
|
|
250
|
+
raise NdtBoxError(result.get("error_class", "UNKNOWN_ERROR"),
|
|
251
|
+
result.get("error", f"桥 {action} 返回失败"),
|
|
252
|
+
result.get("detail"))
|
|
253
|
+
return result.get("data", {})
|
|
254
|
+
|
|
255
|
+
def run_ops(self, ops: list[dict], timeout_ms: int | None = None) -> dict:
|
|
256
|
+
"""在单个 32 位 PS 进程内顺序执行 ops——批量路径避免逐帧 spawn 进程开销。
|
|
257
|
+
|
|
258
|
+
ops item: {"op":"sleep","ms":N} | {"op":"w","hex":"<帧 hex>"} |
|
|
259
|
+
{"op":"r","hex":"<2B 地址 hex>","len":N}
|
|
260
|
+
桥自动为 w/r 前置从机地址; r 的读回按序进入返回 read_hexes (无空格 hex, 每窗一条)。
|
|
261
|
+
"""
|
|
262
|
+
if not ops:
|
|
263
|
+
raise NdtBoxError("CONFIG_ERROR", "run_ops 空序列", None)
|
|
264
|
+
return self._bridge_pipe("run", json.dumps(ops, separators=(",", ":")), timeout_ms)
|
|
265
|
+
|
|
266
|
+
def flash_exec(self, plan: dict, timeout_ms: int | None = None) -> dict:
|
|
267
|
+
"""单进程完整固件烧录 (bridge "flash"), 复刻原厂 GUI 进程模型:
|
|
268
|
+
usb2io(IO10) 断电复位进 boot + USBIOX IIC 全序列在**同一进程同一会话**内连续执行,
|
|
269
|
+
消除跨进程的"断电->擦除"间隔。校验 (逐窗 vs expect_hex) 在桥内完成, 失败即
|
|
270
|
+
{ok:false, TARGET_MISMATCH} -> 本方法转 NdtBoxError (Fail-Closed)。
|
|
271
|
+
|
|
272
|
+
plan: {"off_ms":200,"erase_ms":2000,"page_gap_ms":30,"read_gap_ms":10,
|
|
273
|
+
"writes":["<2B 地址+128B 数据帧 hex>",...],
|
|
274
|
+
"windows":[{"off":0,"expect_hex":"<256B hex>"},...]}
|
|
275
|
+
"""
|
|
276
|
+
return self._bridge_pipe("flash", json.dumps(plan, separators=(",", ":")), timeout_ms)
|
|
277
|
+
|
|
220
278
|
def vbrt_reset(self, off_ms: int = 200) -> dict:
|
|
221
279
|
"""模组断电 off_ms 后恢复上电 (usb2io.dll IO10)——复位进入 IIC boot (IOoperation.VBRTOFF)。"""
|
|
222
280
|
return self._bridge("vbrt", OffMs=off_ms)
|
|
@@ -290,60 +348,43 @@ class ChipSeaFxx:
|
|
|
290
348
|
|
|
291
349
|
def flash_bin(self, file_path: str, erase_ms: int = 2000,
|
|
292
350
|
progress=None) -> dict:
|
|
293
|
-
"""经 NdtBox IIC 烧录 .bin/.nfw 固件到芯海 Fxx。
|
|
351
|
+
"""经 NdtBox IIC 烧录 .bin/.nfw 固件到芯海 Fxx (单进程 flash action)。
|
|
294
352
|
|
|
295
|
-
时序逐行对齐 ChipSea.cs UpdateFW:
|
|
296
|
-
|
|
297
|
-
逐 128B 页写(30ms
|
|
298
|
-
完成魔数 "PASs" -> VBRTOFF(200)
|
|
353
|
+
时序逐行对齐 ChipSea.cs UpdateFW, 进程模型对齐原厂 STDataCollection GUI:
|
|
354
|
+
VBRTOFF(200) 断电复位进 boot -> 100ms -> 擦除魔数 -> erase_ms ->
|
|
355
|
+
逐 128B 页写(30ms) -> 200ms -> 逐 256B 窗回读校验(10ms, 桥内逐窗比对) ->
|
|
356
|
+
完成魔数 "PASs" -> VBRTOFF(200) 复位加载。
|
|
357
|
+
整条序列在**一个 32 位 PS 进程**内连续执行 (usb2io + USBIOX 句柄同持),
|
|
358
|
+
消除跨进程"断电->擦除"间隔, ~15s 量级。帧组装在本层 (可单测),
|
|
359
|
+
校验失败由桥 {ok:false, TARGET_MISMATCH} -> flash_exec 抛 NdtBoxError (Fail-Closed)。
|
|
299
360
|
|
|
300
|
-
progress(phase, done, total) 在
|
|
301
|
-
校验不匹配抛 TARGET_MISMATCH (Fail-Closed, 不伪造成功)。
|
|
361
|
+
progress(phase, done, total) 在 verify 阶段末回调 (逐帧 #progress 由桥转发到 stderr)。
|
|
302
362
|
"""
|
|
303
363
|
import time
|
|
304
|
-
|
|
305
364
|
fw_type, fw = load_firmware(file_path)
|
|
306
365
|
started = time.monotonic()
|
|
307
|
-
pages =
|
|
366
|
+
pages = list(range(0, len(fw), FLASH_WRITE_PAGE))
|
|
308
367
|
windows = list(range(0, len(fw), FLASH_READ_WINDOW))
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
time.sleep(0.200)
|
|
323
|
-
for i, off in enumerate(windows):
|
|
324
|
-
expect = expected_read_window(fw, off)
|
|
325
|
-
got = self._io.raw_write_read(off.to_bytes(2, "big"), FLASH_READ_WINDOW)
|
|
326
|
-
if got != expect:
|
|
327
|
-
diff = next(k for k in range(FLASH_READ_WINDOW) if got[k] != expect[k])
|
|
328
|
-
raise NdtBoxError(
|
|
329
|
-
"TARGET_MISMATCH",
|
|
330
|
-
f"烧录校验失败 @0x{off + diff:04X}",
|
|
331
|
-
f"expect={expect[diff:diff + 8].hex()} got={got[diff:diff + 8].hex()}")
|
|
332
|
-
time.sleep(PAGE_READ_GAP_MS / 1000.0)
|
|
333
|
-
if progress:
|
|
334
|
-
progress("verify", i + 1, len(windows))
|
|
335
|
-
|
|
336
|
-
self._io.raw_write_read(b"\x00\x00" + FINISH_MAGIC, 0)
|
|
337
|
-
time.sleep(0.010)
|
|
338
|
-
self._io.vbrt_reset(200)
|
|
339
|
-
|
|
368
|
+
plan = {
|
|
369
|
+
"off_ms": 200,
|
|
370
|
+
"erase_ms": int(erase_ms),
|
|
371
|
+
"page_gap_ms": PAGE_WRITE_GAP_MS,
|
|
372
|
+
"read_gap_ms": PAGE_READ_GAP_MS,
|
|
373
|
+
"writes": [build_page_write_frame(off, fw[off:off + FLASH_WRITE_PAGE]).hex()
|
|
374
|
+
for off in pages],
|
|
375
|
+
"windows": [{"off": off, "expect_hex": expected_read_window(fw, off).hex()}
|
|
376
|
+
for off in windows],
|
|
377
|
+
}
|
|
378
|
+
res = self._io.flash_exec(plan)
|
|
379
|
+
if progress:
|
|
380
|
+
progress("verify", len(windows), len(windows))
|
|
340
381
|
return {
|
|
341
382
|
"file": file_path,
|
|
342
383
|
"file_type": fw_type,
|
|
343
384
|
"fw_len": len(fw),
|
|
344
385
|
"pages_written": len(pages),
|
|
345
386
|
"read_windows": len(windows),
|
|
346
|
-
"erase_ms": erase_ms,
|
|
347
|
-
"verify": "pass",
|
|
387
|
+
"erase_ms": int(erase_ms),
|
|
388
|
+
"verify": res.get("verify", "pass"),
|
|
348
389
|
"duration_ms": int((time.monotonic() - started) * 1000),
|
|
349
390
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# 输出: stdout 单行 JSON {ok, action, data} 或 {ok:false, error_class, error, detail}。
|
|
5
5
|
param(
|
|
6
6
|
[Parameter(Mandatory = $true)]
|
|
7
|
-
[ValidateSet("open", "read", "write", "version", "connect", "poll", "close", "raw", "vbrt")]
|
|
7
|
+
[ValidateSet("open", "read", "write", "version", "connect", "poll", "close", "raw", "vbrt", "run", "flash")]
|
|
8
8
|
[string]$Action,
|
|
9
9
|
|
|
10
10
|
[string]$DllDir,
|
|
@@ -245,13 +245,18 @@ try {
|
|
|
245
245
|
}
|
|
246
246
|
"raw" {
|
|
247
247
|
# 对齐 ChipSea.cs 的多字节地址事务: 写 [slave, wBuf...] 再读 ReadLen 字节 (IIC.WriteRead 语义)
|
|
248
|
+
# 注意: $wb 不含从机地址, 必须前置 $SlaveAddr 为事务首字节 (USBIO_StreamI2C 以 wBuffer[0] 为从机地址)。
|
|
249
|
+
# 历史上曾遗漏该前置, 导致首字节(页地址)被当作从机地址, 事务从未到达芯片, 校验读回全 FF。
|
|
248
250
|
$wb = Parse-Hex $WriteHex
|
|
249
251
|
if ($wb.Count -eq 0) { Fail "CONFIG_ERROR" "raw 需要 -WriteHex (不含从机地址的写事务字节)" $null }
|
|
250
252
|
if (($wb.Count + 1) -gt 256) { Fail "CONFIG_ERROR" "raw 写事务过长 (含从机地址须 <=256)" $wb.Count }
|
|
251
253
|
if ($ReadLen -lt 0 -or $ReadLen -gt 256) { Fail "CONFIG_ERROR" "raw -ReadLen 越界 (0..256)" $ReadLen }
|
|
254
|
+
$tx = New-Object byte[] ($wb.Count + 1)
|
|
255
|
+
$tx[0] = [byte]$SlaveAddr
|
|
256
|
+
[Array]::Copy($wb, 0, $tx, 1, $wb.Count)
|
|
252
257
|
$h = Open-Device 0
|
|
253
258
|
$rb = New-Object byte[] $ReadLen
|
|
254
|
-
if (-not (Invoke-WriteRead $h $
|
|
259
|
+
if (-not (Invoke-WriteRead $h $tx $rb)) { Fail "TARGET_MISMATCH" ("IIC raw 事务无应答 (w={0}B r={1}B)" -f $tx.Count, $ReadLen) (($tx | ForEach-Object { $_.ToString("X2") }) -join " ") }
|
|
255
260
|
[STDataCollection.Icb]::USBIO_CloseDevice([uint32]0) | Out-Null
|
|
256
261
|
$dataOut = @{ written = $wb.Count; read = $ReadLen }
|
|
257
262
|
if ($ReadLen -gt 0) { $dataOut.hex = (($rb | ForEach-Object { $_.ToString("X2") }) -join " ") }
|
|
@@ -271,6 +276,170 @@ try {
|
|
|
271
276
|
Write-Output (@{ ok = $true; action = "vbrt"; data = @{ off_ms = $OffMs; powered_on = $true } } | ConvertTo-Json -Compress -Depth 4)
|
|
272
277
|
exit 0
|
|
273
278
|
}
|
|
279
|
+
"run" {
|
|
280
|
+
# 批量执行 ops (stdin JSON 数组), 单进程内完成——避免烧录等高频路径逐帧 spawn 32 位 PS 的进程开销。
|
|
281
|
+
# ops item: {"op":"sleep","ms":N} | {"op":"w","hex":"..."} | {"op":"r","hex":"<addr>","len":N}
|
|
282
|
+
# w/r 均前置 $SlaveAddr (同 raw); r 的读回按序收集进 read_hexes (无空格 hex)。
|
|
283
|
+
# 进度行以 "#progress " 前缀写 stdout (非 JSON, Python 侧转发到 stderr), 最终结果为末行 JSON。
|
|
284
|
+
$opsJson = [Console]::In.ReadToEnd()
|
|
285
|
+
$ops = $opsJson | ConvertFrom-Json
|
|
286
|
+
if (-not $ops) { Fail "CONFIG_ERROR" "run 需要 stdin 传入 ops JSON 数组" $null }
|
|
287
|
+
$h = Open-Device 0
|
|
288
|
+
$readBacks = New-Object System.Collections.ArrayList
|
|
289
|
+
$wTotal = 0; $rTotal = 0
|
|
290
|
+
foreach ($op in $ops) { if ($op.op -eq "w") { $wTotal++ } elseif ($op.op -eq "r") { $rTotal++ } }
|
|
291
|
+
$wIdx = 0; $rIdx = 0
|
|
292
|
+
foreach ($op in $ops) {
|
|
293
|
+
if ($op.op -eq "sleep") {
|
|
294
|
+
Start-Sleep -Milliseconds ([int]$op.ms)
|
|
295
|
+
continue
|
|
296
|
+
}
|
|
297
|
+
if ($op.op -eq "w") {
|
|
298
|
+
$wb = Parse-Hex ([string]$op.hex)
|
|
299
|
+
if ($wb.Count -eq 0) { Fail "CONFIG_ERROR" "run 写帧为空 (w #$($wIdx + 1))" $null }
|
|
300
|
+
if (($wb.Count + 1) -gt 256) { Fail "CONFIG_ERROR" "run 写帧过长 (w #$($wIdx + 1))" $wb.Count }
|
|
301
|
+
$tx = New-Object byte[] ($wb.Count + 1)
|
|
302
|
+
$tx[0] = [byte]$SlaveAddr
|
|
303
|
+
[Array]::Copy($wb, 0, $tx, 1, $wb.Count)
|
|
304
|
+
if (-not (Invoke-WriteRead $h $tx (New-Object byte[] 0))) {
|
|
305
|
+
Fail "TARGET_MISMATCH" ("IIC run 写无应答 (w {0}/{1})" -f ($wIdx + 1), $wTotal) (($tx | ForEach-Object { $_.ToString("X2") }) -join " ")
|
|
306
|
+
}
|
|
307
|
+
$wIdx++
|
|
308
|
+
Write-Output ("#progress write {0}/{1}" -f $wIdx, $wTotal)
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
if ($op.op -eq "r") {
|
|
312
|
+
$ab = Parse-Hex ([string]$op.hex)
|
|
313
|
+
$len = [int]$op.len
|
|
314
|
+
if ($ab.Count -eq 0) { Fail "CONFIG_ERROR" "run 读地址为空 (r #$($rIdx + 1))" $null }
|
|
315
|
+
if ($len -lt 1 -or $len -gt 256) { Fail "CONFIG_ERROR" "run 读长度越界 (r #$($rIdx + 1))" $len }
|
|
316
|
+
$tx = New-Object byte[] ($ab.Count + 1)
|
|
317
|
+
$tx[0] = [byte]$SlaveAddr
|
|
318
|
+
[Array]::Copy($ab, 0, $tx, 1, $ab.Count)
|
|
319
|
+
$rb = New-Object byte[] $len
|
|
320
|
+
if (-not (Invoke-WriteRead $h $tx $rb)) {
|
|
321
|
+
Fail "TARGET_MISMATCH" ("IIC run 读无应答 (r {0}/{1})" -f ($rIdx + 1), $rTotal) (($tx | ForEach-Object { $_.ToString("X2") }) -join " ")
|
|
322
|
+
}
|
|
323
|
+
[void]$readBacks.Add((($rb | ForEach-Object { $_.ToString("X2") }) -join ""))
|
|
324
|
+
$rIdx++
|
|
325
|
+
Write-Output ("#progress read {0}/{1}" -f $rIdx, $rTotal)
|
|
326
|
+
continue
|
|
327
|
+
}
|
|
328
|
+
Fail "CONFIG_ERROR" ("run 未知 op: {0}" -f $op.op) $null
|
|
329
|
+
}
|
|
330
|
+
[STDataCollection.Icb]::USBIO_CloseDevice([uint32]0) | Out-Null
|
|
331
|
+
Write-Output (@{ ok = $true; action = "run"; data = @{ writes = $wIdx; reads = $rIdx; read_hexes = @($readBacks) } } | ConvertTo-Json -Compress -Depth 5)
|
|
332
|
+
exit 0
|
|
333
|
+
}
|
|
334
|
+
"flash" {
|
|
335
|
+
# 单进程完整固件烧录 (UpdateFW 语义, 复刻 STDataCollection GUI 进程模型):
|
|
336
|
+
# usb2io(IO10) 断电复位进 boot -> 100ms -> 擦除魔数 -> erase_ms ->
|
|
337
|
+
# stdin 页写帧(128B/页, page_gap) -> 200ms -> stdin 期望窗(256B) 逐窗比对 ->
|
|
338
|
+
# 完成魔数 -> usb2io 断电复位加载。
|
|
339
|
+
# usb2io 与 USBIOX 句柄在本进程内同时持有且全程连续——GUI 常驻进程模型,
|
|
340
|
+
# 消除跨进程的"断电->擦除"间隔 (须落在 app 升级服务的等待窗口内)。
|
|
341
|
+
# stdin JSON: {"off_ms":200,"erase_ms":2000,"page_gap_ms":30,"read_gap_ms":10,
|
|
342
|
+
# "writes":["<帧 hex: 2B 大端地址+128B 数据>",...],
|
|
343
|
+
# "windows":[{"off":0,"expect_hex":"<256B hex>"},...]}
|
|
344
|
+
$planJson = [Console]::In.ReadToEnd()
|
|
345
|
+
$plan = $planJson | ConvertFrom-Json
|
|
346
|
+
if (-not $plan) { Fail "CONFIG_ERROR" "flash 需要 stdin 传入 plan JSON" $null }
|
|
347
|
+
$offMs = [int]$plan.off_ms
|
|
348
|
+
$eraseMs = [int]$plan.erase_ms
|
|
349
|
+
$pageGap = [int]$plan.page_gap_ms
|
|
350
|
+
$readGap = [int]$plan.read_gap_ms
|
|
351
|
+
$h = Open-Device 0
|
|
352
|
+
$u = [STDataCollection.U2io]::USB2IO_Open(1)
|
|
353
|
+
if ([int]$u -eq -1) {
|
|
354
|
+
[STDataCollection.Icb]::USBIO_CloseDevice([uint32]0) | Out-Null
|
|
355
|
+
Fail "PROBE_NOT_FOUND" "USB2IO 打开失败 (设备 1); 确认 DllDir 含 usb2io.dll 且 NdtBox 已连接" $null
|
|
356
|
+
}
|
|
357
|
+
$wTotal = 0; $rTotal = 0
|
|
358
|
+
try {
|
|
359
|
+
# --- 断电复位进 boot (IOoperation.VBRTOFF) ---
|
|
360
|
+
[STDataCollection.U2io]::USB2IO_SetIoCfg($u, 10, 3) | Out-Null
|
|
361
|
+
[STDataCollection.U2io]::USB2IO_SetIoOut($u, 10, 1) | Out-Null
|
|
362
|
+
Start-Sleep -Milliseconds $offMs
|
|
363
|
+
[STDataCollection.U2io]::USB2IO_SetIoOut($u, 10, 0) | Out-Null
|
|
364
|
+
Start-Sleep -Milliseconds 50
|
|
365
|
+
Start-Sleep -Milliseconds 100 # boot ready (UpdateFW Thread.Sleep(100))
|
|
366
|
+
|
|
367
|
+
# --- 整片擦除 (EreaseChipCmd) ---
|
|
368
|
+
$magic = [byte[]](0xAA, 0x55, 0xA5, 0x5A)
|
|
369
|
+
$eb = New-Object byte[] 7
|
|
370
|
+
$eb[0] = [byte]$SlaveAddr
|
|
371
|
+
$eb[1] = 0; $eb[2] = 0
|
|
372
|
+
[Array]::Copy($magic, 0, $eb, 3, 4)
|
|
373
|
+
if (-not (Invoke-WriteRead $h $eb (New-Object byte[] 0))) {
|
|
374
|
+
Fail "TARGET_MISMATCH" "擦除命令无应答 (StreamI2C=false)" (($eb | ForEach-Object { $_.ToString("X2") }) -join " ")
|
|
375
|
+
}
|
|
376
|
+
Start-Sleep -Milliseconds $eraseMs
|
|
377
|
+
|
|
378
|
+
# --- 逐页写 (WriteFw) ---
|
|
379
|
+
$writes = @($plan.writes)
|
|
380
|
+
$wTotal = $writes.Count
|
|
381
|
+
for ($wi = 0; $wi -lt $wTotal; $wi++) {
|
|
382
|
+
$wb = Parse-Hex ([string]$writes[$wi])
|
|
383
|
+
if (($wb.Count + 1) -gt 256) { Fail "CONFIG_ERROR" "flash 页写帧过长 (页 $wi)" $wb.Count }
|
|
384
|
+
$tx = New-Object byte[] ($wb.Count + 1)
|
|
385
|
+
$tx[0] = [byte]$SlaveAddr
|
|
386
|
+
[Array]::Copy($wb, 0, $tx, 1, $wb.Count)
|
|
387
|
+
if (-not (Invoke-WriteRead $h $tx (New-Object byte[] 0))) {
|
|
388
|
+
Fail "TARGET_MISMATCH" ("页写无应答 (write {0}/{1})" -f ($wi + 1), $wTotal) (($tx | ForEach-Object { $_.ToString("X2") }) -join " ")
|
|
389
|
+
}
|
|
390
|
+
if ($pageGap -gt 0) { Start-Sleep -Milliseconds $pageGap }
|
|
391
|
+
Write-Output ("#progress write {0}/{1}" -f ($wi + 1), $wTotal)
|
|
392
|
+
}
|
|
393
|
+
Start-Sleep -Milliseconds 200 # WriteFw 后至校验前 (Thread.Sleep(200))
|
|
394
|
+
|
|
395
|
+
# --- 逐窗回读校验 (ReadFwAndCheck) ---
|
|
396
|
+
$windows = @($plan.windows)
|
|
397
|
+
$rTotal = $windows.Count
|
|
398
|
+
for ($ri = 0; $ri -lt $rTotal; $ri++) {
|
|
399
|
+
$op = $windows[$ri]
|
|
400
|
+
$off = [int]$op.off
|
|
401
|
+
$rb = New-Object byte[] 256
|
|
402
|
+
$tx = New-Object byte[] 3
|
|
403
|
+
$tx[0] = [byte]$SlaveAddr
|
|
404
|
+
$tx[1] = [byte](($off -shr 8) -band 0xFF)
|
|
405
|
+
$tx[2] = [byte]($off -band 0xFF)
|
|
406
|
+
if (-not (Invoke-WriteRead $h $tx $rb)) {
|
|
407
|
+
Fail "TARGET_MISMATCH" ("回读无应答 (read {0}/{1} @0x{2:X4})" -f ($ri + 1), $rTotal, $off) $null
|
|
408
|
+
}
|
|
409
|
+
$gotHex = (($rb | ForEach-Object { $_.ToString("X2") }) -join "").ToLower()
|
|
410
|
+
$expHex = [string]$op.expect_hex
|
|
411
|
+
if ($gotHex -cne $expHex) {
|
|
412
|
+
$k = 0
|
|
413
|
+
while ($k -lt $gotHex.Length -and $gotHex.Substring($k, 2) -eq $expHex.Substring($k, 2)) { $k += 2 }
|
|
414
|
+
Fail "TARGET_MISMATCH" ("烧录校验失败 @0x{0:X4}" -f ($off + ($k / 2))) ("expect={0} got={1}" -f $expHex.Substring($k, 16), $gotHex.Substring($k, 16))
|
|
415
|
+
}
|
|
416
|
+
if ($readGap -gt 0) { Start-Sleep -Milliseconds $readGap }
|
|
417
|
+
Write-Output ("#progress read {0}/{1}" -f ($ri + 1), $rTotal)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
# --- 完成魔数 (UpgradeFinishCmd "PASs") ---
|
|
421
|
+
$fmagic = [byte[]](0x50, 0x41, 0x53, 0x73)
|
|
422
|
+
$fb = New-Object byte[] 7
|
|
423
|
+
$fb[0] = [byte]$SlaveAddr
|
|
424
|
+
$fb[1] = 0; $fb[2] = 0
|
|
425
|
+
[Array]::Copy($fmagic, 0, $fb, 3, 4)
|
|
426
|
+
if (-not (Invoke-WriteRead $h $fb (New-Object byte[] 0))) {
|
|
427
|
+
Fail "TARGET_MISMATCH" "完成魔数无应答 (StreamI2C=false)" (($fb | ForEach-Object { $_.ToString("X2") }) -join " ")
|
|
428
|
+
}
|
|
429
|
+
Start-Sleep -Milliseconds 10
|
|
430
|
+
|
|
431
|
+
# --- 结束断电复位加载新固件 ---
|
|
432
|
+
[STDataCollection.U2io]::USB2IO_SetIoOut($u, 10, 1) | Out-Null
|
|
433
|
+
Start-Sleep -Milliseconds $offMs
|
|
434
|
+
[STDataCollection.U2io]::USB2IO_SetIoOut($u, 10, 0) | Out-Null
|
|
435
|
+
Start-Sleep -Milliseconds 50
|
|
436
|
+
} finally {
|
|
437
|
+
[STDataCollection.U2io]::USB2IO_Close($u) | Out-Null
|
|
438
|
+
[STDataCollection.Icb]::USBIO_CloseDevice([uint32]0) | Out-Null
|
|
439
|
+
}
|
|
440
|
+
Write-Output (@{ ok = $true; action = "flash"; data = @{ verify = "pass"; pages_written = $wTotal; read_windows = $rTotal } } | ConvertTo-Json -Compress -Depth 4)
|
|
441
|
+
exit 0
|
|
442
|
+
}
|
|
274
443
|
}
|
|
275
444
|
} catch {
|
|
276
445
|
Fail "UNKNOWN_ERROR" "bridge 异常: $($_.Exception.Message)" $_.ScriptStackTrace
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
Dual-Tier MCP setup and registration helper (v0.1.
|
|
3
|
+
Dual-Tier MCP setup and registration helper (v0.1.8).
|
|
4
4
|
Inspects installed AI coding agents (Qoder, Claude Code, Antigravity, Cursor)
|
|
5
5
|
and configures global MCP / skills or prints exact registration commands.
|
|
6
6
|
|
|
@@ -242,7 +242,7 @@ if ($Json) {
|
|
|
242
242
|
Write-FwJson ([pscustomobject]$report)
|
|
243
243
|
} else {
|
|
244
244
|
Write-Host "============================================================" -ForegroundColor Cyan
|
|
245
|
-
Write-Host " FirmwareLoop Dual-Tier MCP & Agent Setup Helper (v0.1.
|
|
245
|
+
Write-Host " FirmwareLoop Dual-Tier MCP & Agent Setup Helper (v0.1.8)" -ForegroundColor Green
|
|
246
246
|
Write-Host "============================================================" -ForegroundColor Cyan
|
|
247
247
|
Write-Host ""
|
|
248
248
|
Write-Host "[1] Google Antigravity / Gemini CLI:" -ForegroundColor Yellow
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|