firmwareloop 0.1.5__tar.gz → 0.1.7__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.5 → firmwareloop-0.1.7}/PKG-INFO +12 -7
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/README.md +11 -6
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/firmwareloop.egg-info/PKG-INFO +12 -7
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/pyproject.toml +1 -1
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/__init__.py +1 -1
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/fw_mcp_server.py +115 -14
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/lib/ndtdbg.py +127 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/ndtdbg_bridge.ps1 +44 -1
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/ndtdbg_cli.py +24 -1
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/setup-agent-mcp.ps1 +2 -2
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/LICENSE +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/firmwareloop.egg-info/SOURCES.txt +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/firmwareloop.egg-info/dependency_links.txt +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/firmwareloop.egg-info/entry_points.txt +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/firmwareloop.egg-info/requires.txt +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/firmwareloop.egg-info/top_level.txt +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/setup.cfg +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/acceptance-scenario.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/build.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/can.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/check-qoder-mcp.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/common/build-backends.psm1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/common/fw.psm1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/common/uart_probe.py +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/doctor.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/flash.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/instrument_cli.py +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/lib/__init__.py +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/lib/instruments.py +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/logic_capture.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/logic_decode.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/reset.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/sigrok_csv_to_fwloop.py +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/tools/test-backends.ps1 +0 -0
- {firmwareloop-0.1.5 → firmwareloop-0.1.7}/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.7
|
|
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
|
|
@@ -124,7 +124,7 @@ uv tool install agentic-hil
|
|
|
124
124
|
如需用 Agent 直接操作 NdtBox(固件寄存器读写 / 数据采集 / 逻辑分析仪协议验证),安装后执行一次:
|
|
125
125
|
|
|
126
126
|
```bash
|
|
127
|
-
# 1) 全局注册各 AI Agent(Antigravity / Claude Code / Qoder),并生成机器级配置模板
|
|
127
|
+
# 1) 全局注册各 AI Agent(Antigravity / Claude Code / Qoder / CodeBuddy),并生成机器级配置模板
|
|
128
128
|
fwloop setup
|
|
129
129
|
|
|
130
130
|
# 2) 指向你的 NdtDebugTool 分发包【根目录】——命令自动定位其 lib/(含 USBIOX.DLL)并写入配置
|
|
@@ -134,7 +134,8 @@ fwloop ndtdebugtool "C:/Users/weilin.zhang/Desktop/zwl/NdtDebugTool-v1.0.19"
|
|
|
134
134
|
> 完成以上两步后,Agent 侧即可直接调用 NdtBox 相关工具:
|
|
135
135
|
> `fw_ndtdbg_status`(版本/连通性/模组通断)、`fw_ndtdbg_measure`(数据轮询采集)、
|
|
136
136
|
> `fw_ndtdbg_read_reg` / `fw_ndtdbg_write_reg`(任意寄存器读写,写需 `write_allowed` + `confirm` 双门控)、
|
|
137
|
-
> `fw_ndtdbg_capture_seq`(寄存器序列 + Saleae
|
|
137
|
+
> `fw_ndtdbg_capture_seq`(寄存器序列 + Saleae 波形同窗口取证)、
|
|
138
|
+
> `fw_ndtdbg_flash`(.bin/.nfw 整片擦写烧录,需 `write_allowed` + `confirm` 双门控,默认拒绝)。
|
|
138
139
|
> 前置硬件要求:NdtBox 已装 CH341 驱动;如需 Saleae 波形证据,另装 sigrok-cli 并配置
|
|
139
140
|
> `libusb-win32` 驱动(详见 `docs/` 交付说明)。测试时请退出 STDataCollection.exe 上位机(USB 独占)。
|
|
140
141
|
|
|
@@ -163,7 +164,10 @@ claude mcp add --scope user agentic-hil -- agentic-hil mcp-stdio
|
|
|
163
164
|
|
|
164
165
|
不要依赖 `qoder.cmd --add-mcp`:它写到 `%APPDATA%\Qoder\User\mcp.json`,设置页看不到。
|
|
165
166
|
|
|
166
|
-
4. **
|
|
167
|
+
4. **CodeBuddy IDE**:
|
|
168
|
+
`fwloop setup` 会合并写入用户级配置 `~/.codebuddy/mcp.json`(`mcpServers` 形状与 Qoder 兼容,含官方 `description` 字段;既有条目与 `env` 自定义字段保留)。`~/.codebuddy` 目录不存在时自动跳过。
|
|
169
|
+
|
|
170
|
+
5. **Cursor / 其他 IDE**(在工程根目录的 `.mcp.json` 中并联配置):
|
|
167
171
|
```json
|
|
168
172
|
{
|
|
169
173
|
"mcpServers": {
|
|
@@ -203,7 +207,7 @@ fwloop init
|
|
|
203
207
|
|
|
204
208
|
| 命令 | 适用级别 | 核心作用 | 使用时机 |
|
|
205
209
|
|---|---|---|---|
|
|
206
|
-
| **`fwloop setup`** | **系统全局级** | 给各大 Agent(Antigravity / Claude Code / Qoder / Qoder CN)配置全局 MCP 与同步技能包 | **全电脑只需跑 1 次** |
|
|
210
|
+
| **`fwloop setup`** | **系统全局级** | 给各大 Agent(Antigravity / Claude Code / Qoder / Qoder CN / CodeBuddy)配置全局 MCP 与同步技能包 | **全电脑只需跑 1 次** |
|
|
207
211
|
| **`fwloop init`** | **工程项目级** | 在当前单片机代码目录下生成 `AGENTS.md`、`GEMINI.md`、`CLAUDE.md` 与台架配置 | **每个新单片机项目跑 1 次** |
|
|
208
212
|
| **`fwloop doctor`** | **环境诊断** | 检查编译器(Keil/GCC)、Python 环境、串口与探针连接健康度 | 随时排查环境时使用 |
|
|
209
213
|
| **`fwloop update`** | **自动更新** | 一键自动拉取最新代码并热重载依赖 | 升级工具版本时使用 |
|
|
@@ -359,7 +363,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
359
363
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
360
364
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
361
365
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
362
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.
|
|
366
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.7)
|
|
363
367
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
364
368
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
365
369
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -369,7 +373,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
369
373
|
|
|
370
374
|
---
|
|
371
375
|
|
|
372
|
-
## 能力验证状态(v0.1.
|
|
376
|
+
## 能力验证状态(v0.1.7)
|
|
373
377
|
|
|
374
378
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
375
379
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -390,6 +394,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
390
394
|
| Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
|
|
391
395
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
392
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) | Implemented(时序对齐原厂 UpdateFW, 双闸门 Fail-Closed 已验证)-> Real Hardware 待烧录验收 |
|
|
393
398
|
|
|
394
399
|
---
|
|
395
400
|
|
|
@@ -96,7 +96,7 @@ uv tool install agentic-hil
|
|
|
96
96
|
如需用 Agent 直接操作 NdtBox(固件寄存器读写 / 数据采集 / 逻辑分析仪协议验证),安装后执行一次:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
|
-
# 1) 全局注册各 AI Agent(Antigravity / Claude Code / Qoder),并生成机器级配置模板
|
|
99
|
+
# 1) 全局注册各 AI Agent(Antigravity / Claude Code / Qoder / CodeBuddy),并生成机器级配置模板
|
|
100
100
|
fwloop setup
|
|
101
101
|
|
|
102
102
|
# 2) 指向你的 NdtDebugTool 分发包【根目录】——命令自动定位其 lib/(含 USBIOX.DLL)并写入配置
|
|
@@ -106,7 +106,8 @@ fwloop ndtdebugtool "C:/Users/weilin.zhang/Desktop/zwl/NdtDebugTool-v1.0.19"
|
|
|
106
106
|
> 完成以上两步后,Agent 侧即可直接调用 NdtBox 相关工具:
|
|
107
107
|
> `fw_ndtdbg_status`(版本/连通性/模组通断)、`fw_ndtdbg_measure`(数据轮询采集)、
|
|
108
108
|
> `fw_ndtdbg_read_reg` / `fw_ndtdbg_write_reg`(任意寄存器读写,写需 `write_allowed` + `confirm` 双门控)、
|
|
109
|
-
> `fw_ndtdbg_capture_seq`(寄存器序列 + Saleae
|
|
109
|
+
> `fw_ndtdbg_capture_seq`(寄存器序列 + Saleae 波形同窗口取证)、
|
|
110
|
+
> `fw_ndtdbg_flash`(.bin/.nfw 整片擦写烧录,需 `write_allowed` + `confirm` 双门控,默认拒绝)。
|
|
110
111
|
> 前置硬件要求:NdtBox 已装 CH341 驱动;如需 Saleae 波形证据,另装 sigrok-cli 并配置
|
|
111
112
|
> `libusb-win32` 驱动(详见 `docs/` 交付说明)。测试时请退出 STDataCollection.exe 上位机(USB 独占)。
|
|
112
113
|
|
|
@@ -135,7 +136,10 @@ claude mcp add --scope user agentic-hil -- agentic-hil mcp-stdio
|
|
|
135
136
|
|
|
136
137
|
不要依赖 `qoder.cmd --add-mcp`:它写到 `%APPDATA%\Qoder\User\mcp.json`,设置页看不到。
|
|
137
138
|
|
|
138
|
-
4. **
|
|
139
|
+
4. **CodeBuddy IDE**:
|
|
140
|
+
`fwloop setup` 会合并写入用户级配置 `~/.codebuddy/mcp.json`(`mcpServers` 形状与 Qoder 兼容,含官方 `description` 字段;既有条目与 `env` 自定义字段保留)。`~/.codebuddy` 目录不存在时自动跳过。
|
|
141
|
+
|
|
142
|
+
5. **Cursor / 其他 IDE**(在工程根目录的 `.mcp.json` 中并联配置):
|
|
139
143
|
```json
|
|
140
144
|
{
|
|
141
145
|
"mcpServers": {
|
|
@@ -175,7 +179,7 @@ fwloop init
|
|
|
175
179
|
|
|
176
180
|
| 命令 | 适用级别 | 核心作用 | 使用时机 |
|
|
177
181
|
|---|---|---|---|
|
|
178
|
-
| **`fwloop setup`** | **系统全局级** | 给各大 Agent(Antigravity / Claude Code / Qoder / Qoder CN)配置全局 MCP 与同步技能包 | **全电脑只需跑 1 次** |
|
|
182
|
+
| **`fwloop setup`** | **系统全局级** | 给各大 Agent(Antigravity / Claude Code / Qoder / Qoder CN / CodeBuddy)配置全局 MCP 与同步技能包 | **全电脑只需跑 1 次** |
|
|
179
183
|
| **`fwloop init`** | **工程项目级** | 在当前单片机代码目录下生成 `AGENTS.md`、`GEMINI.md`、`CLAUDE.md` 与台架配置 | **每个新单片机项目跑 1 次** |
|
|
180
184
|
| **`fwloop doctor`** | **环境诊断** | 检查编译器(Keil/GCC)、Python 环境、串口与探针连接健康度 | 随时排查环境时使用 |
|
|
181
185
|
| **`fwloop update`** | **自动更新** | 一键自动拉取最新代码并热重载依赖 | 升级工具版本时使用 |
|
|
@@ -331,7 +335,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
331
335
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
332
336
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
333
337
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
334
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.
|
|
338
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.7)
|
|
335
339
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
336
340
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
337
341
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -341,7 +345,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
341
345
|
|
|
342
346
|
---
|
|
343
347
|
|
|
344
|
-
## 能力验证状态(v0.1.
|
|
348
|
+
## 能力验证状态(v0.1.7)
|
|
345
349
|
|
|
346
350
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
347
351
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -362,6 +366,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
362
366
|
| Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
|
|
363
367
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
364
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) | Implemented(时序对齐原厂 UpdateFW, 双闸门 Fail-Closed 已验证)-> Real Hardware 待烧录验收 |
|
|
365
370
|
|
|
366
371
|
---
|
|
367
372
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firmwareloop
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
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
|
|
@@ -124,7 +124,7 @@ uv tool install agentic-hil
|
|
|
124
124
|
如需用 Agent 直接操作 NdtBox(固件寄存器读写 / 数据采集 / 逻辑分析仪协议验证),安装后执行一次:
|
|
125
125
|
|
|
126
126
|
```bash
|
|
127
|
-
# 1) 全局注册各 AI Agent(Antigravity / Claude Code / Qoder),并生成机器级配置模板
|
|
127
|
+
# 1) 全局注册各 AI Agent(Antigravity / Claude Code / Qoder / CodeBuddy),并生成机器级配置模板
|
|
128
128
|
fwloop setup
|
|
129
129
|
|
|
130
130
|
# 2) 指向你的 NdtDebugTool 分发包【根目录】——命令自动定位其 lib/(含 USBIOX.DLL)并写入配置
|
|
@@ -134,7 +134,8 @@ fwloop ndtdebugtool "C:/Users/weilin.zhang/Desktop/zwl/NdtDebugTool-v1.0.19"
|
|
|
134
134
|
> 完成以上两步后,Agent 侧即可直接调用 NdtBox 相关工具:
|
|
135
135
|
> `fw_ndtdbg_status`(版本/连通性/模组通断)、`fw_ndtdbg_measure`(数据轮询采集)、
|
|
136
136
|
> `fw_ndtdbg_read_reg` / `fw_ndtdbg_write_reg`(任意寄存器读写,写需 `write_allowed` + `confirm` 双门控)、
|
|
137
|
-
> `fw_ndtdbg_capture_seq`(寄存器序列 + Saleae
|
|
137
|
+
> `fw_ndtdbg_capture_seq`(寄存器序列 + Saleae 波形同窗口取证)、
|
|
138
|
+
> `fw_ndtdbg_flash`(.bin/.nfw 整片擦写烧录,需 `write_allowed` + `confirm` 双门控,默认拒绝)。
|
|
138
139
|
> 前置硬件要求:NdtBox 已装 CH341 驱动;如需 Saleae 波形证据,另装 sigrok-cli 并配置
|
|
139
140
|
> `libusb-win32` 驱动(详见 `docs/` 交付说明)。测试时请退出 STDataCollection.exe 上位机(USB 独占)。
|
|
140
141
|
|
|
@@ -163,7 +164,10 @@ claude mcp add --scope user agentic-hil -- agentic-hil mcp-stdio
|
|
|
163
164
|
|
|
164
165
|
不要依赖 `qoder.cmd --add-mcp`:它写到 `%APPDATA%\Qoder\User\mcp.json`,设置页看不到。
|
|
165
166
|
|
|
166
|
-
4. **
|
|
167
|
+
4. **CodeBuddy IDE**:
|
|
168
|
+
`fwloop setup` 会合并写入用户级配置 `~/.codebuddy/mcp.json`(`mcpServers` 形状与 Qoder 兼容,含官方 `description` 字段;既有条目与 `env` 自定义字段保留)。`~/.codebuddy` 目录不存在时自动跳过。
|
|
169
|
+
|
|
170
|
+
5. **Cursor / 其他 IDE**(在工程根目录的 `.mcp.json` 中并联配置):
|
|
167
171
|
```json
|
|
168
172
|
{
|
|
169
173
|
"mcpServers": {
|
|
@@ -203,7 +207,7 @@ fwloop init
|
|
|
203
207
|
|
|
204
208
|
| 命令 | 适用级别 | 核心作用 | 使用时机 |
|
|
205
209
|
|---|---|---|---|
|
|
206
|
-
| **`fwloop setup`** | **系统全局级** | 给各大 Agent(Antigravity / Claude Code / Qoder / Qoder CN)配置全局 MCP 与同步技能包 | **全电脑只需跑 1 次** |
|
|
210
|
+
| **`fwloop setup`** | **系统全局级** | 给各大 Agent(Antigravity / Claude Code / Qoder / Qoder CN / CodeBuddy)配置全局 MCP 与同步技能包 | **全电脑只需跑 1 次** |
|
|
207
211
|
| **`fwloop init`** | **工程项目级** | 在当前单片机代码目录下生成 `AGENTS.md`、`GEMINI.md`、`CLAUDE.md` 与台架配置 | **每个新单片机项目跑 1 次** |
|
|
208
212
|
| **`fwloop doctor`** | **环境诊断** | 检查编译器(Keil/GCC)、Python 环境、串口与探针连接健康度 | 随时排查环境时使用 |
|
|
209
213
|
| **`fwloop update`** | **自动更新** | 一键自动拉取最新代码并热重载依赖 | 升级工具版本时使用 |
|
|
@@ -359,7 +363,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
359
363
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
360
364
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
361
365
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
362
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.
|
|
366
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.1.7)
|
|
363
367
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
364
368
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
365
369
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -369,7 +373,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
369
373
|
|
|
370
374
|
---
|
|
371
375
|
|
|
372
|
-
## 能力验证状态(v0.1.
|
|
376
|
+
## 能力验证状态(v0.1.7)
|
|
373
377
|
|
|
374
378
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
375
379
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -390,6 +394,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
390
394
|
| Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
|
|
391
395
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
392
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) | Implemented(时序对齐原厂 UpdateFW, 双闸门 Fail-Closed 已验证)-> Real Hardware 待烧录验收 |
|
|
393
398
|
|
|
394
399
|
---
|
|
395
400
|
|
|
@@ -1,9 +1,9 @@
|
|
|
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.7).
|
|
4
4
|
|
|
5
5
|
Exposes high-level firmware engineering and hardware management tools to AI Coding
|
|
6
|
-
Agents (Antigravity CLI, Claude Code CLI, Qoder IDE, Cursor) via the Model Context
|
|
6
|
+
Agents (Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, Cursor) via the Model Context
|
|
7
7
|
Protocol (JSON-RPC 2.0 stdio).
|
|
8
8
|
|
|
9
9
|
Tools provided:
|
|
@@ -19,6 +19,7 @@ Tools provided:
|
|
|
19
19
|
- fw_ndtdbg_read_reg: NdtBox IIC register read (ndtdbg_cli.py)
|
|
20
20
|
- fw_ndtdbg_write_reg: NdtBox IIC register write, double-gated (ndtdbg_cli.py)
|
|
21
21
|
- fw_ndtdbg_capture_seq: NdtBox register sequence + concurrent I2C capture for waveform evidence (ndtdbg_cli.py)
|
|
22
|
+
- fw_ndtdbg_flash: NdtBox IIC firmware flash (.bin/.nfw, full-chip erase, double-gated) (ndtdbg_cli.py)
|
|
22
23
|
- fw_logic_capture: Logic analyzer protocol capture (logic_capture.ps1)
|
|
23
24
|
- fw_logic_decode: Logic analyzer protocol decoding & assertion (logic_decode.ps1)
|
|
24
25
|
- fw_get_evidence: Read structured audit run evidence (artifacts/runs/)
|
|
@@ -306,6 +307,18 @@ def handle_fw_ndtdbg_write_reg(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
306
307
|
return res
|
|
307
308
|
|
|
308
309
|
|
|
310
|
+
def handle_fw_ndtdbg_flash(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
311
|
+
"""Erase + flash a .bin/.nfw image onto the NdtBox target chip (ChipSea Fxx) over IIC."""
|
|
312
|
+
cli_script = os.path.join(REPO_ROOT, "tools", "ndtdbg_cli.py")
|
|
313
|
+
cmd = [PYTHON_EXE, cli_script, "flash-bin", "--file", str(args.get("file_path")),
|
|
314
|
+
"--erase-ms", str(int(args.get("erase_ms", 2000)))]
|
|
315
|
+
if args.get("confirm"):
|
|
316
|
+
cmd.append("--confirm")
|
|
317
|
+
_ndtdbg_reg_args(args, cmd)
|
|
318
|
+
timeout_s = min(max(int(args.get("timeout_seconds", 300)), 30), 900)
|
|
319
|
+
return run_process(cmd, timeout=timeout_s)
|
|
320
|
+
|
|
321
|
+
|
|
309
322
|
def handle_fw_ndtdbg_capture_seq(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
310
323
|
"""单次调用内并发: 逻辑分析仪抓取与 NdtBox 寄存器序列同时进行。
|
|
311
324
|
|
|
@@ -1265,6 +1278,47 @@ TOOLS_REGISTRY = {
|
|
|
1265
1278
|
},
|
|
1266
1279
|
"handler": handle_fw_ndtdbg_write_reg,
|
|
1267
1280
|
},
|
|
1281
|
+
"fw_ndtdbg_flash": {
|
|
1282
|
+
"description": "Erase and flash a .bin/.nfw firmware image onto the NdtBox target chip (ChipSea Fxx) over IIC via NdtDebugTool (power-cycle reset into boot -> erase magic AA 55 A5 5A -> 128B page writes -> 256B read-back verify -> finish 'PASs'). DESTRUCTIVE full-chip erase: requires lab.yaml ndtdbg.write_allowed: true AND confirm: true, otherwise PERMISSION_DENIED.",
|
|
1283
|
+
"inputSchema": {
|
|
1284
|
+
"type": "object",
|
|
1285
|
+
"properties": {
|
|
1286
|
+
"file_path": {
|
|
1287
|
+
"type": "string",
|
|
1288
|
+
"description": "Firmware image path (.bin auto-padded to 128-byte alignment with 0xFF; .nfw stripped of its 256-byte header)",
|
|
1289
|
+
},
|
|
1290
|
+
"confirm": {
|
|
1291
|
+
"type": "boolean",
|
|
1292
|
+
"description": "Acknowledge the destructive full-chip erase (double-gated with lab.yaml ndtdbg.write_allowed)",
|
|
1293
|
+
"default": False,
|
|
1294
|
+
},
|
|
1295
|
+
"erase_ms": {
|
|
1296
|
+
"type": "integer",
|
|
1297
|
+
"description": "Erase wait in ms (align with factory UpdateFW: 2000)",
|
|
1298
|
+
"default": 2000,
|
|
1299
|
+
},
|
|
1300
|
+
"timeout_seconds": {
|
|
1301
|
+
"type": "integer",
|
|
1302
|
+
"description": "CLI process timeout; budget ~30s per 64KB image (page writes paced at 30ms)",
|
|
1303
|
+
"default": 300,
|
|
1304
|
+
},
|
|
1305
|
+
"dll_dir": {
|
|
1306
|
+
"type": "string",
|
|
1307
|
+
"description": "NdtDebugTool lib directory containing USBIOX.DLL and usb2io.dll; default from lab.yaml ndtdbg.dll_dir",
|
|
1308
|
+
},
|
|
1309
|
+
"slave_addr": {
|
|
1310
|
+
"type": "string",
|
|
1311
|
+
"description": "IIC slave address (e.g. 0xA0); default from lab.yaml ndtdbg.slave_addr",
|
|
1312
|
+
},
|
|
1313
|
+
"speed": {
|
|
1314
|
+
"type": "integer",
|
|
1315
|
+
"description": "IIC speed 0=20kHz 1=100kHz; default from lab.yaml ndtdbg.iic_speed",
|
|
1316
|
+
},
|
|
1317
|
+
},
|
|
1318
|
+
"required": ["file_path"],
|
|
1319
|
+
},
|
|
1320
|
+
"handler": handle_fw_ndtdbg_flash,
|
|
1321
|
+
},
|
|
1268
1322
|
"fw_ndtdbg_capture_seq": {
|
|
1269
1323
|
"description": "One-call protocol evidence: concurrently runs a NdtBox register write sequence (default 0xBE=01, 0x01=BE 42, poll 0x01, readback 0xBE) and a 3s Saleae/sigrok I2C capture, returning the capture path for fw_logic_decode.",
|
|
1270
1324
|
"inputSchema": {
|
|
@@ -1433,7 +1487,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
1433
1487
|
},
|
|
1434
1488
|
"serverInfo": {
|
|
1435
1489
|
"name": "firmwareloop",
|
|
1436
|
-
"version": "0.1.
|
|
1490
|
+
"version": "0.1.7"
|
|
1437
1491
|
}
|
|
1438
1492
|
}
|
|
1439
1493
|
}
|
|
@@ -1525,7 +1579,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
1525
1579
|
def handle_cli_update() -> int:
|
|
1526
1580
|
"""Handle `firmwareloop update` command."""
|
|
1527
1581
|
print("============================================================")
|
|
1528
|
-
print(" FirmwareLoop Auto-Updater (v0.1.
|
|
1582
|
+
print(" FirmwareLoop Auto-Updater (v0.1.7)")
|
|
1529
1583
|
print("============================================================")
|
|
1530
1584
|
|
|
1531
1585
|
is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
|
|
@@ -1674,6 +1728,39 @@ def configure_qoder_user_mcp(user_home: str) -> List[Dict[str, Any]]:
|
|
|
1674
1728
|
return results
|
|
1675
1729
|
|
|
1676
1730
|
|
|
1731
|
+
CODEBUDDY_USER_MCP_SERVERS = {
|
|
1732
|
+
"firmwareloop": {
|
|
1733
|
+
"command": "fwloop",
|
|
1734
|
+
"type": "stdio",
|
|
1735
|
+
"description": "FirmwareLoop firmware engineering & lab automation MCP server",
|
|
1736
|
+
},
|
|
1737
|
+
"agentic-hil": {
|
|
1738
|
+
"command": "agentic-hil",
|
|
1739
|
+
"args": ["mcp-stdio"],
|
|
1740
|
+
"type": "stdio",
|
|
1741
|
+
"description": "agentic-hil hardware bench MCP server",
|
|
1742
|
+
},
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
|
|
1746
|
+
def configure_codebuddy_user_mcp(user_home: str) -> Dict[str, Any]:
|
|
1747
|
+
"""Write firmwareloop MCP entries into the CodeBuddy IDE user-level ~/.codebuddy/mcp.json.
|
|
1748
|
+
|
|
1749
|
+
CodeBuddy reads the same mcpServers JSON shape as Qoder (type/command/args,
|
|
1750
|
+
plus an optional per-entry "description" per official docs), so the Qoder
|
|
1751
|
+
merge helper is reused with a CodeBuddy-specific payload.
|
|
1752
|
+
"""
|
|
1753
|
+
mcp_dir = os.path.join(user_home, ".codebuddy")
|
|
1754
|
+
mcp_file = os.path.join(mcp_dir, "mcp.json")
|
|
1755
|
+
if not os.path.isdir(mcp_dir):
|
|
1756
|
+
return {"ok": False, "path": mcp_file, "reason": "not_installed"}
|
|
1757
|
+
try:
|
|
1758
|
+
upsert_qoder_mcp_json(mcp_file, CODEBUDDY_USER_MCP_SERVERS)
|
|
1759
|
+
return {"ok": True, "path": mcp_file}
|
|
1760
|
+
except Exception as exc:
|
|
1761
|
+
return {"ok": False, "path": mcp_file, "reason": str(exc)}
|
|
1762
|
+
|
|
1763
|
+
|
|
1677
1764
|
def resolve_ndtdebugtool_lib(tool_root: str) -> str:
|
|
1678
1765
|
"""Resolve the NdtDebugTool lib directory (must contain USBIOX.DLL)."""
|
|
1679
1766
|
if not tool_root or not os.path.isdir(tool_root):
|
|
@@ -1760,9 +1847,9 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
|
|
|
1760
1847
|
|
|
1761
1848
|
|
|
1762
1849
|
def handle_cli_setup() -> int:
|
|
1763
|
-
"""Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, and
|
|
1850
|
+
"""Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, and CodeBuddy IDE."""
|
|
1764
1851
|
print("============================================================")
|
|
1765
|
-
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.1.
|
|
1852
|
+
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.1.7)")
|
|
1766
1853
|
print("============================================================")
|
|
1767
1854
|
print("")
|
|
1768
1855
|
|
|
@@ -1834,7 +1921,19 @@ def handle_cli_setup() -> int:
|
|
|
1834
1921
|
continue
|
|
1835
1922
|
print(f" - {item['edition']}: [Warning] Could not write {item['path']}: {item.get('reason')}")
|
|
1836
1923
|
|
|
1837
|
-
# 4.
|
|
1924
|
+
# 4. CodeBuddy IDE user-level MCP (~/.codebuddy/mcp.json)
|
|
1925
|
+
# Same mcpServers JSON shape as Qoder; supports an optional per-entry "description".
|
|
1926
|
+
print("\n[4] CodeBuddy IDE:")
|
|
1927
|
+
cb_result = configure_codebuddy_user_mcp(user_home)
|
|
1928
|
+
if cb_result.get("ok"):
|
|
1929
|
+
print(f" - User MCP: [OK] Written to {cb_result['path']}")
|
|
1930
|
+
configured_agents.append("CodeBuddy")
|
|
1931
|
+
elif cb_result.get("reason") == "not_installed":
|
|
1932
|
+
print(" - User MCP: ~/.codebuddy not found (Workspace .mcp.json still supported)")
|
|
1933
|
+
else:
|
|
1934
|
+
print(f" - User MCP: [Warning] Could not write {cb_result['path']}: {cb_result.get('reason')}")
|
|
1935
|
+
|
|
1936
|
+
# 5. Global Skills Sync for Antigravity and Claude Code
|
|
1838
1937
|
ag_skills_dir = os.path.join(user_home, ".gemini", "antigravity-cli", "skills")
|
|
1839
1938
|
claude_skills_dir = os.path.join(user_home, ".claude", "skills")
|
|
1840
1939
|
source_skills_dir = os.path.join(REPO_ROOT, "skills")
|
|
@@ -1855,14 +1954,14 @@ def handle_cli_setup() -> int:
|
|
|
1855
1954
|
except Exception:
|
|
1856
1955
|
pass
|
|
1857
1956
|
|
|
1858
|
-
print(f"\n[
|
|
1957
|
+
print(f"\n[5] Global Skills:")
|
|
1859
1958
|
print(f" - Synchronized: [OK] {skills_synced} skills (firmwareloop, fwloop-adapter) to Antigravity & Claude Code")
|
|
1860
1959
|
|
|
1861
1960
|
# 5. Machine-level global lab.yaml (ndtdbg config for the globally installed tool)
|
|
1862
1961
|
global_cfg = ensure_global_lab_yaml()
|
|
1863
1962
|
if global_cfg:
|
|
1864
1963
|
exists = " [已存在, 未覆盖]" if os.path.isfile(global_cfg) else " [已创建]"
|
|
1865
|
-
print("\n[
|
|
1964
|
+
print("\n[6] NdtBox 全局配置:")
|
|
1866
1965
|
print(f" - {global_cfg}{exists}")
|
|
1867
1966
|
print(" - 若未配置, 请运行: fwloop ndtdebugtool \"<NdtDebugTool 根目录>\"")
|
|
1868
1967
|
|
|
@@ -1875,7 +1974,7 @@ def handle_cli_setup() -> int:
|
|
|
1875
1974
|
def handle_cli_init() -> int:
|
|
1876
1975
|
"""Handle `firmwareloop init` command."""
|
|
1877
1976
|
print("============================================================")
|
|
1878
|
-
print(" FirmwareLoop Multi-Agent Project Initializer (v0.1.
|
|
1977
|
+
print(" FirmwareLoop Multi-Agent Project Initializer (v0.1.7)")
|
|
1879
1978
|
print("============================================================")
|
|
1880
1979
|
cwd = os.getcwd()
|
|
1881
1980
|
print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
|
|
@@ -1887,7 +1986,7 @@ def handle_cli_init() -> int:
|
|
|
1887
1986
|
print(" [*] All agent instruction files (AGENTS.md, CLAUDE.md, GEMINI.md, lab.yaml, .mcp.json) already exist.")
|
|
1888
1987
|
print("")
|
|
1889
1988
|
print("============================================================")
|
|
1890
|
-
print("[+] Done! All AI Coding Agents (Claude Code, Qoder, Antigravity, Cursor) are now ready to operate in this repository.")
|
|
1989
|
+
print("[+] Done! All AI Coding Agents (Claude Code, Qoder, Antigravity, CodeBuddy, Cursor) are now ready to operate in this repository.")
|
|
1891
1990
|
print("============================================================")
|
|
1892
1991
|
return 0
|
|
1893
1992
|
else:
|
|
@@ -1896,7 +1995,7 @@ def handle_cli_init() -> int:
|
|
|
1896
1995
|
|
|
1897
1996
|
|
|
1898
1997
|
def print_cli_help() -> None:
|
|
1899
|
-
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.7)
|
|
1900
1999
|
|
|
1901
2000
|
Usage:
|
|
1902
2001
|
fwloop [command] (or: firmwareloop [command])
|
|
@@ -1905,7 +2004,9 @@ Commands:
|
|
|
1905
2004
|
init Initialize multi-agent rules (AGENTS.md, CLAUDE.md, GEMINI.md) & lab.yaml in current project
|
|
1906
2005
|
update Check and update FirmwareLoop to the latest version
|
|
1907
2006
|
doctor Run environment & toolchain diagnostics
|
|
1908
|
-
setup
|
|
2007
|
+
setup Register AI Agent MCP globally & generate machine-level config (%APPDATA%\\FirmwareLoop\\lab.yaml)
|
|
2008
|
+
ndtdebugtool DIR Configure NdtBox: point to the NdtDebugTool folder root (auto-resolves its lib/)
|
|
2009
|
+
dlldir DIR Alias of ndtdebugtool (legacy name, pass the lib/ directory directly)
|
|
1909
2010
|
version, -v Show current version
|
|
1910
2011
|
help, -h Show this help message
|
|
1911
2012
|
|
|
@@ -1934,7 +2035,7 @@ def main() -> None:
|
|
|
1934
2035
|
sys.exit(2)
|
|
1935
2036
|
sys.exit(handle_cli_ndtdebugtool(args[1]))
|
|
1936
2037
|
elif cmd in ["version", "-v", "--version"]:
|
|
1937
|
-
print("FirmwareLoop v0.1.
|
|
2038
|
+
print("FirmwareLoop v0.1.7")
|
|
1938
2039
|
sys.exit(0)
|
|
1939
2040
|
elif cmd in ["help", "-h", "--help"]:
|
|
1940
2041
|
print_cli_help()
|
|
@@ -92,6 +92,53 @@ def pad_bin_128(file_bytes: bytes) -> bytes:
|
|
|
92
92
|
return file_bytes + b"\xFF" * pad
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# 固件烧录 (对齐 ChipSea.cs UpdateFW: 断电复位 -> 整片擦除 -> 页写 -> 回读校验 -> 完成魔数)
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
FLASH_WRITE_PAGE = 128 # WriteFw 每事务 128 字节负载 (IIC 写共 130B = 2B 页地址 + 负载)
|
|
100
|
+
FLASH_READ_WINDOW = 256 # ReadFwAndCheck 每窗回读 256 字节
|
|
101
|
+
ERASE_MAGIC = b"\xAA\x55\xA5\x5A" # EreaseChipCmd 写 @0x0000
|
|
102
|
+
FINISH_MAGIC = b"\x50\x41\x53\x73" # UpgradeFinishCmd "PASs" 写 @0x0000
|
|
103
|
+
BOOT_READY_MS = 100 # VBRTOFF 上电后到发擦除命令的等待 (UpdateFW Thread.Sleep(100))
|
|
104
|
+
PAGE_WRITE_GAP_MS = 30 # 页写间隔 (WriteFw Thread.Sleep(30))
|
|
105
|
+
PAGE_READ_GAP_MS = 10 # 回读窗口间隔 (ReadFwAndCheck Thread.Sleep(10))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def load_firmware(file_path: str) -> tuple[str, bytes]:
|
|
109
|
+
"""读取固件: .nfw 去 256 头 / .bin 128 对齐 0xFF 补齐 (ChipSea.cs LoadFile)。返回 (类型, 载荷)。"""
|
|
110
|
+
if not os.path.isfile(file_path):
|
|
111
|
+
raise NdtBoxError("CONFIG_ERROR", "固件文件不存在", file_path)
|
|
112
|
+
ext = os.path.splitext(file_path)[1].lower()
|
|
113
|
+
try:
|
|
114
|
+
with open(file_path, "rb") as fh:
|
|
115
|
+
raw = fh.read()
|
|
116
|
+
except OSError as exc:
|
|
117
|
+
raise NdtBoxError("CONFIG_ERROR", "固件文件读取失败", str(exc)) from exc
|
|
118
|
+
if ext == ".nfw":
|
|
119
|
+
return "nfw", strip_nfw(raw)
|
|
120
|
+
if ext == ".bin":
|
|
121
|
+
if not raw:
|
|
122
|
+
raise NdtBoxError("CONFIG_ERROR", ".bin 固件为空", file_path)
|
|
123
|
+
return "bin", pad_bin_128(raw)
|
|
124
|
+
raise NdtBoxError("CONFIG_ERROR", "仅支持 .bin / .nfw 固件文件", ext or "(无后缀)")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def build_page_write_frame(offset: int, page: bytes) -> bytes:
|
|
128
|
+
"""页写帧: 页地址大端 2 字节 + <=128 字节负载 (ChipSea.cs WriteFw 逐页自 0x0000 递增)。"""
|
|
129
|
+
if len(page) > FLASH_WRITE_PAGE:
|
|
130
|
+
raise NdtBoxError("CONFIG_ERROR", "页负载超过 128 字节", str(len(page)))
|
|
131
|
+
if not 0 <= offset <= 0xFFFF:
|
|
132
|
+
raise NdtBoxError("CONFIG_ERROR", "页地址越界 (0..0xFFFF)", hex(offset))
|
|
133
|
+
return offset.to_bytes(2, "big") + page
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def expected_read_window(fw: bytes, offset: int) -> bytes:
|
|
137
|
+
"""回读期望窗口: 256 字节, 末窗不足部分 0xFF 补齐 (ChipSea.cs ReadFwAndCheck)。"""
|
|
138
|
+
window = fw[offset:offset + FLASH_READ_WINDOW]
|
|
139
|
+
return window + b"\xFF" * (FLASH_READ_WINDOW - len(window))
|
|
140
|
+
|
|
141
|
+
|
|
95
142
|
# ---------------------------------------------------------------------------
|
|
96
143
|
# 桥调用层
|
|
97
144
|
# ---------------------------------------------------------------------------
|
|
@@ -156,6 +203,24 @@ class NdtBoxIo:
|
|
|
156
203
|
raise NdtBoxError("CONFIG_ERROR", "写入数据过长 (<=128)", str(len(data)))
|
|
157
204
|
self._bridge("write", RegAddr=reg, Data=data.hex())
|
|
158
205
|
|
|
206
|
+
def raw_write_read(self, write: bytes, read_len: int = 0) -> bytes | None:
|
|
207
|
+
"""原始 IIC 事务 (对齐 IIC.WriteRead): 写 write 字节(不含从机地址) 再读 read_len 字节。
|
|
208
|
+
|
|
209
|
+
覆盖 ChipSea 烧录的多字节地址形态 (页写 130B / 页读 [hi,lo]+256)。
|
|
210
|
+
"""
|
|
211
|
+
if not write:
|
|
212
|
+
raise NdtBoxError("CONFIG_ERROR", "raw 写字节为空", None)
|
|
213
|
+
if 1 + len(write) > 256:
|
|
214
|
+
raise NdtBoxError("CONFIG_ERROR", "raw 写事务过长 (含从机地址须 <=256)", str(len(write)))
|
|
215
|
+
if not 0 <= read_len <= 256:
|
|
216
|
+
raise NdtBoxError("CONFIG_ERROR", "raw 读长度越界 (0..256)", str(read_len))
|
|
217
|
+
data = self._bridge("raw", WriteHex=write.hex(), ReadLen=read_len)
|
|
218
|
+
return bytes.fromhex(data["hex"].replace(" ", "")) if read_len > 0 else None
|
|
219
|
+
|
|
220
|
+
def vbrt_reset(self, off_ms: int = 200) -> dict:
|
|
221
|
+
"""模组断电 off_ms 后恢复上电 (usb2io.dll IO10)——复位进入 IIC boot (IOoperation.VBRTOFF)。"""
|
|
222
|
+
return self._bridge("vbrt", OffMs=off_ms)
|
|
223
|
+
|
|
159
224
|
def close(self) -> dict:
|
|
160
225
|
return self._bridge("close")
|
|
161
226
|
|
|
@@ -220,3 +285,65 @@ class ChipSeaFxx:
|
|
|
220
285
|
raise NdtBoxError("CONFIG_ERROR", "poll 时长必须 > 0", str(duration_ms))
|
|
221
286
|
return self._io._bridge("poll", DurationMs=int(duration_ms), IntervalMs=int(interval_ms),
|
|
222
287
|
ReadyTimeoutMs=int(ready_timeout_ms))
|
|
288
|
+
|
|
289
|
+
# -- 固件烧录 (对齐 ChipSea.cs UpdateFW: 仅芯海 Fxx, 整片擦写) --
|
|
290
|
+
|
|
291
|
+
def flash_bin(self, file_path: str, erase_ms: int = 2000,
|
|
292
|
+
progress=None) -> dict:
|
|
293
|
+
"""经 NdtBox IIC 烧录 .bin/.nfw 固件到芯海 Fxx。
|
|
294
|
+
|
|
295
|
+
时序逐行对齐 ChipSea.cs UpdateFW:
|
|
296
|
+
LoadFile -> VBRTOFF(200) 断电复位进 boot -> 100ms -> 擦除魔数 -> erase_ms ->
|
|
297
|
+
逐 128B 页写(30ms 间隔) -> 200ms -> 逐 256B 窗回读校验(10ms 间隔) ->
|
|
298
|
+
完成魔数 "PASs" -> VBRTOFF(200) 复位加载新固件。
|
|
299
|
+
|
|
300
|
+
progress(phase, done, total) 在 write/verify 阶段回调。
|
|
301
|
+
校验不匹配抛 TARGET_MISMATCH (Fail-Closed, 不伪造成功)。
|
|
302
|
+
"""
|
|
303
|
+
import time
|
|
304
|
+
|
|
305
|
+
fw_type, fw = load_firmware(file_path)
|
|
306
|
+
started = time.monotonic()
|
|
307
|
+
pages = [fw[i:i + FLASH_WRITE_PAGE] for i in range(0, len(fw), FLASH_WRITE_PAGE)]
|
|
308
|
+
windows = list(range(0, len(fw), FLASH_READ_WINDOW))
|
|
309
|
+
|
|
310
|
+
self._io.vbrt_reset(200)
|
|
311
|
+
time.sleep(BOOT_READY_MS / 1000.0)
|
|
312
|
+
self._io.raw_write_read(b"\x00\x00" + ERASE_MAGIC, 0)
|
|
313
|
+
time.sleep(0.010)
|
|
314
|
+
time.sleep(erase_ms / 1000.0)
|
|
315
|
+
|
|
316
|
+
for i, page in enumerate(pages):
|
|
317
|
+
self._io.raw_write_read(build_page_write_frame(i * FLASH_WRITE_PAGE, page), 0)
|
|
318
|
+
time.sleep(PAGE_WRITE_GAP_MS / 1000.0)
|
|
319
|
+
if progress:
|
|
320
|
+
progress("write", i + 1, len(pages))
|
|
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
|
+
|
|
340
|
+
return {
|
|
341
|
+
"file": file_path,
|
|
342
|
+
"file_type": fw_type,
|
|
343
|
+
"fw_len": len(fw),
|
|
344
|
+
"pages_written": len(pages),
|
|
345
|
+
"read_windows": len(windows),
|
|
346
|
+
"erase_ms": erase_ms,
|
|
347
|
+
"verify": "pass",
|
|
348
|
+
"duration_ms": int((time.monotonic() - started) * 1000),
|
|
349
|
+
}
|
|
@@ -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")]
|
|
7
|
+
[ValidateSet("open", "read", "write", "version", "connect", "poll", "close", "raw", "vbrt")]
|
|
8
8
|
[string]$Action,
|
|
9
9
|
|
|
10
10
|
[string]$DllDir,
|
|
@@ -16,6 +16,11 @@ param(
|
|
|
16
16
|
[int]$Len = 1,
|
|
17
17
|
[string]$Data = "", # 十六进制字符串, 空格或连续皆可: "A5 01 02"
|
|
18
18
|
|
|
19
|
+
[string]$WriteHex = "", # raw: 写事务字节 (不含从机地址, 桥负责前置), 对齐 IIC.WriteRead(wBuf[0]=slave)
|
|
20
|
+
[int]$ReadLen = 0, # raw: 读字节数 (0=纯写)
|
|
21
|
+
|
|
22
|
+
[int]$OffMs = 200, # vbrt: 模组断电时长 ms (IO10 拉高), 结束后恢复上电
|
|
23
|
+
|
|
19
24
|
[int]$DurationMs = 1000, # poll 时长
|
|
20
25
|
[int]$IntervalMs = 10, # poll 采样间隔
|
|
21
26
|
[int]$ReadyTimeoutMs = 2000 # poll 数据就绪轮询超时
|
|
@@ -55,6 +60,16 @@ namespace STDataCollection {
|
|
|
55
60
|
[DllImport("USBIOX.DLL", CallingConvention = CallingConvention.StdCall)]
|
|
56
61
|
public static extern bool USBIO_StreamI2C(uint iIndex, uint iWriteLength, IntPtr iWriteBuffer, uint iReadLength, IntPtr oReadBuffer);
|
|
57
62
|
}
|
|
63
|
+
public static class U2io {
|
|
64
|
+
[DllImport("USB2IO.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
|
|
65
|
+
public static extern IntPtr USB2IO_Open(int Nbr);
|
|
66
|
+
[DllImport("USB2IO.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
|
|
67
|
+
public static extern int USB2IO_SetIoCfg(IntPtr h, int IoNbr, int IoCfg);
|
|
68
|
+
[DllImport("USB2IO.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
|
|
69
|
+
public static extern int USB2IO_SetIoOut(IntPtr h, int IoNbr, int IoOut);
|
|
70
|
+
[DllImport("USB2IO.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
|
|
71
|
+
public static extern int USB2IO_Close(IntPtr h);
|
|
72
|
+
}
|
|
58
73
|
}
|
|
59
74
|
"@
|
|
60
75
|
}
|
|
@@ -228,6 +243,34 @@ try {
|
|
|
228
243
|
Write-Output (@{ ok = $true; action = "poll"; data = @{ frame_count = $frames.Count; checksum_errors = $checksumErrors; ready_timeouts = $deadFrames; interval_ms = $IntervalMs; frames = $frames } } | ConvertTo-Json -Compress -Depth 6)
|
|
229
244
|
exit 0
|
|
230
245
|
}
|
|
246
|
+
"raw" {
|
|
247
|
+
# 对齐 ChipSea.cs 的多字节地址事务: 写 [slave, wBuf...] 再读 ReadLen 字节 (IIC.WriteRead 语义)
|
|
248
|
+
$wb = Parse-Hex $WriteHex
|
|
249
|
+
if ($wb.Count -eq 0) { Fail "CONFIG_ERROR" "raw 需要 -WriteHex (不含从机地址的写事务字节)" $null }
|
|
250
|
+
if (($wb.Count + 1) -gt 256) { Fail "CONFIG_ERROR" "raw 写事务过长 (含从机地址须 <=256)" $wb.Count }
|
|
251
|
+
if ($ReadLen -lt 0 -or $ReadLen -gt 256) { Fail "CONFIG_ERROR" "raw -ReadLen 越界 (0..256)" $ReadLen }
|
|
252
|
+
$h = Open-Device 0
|
|
253
|
+
$rb = New-Object byte[] $ReadLen
|
|
254
|
+
if (-not (Invoke-WriteRead $h $wb $rb)) { Fail "TARGET_MISMATCH" ("IIC raw 事务无应答 (w={0}B r={1}B)" -f $wb.Count, $ReadLen) (($wb | ForEach-Object { $_.ToString("X2") }) -join " ") }
|
|
255
|
+
[STDataCollection.Icb]::USBIO_CloseDevice([uint32]0) | Out-Null
|
|
256
|
+
$dataOut = @{ written = $wb.Count; read = $ReadLen }
|
|
257
|
+
if ($ReadLen -gt 0) { $dataOut.hex = (($rb | ForEach-Object { $_.ToString("X2") }) -join " ") }
|
|
258
|
+
Write-Output (@{ ok = $true; action = "raw"; data = $dataOut } | ConvertTo-Json -Compress -Depth 4)
|
|
259
|
+
exit 0
|
|
260
|
+
}
|
|
261
|
+
"vbrt" {
|
|
262
|
+
# 模组供电脚 (IO10) 对齐 IOoperation.VBRTON/VBRTOFF: usb2io.dll 设备号 1, IO10 配置 3=输出, 1=断电 0=上电
|
|
263
|
+
$h2 = [STDataCollection.U2io]::USB2IO_Open(1)
|
|
264
|
+
if ([int]$h2 -eq -1) { Fail "PROBE_NOT_FOUND" "USB2IO 打开失败 (设备 1); 确认 DllDir 含 usb2io.dll 且 NdtBox 已连接" $null }
|
|
265
|
+
[STDataCollection.U2io]::USB2IO_SetIoCfg($h2, 10, 3) | Out-Null
|
|
266
|
+
[STDataCollection.U2io]::USB2IO_SetIoOut($h2, 10, 1) | Out-Null
|
|
267
|
+
Start-Sleep -Milliseconds $OffMs
|
|
268
|
+
[STDataCollection.U2io]::USB2IO_SetIoOut($h2, 10, 0) | Out-Null
|
|
269
|
+
Start-Sleep -Milliseconds 50
|
|
270
|
+
[STDataCollection.U2io]::USB2IO_Close($h2) | Out-Null
|
|
271
|
+
Write-Output (@{ ok = $true; action = "vbrt"; data = @{ off_ms = $OffMs; powered_on = $true } } | ConvertTo-Json -Compress -Depth 4)
|
|
272
|
+
exit 0
|
|
273
|
+
}
|
|
231
274
|
}
|
|
232
275
|
} catch {
|
|
233
276
|
Fail "UNKNOWN_ERROR" "bridge 异常: $($_.Exception.Message)" $_.ScriptStackTrace
|
|
@@ -135,6 +135,21 @@ def cmd_poll(io: NdtBoxIo, duration_ms: int, interval_ms: int, max_frames: int,
|
|
|
135
135
|
ok("poll", result, log=f"poll({duration_ms}ms, {interval_ms}ms): {len(frames)} frames")
|
|
136
136
|
|
|
137
137
|
|
|
138
|
+
def cmd_flash_bin(io: NdtBoxIo, config: dict, file_path: str, erase_ms: int, confirm: bool) -> None:
|
|
139
|
+
# 烧录 = 整片擦除 + 页写, 破坏性高于 write-reg, 同款双闸门
|
|
140
|
+
if not confirm or not (config.get("ndtdbg") or {}).get("write_allowed", False):
|
|
141
|
+
fail("PERMISSION_DENIED",
|
|
142
|
+
"固件烧录被默认禁止",
|
|
143
|
+
detail="flash-bin 为整片擦写: 需 lab.yaml ndtdbg.write_allowed: true 且 CLI 传 --confirm")
|
|
144
|
+
|
|
145
|
+
def progress(phase: str, done: int, total: int) -> None:
|
|
146
|
+
print(f"[flash-bin] {phase} {done}/{total}", file=sys.stderr, flush=True)
|
|
147
|
+
|
|
148
|
+
result = ChipSeaFxx(io).flash_bin(file_path, erase_ms=erase_ms, progress=progress)
|
|
149
|
+
ok("flash-bin", result,
|
|
150
|
+
log=f"UpdateFW({file_path}): {result['fw_len']}B, {result['pages_written']} pages, verify={result['verify']}")
|
|
151
|
+
|
|
152
|
+
|
|
138
153
|
# --------------------------------------------------------------------------- main
|
|
139
154
|
|
|
140
155
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -145,7 +160,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
145
160
|
common.add_argument("--slave-addr", help="IIC 从机地址, 默认 0xA0 (支持 0x 前缀)")
|
|
146
161
|
common.add_argument("--speed", help="IIC 速度 0=20kHz 1=100kHz, 默认 1")
|
|
147
162
|
common.add_argument("--lab-config", help="显式指定 lab.yaml 路径")
|
|
148
|
-
common.add_argument("--confirm", action="store_true", help="write-reg 需与 lab.yaml write_allowed 双确认")
|
|
163
|
+
common.add_argument("--confirm", action="store_true", help="write-reg / flash-bin 需与 lab.yaml write_allowed 双确认")
|
|
149
164
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
150
165
|
|
|
151
166
|
sub.add_parser("status", parents=[common], help="连通性 + 固件版本 + 模组通断")
|
|
@@ -165,6 +180,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
165
180
|
p_poll.add_argument("--interval-ms", type=int, default=10)
|
|
166
181
|
p_poll.add_argument("--max-frames", type=int, default=4, help="非 --full 时保留的帧样本数")
|
|
167
182
|
p_poll.add_argument("--full", action="store_true", help="输出全部帧 (可能较大)")
|
|
183
|
+
|
|
184
|
+
f = sub.add_parser("flash-bin", parents=[common],
|
|
185
|
+
help="烧录 .bin/.nfw 固件 (整片擦写, 默认禁止, 需网关+--confirm)")
|
|
186
|
+
f.add_argument("--file", required=True,
|
|
187
|
+
help="固件文件 (.bin 自动 128 对齐补 0xFF; .nfw 去 256 头)")
|
|
188
|
+
f.add_argument("--erase-ms", type=int, default=2000, help="擦除等待 ms (对齐原厂 2000)")
|
|
168
189
|
return p
|
|
169
190
|
|
|
170
191
|
|
|
@@ -187,6 +208,8 @@ def main(argv=None) -> None:
|
|
|
187
208
|
cmd_write_reg(io, config, args.addr, args.data, args.confirm)
|
|
188
209
|
elif args.cmd == "poll":
|
|
189
210
|
cmd_poll(io, args.duration_ms, args.interval_ms, args.max_frames, args.full)
|
|
211
|
+
elif args.cmd == "flash-bin":
|
|
212
|
+
cmd_flash_bin(io, config, args.file, args.erase_ms, args.confirm)
|
|
190
213
|
else: # pragma: no cover
|
|
191
214
|
fail("CONFIG_ERROR", f"unknown cmd: {args.cmd}")
|
|
192
215
|
except NdtBoxError as exc:
|
|
@@ -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.7).
|
|
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.7)" -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
|