Skip to main content

Python framework for analog circuit design automation with SPICE netlist generation and simulation

Project description

circuitsilk

circuitsilk (import silk) is a Python framework for analog circuit design automation. It lets you describe circuits programmatically, generate SPICE netlists, run ngspice simulations, sweep design spaces, and track versions in a local database. The PDK layer is pluggable — SkyWater 130nm is the first supported PDK, with more to follow.

from silk import Module, load_pdk
from silk.simulation import Transient

pdk = load_pdk('sky130')          # reads $PDK_ROOT env var
ckt = Module('PDK_skywater130', 'out', 'diffpair.cir', pdkDetails=pdk)

mn1 = pdk.nmos1p8('mn1', d='voutn', g='vinp', s='tail', b='gnd', w=4.0, l=0.15)
mp1 = pdk.pmos1p8('mp1', d='voutp', g='voutp', s='vdd',  b='vdd',  w=4.0, l=0.15)
ckt.add(mn1, mp1)
ckt.addSimType(Transient(step=1e-9, stop=100e-9))
ckt.simulate(corner='tt')
vout = ckt.loadSignal('v(voutp)')

Installation

pip install circuitsilk          # PyPI (when published)
# — or from source —
git clone https://github.com/...
cd silk
pip install -e ".[dev]"

System requirements:

  • Python 3.9+
  • ngspice (for simulation) — sudo apt install ngspice on Debian/Ubuntu
  • SkyWater 130nm PDK models at $PDK_ROOT (for load_pdk)

The PDK_ROOT environment variable should point to a directory that contains libraries/sky130_fd_pr/latest/models/sky130.lib.spice.

Quick start

See docs/quickstart.md for a step-by-step walkthrough.

Working examples live in silk/examples/:

File What it shows
silk/examples/main.py Differential pair with factory API
silk/examples/factory_api_example.py Full factory API: devices, passives, SPICE inspection
silk/examples/parameter_sweep_example.py Design-space sweep with Space/Variable
silk/examples/ldo_design.py Parameterized LDO with Module + PyNetlist DB tracking
silk/examples/source_examples.py Voltage/current source waveforms

Core concepts

Module — the circuit object

Module is the central design object. It holds devices, sources, passives, and simulation commands and generates SPICE netlists.

from silk import Module
ckt = Module('PDK_skywater130', 'out_dir', 'my_circuit.cir', pdkDetails=pdk)
ckt.add(device1, device2)          # add pre-constructed device objects
ckt.addSource(vsource)             # add a voltage or current source
ckt.addPassive(capacitor)          # add R, C, or L
ckt.addSimType(Transient(...))     # set the simulation type
ckt.simulate(corner='tt')          # generate netlist + run ngspice + load results
sig = ckt.loadSignal('v(out)')     # retrieve a waveform

Factory API — device instantiation

Devices are instantiated directly from the PDK object. Pins and parameters are keyword arguments — IDE autocomplete works on them.

mn = pdk.nmos1p8('mn', d='out', g='in', s='gnd', b='gnd', w=1.0, l=0.35, m=4)
mp = pdk.pmos1p8('mp', d='out', g='in', s='vdd', b='vdd', w=2.0, l=0.35)

# Parameters can be updated by direct attribute assignment after construction:
mn.w = 2.0
mn.d = 'new_out'
print(mn.to_spice())               # inspect the SPICE line

PDK loading

# Option 1: load_pdk (uses $PDK_ROOT env var or ~/pdk)
from silk import load_pdk
pdk = load_pdk('sky130')

# Option 2: explicit paths
import os
from silk.pdk import PDK_skywater130
pdk = PDK_skywater130(
    os.path.join(os.path.dirname(__file__), 'silk', 'pdk', 'data',
                 'skywater_130_device_details.yaml'),
    libraryLocation='/path/to/sky130.lib.spice',
)

Simulation types

from silk.simulation import Transient, AC, DC, Noise, Corner

ckt.addSimType(Transient(step=1e-9, stop=100e-9))
ckt.addSimType(AC('dec', 100, 1e3, 1e9))
ckt.addSimType(DC(source='vin', start=0, stop=1.8, step=0.01))

# Corner is a str-subclass enum — plain strings also work
ckt.simulate(corner=Corner.TT)
ckt.simulate(corner='ff')          # equivalent

Sources

from silk import sources

vs = sources.VoltageSource('vdd', positive='vdd', negative='gnd', dc=1.8)
cs = sources.CurrentSource('ibias', positive='vdd', negative='vnbias', dc=100e-6)

# Waveforms
vs_pulse = sources.VoltageSource('vin', positive='in', negative='gnd',
                                  pulse=sources.Pulse(v1=0, v2=1.8, td=0,
                                                      tr=100e-12, tf=100e-12,
                                                      pw=5e-9, per=10e-9))
vs_sine  = sources.VoltageSource('vin', positive='in', negative='gnd',
                                  sine=sources.Sine(voffset=0, vampl=1, freq=1e6))

Passives

from silk.elements import passives

