agentleFS
Arcadia-1Arcadia-1/virtuoso-bridge-lite/skills/spectre/SKILL.md

spectre

Skill1k starsChanged 11 days agoMITView on GitHub

Run Cadence Spectre simulations remotely via virtuoso-bridge: upload netlists, execute, parse PSF results. TRIGGER when the user wants to run a SPICE/Spectre simulation from a netlist file, do transient/AC/PSS/pnoise analysis outside Virtuoso GUI, parse PSF waveform data, run multiple simulations in parallel across one or more servers, check simulation job status, or mentions Spectre APS/AXS modes. Also triggers for sim-jobs, sim-cancel, or parallel/concurrent simulation requests. Use this for standalone netlist-driven simulation — for GUI-based ADE Maestro simulation, use the virtuoso skill instead.

  • Reads credentials
  • Installs packages

Trending

More talk lately.thin dataabout the whole repository

Dec 2025: 0 mentionsJan 2026: 0 mentionsFeb 2026: 0 mentionsMar 2026: 0 mentionsApr 2026: 0 mentionsMay 2026: 0 mentionsJun 2026: 0 mentionsJul 2026: 0 mentionsAug 2026: 0 mentionsSep 2026: 1 mentionOct 2026: 3 mentionsNov 2026: 0 mentions
Dec 2025Jun 2026Nov 2026
Mentions a month of the Arcadia-1/virtuoso-bridge-lite repository that our weekly crawl found: only 4 in all, so this is a hint, not a trend. There is no adoption data for this file yet.

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 1 is distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Run a .scs netlist locally or on a remote machine through SSH, then parse PSF results into Python dicts.

    line 8
  2. Very specific:

    Check examples/02_spectre/ — use existing examples as a basis

    line 14
  3. Very specific:

    Treat result.ok as the execution contract.

    line 59
  4. Distinctive:

    Use the strict helpers when a downstream calculation must fail loudly instead of silently accepting the wrong file, signal spelling, or …

    line 66
  5. Very specific:

    If a Maestro config you inherit specifies +preset=lx or +preset=vx for a transient performance sim, that's almost always a bug.

    line 179
  6. Very specific:

    If you need 10× smaller PSF files: add a binary parser (e.g., wrap psf_utils — pure Python, pip install).

    line 269
  7. Very specific:

    Gotcha: When inheriting a tran line from Maestro, noiseruns=100 is common.

    line 294

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Spectre Skill

Run a .scs netlist locally or on a remote machine through SSH, then parse PSF results into Python dicts. Independent of VirtuosoClient — no GUI needed.

Before you start

  1. virtuoso-bridge is a Python CLI — install it in a virtual environment with uv pip install -e virtuoso-bridge-lite.
  2. virtuoso-bridge status — check connection, Spectre path, license
  3. Check examples/02_spectre/ — use existing examples as a basis
  4. spectre must be on PATH, or set VB_CADENCE_CSHRC (project or user .env) so the runner can source the Cadence environment, or VB_SPECTRE_BIN (a site wrapper with arguments works). This applies to local and SSH execution; see connection.md.

Core pattern

from virtuoso_bridge.spectre.runner import SpectreSimulator, spectre_mode_args

sim = SpectreSimulator.from_env(
    spectre_args=spectre_mode_args("ax"),  # APS extended (recommended)
    work_dir="./output",
)
result = sim.run_simulation("my_netlist.scs", {})

if result.ok:
    vout = result.data["VOUT"]
else:
    print(result.errors)

With Verilog-A includes:

result = sim.run_simulation(
    "tb_adc.scs",
    {"include_files": ["adc.va", "dac.va"], "spectre_args": ["+aps"]},
)

include_files and per-run spectre_args have the same meaning in local and SSH mode. They stage include files and extend the command; they do not inject arbitrary circuit parameters. For local-only execution, use SpectreSimulator.local(...), or configure VB_REMOTE_HOST=localhost and use from_env().

Result object

AttributeContent
result.okWhether simulation succeeded
result.dataParsed waveforms plus scalar OP values; STRUCT OP entries are flattened as "instance:parameter" (for example "M0:gm")
result.errorsError messages (short, classified); check these whenever result.ok is false
result.metadata["timings"]Execution and parse durations, plus transfer timing in SSH mode
result.metadata["output_dir"]Local path to .raw directory
result.metadata["remote_dir"], ["remote_raw_dir"]Remote run and .raw directories in SSH mode, only with keep_remote_files=True (otherwise they are deleted); e.g. for ViVA openResults on the remote host

