--- name: sim-rtl-flow description: > Run the sim-rtl-flow LangGraph workflow at E:\LangGraph_Design\sim-rtl-flow — the Simulink-to-RTL-to-hardware design pipeline: Simulink -x principle sim (sim-golden) → HUMAN sign-off on simulation performance → optional -q fixed-point → parallel functional partition + RTL/SoC interface spec → HUMAN sign-off on the partition → per-module -E.slx nativization and Xilinx-IP replacement (parallel Send fanout) → Vivado build + timing → hardware-in-the-loop via fpga-imx6-flow or zynq-imx6-flow. It drives the compiled graph in-process (no langgraph dev server) and hands every subagent task back to THIS Claude session — and every human checkpoint to the USER — through a record/replay ledger stored in the design project being worked on. ALWAYS use this skill whenever the user wants to run, start, drive, resume or continue the sim-rtl-flow workflow / Simulink→RTL design flow, or says things like "启动 sim-rtl-flow", "跑 sim-rtl-flow", "运行仿真到RTL的设计流程", "执行 sim-rtl-flow 设计任务", "继续 sim-rtl-flow", "run the sim to rtl flow", "resume the sim-rtl-flow run", even if they don't name the project path. Also use it when the user asks to record a subagent result, to approve or reject one of the two human checkpoints, or to rehearse the graph with the scripted backend. --- ## What this skill does Drives the **sim-rtl-flow** graph (21 top-level nodes + a 3-node per-module subgraph, 1:1 with `Notes/PR01-1.md`, `Notes/PR01-2.md` and `Notes/PR01_sim_rtl_flow.drawio`). The graph contains **no** agent logic — it talks to the outside world through two protocols: `SubagentBackend` (design work) and `ReviewGate` (the two mandatory human sign-offs). This skill plugs a **record/replay** implementation into both: the graph runs in-process until it needs an answer that isn't recorded yet, then stops and prints the request. **You are the subagent; the USER is the reviewer.** You do the work, `record` the answer, and `resume` — the driver replays the ledger, deterministically walks back to where it stopped, and asks for the next thing. | Item | Value | |---|---| | Project root | 见 `~\skills_env.md` 的 `SIM_RTL_FLOW_ROOT`(driver 自动读取,也可 `--project` 覆盖) | | Conda env | `langgraph` (path 见 `~\skills_env.md` 的 `CONDA_LANGGRAPH_PYTHON`;装环境用 `/langgraph-vivado-install`) | | Graph | `sim_rtl_flow.graph:build_workflow` (in-process, no `langgraph dev`) | | Driver | `%USERPROFILE%\.claude\skills\sim-rtl-flow\scripts\run_sim_rtl_flow.py` | | **Run state** | **`\.sim_rtl_flow\runs\\`** — inside the *design project*, never in the workflow repo | | Max attempts | 10 per gate (default) | There is **no LangGraph checkpointer** in the driver, on purpose: graph nodes have no side effects of their own (all side effects live in the subagents, which are replayed), so re-invoking from scratch is cheaper and immune to partial-superstep resume semantics. `ledger.json` *is* the persistence. **Drive the driver with the Bash tool (Git Bash), not PowerShell** — PowerShell 5.1 mangles the double quotes inside `--result '{...}'` payloads. Every command below was run this way and verified. ```bash PY="" RUN="$USERPROFILE/.claude/skills/sim-rtl-flow/scripts/run_sim_rtl_flow.py" export PYTHONIOENCODING=utf-8 ``` The driver resolves the workflow checkout from `SIM_RTL_FLOW_ROOT` in `~\skills_env.md`, so no path is baked into this skill. ## ⚠️ The two human checkpoints are the user's decision, not yours The graph stops at two points that **an agent may not decide**: | checkpoint | where | what the user confirms | |---|---|---| | `sim_performance` | after step 1 | the `-x` sim meets the requirement and may be frozen as **sim-golden** | | `functional_partition` | after step 3 | the pure-sim / RTL / SoC split and the RTL↔SoC interface doc | When one comes up the driver prints a `!!!` banner. **Relay the question and its context to the user verbatim, then wait.** Do not approve on your own judgement, do not infer approval from "the numbers look fine", and do not use `--all-ok` (the driver refuses it for review keys anyway). A rejection **must** carry the user's reason. ```bash "$PY" "$RUN" --work-dir --thread-id record --key review:sim_performance#1 --approve "$PY" "$RUN" --work-dir --thread-id record --key review:sim_performance#1 \ --reject --notes "带外抑制不足 6dB" ``` Rejecting `sim_performance` re-runs step 1; rejecting `functional_partition` re-runs the whole partition + interface superstep. ## Non-negotiable design rules the graph enforces From `Notes/PR01.md` and `Notes/PR01-2.md`; the graph routes on these. 1. **sim-golden is the only acceptance criterion.** `-q`, the partition, and every `-E` version must match the `-x` sim. If it doesn't, record `gate_passed: false` — never record a pass you didn't verify. 2. **One `-E.slx` per RTL submodule**, saved separately (`_A-E.slx`, …). Do not nativize all submodules in one Simulink model. 3. **Never hand-edit MATLAB-generated RTL.** If IP replacement, Vivado timing, or HIL needs a change there, return `change_scope: "simulink_module"` + `affected_modules`. The graph then forces `modify_e:* → sync_sim:*` before any RTL is regenerated. 4. **Editing a `-E` model obliges you to sync the `-x`/`-q` model.** `modify_e:*` edits only the `-E`; the follow-up `sync_sim:*` uses **`simulink-rtl-redev`** to carry the same change into the simulation model and re-verify against sim-golden. The derivation script does *not* translate native-core edits into the m implementation, so changing one side alone means the RTL moved while the simulation still validates the old behaviour. If they don't align, the graph goes back to `modify_e:*` — it will **not** let you regenerate RTL. 5. **Timing fixes change the RTL, not the constraints.** Prefer adding pipeline/delay stages. Only touch timing constraints when the constraint itself is plainly wrong, and say why in `notes`. 6. **HIL is strictly read-only.** `hil:*` arrives with `allow_code_changes=false`. Run `fpga-imx6-flow` / `zynq-imx6-flow` for *diagnosis only*; the moment a code change is needed, stop that sub-flow and report it back here — the fix happens in the sim-rtl-flow main graph, then re-enters Vivado and HIL. ## Run (agent path) ### 1. Write the workflow input `work_dir` is the design project. **All run state goes under `/.sim_rtl_flow/`** — that is where `resume`/`record`/`status` will look. ```bash cat > /c/Users/Administrator/AppData/Local/Temp/claude/input.json <<'EOF' { "requirements_path": "Notes/requirements.md", "work_dir": "E:/Xilinx_design/my_project", "project_name": "demo", "device_family": "fpga-imx6", "fixed_point_required": true, "rtl_modules": ["A", "B", "C"], "soc_modules": ["control"] } EOF ``` | field | meaning | |---|---| | `requirements_path` | user's requirement `.md`, relative to `work_dir` | | `work_dir` | the design project — **absolute path**; also where run state is written | | `project_name` | artifact prefix — `-x.slx`, `_A-E.slx`, `.bit` | | `device_family` | `fpga-imx6` → HIL via `fpga-imx6-flow`; `zynq-imx6` → `zynq-imx6-flow`. Anything else raises at `initialize`. | | `fixed_point_required` | `false` skips the `-q` stage | | `rtl_modules` | submodule names; each gets its own parallel `native → ip_replace` chain. Non-empty. | | `soc_modules` | MCU/SoC-side modules (interface-spec input) | ### 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 ``` `start` takes `work_dir` from the input JSON and prints where state landed. Verified: ``` work dir : E:\Xilinx_design\my_project run dir : E:\Xilinx_design\my_project\.sim_rtl_flow\runs\my-design-001 ======================================================================== HANDOFF — 1 subagent task(s), 0 human review(s) ======================================================================== --- key: simulink_x#1 role : simulink-principle-agent permission : WRITE ALLOWED prompt : 根据需求文档创建或修订 Simulink -x 原理仿真;... ``` Exit codes: **10** = handoff pending, **0** = completed, **1** = `failed`, **2** = crash. ### 3. Answer, then resume Every later command needs to find the run, so pass `--work-dir` (or `cd` into it): ```bash W="--work-dir E:/Xilinx_design/my_project --thread-id my-design-001" "$PY" "$RUN" $W record --key simulink_x#1 \ --result '{"gate_passed": true, "artifacts": ["demo-x.slx"], "notes": "EVM -38dB"}' "$PY" "$RUN" $W resume ``` Loop until `WORKFLOW FINISHED — status: completed`. When several keys are pending, record **each**, then `resume` once. `record --all-ok` fast-forwards pending *subagent* keys with a default success — rehearsal only, and it refuses review keys. ### 4. What you get handed | key | what you actually do | which skill to use | |---|---|---| | `simulink_x#n` | build/revise the `-x` sim, document the performance envelope | MATLAB MCP + `model-based-design-core:building-simulink-models`, `simulating-simulink-models` | | `review:sim_performance#n` | **ask the user** to sign off on sim-golden | — (relay to user) | | `fixed_point#n` | `-q` fixed-point, compare to `-x` | same MATLAB skills | | `partition#n` | pure-sim / RTL / SoC split, must not degrade sim-golden | same | | `interfaces#n` | RTL↔SoC interface doc — register-config or data-stream only | write the doc under `work_dir` | | `review:functional_partition#n` | **ask the user** to sign off on the split + interfaces | — (relay to user) | | `module_plan#n` | split the RTL side into A/B/C…, give order + dependencies (**first pass only**) | inline | | `native:#n` | nativize M into HDL-capable blocks, save `_M-E.slx`, generate RTL | `simulink_rtl_dev` | | `ip_replace:#n` | swap in Xilinx IP, cycle-true compare vs MATLAB RTL | `rtl-ip-replace` | | `modify_e:module#n` / `modify_e:vivado#n` / `modify_e:hil#n` | edit **only** the named `-E.slx` (cycle-true / timing / HIL driven) | `simulink_rtl_dev` | | `sync_sim:#n` | carry that `-E` change into the matching `-x`/`-q` model and re-verify alignment | **`simulink-rtl-redev`** | | `vivado#n` | build the Vivado project, synth+impl+bitstream, check all timing | `vivado-prj-create`, `vivado-mcp` | | `hil:fpga-imx6-flow#n` / `hil:zynq-imx6-flow#n` | HIL debug, **read-only** | `fpga-imx6-flow` / `zynq-imx6-flow` | | `timing_rework#n` | fix non-Simulink RTL / SoC / project timing | inline | | `hil_rework#n` | apply the code change HIL asked for | inline | `#n` is the n-th time that key has been requested this run — retries and rework produce `#2`, `#3`, … ### 5. Answer JSON `"$PY" "$RUN" schema` prints both templates. All fields optional except `approved`. ```json subagent: {"success": true, "gate_passed": true, "artifacts": [], "notes": "", "timing_met": true, "hil_passed": true, "needs_code_change": false, "change_scope": "none", "affected_modules": [], "metadata": {}} review: {"approved": true, "notes": "", "metadata": {}} ``` | field | who reads it | |---|---| | `success` + `gate_passed` | every design node — `false` → retry, then `failed` after 10 attempts | | `artifacts` | `simulink_x`'s first artifact **becomes sim-golden**; `vivado`'s go to HIL as the bitstream | | `timing_met` | `vivado` only | | `hil_passed` | `hil:*` only | | `needs_code_change` + `change_scope` + `affected_modules` | `vivado` and `hil:*` — the rework router | Routing on `change_scope`: - `simulink_module` → **`modify_e:*`** (edit the `-E.slx`) → **`sync_sim:*`** (carry it into `-x`/`-q`, re-verify) → only the affected modules re-run `native → ip_replace` → Vivado → HIL. - `non_simulink_rtl` / `soc` / `hardware` → `timing_rework` (from Vivado) or `hil_rework` (from HIL) → Vivado. - The driver **rejects** `needs_code_change: true` with `change_scope: "none"`. A failing `ip_replace:` (cycle-true mismatch) enters the same chain on its own: `evaluate_modules → modify_e:module → sync_sim:module → native:`. A failing `native:` does **not** — the `-E` isn't sound yet, so it just re-runs nativization. ### 6. Parallel batches (verified) | stage | keys handed over at once | |---|---| | after `-x` / `-q` | `interfaces#1` **and** `partition#1` | | module fanout | one `native:#1` per module (3 for `["A","B","C"]`) | | next module stage | one `ip_replace:#1` per module | Work batched tasks in any order; record all, then `resume`. ## Rehearsal without touching hardware ```bash "$PY" "$RUN" dry-run --input examples/happy_path.json ``` Runs the whole graph against `ScriptedBackend` + auto-approving `ScriptedReviewGate` — no handoffs, no side effects. Verified: `status: completed`, 13 subagent calls listed with their skill and write permission, plus both review checkpoints. To rehearse a branch, add `outcomes` (task → list of results) and/or `reviews` (checkpoint → list of decisions) to the input JSON; `start`/`resume` strip both. ```json "outcomes": {"hil:fpga-imx6-flow": [ {"success": true, "hil_passed": false, "needs_code_change": true, "change_scope": "simulink_module", "affected_modules": ["A"]}, {"success": true, "hil_passed": true}]}, "reviews": {"sim_performance": [{"approved": false, "notes": "EVM 未达标"}]} ``` ## Other commands ```bash "$PY" "$RUN" $W status # ledger + pending + final status "$PY" "$RUN" schema # both answer templates "$PY" "$RUN" mermaid # graph as Mermaid (see gotcha) "$PY" -m unittest discover -s tests # project's own 16 tests — all pass ``` Global flags go **before** the subcommand: `--project`, `--work-dir`, `--thread-id`, `--recursion-limit` (200), `--max-attempts` (10). ## Gotchas - **`resume`/`record`/`status` need `--work-dir`** (or a `cd` into the design project). Only `start` can infer it, from the input JSON. Without it the driver looks in the cwd and reports `no input.json in …`. - **The driver refuses a `work_dir` inside the sim-rtl-flow repo.** Run state belongs to the design project (PR01-2). `examples/happy_path.json` therefore carries a placeholder `E:/Xilinx_design/your_project` — fine for `dry-run`, but replace it before `start`. - **`mermaid` output is still partly misleading.** The forward and rework edges now render (path maps were added), but `plan_modules`' `Send` fanout is a dynamic edge Mermaid can't show. Use `Notes/Result00.md` for the authoritative picture. - **`--max-attempts` must precede the subcommand** and stay the *same value* across `start` and every `resume` — it is runtime context, not persisted state. - **The skill names the graph hands you are real and exact** — `simulink_rtl_dev` (**underscores**, not dashes), `rtl-ip-replace`, `simulink-rtl-redev`. All three are installed. Invoke the named one rather than improvising with MATLAB MCP directly: they carry the project's own gates (`audit_hdl_blocks.m`, cycle-true compare, `check_sync.py`). - **`sync_sim:*` is not optional bookkeeping.** It is the step that keeps the `-x`/`-q` model telling the truth about the `-E` you just changed. Recording it as passed without actually re-simulating means every later gate — Vivado, HIL, sim-golden — is validating a model that no longer matches the RTL. - **`simulink_x`'s first artifact silently becomes sim-golden.** Record `artifacts: []` and the graph substitutes `-x.slx`. Always record the real filename. - **`module_plan` runs only on the first pass.** Rework rounds reuse the plan, so you will not be asked to re-partition the module list mid-run. - **Re-recording a key requires editing `ledger.json` by hand.** `record` only accepts currently-pending keys; anything already in the ledger is replayed forever. To redo a step, delete its entry *and everything after it*, then `resume`. - **Runs created before the PR01-2 change live in the old location** (`\runs\\`) and their ledgers have no `review:*` or `module_plan` keys, so they cannot be resumed under the current graph. Start a fresh thread instead. - **`work_dir` is passed straight through to every subagent request.** Forward slashes are fine on Windows; a relative `"."` resolves against wherever the driver was launched — always use an absolute path. - **`start` wipes the ledger** unless `--keep-ledger`. Use a fresh `--thread-id` per run. ## Troubleshooting | symptom | cause / fix | |---|---| | `sim_rtl_flow package not found under …\src` | `SIM_RTL_FLOW_ROOT` in `~\skills_env.md` is wrong/missing; fix it or pass `--project <检出目录>` | | `sim-rtl-flow 工作流仓库路径未配置` | add `SIM_RTL_FLOW_ROOT = <检出目录>` to `~\skills_env.md` | | `no input.json in …\.sim_rtl_flow\runs\` | missing/incorrect `--work-dir`, or `start` was never run | | `nothing pending — run 'start' or 'resume' first` | the previous invoke completed or crashed; check `status` | | `--key X is not pending; pending = [...]` | typo, or already recorded (see the re-record gotcha) | | `--all-ok 不能用于人工审核断点` | working as intended — the user must decide; use `--approve` / `--reject` | | `驳回必须写明理由:请提供 notes` | add `--notes ""` | | `unknown review fields: ['gate_passed']` | you sent a subagent result to a `review:*` key; use `--approve`/`--reject` | | `needs_code_change=true requires an explicit change_scope` | add `change_scope` | | `MissingReviewGateError` | only if the graph is driven without a review gate — the driver always supplies one | | `device_family must be 'fpga-imx6' or 'zynq-imx6'` | raised in `initialize`; exit 2 | | exit 2 + `recursion_limit` | a rework loop isn't converging — raise `--recursion-limit`, or stop recording `needs_code_change` for the same module | | mojibake in the Chinese prompts | `export PYTHONIOENCODING=utf-8` |