firmwareloop 0.9.0__tar.gz → 0.9.1__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 (40) hide show
  1. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/PKG-INFO +5 -3
  2. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/README.md +2 -2
  3. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/PKG-INFO +5 -3
  4. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/SOURCES.txt +4 -1
  5. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/requires.txt +3 -0
  6. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/pyproject.toml +12 -1
  7. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/__init__.py +1 -1
  8. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/fw_mcp_server.py +64 -29
  9. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/setup-agent-mcp.ps1 +2 -2
  10. firmwareloop-0.9.1/tools/templates/lab.example.yaml +195 -0
  11. firmwareloop-0.9.1/tools/templates/skill-firmwareloop.md +37 -0
  12. firmwareloop-0.9.1/tools/templates/skill-fwloop-adapter.md +190 -0
  13. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/LICENSE +0 -0
  14. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/dependency_links.txt +0 -0
  15. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/entry_points.txt +0 -0
  16. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/top_level.txt +0 -0
  17. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/setup.cfg +0 -0
  18. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/acceptance-scenario.ps1 +0 -0
  19. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/build.ps1 +0 -0
  20. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/can.ps1 +0 -0
  21. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/check-qoder-mcp.ps1 +0 -0
  22. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/common/build-backends.psm1 +0 -0
  23. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/common/fw.psm1 +0 -0
  24. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/common/uart_probe.py +0 -0
  25. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/doctor.ps1 +0 -0
  26. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/flash.ps1 +0 -0
  27. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/instrument_cli.py +0 -0
  28. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/lib/__init__.py +0 -0
  29. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/lib/instruments.py +0 -0
  30. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/lib/ndtdbg.py +0 -0
  31. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/lib/serial_assistant.py +0 -0
  32. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/logic_capture.ps1 +0 -0
  33. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/logic_decode.ps1 +0 -0
  34. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/ndtdbg_bridge.ps1 +0 -0
  35. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/ndtdbg_cli.py +0 -0
  36. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/reset.ps1 +0 -0
  37. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/serial_cli.py +0 -0
  38. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/sigrok_csv_to_fwloop.py +0 -0
  39. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/test-backends.ps1 +0 -0
  40. {firmwareloop-0.9.0 → firmwareloop-0.9.1}/tools/test.ps1 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.9.0
3
+ Version: 0.9.1
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
@@ -24,6 +24,8 @@ Requires-Dist: pytest>=8.0.0
24
24
  Requires-Dist: pyocd>=0.36.0
25
25
  Requires-Dist: pyyaml>=6.0
26
26
  Requires-Dist: agentic-hil>=0.14.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest-xdist>=3.6.0; extra == "dev"
27
29
  Dynamic: license-file
28
30
 
29
31
  # FirmwareLoop (`fwloop`)
@@ -385,7 +387,7 @@ claude mcp remove --scope user agentic-hil
385
387
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
386
388
  ├── demo-make/ 示例固件(Make;构建测试载体)
387
389
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
388
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.0)
390
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.1)
389
391
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
390
392
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
391
393
  ├── CLAUDE.md Claude Code CLI 指南
@@ -395,7 +397,7 @@ claude mcp remove --scope user agentic-hil
395
397
 
396
398
  ---
397
399
 
398
- ## 能力验证状态(v0.9.0)
400
+ ## 能力验证状态(v0.9.1)
399
401
 
400
402
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
401
403
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -357,7 +357,7 @@ claude mcp remove --scope user agentic-hil
357
357
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
358
358
  ├── demo-make/ 示例固件(Make;构建测试载体)
359
359
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
360
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.0)
360
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.1)
361
361
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
362
362
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
363
363
  ├── CLAUDE.md Claude Code CLI 指南
@@ -367,7 +367,7 @@ claude mcp remove --scope user agentic-hil
367
367
 
368
368
  ---
369
369
 
370
- ## 能力验证状态(v0.9.0)
370
+ ## 能力验证状态(v0.9.1)
371
371
 
372
372
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
373
373
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.9.0
3
+ Version: 0.9.1
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
@@ -24,6 +24,8 @@ Requires-Dist: pytest>=8.0.0
24
24
  Requires-Dist: pyocd>=0.36.0
25
25
  Requires-Dist: pyyaml>=6.0
26
26
  Requires-Dist: agentic-hil>=0.14.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest-xdist>=3.6.0; extra == "dev"
27
29
  Dynamic: license-file
28
30
 
29
31
  # FirmwareLoop (`fwloop`)
@@ -385,7 +387,7 @@ claude mcp remove --scope user agentic-hil
385
387
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
386
388
  ├── demo-make/ 示例固件(Make;构建测试载体)
387
389
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
388
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.0)
390
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.1)
389
391
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
390
392
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
391
393
  ├── CLAUDE.md Claude Code CLI 指南
@@ -395,7 +397,7 @@ claude mcp remove --scope user agentic-hil
395
397
 
396
398
  ---
397
399
 
398
- ## 能力验证状态(v0.9.0)
400
+ ## 能力验证状态(v0.9.1)
399
401
 
400
402
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
401
403
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -32,4 +32,7 @@ tools/common/uart_probe.py
32
32
  tools/lib/__init__.py
33
33
  tools/lib/instruments.py
34
34
  tools/lib/ndtdbg.py
35
- tools/lib/serial_assistant.py
35
+ tools/lib/serial_assistant.py
36
+ tools/templates/lab.example.yaml
37
+ tools/templates/skill-firmwareloop.md
38
+ tools/templates/skill-fwloop-adapter.md
@@ -5,3 +5,6 @@ pytest>=8.0.0
5
5
  pyocd>=0.36.0
6
6
  pyyaml>=6.0
7
7
  agentic-hil>=0.14.0
8
+
9
+ [dev]
10
+ pytest-xdist>=3.6.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "firmwareloop"
7
- version = "0.9.0"
7
+ version = "0.9.1"
8
8
  description = "AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -42,9 +42,20 @@ fwloop = "tools.fw_mcp_server:main"
