15 KiB
name, description
| name | description |
|---|---|
| fpga-imx6-flow | 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 (C:\Users\bowei\.conda\envs\langgraph) |
| Graph | app.main_graph:build_graph (in-process; no langgraph dev) |
| Driver | C:\Users\bowei\.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 runre-prints the child's UTF-8 stdout through a GBK codec and crashes on the Chinese interrupt payloads. Use the envpython.exe, orconda run --no-capture-output. The driver also re-configures its stdout to UTF-8.
PY = C:\Users\bowei\.conda\envs\langgraph\python.exe
RUN = C:\Users\bowei\.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:
$py = "C:\Users\bowei\.conda\envs\langgraph\python.exe"
if (-not (Test-Path $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-installfirst (it creates thelanggraphenv withlanggraph+langgraph-checkpoint-sqliteand 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:
& $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:
& $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-idused atstart— that's how the sqlite checkpoint reattaches to the run (concurrency-safe: different tasks → different thread ids → isolated state). proposefans out to BOTHdecide_fpgaanddecide_imx6; withneed_fpga:falseandneed_imx6:falsethe loop goes straight toround_done→ re-program. Setting either true fires the correspondingmodify_*interrupt next.- After
round_donethe graph re-asks the program-mode decision;program_modeyou set inpropose/modify_fpgachooses 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. Thetools_hw(power + USB-Blast relays, ports from~/vivado_mcp_usb.conf),tools_imx6(console restart/kill/log, port from~/fpga_imx6_flow.json'sports.imx6), andtools_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 memoryfpga-imx6-flow-tx-safe: power_on waitssys init overbefore JTAG (else TX dies);_PROJ→SMOD_ATSC;deploy_app→no-op (deploy out-of-band, see above); driver multi-interrupt resume (map byaction);power_offblast-off-first + check retry guard (else infinite loop); serial open-retry on transient err5 (now invivado_mcp.serial_io);program_flash→proven/fpga_flashflasher + Mfg-ID verify (old tcl was a false-positive). Verify a flash landed with JTAGget_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 on192.168.1.125, same subnet —pingworks). The old memory "imx6 not reachable over the network" is outdated; the mgmt port is reachable. Login:root/anywavecom. (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, passwordlr7402— ask the user, 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 giveskex_exchange_identification: Connection closed, the VM's sshd is down/hung — ask the user tosudo systemctl restart sshd.
SSH library quirk (critical)
- The imx6's SSH server is old (legacy ciphers +
ssh-rsahost 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.0thenPYTHONPATH=<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 viacat > fileover an exec channel:(The VM has SFTP;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 = okopen_sftp().get(...)works there.)
Full modify_imx6 flow (validated)
- Windows:
git archive --format=tar -o head_atsc.tar HEAD:imx6/src/atsc_app(13 MB, self-contained). - paramiko→VM (pw
lr7402): SFTP the tar up; extract to a FRESH dir (~/atsc_head_build, never touchatsc_tsoip_build/etc.); build:bash -lc 'source <envsetup>; cd ~/atsc_head_build && make'→ outputimx_atsc_app(~2.92 MB ELF). - paramiko→VM SFTP the built
imx_atsc_appdown to Windows. - paramiko-2.12→imx6 (pw
anywavecom):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 asimx_atsc_app.bak_pre_rx1), md5-verify. - Cold-boot (tools_hw blast_off→power_off→power_on, TX-safe) so rc.local runs the new app.
- Verify: network SSH grep
/var/module_log/imx_atsc_app.logforTALISE_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 HEADapp (b07d73b) is the known-good RX1-on-framerA (M=4/lane0x03/RX1RX2) + correct TX build. On-deviceimx_atsc_app.rx1and anyM=2/lane0x04variant 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. |