Simulation Bring-Up (Behavioral / RTL)
A methodology playbook for getting a behavioral simulation running and passing on the SynthPilot xsim engine, and for diagnosing why it fails when it doesn't. The discipline is the same as timing closure: observe with a tool, classify the symptom, apply the smallest safe change, re-run, and never claim a pass without fresh sim_run output that proves it.
This skill drives the standalone xsim pipeline (sim_compile → sim_run → probe/diagnose), which runs xvlog/xvhdl → xelab → xsim directly and bypasses Vivado's launch_simulation. It does not require synthesis.
When to use
- User says "simulate this", "run the testbench", "set up simulation", "verify this module in sim".
- A testbench exists but fails to compile, fails to elaborate, fails its assertions, prints X, or hangs.
- You need a behavioral testbench authored from scratch for a DUT and then run to a verdict.
- You want to inspect DUT internals over time (waveform-style) to understand a functional bug.
- You want functional code coverage of an RTL block (statement/branch/condition/toggle).
When NOT to use
- Timing signoff / timing closure / CDC / lint. Behavioral sim has no notion of routed delays. Use the timing-closure / cdc-analysis / linting skills. A passing behavioral sim is necessary, not sufficient for signoff.
- Gate-level or post-implementation (SDF) simulation. This engine elaborates RTL behavioral models; it does not run timing-annotated netlists.
- Hardware bring-up / on-chip debug (ILA/VIO/JTAG). Use the hw-debug skill —
hw_ila_*, hw_vio_*, program_device.
- Pure RTL bug hunting with no testbench and no intent to run a sim — if the user only wants static checks, prefer
check_syntax / run_quick_lint (cheaper than a full compile).
Prerequisites (verify first)
Run these before touching files. Do not assume state.
- MCP / Vivado link is alive —
test_connection. If it fails, STOP and tell the user to open Vivado and ensure the Tcl server (tcl_server.tcl) is running in the Tcl Console. Nothing below works without it.
- A project is open —
get_project_info. The xsim engine writes its scratch to <project>/mcp_sim/ and resolves files via get_files -compile_order sources -used_in simulation; there must be a current project. If none, open_project / create_project (ask the user which).
- Know the design —
list_source_files and list_simulation_files. Confirm the DUT RTL is in the project and the intended testbench (if any) is in the sim_1 fileset, not sources_1. A TB in the wrong fileset will silently be excluded from the sim compile order.
- Know the simulation top — the testbench module name.
sim_compile(top_module=...) takes it explicitly, so confirm the exact module name (case-sensitive) from the file, not a guess.
If any prerequisite is missing, resolve it (or ask) before proceeding. Do not fabricate a green result on a half-configured project.
Methodology
One change-class per iteration so cause→effect stays attributable. The pipeline is strictly ordered: author/locate TB → compile → elaborate → run → diagnose. Each later stage assumes the earlier stage produced fresh, clean output.
0. Decide: author a testbench, or run an existing one?
- Existing TB: confirm it is in sim_1 (
list_simulation_files); read it if needed (read_file) to get the exact top_module name. Skip to step 2.
- No TB / author one: continue to step 1.
1. Author a testbench (only if one does not exist)
Prefer structured, machine-checkable output over $display soup — it gives a clean pass/fail verdict the parser can read.
sim_setup_report — deploys mcp_report.vh into the project sim dir and tells you the `include directive. (sim_compile also auto-deploys it, but calling this first documents the macros: `MCP_INIT, `MCP_ASSERT, `MCP_ASSERT_EQ, `MCP_SIGNAL, `MCP_LOG, `MCP_FINISH.)
- Write the TB with
create_testbench_file(file_name="tb_<dut>.v", content=...) — this writes the file and adds it to the sim_1 fileset in one step. Inside the module: `include "mcp_report.vh", call `MCP_INIT in the initial block, drive stimulus, check with `MCP_ASSERT_EQ(name, actual, expected), and call `MCP_FINISH then $finish.
- Testbench hygiene (you are responsible for these — they are the #1 cause of false fails and hangs):
- Always have a finite end — a
$finish reached by a counter/timeout, or use sim_run(run_time="<bounded>"). A free-running clock with no $finish will hang run all.
- Reset before checking. Assert reset for ≥1 clock, deassert, then sample. Checking during reset yields X and false FAILs.
- Use
===/!== (4-state) for X-aware comparisons; `MCP_ASSERT_EQ already uses ===.
- Match the DUT port widths/names exactly — width mismatches elaborate as truncation, not errors.
If the user asks you to change the DUT RTL to make a test pass, treat that as a design change: recommend the edit and ask before applying it (see Safety rails). Fixing the testbench is in-scope; silently rewriting the DUT to chase a green is not.
2. Compile + elaborate
sim_compile(top_module="tb_<name>").
This compiles all sim-used files (VHDL via xvhdl, Verilog/SV via xvlog), auto-discovers glbl.v and include dirs, defines MCP_SIM_REPORT, then elaborates with xelab into snapshot mcp_snapshot. Read the status token on the first line:
| Token |
Meaning |
Next action |
COMPILE_OK: ... |
xvlog/xvhdl + xelab succeeded |
Go to step 3 (run). Note warnings=N — investigate if non-trivial. |
COMPILE_ERROR: N errors in xvlog/xvhdl |
Syntax / file / language error before elaboration |
sim_get_compile_log(stage="compile"), classify (see table), fix, re-sim_compile. |
ELABORATE_ERROR: N errors in xelab |
Compiled, but linking the hierarchy failed (missing module, unresolved instance, library, param) |
sim_get_compile_log(stage="elaborate"), classify, fix, re-sim_compile. |
SIM_LOCKED: ... |
A stale xsimk.exe kernel is locking the snapshot; xelab can't rebuild it |
sim_stop() (kills the kernel + clears the lock), then re-sim_compile. Do not edit RTL — this is not a code error. |
Fix one error class at a time, then re-compile. Do not batch unrelated fixes — a single syntax error often cascades into dozens of phantom downstream errors that vanish once the first is fixed.
3. Run
Choose by expected runtime:
- Short (< ~30 s wall):
sim_run(run_time="all", detail_level=1). Returns the parsed test verdict (detail_level: 0=summary, 1=+failures, 2=+signals at failure times, 3=full JSONL) plus captured $display output.
- Long / multi-minute:
sim_run_async(run_time="all"), then poll sim_get_sim_status() until it reports COMPLETED (it returns RUNNING with a tail + elapsed time while in progress). This is the required pattern for long sims — never block on sim_run for minutes.
- If
sim_run returns a timeout message, the sim may still be running: re-issue via sim_run_async + sim_get_sim_status, or read progress with sim_get_compile_log(stage="sim").
Read the result header:
=== Simulation Completed === with tests=P/T, 0 failed → candidate pass. Verify per Safety rails before claiming success.
=== Simulation Error ===, any FAIL, $fatal, or ERROR: lines → go to step 4.
4. Diagnose (only when a stage failed or the verdict is FAIL)
Classify with the table below before changing anything. Pull the evidence that matches the failing stage:
- Compile/elab errors →
sim_get_compile_log(stage="compile"|"elaborate").
- Run-time failures / X / wrong values →
sim_get_report(detail_level=2) for failure times + signals, then sim_probe(...) to watch the offending signals across the relevant window, and sim_list_signals(scope=...) first if you are unsure of exact hierarchical paths (escaped generate-block names are common).
- Hang/timeout → there is no
$finish or the stimulus never satisfies the exit condition; inspect the TB and bound the run.
Classification table (symptom → cause → smallest safe fix)
Apply the first fix that addresses the actual cause. Testbench and config fixes come before any DUT RTL change.
| Stage / Symptom |
Likely cause |
Smallest safe fix (in order) |
COMPILE_ERROR: syntax error, unexpected token |
TB or RTL syntax; SV used without --sv |
Read the cited file:line (read_file_lines). Fix the source. If SV constructs in a .v file: sim_compile(extra_args="--sv"). Optionally pre-check with check_syntax_file. |
COMPILE_ERROR: cannot find include file |
`include "mcp_report.vh" placed before the module, or header not on a discovered include path |
Move the include inside the module. Ensure headers are registered as Verilog Header file type so sim_compile adds their dir via -i. |
COMPILE_ERROR: missing `define/macro |
A required Verilog define isn't set for sim |
add_verilog_define / set_verilog_defines, or pass sim_compile(extra_args="-d NAME=VALUE"). State the assumption you're making about the define's value. |
ELABORATE_ERROR: module <X> not found / instance ... has no ... |
DUT or a sub-module isn't in the sim compile order; wrong module name |
list_simulation_files + list_source_files. If RTL missing from sim, add_source_file/add_simulation_file; check compile order with check_compile_order. Verify exact module name (case-sensitive). |
ELABORATE_ERROR: unresolved Xilinx primitive / unisim / IP black box |
IP behavioral model or library not pulled in; IP outputs not generated |
Confirm IP outputs exist (generate_ip_outputs / report_ip_status). Pass extra libs via sim_compile(extra_elab_args="-L <lib>"). (glbl.v and the standard unisim/xpm libs are added automatically.) |
ELABORATE_ERROR: port-width / param mismatch |
TB instantiates DUT with wrong width/param |
Fix the TB instantiation to match the DUT. Optionally confirm with check_port_width_mismatch. |
SIM_LOCKED |
Stale xsimk.exe from a prior async run holds the snapshot lock |
sim_stop() then re-sim_compile. Never an RTL fix. |
Run: FAIL with expected≠actual |
Real functional mismatch — DUT bug or wrong expected value in TB |
First confirm the TB's expected value and timing are correct (sample after reset, on the right edge). sim_probe the DUT inputs/outputs around the fail time. If TB is correct and DUT is wrong, recommend the RTL fix — do not silently patch the DUT. |
Run: outputs are X/Z |
Uninitialized reg, missing reset, multiple drivers, unconnected port, or sampling during reset |
sim_probe from t=0 to find where X originates. If TB sampled too early → fix TB (delay/reset). If RTL has an uninitialized/unreset reg or a real X source → check_latches/check_async_reset and recommend the RTL fix. |
Run: hang / timeout, no $finish |
Free-running clock with no terminating condition; stimulus never reaches exit |
Bound it: sim_run(run_time="<finite>"). Then fix the TB to reach $finish deterministically (counter/timeout). If async already launched and stuck: sim_stop(). |
Run: $fatal / assertion from RTL or TB |
An RTL assert/$fatal fired, or a TB self-check `MCP_ASSERT failed |
sim_get_report(detail_level=2) for the time + message, sim_probe the relevant signals at that time, classify as TB-expectation vs real DUT bug, then fix the responsible side. |
| Pass but suspiciously fast / 0 tests |
TB exits before exercising anything, or `MCP_INIT/`MCP_FINISH missing so no JSONL was produced |
Check tests=P/T. If T==0, the TB ran no assertions — fix the TB to actually drive + check. A "pass" with zero assertions is not a pass. |
Diagnose → fix → re-run loop (with STOP conditions)
1. RUN the current stage (sim_compile, then sim_run / sim_run_async+status).
2. READ the status token / verdict (fresh tool output, every iteration).
3. If clean and verdict = pass with tests > 0 -> go to Output (success path).
4. Else CLASSIFY via the table using stage-appropriate evidence
(sim_get_compile_log / sim_get_report / sim_probe / sim_list_signals).
5. Apply ONE smallest-safe fix (config/TB before DUT RTL).
6. Re-run the SAME stage. Record before/after (status token, error count,
tests P/T) so the change's effect is attributable.
7. Repeat.
STOP and hand back to the human when:
- The remaining fix requires a DUT RTL or architectural change — present the exact change, the evidence (probe/report), and the trade-off; let the user approve. Do not silently edit the DUT.
- A
FAIL/X/$fatal could reflect a real DUT bug and the correct expected behavior is ambiguous — ask the user for the intended behavior rather than tuning the TB to mask it.
- Two consecutive iterations produce no net improvement (same error count, same failing test) — stop thrashing; report what you tried and what's left.
- You'd need to delete/weaken the testbench's checks (remove an assertion, loosen a comparison, shrink the run so a failing region never executes) to get green. That is a fake-pass — refuse and surface it.
- A prerequisite turns out to be missing mid-flow (project closed, connection dropped) — re-run prerequisites, don't paper over it.
Safety rails (do not violate)
- Evidence before claims. Never say "simulation passes" / "the DUT works" without a fresh
sim_run/sim_get_sim_status result in this session showing === Simulation Completed ===, 0 failed, and tests > 0. A successful compile (COMPILE_OK) is not a passing simulation — it only proves it built.
- No fake-pass. Do not get to green by deleting/weakening assertions, loosening
=== to ==, shortening the run so a failing window is skipped, ignoring $fatal/X, or declaring a 0-assertion run a "pass." If a check is genuinely wrong, fix it transparently and say why.
- Smallest safe change first. Order of preference: testbench/stimulus fix → compile/elab args & defines → file/fileset/compile-order fix → (only with user approval) DUT RTL change. Never jump to editing the DUT to chase a verdict.
- Recommend RTL edits, never silently apply them. TB edits are in-scope. DUT/architectural edits get presented with evidence and a trade-off, then wait for the user.
- One change-class per iteration. So you can attribute the effect. Re-measure after each change; show before/after.
- Behavioral ≠ signoff. A passing behavioral sim says nothing about timing, CDC, DRC, or routability. Never imply otherwise; point to the appropriate signoff skill.
- Long runs go async. Use
sim_run_async + sim_get_sim_status for multi-minute sims; do not block. If a kernel is stuck or SIM_LOCKED appears, sim_stop() to recover — and note it kills all xsim kernels on the machine.
- Real tools only. Use only the SynthPilot tool names above. If a needed capability (e.g., SDF/gate-level sim, UVM) isn't available, say so plainly rather than inventing a tool.
Output
Report back in this shape:
SIMULATION BRING-UP — <tb_top> on <project>
Pipeline status:
Compile/Elaborate : COMPILE_OK (files=<n>, vhdl=<n>, warnings=<n>) | or COMPILE_ERROR/ELABORATE_ERROR/SIM_LOCKED
Run : Completed | Error | Hang(bounded)
Verdict : <P>/<T> tests passed, <F> failed (tests=0 => NOT a pass)
Evidence (fresh, this session):
- sim_run / sim_get_sim_status: <header line + tests line>
- <sim_get_report / sim_probe / compile log excerpt for any failure>
Failures diagnosed (if any):
- <symptom> -> <classified cause> -> <fix applied> (before: <metric>, after: <metric>)
Changes I made:
- <testbench / fileset / compile-arg edits — list them>
Changes I recommend (NOT applied — need your call):
- <DUT RTL / architectural edits, each with the probe/report evidence and trade-off>
Open questions / STOP reason (if handing back):
- <ambiguous expected behavior, exhausted safe options, etc.>
State explicitly whether the simulation passes with a non-trivial set of assertions (the only thing that counts as success here) or whether it merely compiled. Never round a compile-clean-but-unrun design up to "working."
1---2name: sim-bringup3description: Bring up a behavioral (RTL) simulation end-to-end: stand up or fix a testbench, compile (xvlog/xvhdl), elaborate (xelab), run (xsim), and methodically diagnose compile errors, elaboration failures, $fatal/assertion failures, X-propagation, and hangs. Use when the user says "simulate this", "run the testbench", "set up simulation", "my sim fails / hangs / shows X", "why does my testbench not pass", "add a testbench and verify the DUT", or "debug this RTL in simulation". Requires the SynthPilot MCP server (AI -> server.py -> TCP:9999 -> tcl_server.tcl running inside an open Vivado session). This is BEHAVIORAL sim only (no gate-level/timing sim); it does NOT prove timing closure or signoff. Measure (compile log / sim log / probe) before changing anything, classify the failure, apply the smallest safe fix, then re-run.4---56# Simulation Bring-Up (Behavioral / RTL)78A methodology playbook for getting a behavioral simulation **running and passing** on the SynthPilot xsim engine, and for diagnosing *why* it fails when it doesn't. The discipline is the same as timing closure: **observe with a tool, classify the symptom, apply the smallest safe change, re-run, and never claim a pass without fresh `sim_run` output that proves it.**910This skill drives the standalone xsim pipeline (`sim_compile` → `sim_run` → probe/diagnose), which runs `xvlog`/`xvhdl` → `xelab` → `xsim` directly and bypasses Vivado's `launch_simulation`. It does **not** require synthesis.1112## When to use1314- User says "simulate this", "run the testbench", "set up simulation", "verify this module in sim".15- A testbench exists but **fails to compile, fails to elaborate, fails its assertions, prints X, or hangs**.16- You need a behavioral testbench authored from scratch for a DUT and then run to a verdict.17- You want to inspect DUT internals over time (waveform-style) to understand a functional bug.18- You want functional code coverage of an RTL block (statement/branch/condition/toggle).1920## When NOT to use2122- **Timing signoff / timing closure / CDC / lint.** Behavioral sim has no notion of routed delays. Use the timing-closure / cdc-analysis / linting skills. A passing behavioral sim is **necessary, not sufficient** for signoff.23- **Gate-level or post-implementation (SDF) simulation.** This engine elaborates RTL behavioral models; it does not run timing-annotated netlists.24- **Hardware bring-up / on-chip debug** (ILA/VIO/JTAG). Use the hw-debug skill — `hw_ila_*`, `hw_vio_*`, `program_device`.25- **Pure RTL bug hunting with no testbench and no intent to run a sim** — if the user only wants static checks, prefer `check_syntax` / `run_quick_lint` (cheaper than a full compile).2627## Prerequisites (verify first)2829Run these *before* touching files. Do not assume state.30311. **MCP / Vivado link is alive** — `test_connection`. If it fails, STOP and tell the user to open Vivado and ensure the Tcl server (`tcl_server.tcl`) is running in the Tcl Console. Nothing below works without it.322. **A project is open** — `get_project_info`. The xsim engine writes its scratch to `<project>/mcp_sim/` and resolves files via `get_files -compile_order sources -used_in simulation`; there must be a current project. If none, `open_project` / `create_project` (ask the user which).333. **Know the design** — `list_source_files` and `list_simulation_files`. Confirm the DUT RTL is in the project and the intended testbench (if any) is in the **sim_1** fileset, not sources_1. A TB in the wrong fileset will silently be excluded from the sim compile order.344. **Know the simulation top** — the testbench module name. `sim_compile(top_module=...)` takes it explicitly, so confirm the exact module name (case-sensitive) from the file, not a guess.3536If any prerequisite is missing, resolve it (or ask) before proceeding. Do not fabricate a green result on a half-configured project.3738## Methodology3940One **change-class per iteration** so cause→effect stays attributable. The pipeline is strictly ordered: **author/locate TB → compile → elaborate → run → diagnose**. Each later stage assumes the earlier stage produced fresh, clean output.4142### 0. Decide: author a testbench, or run an existing one?4344- **Existing TB**: confirm it is in sim_1 (`list_simulation_files`); read it if needed (`read_file`) to get the exact `top_module` name. Skip to step 2.45- **No TB / author one**: continue to step 1.4647### 1. Author a testbench (only if one does not exist)4849Prefer structured, machine-checkable output over `$display` soup — it gives a clean pass/fail verdict the parser can read.50511. `sim_setup_report` — deploys `mcp_report.vh` into the project sim dir and tells you the `` `include `` directive. (`sim_compile` also auto-deploys it, but calling this first documents the macros: `` `MCP_INIT ``, `` `MCP_ASSERT ``, `` `MCP_ASSERT_EQ ``, `` `MCP_SIGNAL ``, `` `MCP_LOG ``, `` `MCP_FINISH ``.)522. Write the TB with `create_testbench_file(file_name="tb_<dut>.v", content=...)` — this writes the file **and** adds it to the sim_1 fileset in one step. Inside the module: `` `include "mcp_report.vh" ``, call `` `MCP_INIT `` in the initial block, drive stimulus, check with `` `MCP_ASSERT_EQ(name, actual, expected) ``, and call `` `MCP_FINISH `` then `$finish`.533. Testbench hygiene (you are responsible for these — they are the #1 cause of false fails and hangs):54 - **Always have a finite end** — a `$finish` reached by a counter/timeout, or use `sim_run(run_time="<bounded>")`. A free-running clock with no `$finish` will hang `run all`.55 - **Reset before checking.** Assert reset for ≥1 clock, deassert, *then* sample. Checking during reset yields X and false FAILs.56 - Use `===`/`!==` (4-state) for X-aware comparisons; `` `MCP_ASSERT_EQ `` already uses `===`.57 - Match the DUT port widths/names exactly — width mismatches elaborate as truncation, not errors.5859If the user asks you to *change the DUT RTL* to make a test pass, treat that as a design change: **recommend** the edit and ask before applying it (see Safety rails). Fixing the *testbench* is in-scope; silently rewriting the DUT to chase a green is not.6061### 2. Compile + elaborate6263`sim_compile(top_module="tb_<name>")`.6465This compiles all sim-used files (VHDL via `xvhdl`, Verilog/SV via `xvlog`), auto-discovers `glbl.v` and include dirs, defines `MCP_SIM_REPORT`, then elaborates with `xelab` into snapshot `mcp_snapshot`. Read the **status token** on the first line:6667| Token | Meaning | Next action |68|---|---|---|69| `COMPILE_OK: ...` | xvlog/xvhdl + xelab succeeded | Go to step 3 (run). Note `warnings=N` — investigate if non-trivial. |70| `COMPILE_ERROR: N errors in xvlog/xvhdl` | Syntax / file / language error before elaboration | `sim_get_compile_log(stage="compile")`, classify (see table), fix, re-`sim_compile`. |71| `ELABORATE_ERROR: N errors in xelab` | Compiled, but linking the hierarchy failed (missing module, unresolved instance, library, param) | `sim_get_compile_log(stage="elaborate")`, classify, fix, re-`sim_compile`. |72| `SIM_LOCKED: ...` | A stale `xsimk.exe` kernel is locking the snapshot; xelab can't rebuild it | `sim_stop()` (kills the kernel + clears the lock), then re-`sim_compile`. Do **not** edit RTL — this is not a code error. |7374Fix **one error class at a time**, then re-compile. Do not batch unrelated fixes — a single syntax error often cascades into dozens of phantom downstream errors that vanish once the first is fixed.7576### 3. Run7778Choose by expected runtime:7980- **Short (< ~30 s wall):** `sim_run(run_time="all", detail_level=1)`. Returns the parsed test verdict (`detail_level`: 0=summary, 1=+failures, 2=+signals at failure times, 3=full JSONL) plus captured `$display` output.81- **Long / multi-minute:** `sim_run_async(run_time="all")`, then poll `sim_get_sim_status()` until it reports `COMPLETED` (it returns `RUNNING` with a tail + elapsed time while in progress). This is the required pattern for long sims — never block on `sim_run` for minutes.82- If `sim_run` returns a **timeout** message, the sim may still be running: re-issue via `sim_run_async` + `sim_get_sim_status`, or read progress with `sim_get_compile_log(stage="sim")`.8384Read the result header:8586- `=== Simulation Completed ===` with `tests=P/T, 0 failed` → candidate pass. Verify per Safety rails before claiming success.87- `=== Simulation Error ===`, any `FAIL`, `$fatal`, or `ERROR:` lines → go to step 4.8889### 4. Diagnose (only when a stage failed or the verdict is FAIL)9091Classify with the table below **before** changing anything. Pull the evidence that matches the failing stage:9293- Compile/elab errors → `sim_get_compile_log(stage="compile"|"elaborate")`.94- Run-time failures / X / wrong values → `sim_get_report(detail_level=2)` for failure times + signals, then `sim_probe(...)` to watch the offending signals across the relevant window, and `sim_list_signals(scope=...)` first if you are unsure of exact hierarchical paths (escaped generate-block names are common).95- Hang/timeout → there is no `$finish` or the stimulus never satisfies the exit condition; inspect the TB and bound the run.9697## Classification table (symptom → cause → smallest safe fix)9899Apply the **first** fix that addresses the actual cause. Testbench and config fixes come before any DUT RTL change.100101| Stage / Symptom | Likely cause | Smallest safe fix (in order) |102|---|---|---|103| `COMPILE_ERROR`: `syntax error`, `unexpected token` | TB or RTL syntax; SV used without `--sv` | Read the cited file:line (`read_file_lines`). Fix the source. If SV constructs in a `.v` file: `sim_compile(extra_args="--sv")`. Optionally pre-check with `check_syntax_file`. |104| `COMPILE_ERROR`: `cannot find include file` | `` `include "mcp_report.vh" `` placed before the module, or header not on a discovered include path | Move the include **inside** the module. Ensure headers are registered as `Verilog Header` file type so `sim_compile` adds their dir via `-i`. |105| `COMPILE_ERROR`: missing `` `define ``/macro | A required Verilog define isn't set for sim | `add_verilog_define` / `set_verilog_defines`, or pass `sim_compile(extra_args="-d NAME=VALUE")`. State the assumption you're making about the define's value. |106| `ELABORATE_ERROR`: `module <X> not found` / `instance ... has no ...` | DUT or a sub-module isn't in the sim compile order; wrong module name | `list_simulation_files` + `list_source_files`. If RTL missing from sim, `add_source_file`/`add_simulation_file`; check compile order with `check_compile_order`. Verify exact module name (case-sensitive). |107| `ELABORATE_ERROR`: unresolved Xilinx primitive / `unisim` / IP black box | IP behavioral model or library not pulled in; IP outputs not generated | Confirm IP outputs exist (`generate_ip_outputs` / `report_ip_status`). Pass extra libs via `sim_compile(extra_elab_args="-L <lib>")`. (`glbl.v` and the standard unisim/xpm libs are added automatically.) |108| `ELABORATE_ERROR`: port-width / param mismatch | TB instantiates DUT with wrong width/param | Fix the TB instantiation to match the DUT. Optionally confirm with `check_port_width_mismatch`. |109| `SIM_LOCKED` | Stale `xsimk.exe` from a prior async run holds the snapshot lock | `sim_stop()` then re-`sim_compile`. Never an RTL fix. |110| Run: `FAIL` with expected≠actual | Real functional mismatch — DUT bug **or** wrong expected value in TB | First confirm the TB's expected value and timing are correct (sample after reset, on the right edge). `sim_probe` the DUT inputs/outputs around the fail time. If TB is correct and DUT is wrong, **recommend** the RTL fix — do not silently patch the DUT. |111| Run: outputs are `X`/`Z` | Uninitialized reg, missing reset, multiple drivers, unconnected port, or sampling during reset | `sim_probe` from t=0 to find where X originates. If TB sampled too early → fix TB (delay/reset). If RTL has an uninitialized/unreset reg or a real X source → `check_latches`/`check_async_reset` and **recommend** the RTL fix. |112| Run: hang / timeout, no `$finish` | Free-running clock with no terminating condition; stimulus never reaches exit | Bound it: `sim_run(run_time="<finite>")`. Then fix the TB to reach `$finish` deterministically (counter/timeout). If async already launched and stuck: `sim_stop()`. |113| Run: `$fatal` / assertion from RTL or TB | An RTL `assert`/`$fatal` fired, or a TB self-check `` `MCP_ASSERT `` failed | `sim_get_report(detail_level=2)` for the time + message, `sim_probe` the relevant signals at that time, classify as TB-expectation vs real DUT bug, then fix the responsible side. |114| Pass but suspiciously fast / 0 tests | TB exits before exercising anything, or `` `MCP_INIT ``/`` `MCP_FINISH `` missing so no JSONL was produced | Check `tests=P/T`. If `T==0`, the TB ran no assertions — fix the TB to actually drive + check. A "pass" with zero assertions is **not** a pass. |115116## Diagnose → fix → re-run loop (with STOP conditions)117118```1191. RUN the current stage (sim_compile, then sim_run / sim_run_async+status).1202. READ the status token / verdict (fresh tool output, every iteration).1213. If clean and verdict = pass with tests > 0 -> go to Output (success path).1224. Else CLASSIFY via the table using stage-appropriate evidence123 (sim_get_compile_log / sim_get_report / sim_probe / sim_list_signals).1245. Apply ONE smallest-safe fix (config/TB before DUT RTL).1256. Re-run the SAME stage. Record before/after (status token, error count,126 tests P/T) so the change's effect is attributable.1277. Repeat.128```129130**STOP and hand back to the human when:**131132- The remaining fix requires a **DUT RTL or architectural change** — present the exact change, the evidence (probe/report), and the trade-off; let the user approve. Do not silently edit the DUT.133- A `FAIL`/`X`/`$fatal` could reflect a **real DUT bug** and the correct expected behavior is ambiguous — ask the user for the intended behavior rather than tuning the TB to mask it.134- Two consecutive iterations produce **no net improvement** (same error count, same failing test) — stop thrashing; report what you tried and what's left.135- You'd need to **delete/weaken the testbench's checks** (remove an assertion, loosen a comparison, shrink the run so a failing region never executes) to get green. That is a fake-pass — refuse and surface it.136- A prerequisite turns out to be missing mid-flow (project closed, connection dropped) — re-run prerequisites, don't paper over it.137138## Safety rails (do not violate)1391401. **Evidence before claims.** Never say "simulation passes" / "the DUT works" without a **fresh `sim_run`/`sim_get_sim_status` result** in this session showing `=== Simulation Completed ===`, `0 failed`, **and** `tests > 0`. A successful *compile* (`COMPILE_OK`) is **not** a passing simulation — it only proves it built.1412. **No fake-pass.** Do not get to green by deleting/weakening assertions, loosening `===` to `==`, shortening the run so a failing window is skipped, ignoring `$fatal`/`X`, or declaring a 0-assertion run a "pass." If a check is genuinely wrong, fix it transparently and say why.1423. **Smallest safe change first.** Order of preference: testbench/stimulus fix → compile/elab args & defines → file/fileset/compile-order fix → (only with user approval) DUT RTL change. Never jump to editing the DUT to chase a verdict.1434. **Recommend RTL edits, never silently apply them.** TB edits are in-scope. DUT/architectural edits get presented with evidence and a trade-off, then wait for the user.1445. **One change-class per iteration.** So you can attribute the effect. Re-measure after each change; show before/after.1456. **Behavioral ≠ signoff.** A passing behavioral sim says nothing about timing, CDC, DRC, or routability. Never imply otherwise; point to the appropriate signoff skill.1467. **Long runs go async.** Use `sim_run_async` + `sim_get_sim_status` for multi-minute sims; do not block. If a kernel is stuck or `SIM_LOCKED` appears, `sim_stop()` to recover — and note it kills *all* xsim kernels on the machine.1478. **Real tools only.** Use only the SynthPilot tool names above. If a needed capability (e.g., SDF/gate-level sim, UVM) isn't available, say so plainly rather than inventing a tool.148149## Output150151Report back in this shape:152153```154SIMULATION BRING-UP — <tb_top> on <project>155156Pipeline status:157 Compile/Elaborate : COMPILE_OK (files=<n>, vhdl=<n>, warnings=<n>) | or COMPILE_ERROR/ELABORATE_ERROR/SIM_LOCKED158 Run : Completed | Error | Hang(bounded)159 Verdict : <P>/<T> tests passed, <F> failed (tests=0 => NOT a pass)160161Evidence (fresh, this session):162 - sim_run / sim_get_sim_status: <header line + tests line>163 - <sim_get_report / sim_probe / compile log excerpt for any failure>164165Failures diagnosed (if any):166 - <symptom> -> <classified cause> -> <fix applied> (before: <metric>, after: <metric>)167168Changes I made:169 - <testbench / fileset / compile-arg edits — list them>170171Changes I recommend (NOT applied — need your call):172 - <DUT RTL / architectural edits, each with the probe/report evidence and trade-off>173174Open questions / STOP reason (if handing back):175 - <ambiguous expected behavior, exhausted safe options, etc.>176```177178State explicitly whether the simulation **passes with a non-trivial set of assertions** (the only thing that counts as success here) or whether it merely compiled. Never round a compile-clean-but-unrun design up to "working."