42
42
  fw-mcp = "tools.fw_mcp_server:main"
43
43
  fw = "tools.fw_mcp_server:main"
44
44
 
45
+ # 开发/验收工具链 (不随 wheel 分发给使用者): 并行跑单测用
46
+ # uv pip install -e ".[dev]"
47
+ # python -m pytest tests/unit -n auto --dist=loadfile
48
+ [project.optional-dependencies]
49
+ dev = [
50
+ "pytest-xdist>=3.6.0",
51
+ ]
52
+
45
53
  [tool.setuptools.packages.find]
46
54
  where = ["."]
47
55
  include = ["tools*"]
48
56
 
49
57
  [tool.setuptools.package-data]
50
58
  "*" = ["*.ps1", "*.psm1", "*.yaml", "*.md", "*.json"]
59
+ # 随包分发的模板/资源 (v0.9.1): tools/templates/ 下的 lab.yaml 模板与 skill 副本。
60
+ # 仓库根的 lab/ 与 skills/ 不在 wheel 内, 安装态必须靠这些副本 (防漂移测试锁定一致)。
61
+ "tools" = ["templates/*.yaml", "templates/*.md"]
@@ -2,4 +2,4 @@
2
2
  FirmwareLoop - AI Agent Firmware Development and Lab Automation Platform.
3
3
  """
4
4
 
5
- __version__ = "0.9.0"
5
+ __version__ = "0.9.1"
@@ -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.9.0).
3
+ fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.9.1).
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, DeepSeek Harness,
@@ -817,13 +817,47 @@ def _detect_hardware() -> Dict[str, Any]:
817
817
  return detected
818
818
 
819
819
 
820
+ _PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__))
821
+
822
+
823
+ def _lab_example_template_path() -> str:
824
+ """lab.yaml 唯一模板定位 (v0.9.1 修复安装态 FileNotFoundError):
825
+ 源码仓库态优先 lab/lab.example.yaml (开发真身); wheel 安装态
826
+ (REPO_ROOT=site-packages, 无 lab/ 目录) 回退随包分发的
827
+ tools/templates/lab.example.yaml——两份由防漂移测试逐行锁定一致。"""
828
+ repo = os.path.join(REPO_ROOT, "lab", "lab.example.yaml")
829
+ if os.path.isfile(repo):
830
+ return repo
831
+ return os.path.join(_PACKAGE_DIR, "templates", "lab.example.yaml")
832
+
833
+
834
+ def _skill_template_path(name: str) -> Optional[str]:
835
+ """skill 源文件定位: 源码仓库 skills/<name>/SKILL.md 优先 (skills/ 不随 wheel
836
+ 分发), 安装态回退包内 tools/templates/skill-<name>.md; 均缺失返回 None。"""
837
+ repo = os.path.join(REPO_ROOT, "skills", name, "SKILL.md")
838
+ if os.path.isfile(repo):
839
+ return repo
840
+ pkg = os.path.join(_PACKAGE_DIR, "templates", f"skill-{name}.md")
841
+ return pkg if os.path.isfile(pkg) else None
842
+
843
+
844
+ def _project_lab_yaml_path() -> str:
845
+ """配置写入位置: 源码仓库态 (REPO_ROOT/lab 存在) 写仓库 lab/lab.yaml (工程习惯
846
+ 与既有测试锚点不变); 安装态写调用方工程 CWD/lab/lab.yaml (IDE 以工程根为 CWD
847
+ 启动 MCP), 避免把配置写进 site-packages 自身。"""
848
+ repo_lab = os.path.join(REPO_ROOT, "lab")
849
+ if os.path.isdir(repo_lab):
850
+ return os.path.join(repo_lab, "lab.yaml")
851
+ return os.path.join(os.getcwd(), "lab", "lab.yaml")
852
+
853
+
820
854
  def _build_lab_yaml_from_example(target_dir: str, target_chip: str, build_backend: str,
821
855
  detected_dll_dir: Optional[str]) -> str:
822
856
  """P1-5: lab/lab.example.yaml 为唯一模板 (example 加段 init 自动跟上);
823
857
  仅替换 project.name/target_chip/build_backend/source_dir 与 dut.uart.port
824
858
  (留空 + 注释 "用 fw_scan_hardware 探测", 去掉硬编码 COM3);
