Skip to main content

Python framework for analog circuit design automation targeting the SkyWater 130nm open-source PDK

Project description

circuitsilk

circuitsilk (import silk) is a Python framework for analog circuit design automation targeting the SkyWater 130nm open-source PDK. It lets you describe circuits programmatically, run ngspice simulations, sweep design spaces, and track versions in a local database.

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.0.tar.gz (211.8 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.0-py3-none-any.whl (135.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: circuitsilk-0.1.0.tar.gz
  • Upload date:
  • Size: 211.8 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.0.tar.gz
Algorithm Hash digest
SHA256 7c253e919f62e8e25cd4aaf39b238dfe7f1fc8d282cd497690ce2a775147715d
MD5 ba904dc0458306a8171ed18ebede1501
BLAKE2b-256 746118761bc32364d15e690c351ced8f71c05b6c51661c321bf275e57e75038e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: circuitsilk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 135.0 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 102150d0ffab924134c5490e20516c23639143199ec633c572345b61e59330ac
MD5 568464e97247acfa28713d35168a7029
BLAKE2b-256 f227bd2a35a0baac76700875f056e39d48f2c08b70891f6d12fcb90cab525b94

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