Sim bringup
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.From its SKILL.md
npx -y skills add LNC0831/oh-my-fpga --skill sim-bringupAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 16 stars16 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
17.1 KB, as published. Nobody here has run it
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 viaget_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_filesandlist_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 exacttop_modulename. 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— deploysmcp_report.vhinto the project sim dir and tells you the`includedirective. (sim_compilealso 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_INITin the initial block, drive stimulus, check with`MCP_ASSERT_EQ(name, actual, expected), and call`MCP_FINISHthen$finish. - Testbench hygiene (you are responsible for these — they are the #1 cause of false fails and hangs):
- Always have a finite end — a
$finishreached by a counter/timeout, or usesim_run(run_time="<bounded>"). A free-running clock with no$finishwill hangrun 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_EQalready uses===. - Match the DUT port widths/names exactly — width mismatches elaborate as truncation, not errors.
- Always have a finite end — a
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$displayoutput. - Long / multi-minute:
sim_run_async(run_time="all"), then pollsim_get_sim_status()until it reportsCOMPLETED(it returnsRUNNINGwith a tail + elapsed time while in progress). This is the required pattern for long sims — never block onsim_runfor minutes. - If
sim_runreturns a timeout message, the sim may still be running: re-issue viasim_run_async+sim_get_sim_status, or read progress withsim_get_compile_log(stage="sim").
Read the result header:
=== Simulation Completed ===withtests=P/T, 0 failed→ candidate pass. Verify per Safety rails before claiming success.=== Simulation Error ===, anyFAIL,$fatal, orERROR: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, thensim_probe(...)to watch the offending signals across the relevant window, andsim_list_signals(scope=...)first if you are unsure of exact hierarchical paths (escaped generate-block names are common). - Hang/timeout → there is no
$finishor 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/$fatalcould 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_statusresult in this session showing=== Simulation Completed ===,0 failed, andtests > 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_statusfor multi-minute sims; do not block. If a kernel is stuck orSIM_LOCKEDappears,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."