Documentation · RustEDA · Benchmarks · Apache-2.0
RustSpice simulates transistor-level SPICE netlists (.op, .dc, .tran) and computes discrete adjoint parameter sensitivities with Structure-of-Arrays (SoA) device evaluation, auto-differentiated model kernels and topology-specialized sparse linear solvers.
Part of RustEDA, infrastructure for chip-design recursive self-improvement. Native tools shorten the work between inference steps; verified results guide the next iteration.
Ibex RTL-to-GDS on IHP SG13G2 with EZ130 cells and two SRAM macros, 2026-11-05: RustEDA and LibreLane 3.0.17 (OpenROAD, Yosys, KLayout, Magic, Netgen) ran one after the other on the same 4-vCPU host from the same prepared inputs, and the same signoff scripts checked both layouts.
- 4.3–6.0× faster end to end than the faster ngspice-45 solver on RC decks from the native SPEF of the final Ibex layout (10 decks, 198 to 6,865 unknowns), with 1.4–2.0× less peak memory.
- All 10 cases pass accuracy, and RustSpice is closer to the tightly converged reference than default-tolerance ngspice in every case on the nominal output grid (9 of 10 at each simulator's own output points).
- The RustEDA flow around it finishes 4.2× sooner end to end than LibreLane.
LibreLane's layout fails the shared timing gate (112 maximum-capacitance and fanout violations at TT and SS) and its equivalence check does not complete, so the receipt keeps its own ratio fields null under its publication rule; the multiples here divide the recorded times. Stages are paired by phase, not as identical tasks. RustEDA is not faster everywhere: its repair before global routing takes 1.18× LibreLane's two pre-route resizer steps alone, OpenRCX runs 1.36× longer on RustEDA's DEF, and the latch-up check 1.11× longer on its layout. All stages, gates and losses
The RustEDA Ibex host campaign of 2026-11-05 (IHP SG13G2 / EZ130 with two real
SRAMs, on a 4-vCPU Intel Xeon cloud host with 15.7 GiB RAM and no swap) ran
rustspice bench-suite with RustSpice 0.1.0 (commit
d4d6a50) against ngspice-45, with the deck builder and method of
2026-11-02. The decks come from the native nominal SPEF of the new final layout,
whose largest nets are smaller: the eight largest single nets have 198–291
unknowns (5–10 sinks), and the two multi-net decks have 10 nets with 2,262
unknowns and 40 nets with 6,865, so they are not the decks of 2026-11-02.
All 10 cases pass accuracy. Read at the nominal k × tstep output grid,
RustSpice is closer to the tightly converged ngspice reference than
default-tolerance ngspice in every case; at each simulator's own output points
(without the harness's linear resampling of ngspice's waveform) it is closer in
9 of 10 cases, ngspice on ibex_net_01 (1.03 × 10⁻⁵ against 1.33 × 10⁻⁵), and
the multi-net decks are near level (4.50 × 10⁻⁴ against 4.83 × 10⁻⁴).
RustSpice was 4.32–5.96× faster end to end than the faster ngspice solver,
and used less memory in every case.
| 8 single nets | 10 nets, 2,262 unknowns | 40 nets, 6,865 unknowns | |
|---|---|---|---|
| Accuracy verdict | pass × 8 |
pass |
pass |
| Error / swing, RustSpice | 4.67 × 10⁻⁶ – 1.33 × 10⁻⁵ | 4.50 × 10⁻⁴ | 4.50 × 10⁻⁴ |
| Error / swing, default-tolerance ngspice, nominal grid | 2.09–6.26 × 10⁻⁵ | 1.44 × 10⁻³ | 1.44 × 10⁻³ |
| Error / swing, default-tolerance ngspice, its own output points | 6.68 × 10⁻⁶ – 1.61 × 10⁻⁵ | 4.83 × 10⁻⁴ | 4.83 × 10⁻⁴ |
| Median end to end, faster ngspice solver | 287.99–419.83 ms | 434.57 ms (SPARSE) | 1,688.21 ms (KLU) |
| Median end to end, RustSpice | 57.17–90.02 ms | 86.22 ms | 283.11 ms |
| End-to-end speedup vs the faster ngspice solver | 4.32–5.45× | 5.04× | 5.96× |
| Solve-only speedup vs the faster ngspice solver | 5.07–6.36× | 6.39× | 7.72× |
| Median peak RSS, ngspice SPARSE / RustSpice | 13,872–14,324 / 6,828–7,208 KiB | 20,488 / 12,380 KiB | 34,968 / 24,552 KiB |
Times are medians of seven interleaved repetitions after a correctness warm-up,
from one session on one host (load average 0.6 at the start and 1.0 at the
end); the suite flags some RustSpice medians as noisy (spread above 20 %).
The faster ngspice solver was SPARSE except for the 40-net deck and one single
net (KLU). The scope is unchanged: linear RC interconnect only, no
transistor-level Ibex simulation (RustSpice does not implement IHP's PSP103
models), and RustSpice is not part of the timed RTL-to-GDS flow.
SPICE head-to-head · Host campaign 2026-11-05
The RustEDA Ibex host campaign (IHP SG13G2 / EZ130 with two real SRAMs, on a
4-vCPU Intel Xeon cloud host with 15.7 GiB RAM and no swap) ran
rustspice bench-suite with RustSpice 0.1.0 (commit
cc2426f)
against ngspice-45. The transient decks are linear RC networks from the native
nominal SPEF of the final RustEDA Ibex layout: the eight largest single nets
(548–649 unknowns, 3–10 sinks) and two multi-net decks (10 nets with 5,843
unknowns, 40 nets with 20,023). Each deck drives real nets with a Thevenin ramp,
keeps the extracted resistors and positive capacitors with coupling capacitance
grounded, and adds a fixed load capacitance at each sink pin. Accuracy is
checked at the probed nodes only: the driver and first sink of each single net
(2 probes), and of the first 4 nets in each multi-net deck (8 probes).
All 10 cases pass accuracy: against a tightly converged ngspice
reference, RustSpice's worst error / swing is 7.90 × 10⁻⁴, 39 % of the
tolerance of 2 × 10⁻³ set in each deck. RustSpice was 3.68–5.78× faster end
to end than the faster ngspice solver, which was SPARSE in every case.
| 8 single nets | 10 nets, 5,843 unknowns | 40 nets, 20,023 unknowns | |
|---|---|---|---|
| Accuracy verdict | pass × 8 |
pass |
pass |
| Error / swing, RustSpice | 0.90–2.12 × 10⁻⁴ | 7.90 × 10⁻⁴ | 7.68 × 10⁻⁴ |
| Median end to end, ngspice SPARSE | 534.33–618.43 ms | 1,107.13 ms | 4,072.28 ms |
| Median end to end, RustSpice | 136.28–161.45 ms | 196.78 ms | 704.24 ms |
| End-to-end speedup vs the faster ngspice solver | 3.68–4.18× | 5.63× | 5.78× |
| Solve-only speedup vs the faster ngspice solver | 4.60–5.14× | 7.74× | 7.82× |
| Median peak RSS, ngspice SPARSE / RustSpice | 15,404–15,708 / 33,728–38,416 KiB | 30,724 / 46,232 KiB | 71,608 / 144,152 KiB |
Times are medians of seven interleaved repetitions after a correctness warm-up,
from one session on one shared host. Solve-only pairs ngspice's Total analysis time with RustSpice's analysis_us, which excludes RustSpice's parse and
compile; end to end includes both. RustSpice used more memory than ngspice in
every case. The scope is linear RC interconnect only: RustSpice does not
implement the PSP103 models of the IHP SG13G2 transistors, so no
transistor-level Ibex simulation was run, and RustSpice is not part of the
timed RTL-to-GDS flow. This host's ngspice is ngspice-45; the differential
suite and the benchmark method below cite ngspice-47.
The measured source is the 2026-11-01 update, which includes adaptive transient integration and breakpoint handling, strict SPICE parsing with expressions/includes and explicit resource limits, extended diode/level-1 MOS and controlled-source coverage, and adjoint parameter sensitivities. Batched FP64 DC has a deterministic CPU reference and an optional CUDA kernel with explicit fallback for unsupported/nonconverged cases.
Current source and qualification · SPICE head-to-head · Host campaign method and deviations · All benchmarks
RustEDA publishes its benchmarks on one page: Ibex RTL-to-GDS on IHP SG13G2 / EZ130 against LibreLane, with module head-to-heads. See the RustEDA benchmarks. RustSpice's published performance comparisons are the head-to-heads with ngspice-45 from the 2026-11-05 and 2026-11-02 Ibex host campaigns, on linear RC networks extracted from the Ibex layouts (summarized above); they cover no transistor-level circuit and apply to those hosts only. The benchmark method describes how to measure RustSpice against ngspice on your own host.
rustspice bench-suite runs 951 cases against ngspice-47 as a black-box executable: 857 pass, 16 documented RustSpice gaps, 77 informational, 1 unverified and 0 failing, plus 400/400 random fuzz decks passing and 200/200 hostile mutants handled without a panic. See benchmarks/README.md.
Batched DC operating points on an NVIDIA GPU (rustspice bench-batch, method): one circuit at many corners, one corner per CUDA thread in FP64, with a Newton / LU kernel written in Rust and compiled to PTX by cuda-oxide; the same source, built for the CPU with rayon, is the reference it is tested against. The kernel covers plain diodes and level-1 MOSFETs with their bulk junctions. A corner the kernel does not converge is re-solved by the CPU solver. In the last recorded run, the GPU agreed with the CPU build to 5.6e-16 (bit for bit on the MOSFET circuits) and with the CPU solver to 1.8e-9 (relative, inside its Newton tolerance), and no corner needed a CPU re-solve. Creating the CUDA context and JIT-compiling the PTX is a one-time cost per process, so small batches can run faster on the CPU. MOS levels above 1, diodes with breakdown, series resistance or a knee current, polynomial sources, .nodeset and linear circuits still run on the CPU.
- Three-tier adaptive sparse linear solver (
rustspice-solver::linear): combines symmetric minimum-degree ordering, diagonal-preferring threshold Markowitz pivoting (0.1) and factor-growth monitoring on the unrolled symbolic / Cranelift JIT tier with cached symbolic-oncefaersparse LU (FaerLuCache), dense partial-pivoting LU for small ill-conditioned topologies, and Jacobi-preconditioned conjugate gradient (PCG). - Conflict-free static gather table (
CscGatherTable): pre-stamps linear conductances once during compilation and gathers only active dynamic/nonlinear entries via contiguous offsets with zero atomic contention or binary search. - Adaptive LTE transient integration, quantized LU caching & homotopy (
rustspice-solver): controls step size via divided-difference local truncation error (LTE), lands exactly on waveform breakpoints (PULSE,SIN,EXP,PWL) with L-stableBE -> BE -> BDF-2 -> Trapezoidalrestarts sharing companion scaling (w = phi) across a quantizedhmax / 2^kFactorCache, supports.ic,uic,tstart,tmax,.nodeset, and extended.dcsweeps (source, resistor, temperature, 2-variable nested), and pairspnjlim/fetlimstep-delta checks with adaptiveGminand source-stepping homotopy. - Strict SPICE reader, expression/include engine & resource budgets (
rustspice-core): resolves.paramexpressions,.funcdefinitions,.subckt ... PARAMS:overrides,.include/.libsections, and.temp/.optionstemperature scaling (m=,tc1=,tc2=), refuses unsupported directives or model levels via structuredDiagnosticerrors/warnings, and enforces configurableResourceLimits(--max-elements,--max-nodes,--max-subckt-depth,--max-sweep-points,--max-tran-steps,--max-newton-iters). - Device and element coverage (
rustspice-core::ext): diodes with series resistance, breakdown (BV/IBV), high injection (IKF), recombination current (ISR/NR), transit time and graded depletion capacitance; level-1 MOSFETs with process-derived KP/VTO/GAMMA/PHI (TOX, UO, NSUB, NSS, TPG), LD, RD/RS/RSH, bulk junctions and Meyer gate capacitances; E/F/H controlled sources,POLY(1)E/G sources, coupled inductors (K) and semiconductor.model R/.model C. BJT, JFET, MESFET, B, S, W, T, O, U and MOS LEVEL > 1 are refused with an error. - Discrete adjoint sensitivity (
AdjointSensitivityEngine): computes exact gradients with respect toPparameters in a single transposed solve (J^T λ = ∂L/∂V) rather than2Pperturbed simulations.
Benchmark method · Architecture guide.
Install Rust with rustup and Git. The CPU flow needs no GPU or external solver libraries. Run these commands in one POSIX shell:
git clone https://github.com/lab-emi/RustSpice.git
cd RustSpice
cargo build --release --locked -p rustspice-cli
# 1. Run DC operating point and transient simulation on a 5-stage CMOS inverter chain
./target/release/rustspice run examples/cmos_ring_oscillator.sp --backend cpu
# 2. Compute discrete adjoint parameter sensitivities dV(out)/d(theta)
./target/release/rustspice adjoint examples/cmos_ring_oscillator.sp --target-node out
# 3. Emit auto-differentiated, CSE-optimized device kernel code (msl, wgsl, cuda-tile, cuda-ptx, rust)
./target/release/rustspice codegen --model mosfet-ekv --target msl
# 4. Run the head-to-head accuracy and speed benchmark suite against local ngspice
./target/release/rustspice bench-ngspice --output target/bench-ngspice.md| Command / Interface | Purpose |
|---|---|
rustspice run <netlist> --backend <backend> |
Execute .op, .dc, and .tran analyses on cpu, metal, cuda-tile, cuda-simt, or wasm |
rustspice adjoint <netlist> --target-node <node> |
Compute exact gradients ∂V(node)/∂θ across all circuit parameters in one transposed LU solve |
rustspice codegen --model <m> --target <t> |
Lower symbolic SSA device equations into optimized Rust, MSL (df64), WGSL (df64), CUDA Tile, or CUDA PTX kernels |
rustspice bench-ngspice --output <path> |
Run black-box accuracy and wall-time benchmarks against an external ngspice executable |
rustspice bench-batch --output <path> |
Time batched DC operating points on the CPU batch API, the batch kernel's CPU build and an NVIDIA GPU |
rustspice run <netlist> --internal-points |
Also write the accepted internal time steps between the k * tstep rows of a transient rawfile |
rustspice_py.PySimulator (DLPack) |
Zero-copy DLManagedTensor gradient and waveform export into PyTorch and Apple MLX (examples/ai4eda_opamp_sizing.py) |
- Single physics equation frontend (
#[spice_device]&rustspice-ir): author forward device equationsI(V, θ)andQ(V, θ)once; forward-mode and reverse-mode automatic differentiation synthesize analytical Jacobians (∂I/∂V,∂Q/∂V) and parameter pullbacks (∂I/∂θ), followed by constant folding, algebraic simplification, common subexpression elimination (CSE), and dead code elimination (DCE). - 64-byte aligned SoA & zero-allocation hot loops (
rustspice-core): device instances are packed into cache-alignedDeviceSoaBufferarrays with pre-allocated state vectors and staticCscGatherTablemappings. Zero heap allocations occur during DC sweeps, Newton-Raphson iterations, or transient stepping. - Multi-regime linear and nonlinear solvers (
rustspice-solver): selects automatically between Cranelift JIT unrolled sparse LU, static-pivot symbolic-once LU with factorization caching,faersupernodal LU, and preconditioned conjugate gradient (PCG), guarded bypnjlim/fetlimdamping andGmin/source stepping homotopy. - Heterogeneous execution backends:
- CPU (
rustspice-backend-cpu):pulpruntime SIMD (AVX-512/AVX2/NEON),rayonparallel PVT batching, andCraneliftnative machine-code JIT. - Apple Silicon Metal (
rustspice-backend-metal): unified memory (MTLResourceStorageModeShared) zero-copy buffers withfma-accelerateddf64(float2) shaders andf64iterative refinement. - NVIDIA CUDA dual-track (
rustspice-backend-cuda-tile&rustspice-backend-cuda-simt): tile-based SoA batch evaluation, PTX SIMT gather-stamping + PCG, and batched FP64 DC operating points (one corner per CUDA thread) whose kernel is written in Rust, compiled to PTX by cuda-oxide and differentially tested against its own rayoncpu_refbuild. The CUDA driver is loaded at run time, so the build needs no CUDA toolkit. - WebAssembly & WebGPU (
rustspice-backend-wasm): hybrid WASMsimd128and WGSLdf64compute pipeline.
- CPU (
The documentation site integrates into the unified RustEDA documentation. All source pages also live in this repository.
| Start here | Go deeper |
|---|---|
| Documentation home | Architecture and crate boundaries |
| Benchmark method vs. ngspice-47 | AI4EDA differentiable sizing example |
| CMOS ring oscillator deck | RustEDA documentation |
RustSpice is clean-room Rust. Reference simulators (ngspice) interoperate strictly as black-box external executables over standard SPICE netlists in differential accuracy and performance harnesses.
CARGO_BUILD_JOBS=2 RUST_TEST_THREADS=2 cargo test --workspace --lockedLicensed under Apache-2.0.