Treat result.ok as the execution contract. A nonzero exit, explicit fatal Spectre output, netlist read-in error, or explicit convergence failure returns FAILURE/PARTIAL even if the raw directory contains incomplete files. Do not use a non-empty result.data as proof that the simulation succeeded.

Strict PSF accessors

Use the strict helpers when a downstream calculation must fail loudly instead of silently accepting the wrong file, signal spelling, or malformed data:

from pathlib import Path
from virtuoso_bridge.spectre.psf import (
    frequency_hz,
    read_psf_ascii,
    result_file,
    scalar,
    vector,
)

raw = Path(result.metadata["output_dir"])
ac = read_psf_ascii(result_file(raw, "ac.ac"))
freq = frequency_hz(ac)                  # exact "freq" key; finite, increasing
vout = vector(ac, r"VOUT")              # exact raw PSF key; finite, non-empty

dc = read_psf_ascii(result_file(raw, "dcOp.dc"))
gm = scalar(dc, r"M0:gm")                # exact raw PSF key; one finite real

result_file() requires exactly one matching file below the explicit raw PSF root. The value helpers never normalize names or guess aliases: pass the exact key emitted by the parser, including Spectre's \< / \> escapes. scalar() accepts one finite real number, vector() accepts a non-empty finite numeric vector, and frequency_hz() additionally requires real, strictly increasing samples.

Gotchas (Spectre 21.1 + IC618 lab cluster)

These are silent or near-silent foot-guns from real lab runs:

  • -param X=Y CLI flag is BROKEN. Spectre 21.1 parses the value as a second input netlist → SPECTRE-132: input file has been re-specified as 'X=Y'. Workaround: bake parameters into the netlist (regenerate the master per sweep point with txt.replace("parameters X=0", f"parameters X={val}")).
  • parameters X=Y re-declaration after include "header.scs" does not update DEPENDENT expressions. E.g., header has parameters N=64 t_end=((N+N_extra)/Fs), then later parameters N=256 — N updates but t_end stays at 276 ns (eagerly bound from the first declaration). Symptom: tran stops far too early. Fix: copy header locally and edit the parameters line in place.
  • Default timeout=600 s is too short for noised long-tran. With tranNoise=yes + N≥256 or 6+-way parallel contention, a single run can exceed 600 s wall while spectre is still progressing — bridge reports "Remote command timed out" but spectre.out actually shows clean completion. Fix: SpectreSimulator.from_env(timeout=3600, ...).
  • PSF parser keeps \<> escape chars in signal names. Saved signal DOUT\<0\> parses as dict key r"DOUT\<0\>", not "DOUT<0>". Symptom: KeyError: 'DOUT<0>' even though save list looks right.
  • strobeoutput=all in psfascii outputs only the continuous tran. Despite the docs implying "both continuous + strobed", Spectre 21.1's psfascii emitter writes just the continuous stream into tran.tran.tran. You'll get ~140k samples per signal instead of N strobed values. Fix: either Python-strobe yourself with np.searchsorted(t, k/Fs + offset), or use strobeoutput=strobeonly (which DOES work and shrinks the PSF ~1500×).

Parallel simulation

For a fixed batch, use run_parallel(). It creates a scoped executor for that call and releases it automatically, so concurrency settings never leak between batches:

results = sim.run_parallel([
    (Path("tb_comp.scs"), {}),
    (Path("tb_dac.scs"), {}),
], max_workers=4)

For incremental asynchronous submission, use an explicitly owned pool:

with sim.parallel_pool(max_workers=4) as pool:
    t1 = pool.submit(Path("tb_comp.scs"))
    t2 = pool.submit(Path("tb_dac.scs"))
    result = t1.result()
    results = pool.wait_all([t1, t2])

Each task gets a unique <netlist-stem>__<run-id>/ directory below work_dir, plus its own remote directory when applicable, so even repeated submissions of the same deck do not overwrite PSF data or auxiliary files. For full API and multi-server setup, read references/parallel.md.

