- simulink_rtl_dev / rtl-ip-replace: 从工程级 .claude/skills 迁到用户级并去工程耦合 (模型名/Notes 文档依赖/工程实测基线移出; addpath 与脚本路径改用户级) - rtl-ip-replace/scripts/gen_cmp_tb.py: Vivado 路径不再写死, 改读 VIVADO_PATH / XILINX_VIVADO (兼容 install root / bin / bin\vivado.bat 三种写法), 回落时提示 去设 skills_env.md 的 VIVADO_PATH - simulink-rtl-redev: 新增。原生模型改动后的仿真模型同步规范, 含两个实测过的工具 check_sync.py (三条时序链新鲜度闸门) 与 measure_k.m (时域/频域/包络三估计器互证) - sim-rtl-flow/SKILL.md: 加入 sync_sim:* 步骤, 引用上述三个 skill
18 KiB
name, description
| name | description |
|---|---|
| sim-rtl-flow | 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 | <work_dir>\.sim_rtl_flow\runs\<thread-id>\ — 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.
PY="<CONDA_LANGGRAPH_PYTHON — see ~\skills_env.md>"
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.
"$PY" "$RUN" --work-dir <proj> --thread-id <id> record --key review:sim_performance#1 --approve
"$PY" "$RUN" --work-dir <proj> --thread-id <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.
- sim-golden is the only acceptance criterion.
-q, the partition, and every-Eversion must match the-xsim. If it doesn't, recordgate_passed: false— never record a pass you didn't verify. - One
-E.slxper RTL submodule, saved separately (<project>_A-E.slx, …). Do not nativize all submodules in one Simulink model. - 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 forcesmodify_e:* → sync_sim:*before any RTL is regenerated. - Editing a
-Emodel obliges you to sync the-x/-qmodel.modify_e:*edits only the-E; the follow-upsync_sim:*usessimulink-rtl-redevto 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 tomodify_e:*— it will not let you regenerate RTL. - 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. - HIL is strictly read-only.
hil:*arrives withallow_code_changes=false. Runfpga-imx6-flow/zynq-imx6-flowfor 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
<work_dir>/.sim_rtl_flow/ — that is where resume/record/status will look.
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 — <name>-x.slx, <name>_A-E.slx, <name>.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
"$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):
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:<M>#n |
nativize M into HDL-capable blocks, save <project>_M-E.slx, generate RTL |
simulink_rtl_dev |
ip_replace:<M>#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:<source>#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.
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-runnative → ip_replace→ Vivado → HIL.non_simulink_rtl/soc/hardware→timing_rework(from Vivado) orhil_rework(from HIL) → Vivado.- The driver rejects
needs_code_change: truewithchange_scope: "none".
A failing ip_replace:<M> (cycle-true mismatch) enters the same chain on its own:
evaluate_modules → modify_e:module → sync_sim:module → native:<M>. A failing
native:<M> 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:<M>#1 per module (3 for ["A","B","C"]) |
| next module stage | one ip_replace:<M>#1 per module |
Work batched tasks in any order; record all, then resume.
Rehearsal without touching hardware
"$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.
"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
"$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/statusneed--work-dir(or acdinto the design project). Onlystartcan infer it, from the input JSON. Without it the driver looks in the cwd and reportsno input.json in ….- The driver refuses a
work_dirinside the sim-rtl-flow repo. Run state belongs to the design project (PR01-2).examples/happy_path.jsontherefore carries a placeholderE:/Xilinx_design/your_project— fine fordry-run, but replace it beforestart. mermaidoutput is still partly misleading. The forward and rework edges now render (path maps were added), butplan_modules'Sendfanout is a dynamic edge Mermaid can't show. UseNotes/Result00.mdfor the authoritative picture.--max-attemptsmust precede the subcommand and stay the same value acrossstartand everyresume— 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/-qmodel telling the truth about the-Eyou 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. Recordartifacts: []and the graph substitutes<project_name>-x.slx. Always record the real filename.module_planruns 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.jsonby hand.recordonly accepts currently-pending keys; anything already in the ledger is replayed forever. To redo a step, delete its entry and everything after it, thenresume. - Runs created before the PR01-2 change live in the old location
(
<SIM_RTL_FLOW_ROOT>\runs\<id>\) and their ledgers have noreview:*ormodule_plankeys, so they cannot be resumed under the current graph. Start a fresh thread instead. work_diris 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.startwipes the ledger unless--keep-ledger. Use a fresh--thread-idper 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\<id> |
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 "<user's reason>" |
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 |