firmwareloop 0.2.2__tar.gz → 0.4.0__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.
Files changed (36) hide show
  1. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/PKG-INFO +3 -3
  2. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/README.md +2 -2
  3. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/PKG-INFO +3 -3
  4. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/pyproject.toml +1 -1
  5. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/__init__.py +1 -1
  6. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/fw_mcp_server.py +112 -8
  7. firmwareloop-0.4.0/tools/lib/ndtdbg.py +794 -0
  8. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/ndtdbg_bridge.ps1 +171 -33
  9. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/ndtdbg_cli.py +28 -4
  10. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/setup-agent-mcp.ps1 +2 -2
  11. firmwareloop-0.2.2/tools/lib/ndtdbg.py +0 -512
  12. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/LICENSE +0 -0
  13. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/SOURCES.txt +0 -0
  14. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/dependency_links.txt +0 -0
  15. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/entry_points.txt +0 -0
  16. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/requires.txt +0 -0
  17. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/top_level.txt +0 -0
  18. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/setup.cfg +0 -0
  19. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/acceptance-scenario.ps1 +0 -0
  20. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/build.ps1 +0 -0
  21. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/can.ps1 +0 -0
  22. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/check-qoder-mcp.ps1 +0 -0
  23. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/common/build-backends.psm1 +0 -0
  24. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/common/fw.psm1 +0 -0
  25. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/common/uart_probe.py +0 -0
  26. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/doctor.ps1 +0 -0
  27. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/flash.ps1 +0 -0
  28. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/instrument_cli.py +0 -0
  29. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/lib/__init__.py +0 -0
  30. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/lib/instruments.py +0 -0
  31. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/logic_capture.ps1 +0 -0
  32. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/logic_decode.ps1 +0 -0
  33. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/reset.ps1 +0 -0
  34. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/sigrok_csv_to_fwloop.py +0 -0
  35. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/test-backends.ps1 +0 -0
  36. {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/test.ps1 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.2.2
3
+ Version: 0.4.0
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
@@ -381,7 +381,7 @@ claude mcp remove --scope user agentic-hil
381
381
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
382
382
  ├── demo-make/ 示例固件(Make;构建测试载体)
383
383
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
384
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.2.2)
384
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.4.0)
385
385
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
386
386
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
387
387
  ├── CLAUDE.md Claude Code CLI 指南
@@ -391,7 +391,7 @@ claude mcp remove --scope user agentic-hil
391
391
 
392
392
  ---
393
393
 
394
- ## 能力验证状态(v0.2.2
394
+ ## 能力验证状态(v0.4.0
395
395
 
396
396
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
397
397
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -353,7 +353,7 @@ claude mcp remove --scope user agentic-hil
353
353
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
354
354
  ├── demo-make/ 示例固件(Make;构建测试载体)
355
355
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
356
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.2.2)
356
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.4.0)
357
357
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
358
358
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
359
359
  ├── CLAUDE.md Claude Code CLI 指南
@@ -363,7 +363,7 @@ claude mcp remove --scope user agentic-hil
363
363
 
364
364
  ---
365
365
 
366
- ## 能力验证状态(v0.2.2
366
+ ## 能力验证状态(v0.4.0
367
367
 
368
368
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
369
369
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.2.2
3
+ Version: 0.4.0
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
@@ -381,7 +381,7 @@ claude mcp remove --scope user agentic-hil
381
381
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
382
382
  ├── demo-make/ 示例固件(Make;构建测试载体)
383
383
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
384
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.2.2)
384
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.4.0)
385
385
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
386
386
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
387
387
  ├── CLAUDE.md Claude Code CLI 指南
@@ -391,7 +391,7 @@ claude mcp remove --scope user agentic-hil
391
391
 
392
392
  ---
393
393
 
394
- ## 能力验证状态(v0.2.2
394
+ ## 能力验证状态(v0.4.0
395
395
 
396
396
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
397
397
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "firmwareloop"
7
- version = "0.2.2"
7
+ version = "0.4.0"
8
8
  description = "AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -2,4 +2,4 @@
2
2
  FirmwareLoop - AI Agent Firmware Development and Lab Automation Platform.