Simulation modes

Precision ordering (measured on an 11-bit sub-radix-2 SAR ADC tran, N=128 coherent FFT, ax baseline ≈ 220 s):

argpresetspeedENOB Δ vs apsuse for
"spectre"(none)slowestreferenceleast license demand, basic direct
"aps"+preset=aps1.0× (gold)0.000sign-off accuracy reference
"cx"+preset=cx1.2×−0.03sign-off for designs with mixed-signal stiff loops (cmp metastability)
"ax"+preset=ax2.0×−0.03 (within noise)default for daily work
"mx"+preset=mx3.8×−0.29design exploration, corner sweeps where 0.3 ENOB is acceptable
"lx"+preset=lx5.9×−2.8 (unusable for SAR)small-signal AC / linear DC sweeps; not for circuits with cmp/regen
"vx"+preset=vx8.8×−8.5 (totally fails)verification-style connectivity / DC convergence only — never for transient signal fidelity
spectre_mode_args("ax")     # default for daily transient work
spectre_mode_args("aps")    # reference / sign-off
spectre_mode_args("mx")     # fast iteration if ENOB ≤ 0.3 loss is OK

Critical: SAR / latched-comparator circuits and any topology with metastable regeneration depend on tight reltol (1e-4 or better) to resolve LSB-scale differential inputs. lx relaxes reltol to ~1e-3 and drops ENOB by ~3 bits on such circuits; vx disables LTE bounding entirely and produces garbage. Reserve those two for non-signal-fidelity work (DC, connectivity, link-test).

If a Maestro config you inherit specifies +preset=lx or +preset=vx for a transient performance sim, that's almost always a bug.

When (and when not) to replace cells with Verilog-A for speedup

Verilog-A behavioral replacement of cells is a tempting acceleration lever, but the speedup is non-monotonic in cell size — replacing big cells helps, replacing small cells hurts. Measured on a 11-bit SAR ADC tran (ax mode, N=64, baseline 132s):

Cell replacedTransistor countWall-time changeResult
Output capture DFFs (1-pin behavior, 12 instances × 1 D-FF each)12 × ~10 MOS0% (neutral)✓ Easy, no gain — skip unless cleaning the netlist
Per-bit SAR latch with feedback (12 × ~12 MOS + 4 std cells)~200 MOS total−13% (slower)✗ transition() event-queue overhead × 11 concurrent instances exceeds the BSIM equation savings
StrongARM comparator (47 MOS)47 MOS, 1 instance+9-17%✓ Big cell, single instance — clear win

Rule of thumb: VA replacement helps when the cell is large (≥ 40 MOS) and instantiated once or twice. It hurts when the cell is small (< 20 MOS) and many instances share the same input event source — each @(cross()) adds to the spectre event queue; with N concurrent instances watching the same node, queue overhead grows ~N× while the BSIM savings stay linear in N.

Self-timed feedback loops are extra-fragile: replacing one element of an async chain (e.g., a SAR daisy-chain latch with feedback to CMPCK) requires matching not just the steady-state truth table but the propagation delay and edge timing to within a few ps. Standalone unit-test the VA before integrating into the chain; if the unit-test passes but the chain breaks, suspect transition() td interacting with multiple concurrent listeners.

The actually-effective SAR speed levers (measured, not from VA):

LeverMechanismTypical speedupENOB cost
Cut FFT N (e.g., 128 → 64)Tran stop time scales linearly~40%0 (within meas noise)
strobeoutput=strobeonly + lean saveCuts download + parse overhead; file size 1000× smaller~5-10% wall, 1500× disk0
Replace 1-2 big cells (cmp / opamp) with VASkip BSIM equations for ~50+ MOS~10-20%depends on VA fidelity
Drop LPE std-cell models for schematic-spiRemove per-cell wire parasitics~20%minor timing shift
Increase maxstepFewer solver iterations~20% per 2×depends on circuit, risky for cmp metastability
Spectre mode ax → mxLooser solver tolerance~50%−0.3 ENOB on SAR

The first four stack without ENOB cost. The last two trade accuracy for speed.

Output size control: save list, strobing, format

By default the .scs netlist's tran tran ... directive saves at every solver timestep for every signal — a clocked SAR-style transient at maxstep=5p over hundreds of ns produces 100+ MB of PSF ASCII per signal group. Three knobs:

