sync: BOWEI 2026-08-29 新增 dpd-mig-flow / sim-rtl-flow 两个 skill,去硬编码
- dpd-mig-flow、sim-rtl-flow 首次入库 - 两者的 LangGraph 检出路径、SMOD 参考工程路径、N9320B/板卡 IP 改为 引用 ~/skills_env.md(新增 DPD_MIG_FLOW_ROOT / SIM_RTL_FLOW_ROOT / XILINX_SMOD_ROOT / SMOD_ATSC_REF 等条目) - sim-rtl-flow 示例里写死的 /c/Users/Administrator 临时路径改为 $TMPDIR - 撤出上次遗留的误入库 __pycache__/run_fpga_local.cpython-311.pyc - .gitignore 忽略 orca-cli(指向 ~/.agents/skills 的外部链接)
This commit is contained in:
@@ -369,5 +369,6 @@ __pycache__/
|
||||
/vercel-react-native-skills/
|
||||
/vercel-react-view-transitions/
|
||||
/web-design-guidelines/
|
||||
/orca-cli/
|
||||
/.stfolder/
|
||||
/.claude/
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
---
|
||||
name: dpd-mig-flow
|
||||
description: >
|
||||
Run the dpd-mig-flow LangGraph workflow (checkout path 见 `~\skills_env.md` 的
|
||||
`DPD_MIG_FLOW_ROOT`) — the
|
||||
pipeline that ports the DPD + CFR functionality (FPGA RTL + i.MX6 app) from the ATSC
|
||||
reference project (SMOD_ATSC\my_exciter) into **another SMOD project**, then verifies it
|
||||
on real hardware: survey the reference design, port FPGA and imx6 sides in parallel,
|
||||
build the bitstream and cross-compile the app (both via the fpga-imx6-flow skill),
|
||||
debug on the board with the fake-PA distortion model, and gate on two objective
|
||||
measurements — DPD trains without error and settles to Hold with shoulder ≥ 40 dB, and
|
||||
the shoulder survives a power cycle. It drives the graph in-process (no langgraph dev)
|
||||
and hands its 6 interrupts back to this Claude session to execute.
|
||||
|
||||
ALWAYS use this skill whenever the user wants to port, migrate, or transplant DPD/CFR
|
||||
into another SMOD project, or wants to run/start/drive/resume/continue this workflow —
|
||||
including phrasings like "移植 DPD", "把 DPD 和 CFR 移到 DAB/DVBT2 工程", "跑 dpd-mig-flow",
|
||||
"启动 DPD 移植流程", "继续 DPD 移植", "resume the DPD migration", "port DPD to the DVB-T2
|
||||
exciter", "DPD 带肩不达标怎么迭代", "验证 DPD 断电保持", even when they don't name the
|
||||
project path or say the word "skill". Because the target project is never fixed, this
|
||||
skill always asks the user which Vivado project to port INTO before starting.
|
||||
---
|
||||
|
||||
## What this skill does
|
||||
|
||||
Drives the **dpd-mig-flow** graph (10 nodes / 12 flow-edges, 1:1 with
|
||||
`Notes/dpd-mig-flow.drawio`; plan in `Notes/Task00.md`). Two node classes:
|
||||
|
||||
- **6 interrupt nodes** pause the graph and hand control to **this Claude session**,
|
||||
which does the real work (read reference RTL/C, copy and rewire code, invoke the
|
||||
`fpga-imx6-flow` skill to synthesize / cross-compile / debug on the board).
|
||||
- **2 measurement nodes** run in-process and decide objectively: read DPD state over
|
||||
HTTP/SSH, measure shoulder with the N9320B, power-cycle the board via the COM relay.
|
||||
|
||||
Persistence is a SqliteSaver file, so `start` / `resume` survive across separate process
|
||||
calls, and each target project gets its own thread.
|
||||
|
||||
| Item | Value |
|
||||
|---|---|
|
||||
| Project root | 见 `~\skills_env.md` 的 `DPD_MIG_FLOW_ROOT` |
|
||||
| Conda env | `langgraph` (path 见 `~\skills_env.md` 的 `CONDA_LANGGRAPH_PYTHON`) |
|
||||
| Graph | `app.main_graph:build_graph` (in-process, no langgraph dev) |
|
||||
| Driver | `<project>\run.py`(`start` / `resume` / `status` 三个子命令) |
|
||||
| Checkpoint | `<project>\runs\checkpoint.sqlite`(按 `thread_id` 分区) |
|
||||
| Reference project | 见 `~\skills_env.md` 的 `SMOD_ATSC_REF`,顶层 `...\sources_1\top\smod_top.v` |
|
||||
| Target project | **每次运行由用户指定**,见 Step 1 |
|
||||
| Design source | `Notes/dpd-mig-flow.drawio`,方案 `Notes/Task00.md` |
|
||||
| Self-test | `<project>\smoke_test.py`(mock,68 项检查) |
|
||||
|
||||
```
|
||||
PY = <CONDA_LANGGRAPH_PYTHON — 见 ~\skills_env.md>
|
||||
PROJ = <DPD_MIG_FLOW_ROOT — 见 ~\skills_env.md>
|
||||
RUN = %PROJ%\run.py
|
||||
```
|
||||
|
||||
> **直接用环境里的 python.exe 跑,别用裸 `conda run`**:本机 GBK 控制台下 `conda run` 会用
|
||||
> GBK 重新编码子进程的 UTF-8 stdout,**遇到中文 interrupt payload 会崩**。用环境
|
||||
> `python.exe`,或 `conda run --no-capture-output`。
|
||||
|
||||
## ⚠️ 硬件时序铁律
|
||||
|
||||
**绝不在 USB-Blast 处于连接状态时给板卡上电或断电。** 本工作流的 `check_power_cycle`
|
||||
节点会真的断电重启,其内部顺序已固定为:
|
||||
|
||||
```
|
||||
断开 USB-Blast → 断电 → 等待 → 上电 → 等 app 就绪
|
||||
```
|
||||
|
||||
`power_on()` / `power_off()` 各自还会兜底确认一次 USB-Blast 已断开。断电保持判定不需要
|
||||
JTAG,所以默认**不再接回** USB-Blast(需要接回时把 `env_config.json` 的
|
||||
`power.reconnect_blast` 置 `true`)。手工介入板卡时同样遵守这条。
|
||||
|
||||
继电器 COM 号**唯一来源是 `~/vivado_mcp_usb.conf`**(`[power]` / `[usb_blast]`,逐机而异、
|
||||
不入库),与 vivado-mcp 的 `power_on`/`USB_Blast_on` 共用同一份配置;文件缺失时调 vivado-mcp
|
||||
的 **`detect_serial_relays`** 工具自动探测生成。
|
||||
|
||||
## Step 0 — 确认环境(驱动前必做)
|
||||
|
||||
```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')"
|
||||
}
|
||||
```
|
||||
|
||||
`[OK]` → 进 Step 1。缺失/导入失败 → 告诉用户先跑 **`/langgraph-vivado-install`**,别硬上。
|
||||
|
||||
想先确认工作流本身没坏(不碰任何硬件):
|
||||
|
||||
```powershell
|
||||
& $py "$PROJ\smoke_test.py" # 期望 ALL PASS (68 checks)
|
||||
```
|
||||
|
||||
## Step 1 — 问用户:移植到哪个工程?(这一步不能跳过)
|
||||
|
||||
设计图里全程只说"当前 vivado 工程",因为**它本来就不固定**——这条流水线是为"把 ATSC 的
|
||||
DPD/CFR 搬到任意 SMOD 工程"设计的。所以目标工程**必须每次运行由用户显式给出**:没有默认值、
|
||||
不沿用上次、不从配置文件里读、不替用户猜。`run.py` 会强制校验,缺参数直接非零退出。
|
||||
|
||||
`XILINX_SMOD_ROOT`(见 `~\skills_env.md`)下的候选通常有 `SMOD_DAB`、`SMOD_DVBT2`、`SMOD_DTMB_CG`、
|
||||
`SMOD_DVBS`、`SMOD_SAT`。用 AskUserQuestion 或直接问,把这 7 项凑齐:
|
||||
|
||||
| 参数 | 含义 | 怎么定 |
|
||||
|---|---|---|
|
||||
| `--target-vivado` | 目标 Vivado 工程目录 | 用户指定 |
|
||||
| `--target-xpr` | 目标工程 `.xpr` | 在上面目录里找,找到多个就让用户选 |
|
||||
| `--target-imx6` | 目标配套 imx6 工程目录 | 通常是工程下的 `imx6` |
|
||||
| `--standard` | 调制标准 | DAB / DVBT2 / DTMB / DVBS / SAT |
|
||||
| `--symbol-clk-mhz` | 调制核输出符号采样率时钟 | **决定 DPD/CFR 挂在哪条数据链上**,问用户或从目标顶层读出来核实 |
|
||||
| `--center-hz` | 频谱测量中心频率 | 决定带肩怎么测 |
|
||||
| `--ch-bw-hz` | 信道带宽 | 同上 |
|
||||
|
||||
参数多,可以先写成档案文件再点名(**档案名仍须每次显式给出**,不许自动挑最新的):
|
||||
|
||||
```powershell
|
||||
& $py $RUN start --target-profile "$PROJ\targets\smod_dab.json"
|
||||
```
|
||||
|
||||
`start` 会校验目录/`.xpr` 真实存在、目标不是参考工程(防止误改参考工程 `my_exciter`),派生
|
||||
`thread_id`(含工程指纹,不同工程绝不共用 checkpoint),并把参数落到
|
||||
`runs/<thread_id>/launch.json` 留痕。
|
||||
|
||||
## Step 2 — 启动
|
||||
|
||||
```powershell
|
||||
& $py $RUN start `
|
||||
--target-vivado "<目标工程目录>" `
|
||||
--target-xpr "<目标工程目录>\<name>.xpr" `
|
||||
--target-imx6 "<目标工程目录>\imx6" `
|
||||
--standard DAB --symbol-clk-mhz 8.192 --center-hz 214e6 --ch-bw-hz 1.536e6
|
||||
```
|
||||
|
||||
输出一行 JSON:`{"status":"waiting","thread_id":...,"interrupts":[{"id":...,"task":...,"value":{...}}]}`。
|
||||
**记下 `thread_id`**,后面每次 resume 都要用。
|
||||
|
||||
想先空跑一遍看流程(不碰仪器/串口/板卡):设 `$env:DPD_MIG_MOCK="1"` 再 start。
|
||||
|
||||
## Step 3 — 执行 interrupt,然后 resume(主循环)
|
||||
|
||||
每个 interrupt 的 `value` 里带 `action`、`instructions`(这一步的完整任务书)、`target`
|
||||
(本次运行指定的目标工程,**据此决定往哪个工程写**,别靠记忆)、`expected_resume`(该回填什么)。
|
||||
|
||||
### ⚠️ 并行阶段会同时抛出 2 个 interrupt
|
||||
|
||||
`study_ref` 之后图分成 FPGA 侧和 imx6 侧两条并行支路,`invoke` 会**同时**返回 2 个
|
||||
interrupt(构建阶段同理)。因此 resume **统一用 `{interrupt_id: payload}` 映射**,
|
||||
单值形式会因为无法指定目标而报错卡死。可以分批恢复:只给一个 id,另一个保持挂起。
|
||||
|
||||
### resume 的正确姿势:写文件,别贴 JSON
|
||||
|
||||
Windows PowerShell 5.1 会**吞掉**传给原生 exe 的双引号,直接把 JSON 贴到命令行必然变成非法
|
||||
JSON。用 `--mapping-file`(最省事,Write 出一个文件即可)或 `--mapping-b64`:
|
||||
|
||||
```powershell
|
||||
# resume.json 内容形如:
|
||||
# { "<id_of_port_fpga_rtl>": { "fpga_port_done": true, ... },
|
||||
# "<id_of_port_imx6_src>": { "imx6_port_done": true, ... } }
|
||||
& $py $RUN resume --thread-id "<thread_id>" --mapping-file "<scratch>\resume.json"
|
||||
|
||||
# 随时看卡在哪
|
||||
& $py $RUN status --thread-id "<thread_id>"
|
||||
```
|
||||
|
||||
### 6 个 interrupt 各自要做什么
|
||||
|
||||
| action | 你要做的事 | resume 回填 |
|
||||
|---|---|---|
|
||||
| `survey_reference`<br>(#3) | 通读参考工程的 DPD/CFR 用法,产出调研 md + 待移植文件清单。重点:旁路宏 `DPDENABLE`/`CFR_ENABLE`;发射链 `digital_rms_agc → CFR_cfr_conf → dacfr → DPD`;ORx 反馈链 `120M → rx1_dec3_120to40 → orx_dec_hb_40to10 → RAM2 → orx_iq_udp_tx`;SPI3 仲裁 `spi3_agc_arbiter`;DPD 系数存取 `spi_slave_2_DPD_SL.v` | `ref_survey_md`, `ref_file_list` |
|
||||
| `port_fpga`<br>(#4) | 复制 DPD/CFR 源码进目标工程并加入 `.xpr`;参考 `smod_top.v` 接进目标顶层;**做速率转换**(参考工程 DPD/CFR 跑 40 MHz,目标符号率不同,必须接在调制核输出的正确符号采样率链上并与后续 204b 接口时序匹配);宏旁路;**ORx AGC + SPI3 仲裁与参考工程完全一致**;**保留 fake-PA**;建独立 DPD 寄存器模块(对标 `reg_map_dpd.v`) | `fpga_port_done`, `fpga_ported_files`, `rate_conv_scheme`, `reg_map_dpd_path`, `bypass_macros`, `fake_pa_kept`, `orx_agc_ported` |
|
||||
| `port_imx6`<br>(#5) | 整理并移植 6 项功能:①寄存器控制 ②**DPD 控制状态流程 md(含 mermaid)** ③前面板键盘+液晶 ④SPI3 仲裁 ⑤网页控制+状态显示 ⑥隐藏菜单参数。入口:`bsp/dpd_proc.c`、`bsp/eeprom.c`、`task/lcd_task.c`、`task/uart_user.c`、`adrv9009/adrv9009_config.c`、`web/www*/sys_set.html`。**顺手把 `env_config.json` 的 `board.endpoints` 按实际接口填实** | `imx6_port_done`, `imx6_ported_files`, `dpd_state_md`, `imx6_feature_md`, `imx6_features_ok`(6 项全 true 才放行) |
|
||||
| `build_fpga`<br>(#11) | 调 **`fpga-imx6-flow` skill** 综合实现出 bitstream + flash 文件;重点看速率转换到 204b 那段时序有没有违例 | `build_fpga_ok`, `bit_path`(必须真实存在), `flash_path` |
|
||||
| `build_imx6`<br>(#6) | 调 **`fpga-imx6-flow` skill** 交叉编译出 app | `build_imx6_ok`, `app_path` |
|
||||
| `hw_debug`<br>(#10) | 调 **`fpga-imx6-flow` skill** 下 bit、部署 app、重启;**开 fake-PA**;网页 DPD start 训练;看 ILA/串口;用 N9320B 看 DPD 前后带肩。离开本节点前确保 fake-PA 已开、DPD 已 start、板卡与频谱仪在线——下一个节点会自动读状态+测带肩来判定 | `debug_log`, `fake_pa_on`(必须 true), `dpd_started`(必须 true), `shoulder_before_db`(可选,用于判定改善量) |
|
||||
|
||||
第 2 轮起 `hw_debug` 的 payload 里会带 `previous_failure`:**先看它再动手**。带肩不达标 →
|
||||
查 DPD 收敛 / ORx AGC / 速率转换;断电后带肩掉了 → 查 DPD 系数 save/load 与 eeprom 持久化。
|
||||
|
||||
### 两个判定节点(不需要你介入,除非降级)
|
||||
|
||||
- `check_dpd_train`(#15):等训练收敛 → 读 DPD 状态 → 测带肩。四个条件全过才算通过:
|
||||
训练无报错、状态为 `Hold`、带肩 ≥ 40 dB、(若已知 DPD 前带肩)确有改善。否则回 `hw_debug`。
|
||||
- `check_power_cycle`(#19):测重启前带肩 → 按铁律断电重启 → 等 app 起来 → 复测 →
|
||||
变化 ≤ 1.5 dB 且仍 ≥ 40 dB 且状态仍为 Hold 才算通过。否则回 `hw_debug`。
|
||||
|
||||
**降级裁决**:仪器/板卡够不着或读数矛盾时,节点会抛 `action: "adjudicate"` 的 interrupt,
|
||||
payload 的 `evidence` 里带着已采到的数据。你据此给结论:`{"pass": true/false, "reason": "..."}`。
|
||||
别为了让流程往下走而随手判 true——这两个判据就是整个任务的验收标准。
|
||||
|
||||
> 测量结果按 `(thread, iteration)` 落盘缓存(`runs/<thread>/iter<N>/verify_*.json`)。
|
||||
> interrupt-resume 会让节点从头重跑,缓存保证**一轮之内绝不会把板子断电两次**。
|
||||
|
||||
## Step 4 — 结束
|
||||
|
||||
两个判定都过 → `task_done`,`status` 变 `done`,汇总落 `runs/<thread_id>/summary.json`
|
||||
(含最终带肩、断电前后读数、bit/app 路径、产出的几份 md)。把这些回报给用户。
|
||||
|
||||
迭代超过 `max_iterations`(默认 10)会优雅报错退出——这是让人介入判断"是移植方案的问题还是
|
||||
指标定义的问题",不是让你调大上限硬撑。
|
||||
|
||||
## 真机运行前的检查清单
|
||||
|
||||
`DPD_MIG_MOCK` **不设**(或设为 0)就是真机模式。开跑前确认:
|
||||
|
||||
| 项 | 怎么确认 | 缺了怎么办 |
|
||||
|---|---|---|
|
||||
| 继电器 COM 号 | `~/vivado_mcp_usb.conf` 有 `[power]` 和 `[usb_blast]` | 调 vivado-mcp 的 `detect_serial_relays` |
|
||||
| 板卡 DPD 接口 | `env_config.json` 的 `board.endpoints`(或 `ssh_commands`)已按目标工程实际接口填实 | 还是占位模板时 `tools_board` 会**明确报错**,不会对着不存在的接口测出假结论;先完成 `port_imx6` 再来 |
|
||||
| N9320B | `spectrum.host`(默认取 `~\skills_env.md` 的 `N9320B_HOST`)能 ping 通 | 检查网络/仪器电源 |
|
||||
| 板卡 | `board.ip`(默认取 `~\skills_env.md` 的 `SMOD_BOARD_IP`)能通 | 先上电 |
|
||||
| 频谱判据 | `center_hz` / `ch_bw_hz` 是本次目标标准的正确值 | 问用户 |
|
||||
|
||||
带肩定义(DPD 前后、断电前后必须用**完全相同**的仪器设置,否则数值不可比):带内参考取
|
||||
信道内 PSD 均值,肩部取 `±(ch_bw/2 + ch_bw × shoulder_offset_ratio)` 处 PSD,
|
||||
`shoulder_db = 带内 − 肩部`,取上下边带较差者。RBW/VBW/检测器/平均/参考电平/衰减全部由
|
||||
`env_config.json` 固定。
|
||||
|
||||
## 常见问题
|
||||
|
||||
| 现象 | 原因 / 处理 |
|
||||
|---|---|
|
||||
| `缺少必填的目标工程参数` | 按设计如此(每次必须显式指定),把 7 个参数补齐 |
|
||||
| `目标工程不能是参考工程` | 防误改 `my_exciter` 的保护,检查 `--target-vivado` |
|
||||
| `目标工程参数与本 thread 已记录的不一致` | 中途改了参数。换工程会自动换 thread;只是想改测量频点/带宽就用 `--tag` 开新 thread,别在半途改判据 |
|
||||
| `resume 参数不是合法 JSON` 且引号丢了 | PowerShell 吞引号,改用 `--mapping-file` / `--mapping-b64` |
|
||||
| `未知的 interrupt id` | id 过期了(已被恢复过)。先 `status` 拿当前 id |
|
||||
| `board 接口仍是占位模板` | `port_imx6` 还没把实际网页/命令行接口填进 `env_config.json` |
|
||||
| 控制台中文乱码/崩溃 | 别用裸 `conda run`,直接用环境 `python.exe` |
|
||||
| 改了 `app/*.py` 后行为没变 | 无 dev server 缓存,直接重跑即可;若图结构改到与旧 checkpoint 不兼容,换 `--tag` 或删 `runs/checkpoint.sqlite` |
|
||||
|
||||
## 相关
|
||||
|
||||
- **`fpga-imx6-flow`** — 综合/交叉编译/下载/上板调试都转交它(本图不嵌套调用另一张图,
|
||||
两张图各有 checkpointer 与 interrupt 语义,进程内嵌套会互相污染)
|
||||
- **`smod_atsc_dev`** — SMOD 平台事实、两条烧写路径的选择、上电时序
|
||||
- **`fpga_flash`** / **`fpga_update`** — JTAG 直烧 / 远程升级 mcs
|
||||
- **`langgraph-vivado-install`** — 创建 `langgraph` conda 环境
|
||||
Binary file not shown.
@@ -1,7 +1,8 @@
|
||||
---
|
||||
name: sim-rtl-flow
|
||||
description: >
|
||||
Run the sim-rtl-flow LangGraph workflow at E:\LangGraph_Design\sim-rtl-flow — the
|
||||
Run the sim-rtl-flow LangGraph workflow (checkout path 见 `~\skills_env.md` 的
|
||||
`SIM_RTL_FLOW_ROOT`) — the
|
||||
Simulink-to-RTL-to-hardware design pipeline: read the previous run's design version-tree
|
||||
doc xxx_tree.md to decide what may be reused → Simulink -x principle sim
|
||||
(sim-golden) → HUMAN sign-off on simulation performance → optional -q fixed-point →
|
||||
@@ -228,7 +229,7 @@ regenerated tree has lost last round's attributes. Default, as everywhere here,
|
||||
`<work_dir>/.sim_rtl_flow/`** — that is where `resume`/`record`/`status` will look.
|
||||
|
||||
```bash
|
||||
cat > /c/Users/Administrator/AppData/Local/Temp/claude/input.json <<'EOF'
|
||||
cat > $TMPDIR/sim_rtl_flow_input.json <<'EOF'
|
||||
{
|
||||
"requirements_path": "Notes/requirements.md",
|
||||
"work_dir": "E:/Xilinx_design/my_project",
|
||||
@@ -257,7 +258,7 @@ EOF
|
||||
### 2. Start — exits 10 with a handoff
|
||||
|
||||
```bash
|
||||
"$PY" "$RUN" --thread-id my-design-001 start --input /c/Users/Administrator/AppData/Local/Temp/claude/input.json
|
||||
"$PY" "$RUN" --thread-id my-design-001 start --input $TMPDIR/sim_rtl_flow_input.json
|
||||
```
|
||||
|
||||
`start` takes `work_dir` from the input JSON and prints where state landed. Verified:
|
||||
|
||||
Reference in New Issue
Block a user