Simulation
spice.simulate(netlist) runs after the parts are resolved and before
anything is placed. It is the dynamic version of the ERC's Ohm's-law
rules: instead of computing what a resistor should pass from the declared
rail, it switches the declared rails on from zero in ngspice, with every part
it has a model for in the deck, and reads what the nets settle to. The traces
go to the scope as sim.json beside the
version's board.json; the findings go on the board as gaps and notes, and
the command line prints them under sim.
It is report only. repair.passes() is the bar and does not read it. The
models are a behavioural regulator, a table of generic diodes and a
high-gain amplifier, not the parts, and a bar that moved on them would fail
boards for the model's sake. What it is for is the sentence beside the result:
+3V3 settles at 3.285 V (declared 3.3 V); D1 1.42 mA.
The engine
ngspice, which is what KiCad simulates with. Found in either of two forms:
ngspiceonPATH(brew install ngspice), run in batch mode with-b -r;- failing that, the shared library KiCad ships
(
KiCad.app/Contents/Frameworks/libngspice.0.dylibon macOS,libngspice.so.0on Linux), driven throughctypes—ngSpice_Init,ngSpice_Circ,ngSpice_Command("run"), thenwritefor the raw file.
Either way it runs in a child process under a ten-second timeout, because a
deck ngspice cannot solve can take the process down and a simulation that
fails must cost a note and never the build. soldermask setup prints which
was found; SOLDERMASK_NGSPICE points at a program or a library, and
SOLDERMASK_SIM=off (or --no-sim on the command line) skips the stage, which
the board then says.
No engine is a note — not simulated: ngspice not found — and no file. The scope's tab says there is no simulation; it never shows a flat line.
What is in the deck
Ground is node 0 (two ground nets are one node, and said). Every other net is a node; the stimulus is the design's own.
| Kind | In the deck as | From |
|---|---|---|
| resistor, capacitor, inductor | R, C, L | the value, or the first value in the catalogue description when the design left it blank |
| LED | a diode whose Vf at 20 mA is the colour's (electrical.LED_VF) | the anode/cathode the resolver found on the ordered part, or pin 1 as anode |
| diode | a generic silicon or Schottky diode | the description |
| transistor | generic NPN/PNP (β 150) or NMOS/PMOS (Vth 1.8 V) | pins named B/C/E or G/D/S; the polarity from the description |
| switch | a closed contact (0.1 Ω) when on two nets, and said | — |
| connector | nothing: its pins are nodes | — |
| crystal | nothing | — |
| IC or module, linear regulator | one behavioural source, out = min(in − dropout, Vout), 50 mΩ out | Vout from the part number (AMS1117-3.3, XC6206P332, HT7550, L7805, …), else the catalogue description, else the rail's declaration — and the assumption says which; dropout from the description (1.1V@(800mA)) or the family. An ADJ/FB pin makes it ADJ + 1.25 V with the divider in the deck |
| buck / boost | the same source, an ideal converter with no ripple | as above, dropout 0.3 V / none |
| op-amp / comparator | every unit with +, − and OUT as mid + half·tanh(10⁴·(V₊−V₋)/half), 100 Ω out | the supply pins named V+/VCC and V−/VEE/GND; a comparator is the same with a note that its open collector needs the pull-up the design gives it |
| anything else | nothing: pins open, the part named in unmodelled | — |
A part on fewer than two nets is not in the circuit and is not counted as unmodelled — the resolver already reported the dropped connections, and this stage does not say it twice.
Stimulus. Every power net with a declared voltage_v that no modelled part
drives gets a source ramping from 0 to that voltage in 100 µs. Every power net
with a declared current_a gets a load of V/I to ground. A rail a modelled
regulator drives gets no source: it is the rail the check is about. A power
net that declares no voltage gets nothing, and the assumption says so; a
design with no declared rail is refused rather than run, because a circuit
with nothing switched on settles at zero everywhere and would report that as a
result.
Duration. Five time constants of the slowest RC in view — each
capacitor's farads against the parallel resistance on its net — bounded to
1–10 ms, and the trace resampled onto 2,001 uniform points. .option rshunt=1e9
gives every open pin a path to ground so an unmodelled part's node is not a
singular matrix.
What it reports
- A rail a modelled part drives, against its declaration. Within 5 % is a note (+3V3 settles at 3.285 V (declared 3.3 V)); further is a gap. A 5 V part on a rail declared 3.3 V is said in the assumptions before the run.
- LED current. Past 30 mA is a gap; under 50 µA is a gap (conducts nothing — check its polarity) unless something with no model is on the LED's path — a GPIO of the MCU, through its series resistor — in which case it is not simulated, said as such, and not a gap. Under 1 mA is a note: dim for an indicator.
- The parts without a model, one gap line naming them: their pins are open, so the traces show the rails without whatever they do.
- What was assumed, in
sim.jsonand printed by the command line: where each regulator's numbers came from, which switches were closed, which power nets got no source. - The deck itself rides in
sim.jsonasdeck, so a person can run it through ngspice by hand.
What it does not do
No layout parasitics — there is no layout yet, and the copper's resistance,
the plane's inductance and the coupling between tracks are not in a deck built
from a netlist. Nothing an MCU, a radio or a sensor does: those are
unmodelled, their rails are still traced and still loaded with the current
the design declared for them. No temperature. No switching ripple on a
converter. No current limit on a regulator macro, and its input draws
nothing, so an input rail shows the load the design declared on it rather
than the load behind the regulator. No 555, no charger IC, no logic — a
macro library is a list that grows by the part, and every part not on it is
named.
Measured on the boards already built (11 Sep 2026)
tools/sim_survey.py runs the stage over every version under projects/,
read-only. Of 41 versions (31 distinct designs): 16 simulate; 25 are refused
with a reason — 24 because no net declares a voltage (designs from before
voltage_v existed in the schema) and one because it has no ground net. Of the
16: 11 have a rail a modelled regulator drives, checked against its
declaration, and all 11 settle within 5 %; all 16 have an LED current; 213 of
229 parts are in the deck, the 16 without a model being 11 ICs and 5 modules
(the ESP32-C3 and -S3 modules, the MCP73831 charger, the SHT40 sensor, the
MAX3485 transceiver). Median 0.12 s a board, 0.8 s at most.
Two false findings were caught by that survey before this shipped, and are what the not simulated and absent cases above exist for: an LED on an MCU's GPIO read as not lit — check its polarity on seven versions, and a tactile switch the resolver had already dropped for its pad names was counted again here as a part without a model on six.