1. saveOptions options save=<mode> + explicit save list

save CLKS RSTP I_SAR.VTOPP DOUT\<11\> ... DOUT\<0\>
saveOptions options save=selected
  • save=allpub — every public node + every terminal current (huge default).
  • save=selected — only the nodes/terminals in the explicit save line.
  • save=lvlpub — pub down to a given hierarchy level.

For production runs of large mixed-signal designs, always use save=selected with a curated 10-20 signal list. save=allpub is the most common cause of runaway PSF size on lab-cluster sims.

2. strobeoutput=<mode> (gotcha: "all" is bigger, not smaller)

The tran tran ... directive accepts strobeperiod and strobeoutput:

tran tran stop=t_end maxstep=5p \
    strobeperiod=1/Fs strobeoutput=strobeonly ...
ModeWhat gets savedUse for
strobeoutput=allEvery solver timestep PLUS strobed samples (biggest file)Debugging — need waveform shape between samples
strobeoutput=strobeonlyOnly strobed samples (1 sample per strobeperiod)ENOB / SNDR / corner sweeps where you only need per-cycle values

The name "all" misleads — it means "both continuous and strobed views," not "all signals." Switching to strobeonly typically cuts file size 500×-1500× on N=64..256 sims. For ENOB-only runs of a clocked ADC, strobeonly is the right default.

3. output_format — PSF ASCII vs binary

The bridge currently uses output_format="psfascii" by default, parsed via parse_spectre_psf_ascii. output_format="psfbin" is NOT supported by the in-tree parser (virtuoso_bridge/spectre/parsers.py has no parse_spectre_psf_bin). Passing it will produce a .raw directory the local side cannot read.

If you need 10× smaller PSF files: add a binary parser (e.g., wrap psf_utils — pure Python, pip install). Until then, the size lever is save=selected + strobeoutput=strobeonly, not the format.

Transient noise (tranNoise=yes)

tran tran is deterministic by default — no thermal / 1/f noise injected. Most BSIM models have noise params but they only fire during noise analysis or when tranNoise=yes is on the tran line:

tran tran stop=t_end maxstep=5p \
    tranNoise=yes noisefmax=50G noiseseed=1 noisetmin=1 binnum=16 noiseruns=1 \
    write="spectre.ic" writefinal="spectre.fc" annotate=status
ParamMeaningDefault-ish value
tranNoise=yesEnable the noise injection at alloff
noisefmax=<f>Max frequency for noise integration5×Fclock or 1× signal BW (smaller = faster)
noiseseed=<n>RNG seed for one run1
noisetmin=<t>Earliest time when noise becomes active0 (or 1×Ts to skip startup)
binnum=<n>Frequency-bin discretization (Wiener model)16
noiseruns=<n>Stochastic Monte Carlo runs — N seeds, ensemble output1 (Maestro defaults to 100, which is 100× compute)

Gotcha: When inheriting a tran line from Maestro, noiseruns=100 is common. That makes spectre repeat the full transient 100 times with different noise seeds for ensemble statistics — fine for jitter histograms / phase noise analyses, but lethal for ENOB measurement (which only needs one realization). Override to noiseruns=1 unless you genuinely want ensemble.

ENOB cost of enabling noise on a 11-bit SAR: roughly −0.3 to −0.5 bit (strongarm cmp noise is the dominant source). Compute cost of tranNoise=yes noiseruns=1 is ~1.5-2× a noiseless tran.

References

Load when needed — these contain detailed API docs:

  • references/netlist_syntax.md — Spectre netlist format, analysis statements, parameterization
  • references/parallel.md — Parallel simulation, multi-server, CLI job management, .env configuration

Examples

  • examples/02_spectre/01_inverter_tran.py — inverter transient
  • examples/02_spectre/01_veriloga_adc_dac.py — 4-bit ADC/DAC with Verilog-A
  • examples/02_spectre/02_cap_dc_ac.py — capacitor DC + AC
  • examples/02_spectre/04_strongarm_pss_pnoise.py — StrongArm PSS + Pnoise

Related skills

  • virtuoso — GUI-based Virtuoso workflow (schematic/layout, ADE Maestro). Use when working inside Virtuoso GUI.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.