825
859
  ndtdbg.dll_dir 按探测结果预填, 探测不到保持注释态 + 人工指引。"""
826
- example_path = os.path.join(REPO_ROOT, "lab", "lab.example.yaml")
860
+ example_path = _lab_example_template_path()
827
861
  with open(example_path, encoding="utf-8") as fh:
828
862
  lines = fh.read().splitlines()
829
863
  out: List[str] = []
@@ -871,7 +905,7 @@ def _upgrade_lab_yaml(existing_path: str) -> List[str]:
871
905
  import yaml as _yaml
872
906
  with open(existing_path, encoding="utf-8") as fh:
873
907
  existing = _yaml.safe_load(fh) or {}
874
- example_path = os.path.join(REPO_ROOT, "lab", "lab.example.yaml")
908
+ example_path = _lab_example_template_path()
875
909
  with open(example_path, encoding="utf-8") as fh:
876
910
  example = _yaml.safe_load(fh) or {}
877
911
  added: List[str] = []
@@ -1231,8 +1265,8 @@ fw_serial_capture(power_cycle_off_ms=500) # 抓开机日志(窗口内自动
1231
1265
 
1232
1266
 
1233
1267
  def handle_fw_configure_lab(args: Dict[str, Any]) -> Dict[str, Any]:
1234
- lab_yaml_path = os.path.join(REPO_ROOT, "lab", "lab.yaml")
1235
- example_yaml_path = os.path.join(REPO_ROOT, "lab", "lab.example.yaml")
1268
+ lab_yaml_path = _project_lab_yaml_path()
1269
+ example_yaml_path = _lab_example_template_path()
1236
1270
 
1237
1271
  config: Dict[str, Any] = {}
1238
1272
  if os.path.exists(lab_yaml_path):
@@ -2391,7 +2425,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
2391
2425
  },
2392
2426
  "serverInfo": {
2393
2427
  "name": "firmwareloop",
2394
- "version": "0.9.0"
2428
+ "version": "0.9.1"
2395
2429
  }
2396
2430
  }
2397
2431
  }
@@ -2526,7 +2560,7 @@ def _settle_stashed_entrypoints(stashed) -> None:
2526
2560
  def handle_cli_update() -> int:
2527
2561
  """Handle `firmwareloop update` command."""
2528
2562
  print("============================================================")
2529
- print(" FirmwareLoop Auto-Updater (v0.9.0)")
2563
+ print(" FirmwareLoop Auto-Updater (v0.9.1)")
2530
2564
  print("============================================================")
2531
2565
 
2532
2566
  is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
@@ -3173,7 +3207,7 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
3173
3207
  def handle_cli_setup() -> int:
3174
3208
  """Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, DeepSeek Harness (DSH), and ZCode IDE."""
3175
3209
  print("============================================================")
3176
- print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.9.0)")
3210
+ print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.9.1)")
3177
3211
  print("============================================================")
3178
3212
  print("")
3179
3213
 
@@ -3288,27 +3322,28 @@ def handle_cli_setup() -> int:
3288
3322
  print(f" - User MCP: [Warning] Could not write {zc_result['path']}: {zc_result.get('reason')}")
3289
3323
 
3290
3324
  # 7. Global Skills Sync for Antigravity and Claude Code
3325
+ # 源解析 (v0.9.1): 源码仓库 skills/<name>/SKILL.md 优先, 安装态回退包内
3326
+ # tools/templates/skill-<name>.md (skills/ 不随 wheel 分发, 此前安装态静默 0 同步)。
3327
+ print(f"\n[7] Global Skills:")
3291
3328
  ag_skills_dir = os.path.join(user_home, ".gemini", "antigravity-cli", "skills")
3292
3329
  claude_skills_dir = os.path.join(user_home, ".claude", "skills")
3293
- source_skills_dir = os.path.join(REPO_ROOT, "skills")
3294
3330
  skills_synced = 0
3295
- if os.path.exists(source_skills_dir):
3296
- for sk in ["firmwareloop", "fwloop-adapter"]:
3297
- sk_src = os.path.join(source_skills_dir, sk, "SKILL.md")
3298
- if os.path.exists(sk_src):
3299
- try:
3300
- ag_target = os.path.join(ag_skills_dir, sk)
3301
- os.makedirs(ag_target, exist_ok=True)
3302
- shutil.copyfile(sk_src, os.path.join(ag_target, "SKILL.md"))
3303
-
3304
- cl_target = os.path.join(claude_skills_dir, sk)
3305
- os.makedirs(cl_target, exist_ok=True)
3306
- shutil.copyfile(sk_src, os.path.join(cl_target, "SKILL.md"))
3307
- skills_synced += 1
3308
- except Exception:
3309
- pass
3310
-
3311
- print(f"\n[7] Global Skills:")
3331
+ for sk in ["firmwareloop", "fwloop-adapter"]:
3332
+ sk_src = _skill_template_path(sk)
3333
+ if not sk_src:
3334
+ print(f" - {sk}: [Warning] skill 源缺失 (仓库 skills/ 与包内 templates/ 均未找到)")
3335
+ continue
3336
+ try:
3337
+ ag_target = os.path.join(ag_skills_dir, sk)
3338
+ os.makedirs(ag_target, exist_ok=True)
3339
+ shutil.copyfile(sk_src, os.path.join(ag_target, "SKILL.md"))
3340
+
3341
+ cl_target = os.path.join(claude_skills_dir, sk)
3342
+ os.makedirs(cl_target, exist_ok=True)
3343
+ shutil.copyfile(sk_src, os.path.join(cl_target, "SKILL.md"))
3344
+ skills_synced += 1
3345
+ except Exception as exc: # noqa: BLE001
3346
+ print(f" - {sk}: [Warning] {exc}")
3312
3347
  print(f" - Synchronized: [OK] {skills_synced} skills (firmwareloop, fwloop-adapter) to Antigravity & Claude Code")
3313
3348
 
3314
3349
  # 8. Machine-level global lab.yaml (ndtdbg config for the globally installed tool)
@@ -3328,7 +3363,7 @@ def handle_cli_setup() -> int:
3328
3363
  def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
3329
3364
  """Handle `firmwareloop init [target_chip]` command."""
3330
3365
  print("============================================================")
3331
- print(" FirmwareLoop Multi-Agent Project Initializer (v0.9.0)")
3366
+ print(" FirmwareLoop Multi-Agent Project Initializer (v0.9.1)")
3332
3367
  print("============================================================")
3333
3368
  cwd = os.getcwd()
3334
3369
  print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
@@ -3361,7 +3396,7 @@ def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
3361
3396
 
3362
3397
 
3363
3398
  def print_cli_help() -> None:
3364
- print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.9.0)
3399
+ print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.9.1)
3365
3400
 
3366
3401
  Usage:
3367
3402
  fwloop [command] (or: firmwareloop [command])
@@ -3407,7 +3442,7 @@ def main() -> None:
3407
3442
  sys.exit(2)
3408
3443
  sys.exit(handle_cli_ndtdebugtool(args[1]))
3409
3444
  elif cmd in ["version", "-v", "--version"]:
3410
- print("FirmwareLoop v0.9.0")
3445
+ print("FirmwareLoop v0.9.1")
3411
3446
  sys.exit(0)
3412
3447
  elif cmd in ["help", "-h", "--help"]:
3413
3448
  print_cli_help()
@@ -1,6 +1,6 @@
1
1
  <#
2
2
  .SYNOPSIS
3
- Dual-Tier MCP setup and registration helper (v0.9.0).
3
+ Dual-Tier MCP setup and registration helper (v0.9.1).
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.9.0)" -ForegroundColor Green
245
+ Write-Host " FirmwareLoop Dual-Tier MCP & Agent Setup Helper (v0.9.1)" -ForegroundColor Green
246
246
  Write-Host "============================================================" -ForegroundColor Cyan
247
247
  Write-Host ""
248
248
  Write-Host "[1] Google Antigravity / Gemini CLI:" -ForegroundColor Yellow
@@ -0,0 +1,195 @@
1
+ # lab/lab.example.yaml - lab configuration template (Spec §6/§9/§20/§24/§25)
2
+ #
3
+ # REAL values (COM ports, probe serials, instrument IPs, fixture IDs, secrets)
4
+ # MUST NEVER be committed. Copy this file to lab/lab.yaml (gitignored) and fill
5
+ # in your bench. Everything here is a placeholder; the default mode is the
6
+ # simulator, which exercises the pipeline without silicon.
7
+ #
8
+ # Copy: Copy-Item lab\lab.example.yaml lab\lab.yaml
9
+ # Check: .\tools\doctor.ps1 -Json
10
+ # Run: .\tools\test.ps1 -Json
11
+
12
+ project:
13
+ name: open-embedder-demo
14
+ # Build backend hint for tools/build.ps1 auto-detection ("cmake", "keil",
15
+ # "iar", "zephyr", "esp-idf", "platformio", "make"). Leave empty for auto.
16
+ build_backend: cmake
17
+ source_dir: demo-firmware
18
+
19
+ # Device under test. UART mode: "simulator" (default, runs the compiled
20
+ # artifact as a subprocess) or "serial" (real COM port via pyserial).
21
+ dut:
22
+ target:
23
+ # Expected probe identity (Spec §9): when a probe is configured, an
24
+ # identity mismatch MUST abort immediately with TARGET_MISMATCH.
25
+ expected_target: null # e.g. "STM32F401RE" when real silicon
26
+ probe_serial: null # e.g. "000123456789"
27
+ reset:
28
+ # Command line used by tools/reset.ps1 / Agentic HIL when configured.
29
+ tool: null # "agentic-hil" | "openocd" | vendor CLI
30
+ uart:
31
+ mode: simulator # "simulator" | "serial"
32
+ port: COM5 # only used when mode=serial
33
+ baud: 115200
34
+ boot_expect: # Spec §11: expect() not sleep()
35
+ - pattern: "Bootloader"
36
+ timeout_ms: 2000
37
+ - pattern: "Application started"
38
+ timeout_ms: 3000
39
+
40
+ # 自研/自定义调试工具接入模板(可通过 fwloop-adapter skill 交互配置)
41
+ custom_tools:
42
+ flash:
43
+ enabled: false
44
+ command: "D:/Tools/my_flasher.exe --target {target_chip} --file {artifact_path}"
45
+ timeout_ms: 60000
46
+ reset:
47
+ enabled: false
48
+ command: "D:/Tools/my_reset.exe --port {uart_port}"
49
+ timeout_ms: 10000
50
+ log_capture:
51
+ enabled: false
52
+ command: "python D:/Tools/my_logger.py --port {uart_port} --out {output_file}"
53
+ timeout_ms: 30000
54
+
55
+ # NdtBox (USB->IIC) 调试/采集配置, 供 tools/ndtdbg_cli.py 使用 (芯海 Fxx 等)。
56
+ # dll_dir 指向 NdtDebugTool 分发包的 lib 目录 (含 USBIOX.DLL/x86-32, 由 32 位 PS 桥加载)。
57
+ # 使用前退出 STDataCollection.exe (USB 独占)。固件升级为二期能力, 本期不含。
58
+ ndtdbg:
59
+ dll_dir: "C:/path/to/NdtDebugTool/lib"
60
+ chip_type: 0 # 0=芯海Fxx 1=ST 2=Maxim 3=AW 4=芯海M68 (本期仅 0)
61
+ slave_addr: "0xA0" # IIC 从机地址 (0=0xA0 1=0x92 2=0xA0 3=0xB8 4=0xE4)
62
+ iic_speed: 1 # 0=20kHz 1=100kHz
63
+ timeout_ms: 60000
64
+ write_allowed: false # write-reg 还需 CLI --confirm 双确认; 保持 false 即永不写
65
+ stream: # fw_ndtdbg_stream 默认值 (均可省略)
66
+ default_interval_ms: 100
67
+ fail_threshold: 50
68
+ # regs: ["0xEE:8:ch"] # 目标数据寄存器表 (未显式传参时的项目级默认; 缺省且调用未传时 Fail-Closed)
69
+ # 协议 profile (产品协议知识沉淀, agent 无需考古固件源码):
70
+ # 纯读轮询 (continuous): 数据源为持续流, 不配 pre_write 即可
71
+ # 请求-响应 (request-response): 每帧先写命令再读, 配 pre_write 序列
72
+ # pre_write: ["0xEC:10"] # 每帧先写 0xEC=10 触发刷新 (请求-响应式产品示例)
73
+ # 交插写压力剖面 (P1-1, 需 write_allowed: true 且 MCP confirm: true):
74
+ # interleave_writes: ["0x56:cycle:00,01,02,03:every=8"] # 每 8 帧轮换写 (并发时序压力)
75
+ # interleave_writes: ["0xBE:once:01:frame=5"] # 第 5 帧写一次
76
+ # write_allowed 属于会话级临时态: true 用完即关, 不建议写入任何常驻配置文件
77
+
78
+ # 产品协议 profile (第三层知识的外置载体; 需根据目标固件协议配置):
79
+ # target_profile:
80
+ # stream_regs: ["0xEE:8:ch"] # stream 目标数据寄存器表 (P0-1)
81
+ # measure_frame: # fw_ndtdbg_measure 的帧解析 (P1-1)
82
+ # data_reg: "0xEE" # 数据帧寄存器
83
+ # checksum: "negated-sum-le16" # 校验算法: none | negated-sum-le16
84
+ # decode: "int16le" # 通道解码: int16le | uint16le | int8 | uint8 | raw
85
+ # min_len: 4 # 最短帧长度 (字节)
86
+ # write_loop: # selftest 写读回环自验序列 (P2-2)
87
+ # addr: "0xBE"
88
+ # write_data: "01"
89
+ # restore_data: "00"
90
+
91
+ # 串口助手 (fw_serial_* 工具族, 供 tools/serial_cli.py 使用) - 长驻会话收发/帧解析/采集。
92
+ # 端口参数均为 fw_serial_open 的缺省值; 会话 JSONL 日志 (带时间戳 RX/TX) 自动落 log_dir。
93
+ serial:
94
+ # port: COM5 # fw_serial_open 缺省端口 - 刻意留空 (Fail-Closed):
95
+ # # 未显式配置时 open real 直接 CONFIG_ERROR, 避免误开未知端口
96
+ baud: 115200
97
+ data_bits: 8
98
+ parity: "N"
99
+ stop_bits: 1
100
+ log_dir: artifacts/serial # 会话日志目录 (相对工程根, 自动创建)
101
+ parser: # fw_serial_parse 默认规则 (超级报文简化, 均可省略)
102
+ mode: none # none | delimiter | fixed_length
103
+ # delimiter: "0D 0A" # HEX 分隔符 (delimiter 模式)
104
+ # fixed_length: 16 # 帧长 (fixed_length 模式)
105
+ checksum: none # none | sum8 | crc16-modbus (帧尾校验)
106
+ fields: [] # [{name, offset, type: u8|i8|u16le|u32le|f32le|hex|text, len}]
107
+ # script: # simulator 后端可编程响应 (单测/离线演示用)
108
+ # - on_rx: "41 54" # 收到 "AT" 前缀时回复 "OK 42", 其余帧默认 echo
109
+ # reply: "4F 4B 20 34 32"
110
+
111
+ # Instruments for tools/instrument_cli.py (Spec §14-16). backend=simulator
112
+ # returns modelled values; backend=visa drives PyVISA/SCPI. Override SCPI
113
+ # commands per instrument when your device differs from the default recipes.
114
+ instruments:
115
+ scope1:
116
+ type: scope
117
+ backend: simulator # switch to "visa" for real hardware
118
+ resource: "TCPIP::192.168.1.50::INSTR" # VISA resource string
119
+ commands:
120
+ measure_frequency: ":MEASure:FREQuency? {ch}"
121
+ psu1:
122
+ type: power_supply
123
+ backend: simulator # switch to "visa" for real hardware
124
+ resource: "USB0::0x1AB1::0x0E11::DP832A0000::INSTR"
125
+ dmm1:
126
+ type: dmm
127
+ backend: simulator
128
+ resource: "USB0::0x1AB1::0x0E11::DM3058::INSTR"
129
+ awg1:
130
+ type: signal_generator
131
+ backend: simulator
132
+ resource: "TCPIP::192.168.1.51::INSTR"
133
+ relay1:
134
+ type: relay
135
+ backend: simulator
136
+ resource: "TCPIP::192.168.1.52::INSTR"
137
+ relay_channel: 1
138
+
139
+ # Simulated measurement values used by backend=simulator (dry runs).
140
+ simulated_measurements:
141
+ scope:
142
+ frequency: 20000.0 # Hz
143
+ duty: 50.0 # %
144
+ vpp: 3.3 # V
145
+ rms: 1.65 # V
146
+ rise_time: 0.000000005 # s
147
+ psu:
148
+ voltage: 3.3 # V
149
+ current: 0.035 # A
150
+ power: 0.1155 # W
151
+
152
+ # Autonomous repair loop policy (Spec §20). Arms the AI agent with hard stops.
153
+ agent_loop:
154
+ max_code_iterations: 3
155
+ max_flash_attempts_per_iteration: 2
156
+ stop_on_hardware_mismatch: true
157
+ stop_on_permission_denied: true
158
+ stop_on_safety_violation: true
159
+
160
+ # Permission model (Spec §24) - documented defaults; enforcement lives in the
161
+ # tools and the AI_DEV_GUIDE AGENT RULES.
162
+ permissions:
163
+ auto_allowed:
164
+ - build
165
+ - probe
166
+ - uart_rx
167
+ - can_rx
168
+ - read_register
169
+ - read_memory
170
+ - logic_capture
171
+ - scope_measure
172
+ - psu_measure
173
+ - pytest
174
+ - report_read
175
+ require_confirmation:
176
+ - flash
177
+ - reset
178
+ - uart_tx
179
+ - can_tx
180
+ - power_cycle
181
+ - relay
182
+ - psu_output
183
+ - awg_output
184
+ permanently_forbidden:
185
+ - mass_erase
186
+ - otp
187
+ - fuse
188
+ - option_bytes
189
+ - rdp
190
+ - secure_boot
191
+ - production_lock
192
+ - signing_key
193
+ - raw_debugger
194
+ - raw_scpi
195
+ - unbounded_voltage_current
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: firmwareloop
3
+ description: FirmwareLoop workflow skill for orchestrating firmware builds (Keil/CMake/Make/etc.), probe detection (ST-LINK/J-Link), interactive lab configuration, pytest HIL testing, I2C/SPI logic analyzer captures, PyVISA instrument measurements, and end-to-end hardware acceptance.
4
+ ---
5
+
6
+ # FirmwareLoop Skill
7
+
8
+ Use this skill when developing, building, testing, or diagnosing embedded firmware within the FirmwareLoop repository.
9
+
10
+ ## Capabilities & Tools
11
+
12
+ ### Upper-Tier MCP Tools (`firmwareloop`)
13
+ - `fw_doctor`: Run environmental health check.
14
+ - `fw_configure_lab`: Interactively configure or update project parameters, target chip (e.g. STM32F103C8), build backend (e.g. keil), source directory, COM port, and debugger probe in `lab/lab.yaml`.
15
+ - `fw_scan_hardware`: Scan and identify attached hardware debuggers (ST-LINK, J-Link, CMSIS-DAP) and COM ports.
16
+ - `fw_build`: Compile firmware across 7 backends (Keil, CMake, Make, IAR, PlatformIO, Zephyr, ESP-IDF).
17
+ - `fw_flash`: Flash compiled firmware image into target MCU.
18
+ - `fw_reset`: Hardware or software reset of the target MCU.
19
+ - `fw_run_hil_test`: Run 12-item pytest HIL automated testing.
20
+ - `fw_acceptance_scenario`: Execute end-to-end acceptance scenario.
21
+ - `fw_measure`: Query PyVISA/SCPI instrument readings (frequency, duty cycle, Vpp, voltage, current).
22
+ - `fw_logic_capture` / `fw_logic_decode`: Digital protocol capture and verification (I2C, SPI, UART).
23
+ - `fw_get_evidence`: Retrieve audit run artifacts and reports.
24
+
25
+ ### Standard CLI Fallback (when MCP is not connected)
26
+ ```powershell
27
+ .\tools\doctor.ps1 -Json
28
+ .\tools\build.ps1 -Configuration Debug -Clean -Json
29
+ .\tools\test.ps1 -Json
30
+ .\tools\acceptance-scenario.ps1 -Json
31
+ ```
32
+
33
+ ## Mandatory Rules
34
+ 1. **Real-Hardware-First & Zero Fake Data**: Never fake or mock hardware results. When compilers, debuggers, or MCU targets are missing, fail closed immediately and output explicit error diagnostics and user setup guidance.
35
+ 2. **Max 3 Code Iterations**: Never loop infinitely fixing code.
36
+ 3. **Fail Closed on Safety**: Never bypass `limits.yaml`.
37
+ 4. **Verify Evidence**: Ensure all evidence is captured in `artifacts/runs/<run_id>/`.
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: fwloop-adapter
3
+ description: 帮助用户将企业自研或第三方 Debug 探针、固件烧录工具、芯片复位脚本、串口/CAN Log 抓取工具或私有协议分析器快速接入 FirmwareLoop。当用户说"接入自研/自定义工具"、"配置私有烧录器"、"使用我们公司的 debug 脚本"或提供 GUI 上位机时触发。
4
+ ---
5
+
6
+ # FirmwareLoop 自定义工具接入 Skill (fwloop-adapter)
7
+
8
+ 当用户希望将公司内部自研的烧录器、复位工具、串口抓包脚本或私有硬件调试工具接入 FirmwareLoop 时,遵循以下标准化工程流程与小白用户引导规范。
9
+
10
+ ## 适用场景
11
+ 1. 用户拥有专属的命令行烧录工具(如 `my_flasher.exe`、`custom_flash.py`、J-Link 内部批处理脚本)。
12
+ 2. 用户拥有专用的串口/CAN 抓包与日志采集工具。
13
+ 3. 用户拥有私有的板卡供电或复位控制上位机。
14
+ 4. **用户只有带界面(GUI)的 exe 上位机软件,不知道如何接入自动化工作流。**
15
+
16
+ ---
17
+
18
+ ## 面对纯 GUI 上位机软件的专项引导流程(小白友好,先探查后求助)
19
+
20
+ 如果用户告知 **“我们只有一个带界面的 exe 上位机软件(需要用鼠标点按钮选择文件、点击下载/抓包),没有命令行”**,Agent **严禁直接报错或直接让用户去找人**,必须按以下**由近及远、先探查本地现场后求助同事的 4 级阶梯流程**主动引导用户:
21
+
22
+ ### 阶梯 1:先让用户提供上位机所在文件夹路径(最先执行,探查本地现有资源)
23
+ Agent 首先引导用户提供上位机所在的本地目录:
24
+ > “请把您这个上位机所在的文件夹路径(如 `D:\tools\NdtDebugTool-v1.0.19`)发给我,我先帮您扫描一下目录内部的资源。”
25
+ * **Agent 内部执行动作**:
26
+ 1. 扫描同目录下是否存在对应的命令行工具(如 `_cli.exe`、`_console.exe`);
27
+ 2. 扫描同目录下是否有使用说明书(`shouce.md`、`readme.txt`、通信协议规范文档);
28
+ 3. 扫描同目录下是否有底层通信动态库(如 `lib/USBIOX.DLL`、`FTD2XX.DLL`、`CH341.DLL`)或配置文件(`config.ini`);
29
+ 4. 测试运行 `tool.exe --help`、`-h`、`-s` 探测是否存在隐藏的静默命令行开关。
30
+
31
+ ### 阶梯 2:基于目录资源或引导用户提供协议/源码,Agent 编写 Python 脚本代打(无需改上位机)
32
+ 如果阶梯 1 发现了底层通信库(如 `USBIOX.DLL`、`usb2io.dll`)或协议说明,或者用户能提供单片机端代码:
33
+ > “我在您的上位机目录下发现了底层通信库(如 `lib/USBIOX.DLL`)与使用手册。如果您能提供单片机端的 Bootloader 源码或通信协议,我可以直接为您编写一个纯 Python 脚本,直接与硬件通信完成烧录/抓包,完全不需要再打开那个界面软件!”
34
+ * **Agent 内部执行动作**:
35
+ 在 `lab/adapters/` 下编写 Python 驱动脚本(利用 `ctypes` 调用 DLL 或通过串口发包),直接实现标准 JSON 接口,彻底解决接入。
36
+
37
+ ### 阶梯 3:如果本地资源完全不足(真正黑盒),再生成对接说明去找上位机开发同事
38
+ 只有当阶梯 1 和 2 确认该上位机没有任何文档、没有任何底层库、没有协议且是纯黑盒 GUI 时,Agent 才生成《致上位机开发同事的技术说明函》:
39
+ > “我检查了上位机目录,发现它是一个纯黑盒界面程序,且缺少通信协议。您可以将以下需求直接转发给开发该上位机的同事,请他协助加几行命令行调用支持:”
40
+ ```text
41
+ 【需求对接】为上位机增加静默命令行调用支持(适配自动化 AI Agent)
42
+ Hi 同事,我们正在引入 FirmwareLoop 自动化工作流。为了能通过终端自动调用您的上位机执行烧录/抓包,需要您协助提供以下轻量支持(任选其一即可):
43
+ 1. 方案 A(推荐):在现有上位机程序中,增加启动参数判断(例如 `tool.exe --cli --file <固件路径> --port <COM口>`),当传入这些参数时直接在后台静默执行核心烧录/抓包函数,无需弹出窗口;
44
+ 2. 方案 B:直接编译一个对应的控制台版本(如 `tool_cli.exe`);
45
+ 3. 方案 C:如果方便,请分享一下上位机通信协议文档或底层通信动态库(DLL / Python SDK)。
46
+ 执行要求:成功请 exit(0),失败请 exit(1) 并将报错信息打印至 stdout 或写入 log 文件。感谢支持!
47
+ ```
48
+
49
+ ### 阶梯 4:最终兜底方案(UI 自动化 pywinauto 后台点击)
50
+ 如果上位机同事也无法修改,且无法获取协议,Agent 基于 `pywinauto` 编写后台自动化脚本模拟点击按钮完成烧录/抓包。
51
+
52
+ ---
53
+
54
+ ## 引导用户与内部工具开发工程师的对接清单(Checklist)
55
+
56
+ 当用户的自研工具缺少 CLI 接口、需要弹窗点击或行为不明确时,**Agent 应当主动提醒并引导用户,向负责开发该工具的内部工程师提供以下 5 大对接需求**:
57
+
58
+ 1. **提供无界面的静默命令行接口(Headless / Non-Interactive CLI)**:
59
+ - *说明*:Agent 无法通过屏幕点击 GUI 弹窗,工具必须支持纯命令行调用(如 `tool.exe --flash <固件路径> --chip <芯片型号>` 或提供 Python SDK/脚本),全程无需人工按键交互。
60
+ 2. **严格规范退出码(Standard Exit Codes)**:
61
+ - *说明*:如果工具报错了但退出码仍返回 0,Agent 会误判为成功。要求成功时必须 `exit(0)`,任何失败(探针未连、校验失败、超时)必须返回**非 0 退出码**(如 `exit(1)`)。
62
+ 3. **标准输出与日志重定向(Stdout / Log File Export)**:
63
+ - *说明*:Agent 需要根据工具打印的报错细节来排查代码。工具应将诊断信息输出到标准输出(stdout/stderr),或支持参数 `--log-file <path>` 导出文件。
64
+ 4. **明确动态传参占位符(Parameter Mapping)**:
65
+ - 约定工具接收的参数格式:固件路径(`{artifact_path}`)、芯片型号(`{target_chip}`)、串口号(`{uart_port}`)、波特率(`{baud}`)、探针序列号(`{probe_serial}`)。
66
+ 5. **内置操作超时与复位机制(Timeout & Auto-Reset)**:
67
+ - *说明*:工具在目标板卡无响应时需内置超时保护(防止进程死锁挂起 Agent);烧录完成后建议支持可选的自动复位启动(如 `--reset` 参数)。
68
+
69
+ ---
70
+
71
+ ## 标准化接入流程
72
+
73
+ ### 第一步:分析自研工具的接口特征
74
+ 先查看或向用户确认该工具的调用方式:
75
+ 1. **调用形式**:是可执行文件(`.exe`)、Python 脚本(`.py`)、PowerShell 脚本(`.ps1`)还是 C/C++ 动态链接库。
76
+ 2. **入参要求**:
77
+ - 固件烧录类:通常需要目标芯片型号(`{target_chip}`)、固件绝对路径(`{artifact_path}`)、探针序列号(`{probe_serial}`)。
78
+ - 串口/日志类:通常需要 COM 端口号(`{uart_port}`)、波特率(`{baud}`)、日志输出文件(`{output_file}`)、抓取时长(`{duration_ms}`)。
79
+ 3. **退出码与输出规范**:
80
+ - 成功时返回退出码 `0`。
81
+ - 失败时返回非 0 退出码,并在 stdout/stderr 打印具体错误原因。
82
+
83
+ ### 第二步:选择接入模式并实施
84
+
85
+ #### 模式 A:配置驱动型(针对标准 CLI 工具,最推荐)
86
+ 如果用户工具能够直接通过命令行参数完成任务:
87
+ 1. 打开或创建 `lab/lab.yaml`。
88
+ 2. 在 `custom_tools` 节点下配置命令模板(支持占位符 `{artifact_path}`, `{target_chip}`, `{uart_port}`, `{output_file}`):
89
+ ```yaml
90
+ custom_tools:
91
+ flash:
92
+ enabled: true
93
+ command: "D:/MyTools/flasher.exe -c {target_chip} -f {artifact_path}"
94
+ timeout_ms: 60000
95
+ reset:
96
+ enabled: true
97
+ command: "D:/MyTools/reset_tool.exe -p {uart_port}"
98
+ timeout_ms: 10000
99
+ log_capture:
100
+ enabled: true
101
+ command: "python D:/MyTools/logger.py --port {uart_port} --out {output_file}"
102
+ timeout_ms: 30000
103
+ ```
104
+
105
+ #### 模式 B:脚本适配器型(针对复杂参数或前置处理)
106
+ 如果自研工具有前置握手、环境依赖或格式转换要求:
107
+ 1. 在 `lab/adapters/` 目录下创建轻量包装脚本(例如 `lab/adapters/flash_custom.py` 或 `lab/adapters/flash_custom.ps1`)。
108
+ 2. 包装脚本负责:
109
+ - 接收标准参数并调用内部自研工具;
110
+ - 拦截并捕获原始日志,重定向落盘到 `artifacts/logs/`;
111
+ - 输出统一的结构化 JSON(包含 `schema`, `ok`, `error_class`, `log`)。
112
+
113
+ #### 模式 C:并联 MCP 服务型(针对复杂常驻服务或 SDK)
114
+ 如果自研工具本身是完整的服务:
115
+ 在工程根目录的 `.mcp.json` 中并联注册自研 MCP 服务:
116
+ ```json
117
+ {
118
+ "mcpServers": {
119
+ "fwloop": {
120
+ "command": "uvx",
121
+ "args": ["firmwareloop"]
122
+ },
123
+ "custom-debug-tool": {
124
+ "command": "python",
125
+ "args": ["D:/MyTools/company_mcp_server.py"]
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ ### 第三步:真实物理联调验证(Dry-Run)
132
+ 1. 严禁假跑!必须在真实连接硬件的前提下运行一次试运行。
133
+ 2. 检查输出日志是否成功写入 `artifacts/logs/`。
134
+ 3. 验证返回码与异常拦截机制(例如拔掉 USB 探针后,确认工具能正确报错并被 Agent 捕获)。
135
+
136
+ ### 第四步:交付与提示
137
+ 向用户展示配置位置,并说明后续在对话中可以直接使用自然语言调度:
138
+ - “*使用我们公司的烧录工具把固件刷入芯片*”
139
+ - “*启动自研抓包工具采集 5 秒串口日志*”
140
+
141
+ ---
142
+
143
+ ## 可选手法:目标侧"只读诊断插桩"(通用模式)
144
+
145
+ **适用**:目标固件"挂死 / 反复重启",没有可用串口输出(或写通道不可用),但工具族能读寄存器。
146
+ 思路:让固件把**运行痕迹暴露成只读寄存器**,Agent 一次读回即可定位阶段与崩溃现场,
147
+ 不必"改一处 → 烧一次 → 猜一次"。
148
+
149
+ > **⚠️ 归属规则(先看这条)**:本 skill 会**随包分发**(`fwloop init` / `fwloop setup` 会把它
150
+ > 复制到用户工程与各 Agent 的 skills 目录),因此**这里只放与具体项目、具体芯片无关的通用内容**。
151
+ > 每个项目的**地址约定、寄存器含义、现场案例属于项目资产**,请记录到:
152
+ > - 该项目仓库内的 `docs/<project>-probe-convention.md`,或该项目自建的 project skill;
153
+ >
154
+ > 本 skill 负责教你**怎么设计**这套插桩、以及**记录到哪里**,不承载任何单一项目的约定。
155
+
156
+ ### 通用模式(由粗到细四件套)
157
+
158
+ 1. **阶段编号**:最后到达的启动边界;
159
+ 2. **阶段追踪环**:最近 N 步的环形缓冲(看清"路径",而不只是终点);
160
+ 3. **计数器组**:主循环迭代数、可疑等待循环的自旋计数、崩溃现场(fault 状态字 / PC / LR / xPSR);
161
+ 4. **运行时可调"停靠级别"**:写后断电即可逐级二分崩溃点。
162
+
163
+ ### 通用约束(与芯片无关,都是踩出来的)
164
+
165
+ 1. 插桩数组放 **NOINIT** 段:复位/软复位不清零,才看得到"重启前最后执行到哪";
166
+ 2. 尽量**只读注册**,不要占用可写寄存器语义;
167
+ 3. 注册表必须按**地址升序**:乱序会导致从机自检失败 / 初始化提前 `return`
168
+ ⇒ 通信通道根本没起来,现象与"目标掉电"完全一样(极易误判);
169
+ 4. **避开厂商协议占用的地址**:例——烧录工具会往某个固定地址写擦除/完成魔数,
170
+ 插桩变量放在那里会污染工具的判据;
171
+ 5. 采集遵守工具族的协议约束(升序读、单次长度上限、关键字节双读校验——见各工具描述);
172
+ 6. 插桩代码用 `#if` 开关包裹:关闭时零成本、零 RAM。
173
+
174
+ ### 固件侧两条"已知良方"(与芯片无关)
175
+
176
+ 1. **"通道先行"**:把"能被工具读到的通道"(I2C 从机 / 串口)初始化提到所有可疑初始化**之前**
177
+ ⇒ 之后无论哪里崩,设备仍然可读(把它写在最后,往往会被插桩本身锁死);
178
+ 2. **fault handler 禁止裸自旋**(`B .`):其优先级高于一切可配置中断,死等会饿死通信中断,
179
+ 目标"从总线上消失"(工具侧表现为 `BUS_TIMEOUT`)⇒ 应"记录现场 + 复位"。
180
+ (进阶手法"改写异常返回地址 → 恢复函数 → 重建通道 → 停靠"与芯片强相关,换目标需复测。)
181
+
182
+ ### 最小闭环(通用)
183
+
184
+ ```text
185
+ fw_ndtdbg_power_cycle(off_ms=500) # 复位(不需 write_allowed)
186
+ fw_ndtdbg_status # 分级结论 + 判据表
187
+ fw_ndtdbg_read_reg(reg=..., len=...) # 读"项目约定文档"里定义的诊断寄存器
188
+ fw_ndtdbg_flash(...) # 改完固件自助烧录(含烧后冷启动冒烟)
189
+ fw_serial_capture(power_cycle_off_ms=500) # 抓开机日志(窗口内自动复位)
190
+ ```
File without changes
File without changes
File without changes