r = passives.Resistor('r1',   pos='out', neg='gnd', resistance=10e3)
c = passives.Capacitor('c1',  pos='out', neg='gnd', capacitance=100e-12)
l = passives.Inductor('l1',   pos='a',   neg='b',   inductance=1e-9)
ckt.addPassive(r)
ckt.addPassive(c)
ckt.addPassive(l)

Ideal elements (ElementLibrary)

from silk.elements import elements

r  = elements.resistor('r1',  p='out', n='gnd', value=10e3)
c  = elements.capacitor('c1', p='out', n='gnd', value=100e-15)
vs = elements.vsource('vdd',  p='vdd', n='gnd', dc=1.8)
ckt.add(r, c, vs)

Design-space exploration

from silk import ModuleVariable, VarType
from silk.optimize import Space, Variable, Optimizer

# Create variables with bounds
w_n = pdk.nmos1p8.w.variable(name='w_n', value=1.0, bounds=(0.42, 20.0))
w_p = pdk.pmos1p8.w.variable(name='w_p', value=2.0, bounds=(0.42, 20.0))
l   = pdk.nmos1p8.l.variable(name='l',   value=0.15, bounds=(0.15, 1.0))

# Pass ModuleVariable objects directly as device parameters
mn = pdk.nmos1p8('mn', d='out', g='in', s='gnd', b='gnd', w=w_n, l=l)
mp = pdk.pmos1p8('mp', d='out', g='in', s='vdd', b='vdd', w=w_p, l=l)

# Build a ParameterSpace and sample it
space = Space([w_n, w_p, l])
points = space.sample(20, method='lhs')    # Latin hypercube sampling
# also: method='random', 'sobol', 'grid'

for pt in points:
    space.apply(pt)                        # updates variable .value fields
    ckt.generateNetlist(corner='tt')       # picks up new values automatically

Schema export (GUI / code-gen integration)

from silk import export_schema, module_schema
import json

# Export all PDK devices as a machine-readable dict
schema = export_schema(pdk)
print(json.dumps(schema, indent=2))

# Export a single Module's interface
mod_schema = module_schema(my_module)

Database / version tracking

from silk.db import Database, TrackedModule, DesignStage

db = Database('designs.db')
tracked = TrackedModule(
    'PDK_skywater130', 'out', 'ldo.cir', pdkDetails=pdk,
    circuit_name='ldo_nmos', version='1.0',
    stage=DesignStage.DEV, db_path='designs.db',
)
tracked.create_circuit_version(description='Initial attempt')
# ... build circuit ...
tracked.store_results(results, corner='tt', temperature=27)
tracked.promote()                   # DEV → STABLE → POST_LAYOUT → TAPEOUT

Code generation

from silk.codegen import Generator

gen = Generator(pdk)
code = gen.generate(my_module)
print(code)

Full API reference

See docs/api-reference.md.

Backward compatibility

Old pynetlist.* class names are still importable via silk._compat:

from silk._compat import PyModule, PySignal, ParameterSpace  # deprecated

The PyNetlist class is available at silk.circuit.netlist.PyNetlist as a thin subclass of Module with optional legacy DB tracking.

Package layout

silk/
  __init__.py         top-level re-exports
  exceptions.py       CircuitError hierarchy
  _compat.py          backward-compat aliases for old Py* names

  circuit/            Module, ModuleTestbench, schema export
  pdk/                PDK_skywater130, BasePDK, load_pdk
  elements/           ElementLibrary, passives, AnalogElement
  sources/            VoltageSource, CurrentSource, waveforms
  signals/            Signal, NgspiceSignal, metrics
  simulation/         Transient, AC, DC, Noise, Corner, NgspiceRunner
  optimize/           Space, Variable, Optimizer
  db/                 Database, TrackedModule, DesignStage
  codegen/            Generator
  utils/              internal helpers
  examples/           runnable example scripts

Development

pip install -e ".[dev]"
pytest tests/ -q

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

circuitsilk-0.1.1.tar.gz (160.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

circuitsilk-0.1.1-py3-none-any.whl (78.2 kB view details)

Uploaded Python 3

File details

Details for the file circuitsilk-0.1.1.tar.gz.

File metadata

  • Download URL: circuitsilk-0.1.1.tar.gz
  • Upload date:
  • Size: 160.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for circuitsilk-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e83d67208593307a566683e42083af90b378a71fead5f5baa54990b2569d1903
MD5 0218b2e0741daf991f42ab1aab82f58e
BLAKE2b-256 46203dab277600a118a05ef50741287a884da4ecb613bba8d425eddd37345797

See more details on using hashes here.

File details

Details for the file circuitsilk-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: circuitsilk-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 78.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for circuitsilk-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b06eb07e2a86a815777595891f16ffb1503ac83e316fd5459e87d614deb81a6a
MD5 3ff9dc45965e705f4894780dd14df145
BLAKE2b-256 b20432bd452d6cf938c6fb4cde2ebb107f1e02eee9b051e05eb6a900285e365e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page