Files
skills-win/fpga-imx6-flow/SKILL.md
T
Myfro9andClaude Opus 5 bb71470e07 sync: WIN11-BOWEI 2026-09-02 新增 bmem 记忆 skill;imx6-flow/plan-review 文档与脚本更新
远端 pack 服务仍故障(git-upload-pack 返回 0008NAK 后截断),push 待服务器恢复。
未跟踪的 5 个归档临时副本(rtl-ip-replace / sim-rtl-flow / simulink_rtl_dev /
simulink-rtl-redev / zynq-imx6-flow)刻意不入库,待 pull 后由远端提供。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0135YszNe4pwwJxwaLqKTCKG
2026-09-02 14:53:28 -05:00

322 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 + ILA; for RF/TX requirements also capture the requested analyzer spectrum; summarize all evidence | `{"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 (`CROSS_VM_USER@CROSS_VM_HOST`, see `~\skills_env.md`) 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.
### Spectrum-analyzer evidence during `analyze`
When the requirement includes RF or transmitter output, augment the ILA/console evidence
with a registered spectrum-analyzer MCP:
- N9320B (tool prefix `mcp__mcp_n9320b__`): call `get_idn`, then `get_settings`,
then `get_spectrum` with `mark_peak=true` and a stable `csv_path`.
- DSA815 (tool prefix `mcp__mcp_dsa815__`): call `dsa_get_identity`, then
`dsa_get_status`, then `dsa_capture_plot`.
Use the analyzer named by the requirement. If none is named, prefer DSA815. Capture both
only when the requirement asks for a cross-check or results need independent confirmation.
For DSA815 specifically:
1. Call `dsa_get_identity`, then `dsa_get_status` to verify serial `DSA8A194401321` and
record the current sweep/unit settings.
2. Call `dsa_capture_plot` for the current Trace; use `dsa_capture_spectrum` only when
downstream numerical processing needs all 601 points.
3. Put the PNG/CSV paths, peak frequency/power, span, RBW and VBW in `analysis_log`.
4. Prefer read-only capture. If the requirement forces a frequency/bandwidth change,
save the original status, use the validated setting tools, and restore the original
values after capture. Never enable `dsa_scpi_write` for routine flow work.
Always put the selected analyzer identity, artifact paths and measured settings in
`analysis_log`. If identity/connectivity fails, record that the RF evidence is unavailable
and do not claim the RF acceptance criterion passed from ILA or console data alone.
## 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: `SMOD_BOARD_IP` in `~\skills_env.md`** (the Windows host is on the 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 (VMware, Ubuntu 16.04.3)**: host/user/password = `CROSS_VM_HOST` /
`CROSS_VM_USER` / `CROSS_VM_PASS` in `~\skills_env.md` — read them from there, don't
guess/brute-force and **don't hardcode the IP** (it moved from `192.168.223.128` to
`192.168.154.128` when the VMware segment changed). Toolchain env-setup:
`CROSS_VM_ENV_SETUP` (currently
`/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 —
`sudo systemctl restart ssh`. If it gives **`Connection refused`**, sshd isn't installed at
all → see *VM prerequisite check* below.
**VM prerequisite check (do this BEFORE the first cross-compile of a session)**
A freshly-installed VMware Ubuntu has **neither sshd nor the toolchain**. Check both up
front — discovering it half-way through `modify_imx6` wastes a whole loop iteration:
```bash
# 1. is sshd even listening? (Connection refused => not installed)
ssh -o ConnectTimeout=5 $CROSS_VM_USER@$CROSS_VM_HOST 'ls -d /opt/fsl-imx-x11/*'
```
- **`Connection refused`** → sshd not installed. You **cannot** fix this over SSH; use
**`vmrun`** (VMware Tools channel, `VMRUN_PATH` + `CROSS_VM_VMX` in `~\skills_env.md`):
```
vmrun -T ws -gu <user> -gp <pass> CopyFileFromHostToGuest <vmx> <local.sh> /tmp/s.sh
vmrun -T ws -gu <user> -gp <pass> runProgramInGuest <vmx> /bin/bash /tmp/s.sh
vmrun -T ws -gu <user> -gp <pass> CopyFileFromGuestToHost <vmx> /tmp/out.log <local.log>
```
Then in the guest: `sudo apt-get install -y openssh-server && sudo systemctl enable --now ssh`.
- **`ls: No such file`** (SSH works, no `/opt/fsl-imx-x11/*`) → toolchain missing, install it.
*Installing the toolchain* — the Yocto/FSL SDK installer lives with the project notes at
`…\my_exciter\Notes_imx6\agt_devlp_ext_260616\fsl-imx-x11-glibc-x86_64-meta-toolchain-qt5-cortexa7hf-neon-toolchain-4.1.15-2.0.0.sh`
(776 MB). `scp` it to the VM, verify `md5sum` matches the local copy, then:
```bash
chmod +x toolchain.sh && sudo ./toolchain.sh -y -d /opt/fsl-imx-x11/4.1.15-2.0.0
```
`-y` makes it non-interactive; `-d` must match `CROSS_VM_ENV_SETUP`'s parent. Verify by
compiling a throwaway `.c` and confirming `file` reports **`ELF 32-bit LSB … ARM, EABI5`** —
a successful `gcc --version` alone does not prove the sysroot is wired up.
Gotchas seen on a real install (2026-07-20):
- `apt-get` may fail with `Could not get lock /var/lib/dpkg/lock` because **`unattended-upgrades`
runs on boot**. Don't kill it — poll `fuser /var/lib/dpkg/lock` until it clears (~90 s).
- Xenial is EOL. `us.archive.ubuntu.com` still served it here, but if `apt-cache policy
openssh-server` shows no candidate, rewrite `/etc/apt/sources.list` to
`old-releases.ubuntu.com` (back it up first).
- Scripts pushed via `CopyFileFromHostToGuest` must have **LF** line endings; CRLF makes
bash fail obscurely. Write them with `[System.IO.File]::WriteAllText`, not `Set-Content`.
**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); for TX use `mcp_dsa815.dsa_capture_plot`
and retain PNG/CSV + peak/settings; (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. |