Files
skills-win/sim-rtl-flow/SKILL.md
T

32 KiB
Raw Blame History

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: 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 → parallel functional partition + RTL/SoC interface spec → HUMAN sign-off on the partition (skippable when history says the partition is unchanged) → per-module -E.slx nativization and Xilinx-IP replacement (parallel Send fanout, modules the history proved good are skipped) → constraint-primitive injection into the delivered RTL tree → Vivado build + timing → hardware-in-the-loop via fpga-imx6-flow or zynq-imx6-flow → write a fresh xxx<date>_tree.md summarising every -x / -q / -E version. 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 (27 top-level nodes + a 5-node per-module subgraph, 1:1 with Notes/PR01-1.md, Notes/PR01-2.md and Notes/PR01_sim_rtl_flow.drawio, plus the constraint-primitive stage from Notes/PR23_flow_constraint_stage.md).

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 design version-tree doc xxx<date>_tree.md

Every run reads the newest history doc and writes a new one. That doc is the only mechanism stopping the flow from re-doing — and thereby re-breaking — design work that a previous run already validated.

Location <work_dir>/<history_dir>/, where history_dir defaults to the folder holding requirements_path (usually Notes)
Name <project_name><YYYYMMDD-HH>_tree.md — project name + date and hour, zero-padded, so name order == time order. A hand-written xxx_tree.md with no stamp counts as the oldest (it is the human-supplied starting point). Pre-rename sim-rtl-flow-<date>.md files are still read when no *_tree.md exists
Read start freezes the newest matching file into input.json as history_path; the history_review#1 task interprets it
Written the design_summary#1 task, at the end of every run — completed and failed

history_review returns its verdict in metadata, and that verdict is what lets the graph skip work:

metadata field effect if false / listed
redo_fixed_point false → the -q stage is skipped entirely. Before answering false, re-check the historic -q against the current word-length rule — widths that violate it, or a history doc that never recorded widths, mean true
redo_partition + partition_review_required both false → the functional_partition human checkpoint is skipped
reuse_modules listed module → its native:M + ip_replace:M are skipped; result recorded as stage: history_reuse
redo_modules listed module always re-runs, even if also in reuse_modules
redo_constraints + constraint_spec_hash redo_constraints: false → the constraint_primitives stage is skipped, but only if no module regenerated RTL this round (graph-enforced). Answer false only after actually hashing the current spec file and matching it against the hash in the history doc; no hash recorded = true

Every default is "redo". Omit a field and the stage runs. Fail the history_review gate (gate_passed: false) and nothing is skipped. Recording a skip you did not verify means a later gate — Vivado, HIL, sim-golden — validates a design nobody checked this run.

Four things force everything back to redo, regardless of the history doc:

  1. simulink_x running a second time (a rejected sim_performance, or a retry) — sim-golden may have moved, so the history describes a different -x;
  2. partition#n returning metadata.partition_changed: true — the human checkpoint comes back even if history said to skip it;
  3. any module named by Vivado or HIL rework — the reuse pool is consumed after the first module round and never applies to a rework round;
  4. the user having already answered functional_partition once — a rejection can never be swallowed by a later "history says skip it".

When you write the summary, artifacts[0] must be the real path you wrote. If the summary cannot be written after --max-attempts, the run is reported failed even if the design itself passed — because the next run would silently lose its basis for skipping anything.

⚠️ 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 — skipped only when the history doc says the partition is unchanged (see above)

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.

  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. The fixed-point word-length rule ranks alongside sim-golden. Only multiplies and adds of 16bit or less — after a 16×16 product, truncate back to 16bit before anything downstream; widths must not accumulate stage by stage. The one exception is loop accumulation: the accumulator is at least 32bit, and its result is truncated before it feeds the rest of the design. fixed_point#n fails on a violation even when the performance matches sim-golden, and native:*, modify_e:*, sync_sim:* may not widen anything back. The rule and its machine-readable form arrive in every affected task as context.wordlength_rule / context.wordlength_limits.
  3. One -E.slx per RTL submodule, saved separately (<project>_A-E.slx, …). Do not nativize all submodules in one Simulink model.
  4. 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. The one sanctioned way to add something HDL Coder cannot express is constraint_primitives — and it is not an exception to this rule. See below.
  5. 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.
  6. 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.
  7. 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.
  8. Skipping a step needs written evidence, and every run leaves some. The only grounds for not executing a stage are a history doc that records it as verified and unaffected. Every run ends by writing the next xxx<date>_tree.md; anything you could not verify goes in as not verified, so the next run redoes it.
  9. Synthesis attributes are injected, never typed in. See the next section.

