From 6535dd83d4467e2bfbfb0ba7187a212558b44056 Mon Sep 17 00:00:00 2001 From: bowei Date: Wed, 19 Aug 2026 12:50:39 +0800 Subject: [PATCH] =?UTF-8?q?sync:=20SHANGHAI-U9=202026-08-19=20sim-rtl-flow?= =?UTF-8?q?=20=E5=A2=9E=E7=BA=A6=E6=9D=9F=E5=8E=9F=E8=AF=AD=E9=98=B6?= =?UTF-8?q?=E6=AE=B5=E4=B8=8E=E7=89=88=E6=9C=AC=E8=AF=B4=E6=98=8E=E6=A0=91?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=87=8D=E5=91=BD=E5=90=8D=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20ledger=20=E8=BF=81=E7=A7=BB=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sim-rtl-flow/SKILL.md | 114 +++++++++++++++--- .../scripts/migrate_constraint_ledger.py | 93 ++++++++++++++ sim-rtl-flow/scripts/run_sim_rtl_flow.py | 19 ++- 3 files changed, 205 insertions(+), 21 deletions(-) create mode 100644 sim-rtl-flow/scripts/migrate_constraint_ledger.py diff --git a/sim-rtl-flow/SKILL.md b/sim-rtl-flow/SKILL.md index bd9aead..91419d2 100644 --- a/sim-rtl-flow/SKILL.md +++ b/sim-rtl-flow/SKILL.md @@ -2,14 +2,15 @@ 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: read the previous run's design history - doc sim-rtl-flow-.md to decide what may be reused → Simulink -x principle sim + 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) → Vivado build + timing → hardware-in-the-loop via - fpga-imx6-flow or zynq-imx6-flow → write a fresh sim-rtl-flow-.md summarising + 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_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 @@ -26,8 +27,9 @@ description: > ## What this skill does -Drives the **sim-rtl-flow** graph (25 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`). +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 @@ -64,7 +66,7 @@ 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 history doc `sim-rtl-flow-.md` +## 📄 The design version-tree doc `xxx_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 @@ -73,7 +75,7 @@ that a previous run already validated. | | | |---|---| | Location | `//`, where `history_dir` defaults to the folder holding `requirements_path` (usually `Notes`) | -| Name | `sim-rtl-flow-.md` — date **and hour**, zero-padded, so name order == time order | +| Name | `_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-.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 | @@ -86,6 +88,7 @@ graph skip work: | `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 @@ -151,6 +154,8 @@ From `Notes/PR01.md` and `Notes/PR01-2.md`; the graph routes on these. 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 @@ -167,8 +172,53 @@ From `Notes/PR01.md` and `Notes/PR01-2.md`; the graph routes on these. 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 `sim-rtl-flow-.md`; anything you + unaffected. Every run ends by writing the next `xxx_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) @@ -200,7 +250,8 @@ EOF | `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) | -| `history_dir` | optional — folder holding `sim-rtl-flow-.md`; defaults to the folder of `requirements_path` | +| `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_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 @@ -244,7 +295,7 @@ keys with a default success — rehearsal only, and it refuses review keys. | key | what you actually do | which skill to use | |---|---|---| -| `history_review#n` | read `sim-rtl-flow-.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 | +| `history_review#n` | read `xxx_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 | @@ -254,13 +305,14 @@ keys with a default success — rehearsal only, and it refuses review keys. | `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` | +| `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:#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 `/sim-rtl-flow-.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 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 | +| `design_summary#n` | write `/_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`, … @@ -281,6 +333,7 @@ review: {"approved": true, "notes": "", "metadata": {}} | `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 | @@ -305,6 +358,7 @@ A failing `ip_replace:` (cycle-true mismatch) enters the same chain on its ow | after `-x` / `-q` | `interfaces#1` **and** `partition#1` | | module fanout | one `native:#1` per module (3 for `["A","B","C"]`) — **minus any module reused from history** | | next module stage | one `ip_replace:#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. @@ -334,12 +388,16 @@ decisions) to the input JSON; `start`/`resume` strip both. ## Other commands ```bash -"$PY" "$RUN" $W status # ledger + pending + final status +"$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 16 tests — all pass +"$PY" -m unittest discover -s tests # project's own 59 tests — all pass ``` +`scripts/migrate_constraint_ledger.py [--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). @@ -368,9 +426,30 @@ Global flags go **before** the subcommand: `--project`, `--work-dir`, `--thread- 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. +- **`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 + `#`, 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 --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 `; `status` alone cannot prove replay because it never + invokes the graph. - **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 @@ -380,7 +459,7 @@ Global flags go **before** the subcommand: `--project`, `--work-dir`, `--thread- 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* `sim-rtl-flow-.md` at the end, + every `resume`: the run itself writes a *newer* `xxx_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. @@ -390,7 +469,7 @@ Global flags go **before** the subcommand: `--project`, `--work-dir`, `--thread- - **`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 + `运行已结束但版本说明树文件 … 未能生成` 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 @@ -408,5 +487,6 @@ Global flags go **before** the subcommand: `--project`, `--work-dir`, `--thread- | `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` | diff --git a/sim-rtl-flow/scripts/migrate_constraint_ledger.py b/sim-rtl-flow/scripts/migrate_constraint_ledger.py new file mode 100644 index 0000000..7facdfc --- /dev/null +++ b/sim-rtl-flow/scripts/migrate_constraint_ledger.py @@ -0,0 +1,93 @@ +"""Seed constraint_primitives#N placeholders into an existing sim-rtl-flow ledger. + +The `constraint_primitives` node was added to the graph after this run started. +Replay is keyed by `#`, so a run whose ledger predates the node rewinds +to the first unrecorded `constraint_primitives#1` instead of resuming where it +left off. Seeding one placeholder per module round that already happened lets +the replay walk straight through to the real pending key. + +The placeholders record the truth: those rounds went to Vivado WITHOUT any +constraint injection, because the step did not exist yet. The first round that +runs after the migration gets a real injection. + +Usage: + python migrate_constraint_ledger.py [--apply] +Without --apply it only prints what it would do. +""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + +PLACEHOLDER = { + "success": True, + "gate_passed": True, + "artifacts": [], + "notes": ( + "迁移占位:这一轮模块处理发生在『约束原语』步骤并入 sim-rtl-flow 之前," + "当时并未做任何综合属性注入,该轮 Vivado 跑的是未注入的 RTL。" + "本条只为让 replay 走过历史轮次;之后的每一轮都会真正执行注入。" + ), + "timing_met": True, + "hil_passed": True, + "needs_code_change": False, + "change_scope": "none", + "affected_modules": [], + "metadata": { + "migration_placeholder": True, + "injected": {}, + "unmatched": [], + "spec_path": "", + "spec_hash": "", + }, +} + + +def main(argv: list[str]) -> int: + if len(argv) < 3: + print(__doc__) + return 2 + run_dir = Path(argv[1]) + count = int(argv[2]) + apply = "--apply" in argv + + ledger_path = run_dir / "ledger.json" + ledger = json.loads(ledger_path.read_text(encoding="utf-8")) + + added = [] + for n in range(1, count + 1): + key = f"constraint_primitives#{n}" + if key in ledger: + print(f" skip {key} (already recorded)") + continue + ledger[key] = dict(PLACEHOLDER) + added.append(key) + + print(f"ledger: {ledger_path}") + print(f"would add: {added}") + if not apply: + print("(dry run — pass --apply to write)") + return 0 + + backup = ledger_path.with_suffix(".json.pre_constraint_migration") + if not backup.exists(): + backup.write_text( + json.dumps( + json.loads(ledger_path.read_text(encoding="utf-8")), + ensure_ascii=False, + indent=2, + ), + encoding="utf-8", + ) + print(f"backup -> {backup}") + ledger_path.write_text( + json.dumps(ledger, ensure_ascii=False, indent=2), encoding="utf-8" + ) + print(f"applied {len(added)} placeholder(s)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) diff --git a/sim-rtl-flow/scripts/run_sim_rtl_flow.py b/sim-rtl-flow/scripts/run_sim_rtl_flow.py index 4d88e24..48080db 100644 --- a/sim-rtl-flow/scripts/run_sim_rtl_flow.py +++ b/sim-rtl-flow/scripts/run_sim_rtl_flow.py @@ -402,7 +402,7 @@ def _print_final(state: Any, replayed: list[str]) -> None: if review: mark = "批准" if review.get("approved") else "驳回" if (review.get("metadata") or {}).get("skipped_by_history"): - mark = "按历史设计说明文件跳过" + mark = "按版本说明树文件跳过" print(f"{label} : {mark} {review.get('notes', '')}".rstrip()) plan = state.get("history_plan") or {} print(f"历史文件 · 参考 : {plan.get('history_path') or '(none)'}") @@ -421,6 +421,17 @@ def _print_final(state: Any, replayed: list[str]) -> None: f" - {item['module']}: stage={item['stage']} " f"success={item['success']} artifacts={item.get('artifacts')}" ) + constraints = state.get("constraint_result") or {} + if constraints: + print( + "约束原语 : " + f"source={constraints.get('source')} " + f"passed={constraints.get('passed')} " + f"spec={constraints.get('spec_path') or '(未回报)'} " + f"hash={constraints.get('spec_hash') or '(未回报 — 下轮必须重注)'} " + f"injected={_jsonable(constraints.get('injected'))} " + f"idempotent={constraints.get('idempotent_verified')}" + ) print(f"vivado : {_jsonable(state.get('vivado_result'))}") print(f"hil : {_jsonable(state.get('hil_result'))}") print(f"\nevents ({len(state.get('events', []))}):") @@ -473,8 +484,8 @@ def cmd_start(args, api) -> int: payload.pop("outcomes", None) payload.pop("reviews", None) work_dir = _resolve_work_dir(args, payload) - # 把"参考哪一份历史设计说明文件"冻结进 input.json:本次运行结束时会写出一份 - # 新的 sim-rtl-flow-.md,若每次 resume 都重新扫描目录,replay 就会在 + # 把"参考哪一份版本说明树文件"冻结进 input.json:本次运行结束时会写出一份 + # 新的 xxx_tree.md,若每次 resume 都重新扫描目录,replay 就会在 # 半路换掉参考文件,走出一条与已记录 ledger 不同的路。 if "history_path" not in payload: history_dir = api["history_dir_for"]( @@ -482,7 +493,7 @@ def cmd_start(args, api) -> int: ) payload["history_dir"] = history_dir payload["history_path"] = api["discover_history_doc"]( - str(work_dir), history_dir + str(work_dir), history_dir, payload.get("project_name", "") ) run = RunDir(work_dir, args.thread_id) run.set_input(payload)