3
3
  """
4
4
 
5
- __version__ = "0.2.2"
5
+ __version__ = "0.4.0"
@@ -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.2.2).
3
+ fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.4.0).
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
@@ -360,6 +360,8 @@ def handle_fw_ndtdbg_stream(args: Dict[str, Any]) -> Dict[str, Any]:
360
360
  cmd.extend(["--fail-threshold", str(int(args["fail_threshold"]))])
361
361
  if args.get("require_target"):
362
362
  cmd.append("--require-target")
363
+ if args.get("pre_write"):
364
+ cmd.extend(["--pre-write", *[str(p) for p in args["pre_write"]]])
363
365
  _ndtdbg_reg_args(args, cmd)
364
366
  return _ndtdbg_run(cmd, timeout=int(duration_s) + 120)
365
367
 
@@ -1387,7 +1389,7 @@ TOOLS_REGISTRY = {
1387
1389
  "handler": handle_fw_ndtdbg_flash,
1388
1390
  },
1389
1391
  "fw_ndtdbg_stream": {
1390
- "description": "Stream-read a set of IIC registers from the NdtBox target chip (ChipSea Fxx) at a fixed interval via one long-lived USB session. Read-only. Writes CSV with timestamps; returns JSON summary (frames, FF_GAP/IO_FAIL counts, interval stats). For capturing sensor/debug data streams (e.g. PT488 0xEE 4-channel data). Typical combo: write_reg(0xBE=01, confirm) to start debug_mode, then stream(0xEE).",
1392
+ "description": "Stream-read a set of IIC registers from the NdtBox target chip (ChipSea Fxx) at a fixed interval via one long-lived USB session. Read-only. Writes CSV with timestamps; returns JSON summary (frames, FF_GAP/IO_FAIL counts, interval stats, io_overhead). Supports two protocol profiles: continuous (pure-read polling) and request-response (pre_write command written before every frame, e.g. PT488 0x10/0x14 channels). Per-frame IO overhead is reported separately in io_overhead_avg_ms.",
1391
1393
  "inputSchema": {
1392
1394
  "type": "object",
1393
1395
  "properties": {
@@ -1437,6 +1439,11 @@ TOOLS_REGISTRY = {
1437
1439
  "description": "Probe reg 0x10 before capture; abort with TARGET_MISMATCH when the chip does not answer.",
1438
1440
  "default": False,
1439
1441
  },
1442
+ "pre_write": {
1443
+ "type": "array",
1444
+ "items": {"type": "string"},
1445
+ "description": "Request-response protocol profile: commands 'addr:data' written before EVERY frame (e.g. ['0xEC:10']) so request-response channels refresh each read. Omit for continuous (pure-read) streams. Defaults from lab.yaml ndtdbg.stream.pre_write when omitted.",
1446
+ },
1440
1447
  "dll_dir": {"type": "string", "description": "NdtDebugTool lib dir; default from lab.yaml ndtdbg.dll_dir"},
1441
1448
  "slave_addr": {"type": "string", "description": "IIC slave address, e.g. 0xA0; default from lab.yaml"},
1442
1449
  },
@@ -1619,7 +1626,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
1619
1626
  },
1620
1627
  "serverInfo": {
1621
1628
  "name": "firmwareloop",
1622
- "version": "0.2.2"
1629
+ "version": "0.4.0"
1623
1630
  }
1624
1631
  }
1625
1632
  }
@@ -1754,7 +1761,7 @@ def _settle_stashed_entrypoints(stashed) -> None:
1754
1761
  def handle_cli_update() -> int:
1755
1762
  """Handle `firmwareloop update` command."""
1756
1763
  print("============================================================")
1757
- print(" FirmwareLoop Auto-Updater (v0.2.2)")
1764
+ print(" FirmwareLoop Auto-Updater (v0.4.0)")
1758
1765
  print("============================================================")
1759
1766
 
1760
1767
  is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
@@ -1823,6 +1830,100 @@ def handle_cli_update() -> int:
1823
1830
  return res.returncode
1824
1831
 
1825
1832
 
1833
+ def handle_cli_selftest(target: str = "ndtdbg") -> int:
1834
+ """`fwloop selftest ndtdbg`: 按 BACKLOG 验收标准自动回归 (需硬件在环)。
1835
+
1836
+ 流程: status 分层探测 -> stream 冒烟 (100ms x 10s, 帧守恒/性能线/gaps 断言) ->
1837
+ 写读回环 (write_allowed 双门内, 未授权 SKIP) -> 结构化汇总。任一 FAIL 则 exit 1。
1838
+ """
1839
+ if target != "ndtdbg":
1840
+ print(f"[-] Unknown selftest target: {target} (supported: ndtdbg)")
1841
+ return 2
1842
+ print("============================================================")
1843
+ print(" FirmwareLoop Self-Test: ndtdbg (BACKLOG 验收标准)")
1844
+ print("============================================================")
1845
+ results: list = []
1846
+
1847
+ def run_cli(*args, timeout=120):
1848
+ cli = os.path.join(REPO_ROOT, "tools", "ndtdbg_cli.py")
1849
+ try:
1850
+ proc = subprocess.run([sys.executable, cli, *args], capture_output=True,
1851
+ text=True, encoding="utf-8", errors="replace", timeout=timeout)
1852
+ except subprocess.TimeoutExpired:
1853
+ return -1, {"ok": False, "error_class": "INSTRUMENT_TIMEOUT",
1854
+ "error": f"子命令超时 >{timeout}s (疑似 USB 会话被残留进程占用或设备挂死)"}
1855
+ body = None
1856
+ for line in reversed((proc.stdout or "").strip().splitlines()):
1857
+ line = line.strip()
1858
+ if line.startswith("{"):
1859
+ try:
1860
+ body = json.loads(line)
1861
+ break
1862
+ except Exception:
1863
+ continue
1864
+ return proc.returncode, body
1865
+
1866
+ def report(name, status, detail=""):
1867
+ results.append((name, status, detail))
1868
+
1869
+ # 1. status 分层探测
1870
+ print("\n[1/4] status 探测 (Box / IIC bus / target chip) ...")
1871
+ rc, body = run_cli("status", timeout=30)
1872
+ if rc == 0 and body and body.get("ok"):
1873
+ data = body.get("data") or {}
1874
+ report("status", "PASS", data.get("summary", ""))
1875
+ else:
1876
+ report("status", "FAIL", (body or {}).get("error_class", f"exit {rc}"))
1877
+
1878
+ # 2. stream 冒烟 (100ms x 10s)
1879
+ print("[2/4] stream 冒烟 (100ms x 10s, int16 解码) ...")
1880
+ rc, body = run_cli("stream", "--regs", "0xEE:8:ch", "--interval-ms", "100",
1881
+ "--duration-s", "10", "--decode", "int16le", timeout=60)
1882
+ stream_data = (body or {}).get("data") if isinstance(body, dict) else None
1883
+ if rc == 0 and stream_data:
1884
+ total = stream_data.get("frames_total", 0)
1885
+ conserved = (stream_data.get("frames_ok", 0) + stream_data.get("frames_ff_gap", 0)
1886
+ + stream_data.get("frames_io_fail", 0)) == total
1887
+ avg = stream_data.get("interval_actual_avg_ms")
1888
+ report("stream.frames_conserved", "PASS" if conserved else "FAIL",
1889
+ f"total={total} (ok={stream_data.get('frames_ok')}, "
1890
+ f"ff_gap={stream_data.get('frames_ff_gap')}, io_fail={stream_data.get('frames_io_fail')})")
1891
+ report("stream.interval_perf", "PASS" if (avg is not None and avg <= 110) else "FAIL",
1892
+ f"avg={avg}ms (target 100ms, 验收线 <=110ms)")
1893
+ report("stream.io_overhead", "PASS" if stream_data.get("io_overhead_avg_ms") is not None else "FAIL",
1894
+ f"io_overhead_avg_ms={stream_data.get('io_overhead_avg_ms')}")
1895
+ report("stream.gaps", "PASS", f"gaps_n={stream_data.get('gaps_n', 0)} (仅 >2x target 计数)")
1896
+ else:
1897
+ report("stream", "FAIL", (body or {}).get("error_class", f"exit {rc}"))
1898
+
1899
+ # 3. 写读回环 (write_allowed 双门内; 未授权 SKIP, 不算 FAIL)
1900
+ print("[3/4] 写读回环 (0xBE debug_mode, 需 lab.yaml write_allowed) ...")
1901
+ rc, body = run_cli("write-reg", "--addr", "0xBE", "--data", "01", "--confirm")
1902
+ if rc == 0 and body and body.get("ok"):
1903
+ report("write.readback", "PASS", "0xBE=01 写入成功 (debug_mode 开启)")
1904
+ run_cli("write-reg", "--addr", "0xBE", "--data", "00", "--confirm")
1905
+ report("write.restore", "PASS", "0xBE=00 已恢复")
1906
+ else:
1907
+ err_class = (body or {}).get("error_class", f"exit {rc}")
1908
+ if err_class == "PERMISSION_DENIED":
1909
+ report("write.readback", "SKIP", "write_allowed 未开启 (双门保护), 跳过写回环")
1910
+ else:
1911
+ report("write.readback", "FAIL", err_class)
1912
+
1913
+ # 4. 汇总
1914
+ print("\n============================================================")
1915
+ for name, status, detail in results:
1916
+ print(f" [{status}] {name}" + (f" — {detail}" if detail else ""))
1917
+ failed = [r for r in results if r[1] == "FAIL"]
1918
+ skipped = [r for r in results if r[1] == "SKIP"]
1919
+ overall = "FAIL" if failed else "PASS"
1920
+ print("============================================================")
1921
+ print(f" Self-Test Result: {overall} "
1922
+ f"({len(results) - len(failed) - len(skipped)} pass, {len(failed)} fail, {len(skipped)} skip)")
1923
+ print("============================================================")
1924
+ return 1 if failed else 0
1925
+
1926
+
1826
1927
  def handle_cli_doctor() -> int:
1827
1928
  """Handle `firmwareloop doctor` command."""
1828
1929
  print("[*] Running FirmwareLoop environment diagnostics...")
@@ -2029,7 +2130,7 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
2029
2130
  def handle_cli_setup() -> int:
2030
2131
  """Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, and CodeBuddy IDE."""
2031
2132
  print("============================================================")
2032
- print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.2.2)")
2133
+ print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.4.0)")
2033
2134
  print("============================================================")
2034
2135
  print("")
2035
2136
 
@@ -2154,7 +2255,7 @@ def handle_cli_setup() -> int:
2154
2255
  def handle_cli_init() -> int:
2155
2256
  """Handle `firmwareloop init` command."""
2156
2257
  print("============================================================")
2157
- print(" FirmwareLoop Multi-Agent Project Initializer (v0.2.2)")
2258
+ print(" FirmwareLoop Multi-Agent Project Initializer (v0.4.0)")
2158
2259
  print("============================================================")
2159
2260
  cwd = os.getcwd()
2160
2261
  print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
@@ -2175,7 +2276,7 @@ def handle_cli_init() -> int:
2175
2276
 
2176
2277
 
2177
2278
  def print_cli_help() -> None:
2178
- print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.2.2)
2279
+ print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.4.0)
2179
2280
 
2180
2281
  Usage:
2181
2282
  fwloop [command] (or: firmwareloop [command])
@@ -2184,6 +2285,7 @@ Commands:
2184
2285
  init Initialize multi-agent rules (AGENTS.md, CLAUDE.md, GEMINI.md) & lab.yaml in current project
2185
2286
  update Check and update FirmwareLoop to the latest version
2186
2287
  doctor Run environment & toolchain diagnostics
2288
+ selftest [target] Hardware self-test regression (default: ndtdbg; BACKLOG acceptance)
2187
2289
  setup Register AI Agent MCP globally & generate machine-level config (%APPDATA%\\FirmwareLoop\\lab.yaml)
2188
2290
  ndtdebugtool DIR Configure NdtBox: point to the NdtDebugTool folder root (auto-resolves its lib/)
2189
2291
  dlldir DIR Alias of ndtdebugtool (legacy name, pass the lib/ directory directly)
@@ -2205,6 +2307,8 @@ def main() -> None:
2205
2307
  sys.exit(handle_cli_update())
2206
2308
  elif cmd in ["doctor", "check"]:
2207
2309
  sys.exit(handle_cli_doctor())
2310
+ elif cmd == "selftest":
2311
+ sys.exit(handle_cli_selftest(args[1] if len(args) > 1 else "ndtdbg"))
2208
2312
  elif cmd in ["setup", "register"]:
2209
2313
  sys.exit(handle_cli_setup())
2210
2314
  elif cmd in ["ndtdebugtool", "dlldir"]:
@@ -2215,7 +2319,7 @@ def main() -> None:
2215
2319
  sys.exit(2)
2216
2320
  sys.exit(handle_cli_ndtdebugtool(args[1]))
2217
2321
  elif cmd in ["version", "-v", "--version"]:
2218
- print("FirmwareLoop v0.2.2")
2322
+ print("FirmwareLoop v0.4.0")
2219
2323
  sys.exit(0)
2220
2324
  elif cmd in ["help", "-h", "--help"]:
2221
2325
  print_cli_help()