🔧 步骤 4-3 · 约束原语 constraint_primitives

Between ip_replace:* and vivado, one global node injects the synthesis attributes HDL Coder cannot express — DONT_TOUCH, shreg_extract="no", KEEP (ILA instantiation later) — into the delivered RTL tree.

Why it exists. Synthesis merges bit-identical registers. A 32-bit pipeline (gk_tp_ra) was folded away with [Synth 8-4471] merging register … into gk_RUN_out1_reg[31:0]: the net name survives, the flip-flops are gone, and the timing report shows nothing at all — it was caught only by the area accounting. An earlier instance of the same merge cost 178 failing endpoints. HDL Coder has no way to express an anti-merge attribute, so without this stage the design silently ships without them, every single regeneration.

Why it is not "hand-editing generated RTL". The rule exists to stop the RTL and the model from silently diverging. This stage is driven by a declarative spec, is idempotent, and is re-runnable: regenerate the RTL, run it again, and the result is byte-for-byte the same. A replayable injector produces no divergence, so it is not a hand edit — and it is the only sanctioned route for these attributes. Hand-typing one into a generated file is still forbidden.

Where it sits, and why globally rather than per module. The spec is one file for the whole design and its attributes may target cross-module or top-level signals; the injector operates on the whole ip_replace-output tree. A global node also covers modules reused from history — those skip native/ip_replace but their RTL is still in the delivered tree and still needs the attributes.

Invariant: every path that changed the RTL tree re-injects before Vivado. main_timing_rework and main_hil_rework therefore return to constraint_primitives, not straight to vivado. A plain vivado retry does not re-inject (it changed nothing).

failure shape route
gate_passed: false retry constraint_primitives — an injection gate failure is normally a spec/script problem, not a model problem. Never goes to modify_e on its own
metadata.idempotent_verified: false fails the gate on its own, even with gate_passed: true — then retries
needs_code_change: true + change_scope: "simulink_module" + affected_modules modify_e:constraints → sync_sim:constraints → only those modules re-run native → ip_replace → re-inject. Use this only when the spec proves the signal's shape in the -E model is wrong (target absent, structurally merged, width/hierarchy mismatch)

Reuse. Skipped only when all three hold: a history doc exists, history_review returned redo_constraints: false (it must have hashed the current spec and compared it to the hash in the history doc), and no module regenerated RTL this round. The last condition is enforced by the graph — a subagent cannot override it, because a regenerated tree has lost last round's attributes. Default, as everywhere here, is redo.

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)
constraint_spec_path optional — the constraint-primitive spec file, relative to work_dir. A hint only; the subagent's reported metadata.spec_path is authoritative
history_dir optional — folder holding xxx<date>_tree.md; defaults to the folder of requirements_path
history_path optional — pin one history doc. Omit and start fills in the newest it finds; set "" to declare "no history, do everything".

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
history_review#n read xxx<date>_tree.md + the requirement doc, decide what may be reused; verdict in metadata (first task of every run that has a history doc) inline — read the files, don't guess
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, and hold the word-length rule (mult/add ≤16bit with truncation after the product; loop accumulators ≥32bit then truncated). Report metadata.{wordlength_compliant, wordlength_violations, wordlengths} — a violation fails the gate even when the performance matches sim-golden 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
constraint_primitives#n inject the spec's synthesis attributes into the delivered RTL tree; gate = every spec entry hit its target + normalised line diff clean + re-run byte-identical. Report metadata.{injected, unmatched, idempotent_verified, spec_path, spec_hash} inline (the injector script + spec; see Notes/PR23_constraint_primitives.md)
modify_e:module#n / modify_e:vivado#n / modify_e:hil#n / modify_e:constraints#n edit only the named -E.slx (cycle-true / timing / HIL / wrong-signal-shape 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
design_summary#n write <history_dir>/<project_name><YYYYMMDD-HH>_tree.md (path handed to you in context.target_path) covering every -x/-q/-E version, the -q word lengths (multiplier/adder widths, truncation points, loop-accumulator widths) and whether they satisfy the word-length rule — unrecorded widths force the next run to redo -q — the constraint-primitive spec path and its hash, which attributes were injected and how many signals each hit (an unrecorded hash forces the next run to re-inject), the partition, per-module status, Vivado/HIL verdicts, what was reused and what is still open; return the real path in artifacts (last task of every run, success or failure) 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
metadata.wordlength_compliant + wordlength_violations + wordlengths fixed_point — a violation (or a bare wordlength_compliant: false) fails the gate on its own; wordlengths is what the history doc records
metadata.injected + unmatched + idempotent_verified + spec_path + spec_hash constraint_primitives — idempotent_verified: false fails the gate on its own; spec_hash is what the history doc records, and without it the next run must re-inject
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:<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"]) — minus any module reused from history
next module stage one ip_replace:<M>#1 per module
after the module gate constraint_primitives#1 — a single global task, never per module

history_review#1 and design_summary#1 are always single-task batches, at the very start and the very end of the run.

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 (read-only)
"$PY" "$RUN" schema                   # both answer templates
"$PY" "$RUN" mermaid                  # graph as Mermaid (see gotcha)
"$PY" -m unittest discover -s tests   # project's own 59 tests — all pass

scripts/migrate_constraint_ledger.py <run_dir> <rounds> [--apply] back-fills constraint_primitives#N placeholders into a ledger written before that node existed — see the gotcha below.

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 <project_name>-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.
  • constraint_primitives failing is not a reason to touch the -E model. Record gate_passed: false and fix the spec or the injector; the graph retries. Only escalate to change_scope: "simulink_module" when the target signal genuinely isn't the shape the spec needs. Getting this backwards turns a spec typo into a model rework round.
  • Recording idempotent_verified: true without re-running the injector defeats the whole justification. Idempotence is what makes this stage not a hand edit. If you did not actually re-run and compare, record false.
  • spec_hash is the next run's only basis for skipping this stage. Omit it and the next run re-injects — which is the safe default, not a bug.
  • 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.
  • Adding a node rewinds every in-flight run. Replay is keyed by <task>#<n>, so a ledger written before constraint_primitives existed halts at constraint_primitives#1 — right after the first module round, potentially many rounds back from where the run actually was. The run is not damaged (nothing is deleted), it just asks one extra question per module round already completed. Fix: scripts/migrate_constraint_ledger.py <run_dir> <number-of-module-rounds> --apply, which backs the ledger up to ledger.json.pre_constraint_migration and seeds one honest placeholder per past round (migration_placeholder: true, empty injected, empty spec_hash — those rounds really did go to Vivado uninjected). The next round gets a real injection. Rehearse on a copy first: copy the run dir elsewhere and resume --work-dir <copy>; status alone cannot prove replay because it never invokes the graph.
  • Runs created before the PR01-2 change live in the old location (<SIM_RTL_FLOW_ROOT>\runs\<id>\) 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.
  • start freezes history_path into input.json. It must not be re-scanned on every resume: the run itself writes a newer xxx<date>_tree.md at the end, and a re-scan would make a later replay reference a different history doc than the one already recorded in the ledger. Pinning it also makes resume after WORKFLOW FINISHED a no-op instead of a spurious new handoff.
  • A reused module produces no native:/ip_replace: keys at all. If you expected three modules and only got one task, check the modules_reused_from_history event — that is the graph honouring your own history_review verdict, not a lost module.
  • design_summary is the deliverable, not a formality. Record it only after the file really exists on disk with real content; artifacts[0] must be its path.
  • The summary can fail an otherwise-good run. status: failed with 运行已结束但版本说明树文件 … 未能生成 means the design work passed but the history doc did not get written — fix that and re-run the summary, don't ignore it.

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
an old run suddenly asks for constraint_primitives#1 its ledger predates the node — run scripts/migrate_constraint_ledger.py (see gotcha)
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