Files
skills-win/fpga-imx6-flow/SKILL.md
T
boweiandClaude Opus 4.8 fbfa060b67 Portabilize hardcoded C:\Users\bowei paths across skill docs
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>
2026-07-20 20:35:43 +08:00

15 KiB
Raw Blame History

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 (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:

$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:

& $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-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:
    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.