diff --git a/.gitignore b/.gitignore index c02de40..838513c 100644 --- a/.gitignore +++ b/.gitignore @@ -369,5 +369,6 @@ __pycache__/ /vercel-react-native-skills/ /vercel-react-view-transitions/ /web-design-guidelines/ +/orca-cli/ /.stfolder/ /.claude/ diff --git a/dpd-mig-flow/SKILL.md b/dpd-mig-flow/SKILL.md new file mode 100644 index 0000000..a98951d --- /dev/null +++ b/dpd-mig-flow/SKILL.md @@ -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 | `\run.py`(`start` / `resume` / `status` 三个子命令) | +| Checkpoint | `\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 | `\smoke_test.py`(mock,68 项检查) | + +``` +PY = +PROJ = +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//launch.json` 留痕。 + +## Step 2 — 启动 + +```powershell +& $py $RUN start ` + --target-vivado "<目标工程目录>" ` + --target-xpr "<目标工程目录>\.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 内容形如: +# { "": { "fpga_port_done": true, ... }, +# "": { "imx6_port_done": true, ... } } +& $py $RUN resume --thread-id "" --mapping-file "\resume.json" + +# 随时看卡在哪 +& $py $RUN status --thread-id "" +``` + +### 6 个 interrupt 各自要做什么 + +| action | 你要做的事 | resume 回填 | +|---|---|---| +| `survey_reference`
(#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`
(#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`
(#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`
(#11) | 调 **`fpga-imx6-flow` skill** 综合实现出 bitstream + flash 文件;重点看速率转换到 204b 那段时序有没有违例 | `build_fpga_ok`, `bit_path`(必须真实存在), `flash_path` | +| `build_imx6`
(#6) | 调 **`fpga-imx6-flow` skill** 交叉编译出 app | `build_imx6_ok`, `app_path` | +| `hw_debug`
(#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//iter/verify_*.json`)。 +> interrupt-resume 会让节点从头重跑,缓存保证**一轮之内绝不会把板子断电两次**。 + +## Step 4 — 结束 + +两个判定都过 → `task_done`,`status` 变 `done`,汇总落 `runs//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 环境 diff --git a/fpga-goldenref-flow/scripts/__pycache__/run_fpga_local.cpython-311.pyc b/fpga-goldenref-flow/scripts/__pycache__/run_fpga_local.cpython-311.pyc deleted file mode 100644 index df9663a..0000000 Binary files a/fpga-goldenref-flow/scripts/__pycache__/run_fpga_local.cpython-311.pyc and /dev/null differ diff --git a/sim-rtl-flow/SKILL.md b/sim-rtl-flow/SKILL.md index 91419d2..2d7fec1 100644 --- a/sim-rtl-flow/SKILL.md +++ b/sim-rtl-flow/SKILL.md @@ -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, `/.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: