Replace machine-specific C:\Users\bowei\... paths in 7 skill docs with %USERPROFILE% / $env:USERPROFILE (or $USERPROFILE in Git Bash blocks). Conda paths use a two-layout resolver (miniconda3\envs vs .conda\envs) or reference ~\skills_env.md CONDA_LANGGRAPH_PYTHON, since the conda location is machine-specific. Fixes paths that resolved wrong on non-bowei machines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
249 lines
15 KiB
Markdown
249 lines
15 KiB
Markdown
---
|
||
name: fpga-imx6-flow
|
||
description: >
|
||
Run the fpga-imx6-flow LangGraph workflow at E:\LangGraph_Design\FPGA-imx6-flow —
|
||
the closed-loop SMOD (FPGA + i.MX6) hardware-in-the-loop design/debug pipeline:
|
||
power on the board, connect JTAG, program bitstream/flash, restart the imx6 app,
|
||
analyze (ILA + imx6 serial console), judge against design requirements, and iterate by
|
||
modifying RTL/imx6 code. It drives the graph in-process (no langgraph dev) and
|
||
hands the 5 interrupts (analyze / meets-requirement / propose / modify-FPGA /
|
||
modify-imx6) back to this Claude session to execute.
|
||
|
||
ALWAYS use this skill whenever the user wants to run, start, drive, resume, or
|
||
continue the fpga-imx6-flow workflow / SMOD FPGA+imx6 design loop, or says things
|
||
like "启动 fpga-imx6-flow", "跑 imx6 设计流程", "run the imx6 FPGA flow", "执行
|
||
SMOD 设计任务", "继续 imx6 工作流", "resume the imx6 design loop", even if they
|
||
don't name the project path. Before driving the workflow this skill first confirms
|
||
the langgraph conda environment from /langgraph-vivado-install is in place.
|
||
---
|
||
|
||
## What this skill does
|
||
|
||
Drives the **fpga-imx6-flow** graph (24 nodes / 34 flow-edges, 1:1 with
|
||
`Notes/fpga-imx6-flow.excalidraw`). The graph's deterministic hardware/check nodes
|
||
(power, JTAG, program, restart) run in-process; its **5 interrupt nodes** pause and
|
||
hand control to **this Claude session**, which performs the real work (analysis,
|
||
judgment, RTL/app edits) and resumes the same thread. Persistence is a SqliteSaver
|
||
file, so `start`/`resume` survive across separate process calls.
|
||
|
||
| Item | Value |
|
||
|---|---|
|
||
| Project root | `E:\LangGraph_Design\FPGA-imx6-flow` |
|
||
| Conda env | `langgraph` (path 见 `~\skills_env.md` 的 `CONDA_LANGGRAPH_PYTHON`) |
|
||
| Graph | `app.main_graph:build_graph` (in-process; no langgraph dev) |
|
||
| Driver | `%USERPROFILE%\.claude\skills\fpga-imx6-flow\scripts\run_imx6_flow.py` |
|
||
| Checkpoint | `<project>\runs\skill_checkpoint.sqlite` (keyed by `--thread-id`) |
|
||
| Design source | `Notes/fpga-imx6-flow.excalidraw`, plan `Notes/Task00.md` |
|
||
|
||
## ⚠️ 硬件时序铁律(上电 / USB-Blast / 烧写,务必遵守)
|
||
|
||
**每次给 SMOD 上电之前,USB-Blast 必须先断开;上电完成之后再重新连接。** 绝不在
|
||
USB-Blast 处于连接状态时给板卡上电或断电(保护 JTAG 适配器与板卡)。
|
||
|
||
标准时序(图拓扑已按此实现,手动操作或写 `tools_*` / 真机脚本时也必须遵守):
|
||
|
||
```
|
||
断电(power_off) → 确认已断电 → 断开USB-Blast(blast_off) → 确认已断开
|
||
→ 上电(power_on) → 确认已上电 → 连接USB-Blast(blast_on) → 连接JTAG/烧写/抓ILA
|
||
```
|
||
|
||
**烧写 flash 的完整闭环**(固化后必须断电重启从 flash 加载再验证):
|
||
|
||
```
|
||
断电 → 断开USB-Blast → 上电 → 连接USB-Blast → 用 /fpga_flash 烧写flash
|
||
→ 断电 → 断开USB-Blast → 上电 → 连接USB-Blast → 通过ILA验证flash内容正确
|
||
```
|
||
|
||
- 在 LangGraph 图里:`power_off → check_power_off →(是) blast_off_a → check_blast_off
|
||
→(是) power_on → check_power →(是) blast_on → check_hw`。`flash_burn` 烧写后回到
|
||
`power_off` 做整机断电重启,再次走完上述上电时序后才 `analyze`(抓ILA验证)。
|
||
- **强化确认**:`power_on` 节点在上电前调用 `_ensure_blast_off_before_power()` 兜底确认
|
||
USB-Blast 已断开;若发现仍连接会强制断开并在 `history` 标记 `rule_violation=True`。
|
||
- 两个继电器的**串口号不在本工作流里绑定**:唯一来源是 `~/vivado_mcp_usb.conf`(逐机而异,
|
||
不入库)。`app/tools_hw.py` 通过 `vivado_mcp.serial_config.load_serial_ports()` 读取它,
|
||
串口收发/组帧/CRC 也复用 `vivado_mcp.serial_io`——协议只有一份实现。
|
||
文件不存在时调 vivado-mcp 的 **`detect_serial_relays`** 工具自动探测生成。
|
||
(对应的 MCP 工具是 `power_on`/`power_off`/`USB_Blast_on`/`USB_Blast_off`,和这里的
|
||
`tools_hw` 走同一套底层。)
|
||
|
||
> **Run via the env python directly** (not bare `conda run`): on this Windows GBK
|
||
> console, `conda run` re-prints the child's UTF-8 stdout through a GBK codec and
|
||
> **crashes on the Chinese interrupt payloads**. Use the env `python.exe`, or
|
||
> `conda run --no-capture-output`. The driver also re-configures its stdout to UTF-8.
|
||
|
||
```
|
||
PY = <CONDA_LANGGRAPH_PYTHON — see ~\skills_env.md>
|
||
RUN = %USERPROFILE%\.claude\skills\fpga-imx6-flow\scripts\run_imx6_flow.py
|
||
```
|
||
|
||
## Step 0 — Confirm the environment (REQUIRED before driving)
|
||
|
||
The workflow runs in the `langgraph` conda env created by **`/langgraph-vivado-install`**.
|
||
Verify it before starting, so a missing env surfaces as a clear instruction rather
|
||
than a cryptic import error mid-run:
|
||
|
||
```powershell
|
||
$py = @("$env:USERPROFILE\miniconda3\envs\langgraph\python.exe","$env:USERPROFILE\.conda\envs\langgraph\python.exe") | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||
if (-not $py) { Write-Host "[MISSING] langgraph env" } else {
|
||
& $py -c "import langgraph; from langgraph.checkpoint.sqlite import SqliteSaver; from langgraph.types import Command; print('[OK] langgraph + checkpoint-sqlite')"
|
||
}
|
||
```
|
||
|
||
- **If `[OK]`** → proceed to Step 1.
|
||
- **If missing / import fails** → tell the user the env isn't ready and to run
|
||
**`/langgraph-vivado-install`** first (it creates the `langgraph` env with
|
||
`langgraph` + `langgraph-checkpoint-sqlite` and the Vivado link). Do not proceed
|
||
until it succeeds.
|
||
|
||
**For a real-hardware run** (`--real`), additionally confirm the machine's two config
|
||
files — **both live in `~`, neither is in the repo** (they hold COM numbers, VM/imx6
|
||
passwords, and the Vivado path, all of which differ per machine):
|
||
|
||
| 文件 | 装什么 | 缺了怎么办 |
|
||
|---|---|---|
|
||
| `~/vivado_mcp_usb.conf` | 电源继电器 + USB-Blast 继电器的 COM 号 | 调 vivado-mcp 的 **`detect_serial_relays`** 工具自动探测生成 |
|
||
| `~/fpga_imx6_flow.json` | imx6 控制台 COM 号 (`ports.imx6`)、`VIVADO_BIN_PATH`、虚拟机/imx6 口令 | 照工程根的 `fpga_imx6_flow.example.json` 手工创建 |
|
||
|
||
List the machine's ports with `[System.IO.Ports.SerialPort]::GetPortNames()`.
|
||
工程根目录下**不该**再有 `env_config.json`——它已废弃,`app/config.py` 见到就报错。
|
||
**Default runs are MOCK** (dry-run of the design loop with no hardware), because the
|
||
workflow's `tools_*` real-hardware paths are still TODO — see "Mock vs real" below.
|
||
|
||
## Step 1 — Start the workflow
|
||
|
||
Ask the user (or infer) the first-pass program mode (`bitstream` default, or `flash`)
|
||
and any `--bit-path`/`--flash-path`. Then start:
|
||
|
||
```powershell
|
||
& $py $RUN start --project-dir "E:\LangGraph_Design\FPGA-imx6-flow" `
|
||
--thread-id "imx6-<short-task-name>" --program-mode bitstream
|
||
# add --real only when hardware tools are wired and the board is connected
|
||
```
|
||
|
||
The driver prints **one JSON line** and exits:
|
||
|
||
| Exit | `status` | Meaning |
|
||
|---|---|---|
|
||
| 0 | `done` | Workflow finished (符合设计要求 / 迭代到上限) → Step 3 |
|
||
| 1 | `error` | Read `error`, diagnose, report to user |
|
||
| 2 | `interrupt` | An interrupt fired → read `interrupt.action` → Step 2 |
|
||
|
||
## Step 2 — Handle the interrupt, then resume
|
||
|
||
On exit 2, the JSON's `interrupt.action` tells you what the session must do. Perform
|
||
it, then resume the **same `--thread-id`** with a JSON payload. Repeat until exit 0.
|
||
|
||
| `action` | excalidraw node | What you (the session) do | Resume JSON |
|
||
|---|---|---|---|
|
||
| `analyze` | 通过ila/串口分析 | Grab imx6 serial log + pull ILA waveform (vivado-mcp); summarize | `{"analysis_log":"<摘要/路径>"}` |
|
||
| `meets_requirement` | 符合设计要求? | Judge the analysis against the design requirement | `{"meets": true}` or `{"meets": false}` |
|
||
| `propose` | 分析原因,提出修改方案 | Diagnose root cause; decide what to change + next program mode | `{"reason":"...","need_fpga": true/false,"need_imx6": true/false,"program_mode":"bitstream"或"flash"}` |
|
||
| `modify_fpga` | 改 RTL→综合→出 bit/flash | Edit RTL, run Vivado synth/impl, produce bit + flash files | `{"bit_path":"...","flash_path":"...","program_mode":"flash"}` |
|
||
| `modify_imx6` | VM 改 app→交叉编译 | In the Ubuntu VM (`bowei@192.168.223.128`) edit app + cross-compile | `{"app_path":"..."}` |
|
||
|
||
Resume command:
|
||
|
||
```powershell
|
||
& $py $RUN resume --project-dir "E:\LangGraph_Design\FPGA-imx6-flow" `
|
||
--thread-id "imx6-<short-task-name>" --resume-json '{"meets": false}'
|
||
```
|
||
|
||
Notes:
|
||
- Always pass the **same `--thread-id`** used at `start` — that's how the sqlite
|
||
checkpoint reattaches to the run (concurrency-safe: different tasks → different
|
||
thread ids → isolated state).
|
||
- `propose` fans out to BOTH `decide_fpga` and `decide_imx6`; with `need_fpga:false`
|
||
and `need_imx6:false` the loop goes straight to `round_done` → re-program. Setting
|
||
either true fires the corresponding `modify_*` interrupt next.
|
||
- After `round_done` the graph re-asks the program-mode decision; `program_mode` you
|
||
set in `propose`/`modify_fpga` chooses bitstream (download bit) vs flash (re-burn +
|
||
full power/connect re-sequence). This is the big closed loop.
|
||
|
||
## Step 3 — Finish
|
||
|
||
On exit 0 (`status: done`), report to the user: iterations completed and that the
|
||
design met requirements (or hit the iteration cap). Offer to inspect
|
||
`<project>\runs\` artifacts or the per-step `history`.
|
||
|
||
To abandon a paused run and start clean, delete `<project>\runs\skill_checkpoint.sqlite`
|
||
(or use a fresh `--thread-id`).
|
||
|
||
## Mock vs real
|
||
|
||
- **MOCK (default)** — `FPGA_IMX6_MOCK=1`; hardware/check nodes return success
|
||
immediately. Use this to dry-run the *design-loop logic* and the 5 interrupts with
|
||
no board attached. Great for demonstrating/validating the flow.
|
||
- **Real (`--real`)** — drives actual hardware. The `tools_hw` (power + USB-Blast relays,
|
||
ports from `~/vivado_mcp_usb.conf`), `tools_imx6` (console restart/kill/log, port from
|
||
`~/fpga_imx6_flow.json`'s `ports.imx6`), and `tools_vivado` (JTAG program/flash) real paths are
|
||
**now implemented and validated end-to-end (2026-07-05)** on the SMOD_ATSC board. Getting there
|
||
took **7 fixes** — see memory `fpga-imx6-flow-tx-safe`: power_on waits `sys init over` before
|
||
JTAG (else TX dies); `_PROJ`→SMOD_ATSC; `deploy_app`→no-op (deploy out-of-band, see above);
|
||
driver multi-interrupt resume (map by `action`); `power_off` blast-off-first + check retry guard
|
||
(else infinite loop); serial open-retry on transient err5 (now in `vivado_mcp.serial_io`);
|
||
`program_flash`→proven
|
||
`/fpga_flash` flasher + Mfg-ID verify (old tcl was a false-positive). Verify a flash landed with
|
||
JTAG `get_hw_ilas` (iter0 = **1 ILA / 0 VIO**; golden = **0 ILA / 2 VIO**).
|
||
|
||
## imx6 access + file transfer (modify_imx6 / deploy step) — PROVEN 2026-07-05
|
||
|
||
The `deploy_app` graph node is a no-op (deploy is done **out-of-band by the session**).
|
||
Here is the working recipe for the `modify_imx6` interrupt (cross-compile HEAD's app and
|
||
push it to the imx6). All of this is **network-based and fast** — no slow serial file push.
|
||
|
||
**Hosts / creds**
|
||
- **imx6 mgmt: `192.168.1.143`** (Windows host is on `192.168.1.125`, same subnet — `ping` works).
|
||
The old memory "imx6 not reachable over the network" is **outdated**; the mgmt port is reachable.
|
||
Login: **`root`**, password = `SMOD_SSH_PASS` in `~\skills_env.md`. (The serial console still works too but its login auto-logs-out and
|
||
is flaky; prefer network SSH for imx6 shell/log reads.)
|
||
- **Cross-compile VM: `bowei@192.168.223.128`** (Ubuntu; password = `CROSS_VM_PASS` in
|
||
`~\skills_env.md` — read it from there, don't guess/brute-force). Toolchain env-setup:
|
||
`/opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi`.
|
||
If VM SSH gives `kex_exchange_identification: Connection closed`, the VM's sshd is down/hung —
|
||
ask the user to `sudo systemctl restart sshd`.
|
||
|
||
**SSH library quirk (critical)**
|
||
- The imx6's SSH server is **old (legacy ciphers + `ssh-rsa` host key)**. paramiko **5.0 fails**
|
||
(`unknown cipher` / `no acceptable host key`). Install **paramiko 2.12** to a temp dir and use it
|
||
for the imx6 (it has TripleDES/aes-cbc/ssh-rsa):
|
||
`python -m pip install --target <dir> paramiko==2.12.0` then `PYTHONPATH=<dir> python ... -W ignore`.
|
||
The **VM** SSH is modern, so the env's paramiko 5.0 works for the VM (or use 2.12 for both).
|
||
- The imx6 SSH has **no SFTP subsystem** (`open_sftp()` → `EOF during negotiation`). Transfer files
|
||
via **`cat > file` over an exec channel**:
|
||
```python
|
||
ch = imx6_client.get_transport().open_session()
|
||
ch.exec_command("cat > /aw/anywave/app/imx_atsc_app_head")
|
||
with open(local_bin, "rb") as f: ch.sendall(f.read())
|
||
ch.shutdown_write(); ch.recv_exit_status() # 0 = ok
|
||
```
|
||
(The VM has SFTP; `open_sftp().get(...)` works there.)
|
||
|
||
**Full modify_imx6 flow (validated)**
|
||
1. Windows: `git archive --format=tar -o head_atsc.tar HEAD:imx6/src/atsc_app` (13 MB, self-contained).
|
||
2. paramiko→VM (pw: `CROSS_VM_PASS` from `~\skills_env.md`): SFTP the tar up; extract to a **FRESH dir** (`~/atsc_head_build`, never
|
||
touch `atsc_tsoip_build/` etc.); build:
|
||
`bash -lc 'source <envsetup>; cd ~/atsc_head_build && make'` → output `imx_atsc_app` (~2.92 MB ELF).
|
||
3. paramiko→VM SFTP the built `imx_atsc_app` down to Windows.
|
||
4. paramiko-2.12→imx6 (pw: `SMOD_SSH_PASS` from `~\skills_env.md`): `cat >`-push it to `/aw/anywave/app/imx_atsc_app_head`,
|
||
`chmod 755`, `cp -a imx_atsc_app_head imx_atsc_app` (swap active; golden stays as
|
||
`imx_atsc_app.bak_pre_rx1`), md5-verify.
|
||
5. Cold-boot (tools_hw blast_off→power_off→power_on, TX-safe) so rc.local runs the new app.
|
||
6. Verify: network SSH grep `/var/module_log/imx_atsc_app.log` for `TALISE_radioOn` / `fb init state ok`
|
||
/ `fpga[0xBB]=0x03` (sync=1/tvalid=1); spectrum for TX; (ILA needs an `.ltx` → skip unless re-impl).
|
||
|
||
> The `git HEAD` app (b07d73b) is the known-good **RX1-on-framerA (M=4/lane0x03/RX1RX2) + correct TX**
|
||
> build. On-device `imx_atsc_app.rx1` and any `M=2/lane0x04` variant **break TX** — do not use them.
|
||
|
||
## Troubleshooting
|
||
|
||
| Symptom | Fix |
|
||
|---|---|
|
||
| `UnicodeEncodeError: 'gbk' ...` | Don't use bare `conda run`; use the env `python.exe` or `conda run --no-capture-output`. |
|
||
| imx6 SSH `unknown cipher` / `no acceptable host key` (paramiko 5) | Use **paramiko 2.12** (temp `--target` dir) for the imx6 legacy SSH; see "imx6 access" above. |
|
||
| imx6 `open_sftp` → `EOF during negotiation` | imx6 has no SFTP subsystem; push files via `cat > file` over an exec channel. |
|
||
| VM SSH `kex_exchange_identification: Connection closed` | VM sshd down/hung — user `sudo systemctl restart sshd`. |
|
||
| `import failed ... Run /langgraph-vivado-install` | The `langgraph` env is missing/incomplete → run `/langgraph-vivado-install`. |
|
||
| Exit 1 `NotImplementedError ... tools_*` | A `--real` run hit an unwired hardware tool. Run without `--real` (mock), or implement `tools_*`. |
|
||
| `resume` seems to start over | You passed a different `--thread-id` than `start`. Use the same one. |
|
||
| Need to see the graph visually | Optional: `langgraph dev` via the project's `langgraph.json` opens Studio — not required to run. |
|