In [1]:
from IPython.core.display import HTML
HTML(open('custom.css', 'r').read())
Out[1]:
SKiDL is a Python package for describing the interconnection of electronic devices using text (instead of schematics). PySpice is an interface for controlling an external SPICE circuit simulator from Python. This document demonstrates how circuits described using SKiDL can be simulated under a variety of conditions using PySpice with the results displayed in an easily-shared Jupyter notebook.
There are existing SPICE simulators that analyze schematics created by CAD packages like KiCad. There are also versions with their own GUI like LTSpice. What advantages does a combination of SKiDL, PySpice and ngspice offer?
This notebook assumes you're using ngspice version 30. To install ngspice for linux, do:
sudo apt-get update
sudo apt-get install ngspice
For Windows:
C:\Program Files
. The top-level folder should be named Spice64_dll
so PySpice can find it.dll-vs
to bin_dll
.Next, for either OS, install SKiDL:
pip install skidl
After that, you'll have to manually install PySpice (it must be version 1.3.2 or higher):
pip install "PySpice>=1.3.2"
Finally, place an spinit
file in the same folder as the notebook you're trying to run.
This contains the ngspice initialization commands as discussed in the ngspice manual.
Typically, I just enable the use of Pspice models and set the number of processing threads as follows:
set ngbehavior=ps
set num_thread=4
The following examples demonstrate some of the ways of using SKiDL and PySpice to simulate electronics. While shown using the Jupyter notebook, these examples will also work by placing the Python code into a file and executing it with a Python interpreter.
The following code snippet is needed at the beginning of every example.
It loads the matplotlib
package for generating graphs, and SKiDL + PySpice packages for describing and simulating circuitry.
In [2]:
# Load the package for drawing graphs.
import matplotlib.pyplot as plt
# Omit the following line if you're not using a Jupyter notebook.
%matplotlib inline
# Load the SKiDL + PySpice packages and initialize them for doing circuit simulations.
from skidl.pyspice import *
print(lib_search_paths)
The following example connects a 1 K$\Omega$ resistor to a voltage source whose value is ramped from 0 to 1 volts. The current through the resistor is plotted versus the applied voltage.
In [3]:
reset() # This will clear any previously defined circuitry.
# Create and interconnect the components.
vs = V(ref='VS', dc_value = 1 @ u_V) # Create a voltage source named "VS" with an initial value of 1 volt.
r1 = R(value = 1 @ u_kOhm) # Create a 1 Kohm resistor.
vs['p'] += r1[1] # Connect one end of the resistor to the positive terminal of the voltage source.
gnd += vs['n'], r1[2] # Connect the other end of the resistor and the negative terminal of the source to ground.
# Simulate the circuit.
circ = generate_netlist() # Translate the SKiDL code into a PyCircuit Circuit object.
sim = circ.simulator() # Create a simulator for the Circuit object.
dc_vals = sim.dc(VS=slice(0, 1, 0.1)) # Run a DC simulation where the voltage ramps from 0 to 1V by 0.1V increments.
# Get the voltage applied to the resistor and the current coming out of the voltage source.
voltage = dc_vals[node(vs['p'])] # Get the voltage applied by the positive terminal of the source.
current = -dc_vals['VS'] # Get the current coming out of the positive terminal of the voltage source.
# Print a table showing the current through the resistor for the various applied voltages.
print('{:^7s}{:^7s}'.format('V', ' I (mA)'))
print('='*15)
for v, i in zip(voltage.as_ndarray(), current.as_ndarray()*1000):
print('{:6.2f} {:6.2f}'.format(v, i))
# Create a plot of the current (Y coord) versus the applied voltage (X coord).
figure = plt.figure(1)
plt.title('Resistor Current vs. Applied Voltage')
plt.xlabel('Voltage (V)')
plt.ylabel('Current (mA)')
plt.plot(voltage, current*1000) # Plot X=voltage and Y=current (in milliamps, so multiply it by 1000).
plt.show()
This example shows the time-varying voltage of a capacitor charged through a resistor by a pulsed voltage source.
In [4]:
reset() # Clear out the existing circuitry from the previous example.
# Create a pulsed voltage source, a resistor, and a capacitor.
vs = PULSEV(initial_value=0, pulsed_value=5@u_V, pulse_width=1@u_ms, period=2@u_ms) # 1ms ON, 1ms OFF pulses.
r = R(value=1@u_kOhm) # 1 Kohm resistor.
c = C(value=1@u_uF) # 1 uF capacitor.
r['+', '-'] += vs['p'], c['+'] # Connect the resistor between the positive source terminal and one of the capacitor terminals.
gnd += vs['n'], c['-'] # Connect the negative battery terminal and the other capacitor terminal to ground.
# Simulate the circuit.
circ = generate_netlist() # Create the PySpice Circuit object from the SKiDL code.
sim = circ.simulator() # Get a simulator for the Circuit object.
waveforms = sim.transient(step_time=0.01@u_ms, end_time=10@u_ms) # Run a transient simulation from 0 to 10 msec.
# Get the simulation data.
time = waveforms.time # Time values for each point on the waveforms.
pulses = waveforms[node(vs['p'])] # Voltage on the positive terminal of the pulsed voltage source.
cap_voltage = waveforms[node(c['+'])] # Voltage on the capacitor.
# Plot the pulsed source and capacitor voltage values versus time.
figure = plt.figure(1)
plt.title('Capacitor Voltage vs. Source Pulses')
plt.xlabel('Time (ms)')
plt.ylabel('Voltage (V)')
plt.plot(time*1000, pulses) # Plot pulsed source waveform.
plt.plot(time*1000, cap_voltage) # Plot capacitor charging waveform.
plt.legend(('Source Pulses', 'Capacitor Voltage'), loc=(1.1, 0.5))
plt.show()
A voltage source whose output is controlled by another voltage source is demonstrated in this example.
In [5]:
reset() # Clear out the existing circuitry from the previous example.
# Connect a sine wave to the control input of a voltage-controlled voltage source.
vs = SINEV(amplitude=1@u_V, frequency=100@u_Hz) # 1V sine wave source at 100 Hz.
vs['n'] += gnd # Connect the negative terminal of the sine wave to ground.
vc = VCVS(gain=2.5) # Voltage-controlled voltage source with a gain of 2.5.
vc['ip', 'in'] += vs['p'], gnd # Connect the sine wave to the input port of the controlled source.
vc['op', 'on'] += Net(), gnd # Connect the output port of the controlled source to a net and ground.
rl = R(value=1@u_kOhm)
rl[1,2] += vc['op'], gnd
r = R(value=1@u_kOhm)
r[1,2] += vs['p'], gnd
# Simulate the circuit.
circ = generate_netlist() # Create the PySpice Circuit object from the SKiDL code.
print(circ)
sim = circ.simulator() # Get a simulator for the Circuit object.
waveforms = sim.transient(step_time=0.01@u_ms, end_time=20@u_ms) # Run a transient simulation from 0 to 20 msec.
# Get the time-varying waveforms of the sine wave source and the voltage-controlled source.
time = waveforms.time
vin = waveforms[node(vs['p'])]
vout = waveforms[node(vc['op'])]
# Plot the input and output waveforms. Note that the output voltage is 2.5x the input voltage.
figure = plt.figure(1)
plt.title('Input and Output Sine Waves')
plt.xlabel('Time (ms)')
plt.ylabel('Voltage (V)')
plt.plot(time*1000, vin)
plt.plot(time*1000, vout)
plt.legend(('Input Voltage', 'Output Voltage'), loc=(1.1, 0.5))
plt.show()
This example shows a current source controlled by the current driven through a resistor by a voltage source.
In [6]:
reset() # Clear out the existing circuitry from the previous example.
# Use the current driven through a resistor to control another current source.
vs = SINEV(amplitude=1@u_V, frequency=100@u_Hz) # 100 Hz sine wave voltage source.
rs = R(value=1@u_kOhm) # Resistor connected to the voltage source.
rs[1,2] += vs['p'], gnd # Connect resistor from positive terminal of voltage source to ground.
vs['n'] += gnd # Connect the negative terminal of the voltage source to ground.
vc = CCCS(control=vs, gain=2.5) # Current source controlled by the current entering the vs voltage source.
rc = R(value=1@u_Ohm) # Resistor connected to the current source.
rc[1,2] += vc['p'], gnd # Connect resistor from the positive terminal of the current source to ground.
vc['n'] += gnd # Connect the negative terminal of the current source to ground.
# Simulate the circuit.
circ = generate_netlist()
sim = circ.simulator()
waveforms = sim.transient(step_time=0.01@u_ms, end_time=20@u_ms)
# Get the time-varying waveforms of the currents from the sine wave source and the current-controlled current source.
time = waveforms.time
i_vs = waveforms[vs.ref]
i_vc = waveforms[node(rc[1])] / rc.value # Current-source current is the voltage across the resistor / resistance.
# Plot the waveforms. Note the input and output currents are out of phase since the output current is calculated
# based on the current *leaving* the positive terminal of the controlled current source and entering the resistor,
# whereas the current in the controlling voltage source is calculated based on what is *entering* the positive terminal.
figure = plt.figure(1)
plt.title('Control Current vs. Output Current')
plt.xlabel('Time (ms)')
plt.ylabel('Current (mA)')
plt.plot(time*1000, i_vs)
plt.plot(time*1000, i_vc)
plt.legend(('Control Current', 'Output Current'), loc=(1.1, 0.5))
plt.show()
The voltages at the beginning and end of an ideal transmission line are shown in this example.
In [7]:
reset() # Clear out the existing circuitry from the previous example.
# Create a 1 GHz sine wave source, drive it through a 70 ohm ideal transmission line, and terminate it with a 140 ohm resistor.
vs = SINEV(amplitude=1@u_V, frequency=1@u_GHz)
t1 = T(impedance=70@u_Ohm, frequency=1@u_GHz, normalized_length=10.0) # Trans. line is 10 wavelengths long.
rload = R(value=140@u_Ohm)
vs['p'] += t1['ip'] # Connect source to positive input terminal of trans. line.
rload[1] += t1['op'] # Connect resistor to positive output terminal of trans. line.
gnd += vs['n'], t1['in','on'], rload[2] # Connect remaining terminals to ground.
# Simulate the transmission line.
circ = generate_netlist()
sim = circ.simulator()
waveforms = sim.transient(step_time=0.01@u_ns, end_time=20@u_ns)
# Get the waveforms at the beginning and end of the trans. line.
time = waveforms.time * 10**9
vin = waveforms[node(vs['p'])] # Input voltage at the beginning of the trans. line.
vout = waveforms[node(rload['1'])] # Output voltage at the terminating resistor of the trans. line.
# Plot the input and output waveforms. Note that it takes 10 nsec for the input to reach the end of the
# transmission line, and there is a 33% "bump" in the output voltage due to the mismatch between the
# 140 ohm load resistor and the 70 ohm transmission line impedances.
figure = plt.figure(1)
plt.title('Output Voltage vs. Input Voltage')
plt.xlabel('Time (ns)')
plt.ylabel('Voltage (V)')
plt.plot(time, vin)
plt.plot(time, vout)
plt.legend(('Input Voltage', 'Output Voltage'), loc=(1.1, 0.5))
plt.show()
The use of SPICE models is demonstrated in this example of a common-emitter transistor amplifier.
For this example, a subdirectory called SpiceLib
was created with a single file 2N2222A.lib
that holds
the .MODEL
statement for that particular type of transistor.
In [8]:
reset() # Clear out the existing circuitry from the previous example.
# Create a transistor, power supply, bias resistors, collector resistor, and an input sine wave source.
q = BJT(model='2n2222a') # 2N2222A NPN transistor. The model is stored in a directory of SPICE .lib files.
vdc = V(dc_value=5@u_V) # 5V power supply.
rs = R(value=5@u_kOhm) # Source resistor in series with sine wave input voltage.
rb = R(value=25@u_kOhm) # Bias resistor from 5V to base of transistor.
rc = R(value=1@u_kOhm) # Load resistor connected to collector of transistor.
vs = SINEV(amplitude=0.01@u_V, frequency=1@u_kHz) # 1 KHz sine wave input source.
q['c', 'b', 'e'] += rc[1], rb[1], gnd # Connect transistor CBE pins to load & bias resistors and ground.
vdc['p'] += rc[2], rb[2] # Connect other end of load and bias resistors to power supply's positive terminal.
vdc['n'] += gnd # Connect negative terminal of power supply to ground.
rs[1,2] += vs['p'], q['b'] # Connect source resistor from input source to base of transistor.
vs['n'] += gnd # Connect negative terminal of input source to ground.
# Simulate the transistor amplifier. This requires a SPICE library containing a model of the 2N2222A transistor.
circ = generate_netlist(libs='SpiceLib') # Pass the directory to the SPICE model library when creating circuit.
print(circ)
sim = circ.simulator()
waveforms = sim.transient(step_time=0.01@u_ms, end_time=5@u_ms)
# Get the input source and amplified output waveforms.
time = waveforms.time
vin = waveforms[node(vs['p'])] # Input source voltage.
vout = waveforms[node(q['c'])] # Amplified output voltage at collector of the transistor.
# Plot the input and output waveforms.
figure = plt.figure(1)
plt.title('Output Voltage vs. Input Voltage')
plt.xlabel('Time (ms)')
plt.ylabel('Voltage (V)')
plt.plot(time*1000, vin)
plt.plot(time*1000, vout)
plt.legend(('Input Voltage', 'Output Voltage'), loc=(1.1, 0.5))
plt.show()
XSPICE parts can model a variety of functions (ADCs, DACs, etc.) having different I/O requirements, so SKiDL handles them a bit differently:
io
parameter is needed to specify the input and output pins. This parameter is either a comma-separated string or an array of strings listing the pin names in the order required for the particular XSPICE part. XSPICE I/O ports can be scalar (indicated by names which are simple strings) or vectors (indicated by names ending with "[]").model
parameter is required that specifies the parameters affecting the behavior of the given XSPICE part. This is passed as an XspiceModel
object.
In [9]:
reset() # Clear out the existing circuitry from the previous example.
# Sinusoidal voltage source.
vin = sinev(offset=1.65 @ u_V, amplitude=1.65 @ u_V, frequency=100e6)
# Component declarations showing various XSPICE styles.
# Creating an XSPICE part from the pyspice library.
adc = Part(
"pyspice",
"A",
io="anlg_in[], dig_out[]", # Two vector I/O ports in a string.
model=XspiceModel(
"adc", # The name assigned to this particular model instance.
"adc_bridge", # The name of the XSPICE part associated with this model.
# The rest of the arguments are keyword parameters for the model.
in_low=0.05 @ u_V,
in_high=0.1 @ u_V,
rise_delay=1e-9 @ u_s,
fall_delay=1e-9 @ u_s,
),
tool=SKIDL
)
# Creating an XSPICE part using the SPICE abbreviation 'A'.
buf = A(
io="buf_in, buf_out", # Two scalar I/O ports in a string.
model=XspiceModel(
"buf",
"d_buffer",
rise_delay=1e-9 @ u_s,
fall_delay=1e-9 @ u_s,
input_load=1e-12 @ u_s,
),
)
# Creating an XSPICE part using the XSPICE alias.
dac = XSPICE(
io=["dig_in[]", "anlg_out[]"], # Two vector ports in an array.
model=XspiceModel("dac", "dac_bridge", out_low=1.0 @ u_V, out_high=3.3 @ u_V),
)
r = R(value=1 @ u_kOhm) # Load resistor.
# Connections: sine wave -> ADC -> buffer -> DAC.
vin["p, n"] += adc["anlg_in"][0], gnd # Attach to first pin in ADC anlg_in vector of pins.
adc["dig_out"][0] += buf["buf_in"] # Attach first pin of ADC dig_out vector to buffer.
buf["buf_out"] += dac["dig_in"][0] # Attach buffer output to first pin of DAC dig_in vector of pins.
r["p,n"] += dac["anlg_out"][0], gnd # Attach first pin of DAC anlg_out vector to load resistor.
circ = generate_netlist(libs="SpiceLib")
print(circ)
sim = circ.simulator()
waveforms = sim.transient(step_time=0.1 @ u_ns, end_time=50 @ u_ns)
time = waveforms.time
vin = waveforms[node(vin["p"])]
vout = waveforms[node(r["p"])]
print('{:^7s}{:^7s}'.format('vin', 'vout'))
print('='*15)
for v1, v2 in zip(vin.as_ndarray(), vout.as_ndarray()):
print('{:6.2f} {:6.2f}'.format(v1, v2))
SKiDL lets you describe a circuit inside a function, and then call that function to create hierarchical designs that can be analyzed with SPICE. This example defines a simple transistor inverter and then cascades several of them.
In [10]:
reset() # You know what this does by now, right?
# Create a power supply for all the following circuitry.
pwr = V(dc_value=5@u_V)
pwr['n'] += gnd
vcc = pwr['p']
# Create a logic inverter using a transistor and a few resistors.
@subcircuit
def inverter(inp, outp):
'''When inp is driven high, outp is pulled low by transistor. When inp is driven low, outp is pulled high by resistor.'''
q = BJT(model='2n2222a') # NPN transistor.
rc = R(value=1@u_kOhm) # Resistor attached between transistor collector and VCC.
rc[1,2] += vcc, q['c']
rb = R(value=10@u_kOhm) # Resistor attached between transistor base and input.
rb[1,2] += inp, q['b']
q['e'] += gnd # Transistor emitter attached to ground.
outp += q['c'] # Inverted output comes from junction of the transistor collector and collector resistor.
# Create a pulsed voltage source to drive the input of the inverters. I set the rise and fall times to make
# it easier to distinguish the input and output waveforms in the plot.
vs = PULSEV(initial_value=0, pulsed_value=5@u_V, pulse_width=0.8@u_ms, period=2@u_ms, rise_time=0.2@u_ms, fall_time=0.2@u_ms) # 1ms ON, 1ms OFF pulses.
vs['n'] += gnd
# Create three inverters and cascade the output of one to the input of the next.
outp = Net() * 3 # Create three nets to attach to the outputs of each inverter.
inverter(vs['p'], outp[0]) # First inverter is driven by the pulsed voltage source.
inverter(outp[0], outp[1]) # Second inverter is driven by the output of the first.
inverter(outp[1], outp[2]) # Third inverter is driven by the output of the second.
# Simulate the cascaded inverters.
circ = generate_netlist(libs='SpiceLib') # Pass-in the library where the transistor model is stored.
sim = circ.simulator()
waveforms = sim.transient(step_time=0.01@u_ms, end_time=5@u_ms)
# Get the waveforms for the input and output.
time = waveforms.time
inp = waveforms[node(vs['p'])]
outp = waveforms[node(outp[2])] # Get the output waveform for the final inverter in the cascade.
# Plot the input and output waveforms. The output will be the inverse of the input since it passed
# through three inverters.
figure = plt.figure(1)
plt.title('Output Voltage vs. Input Voltage')
plt.xlabel('Time (ms)')
plt.ylabel('Voltage (V)')
plt.plot(time*1000, inp)
plt.plot(time*1000, outp)
plt.legend(('Input Voltage', 'Output Voltage'), loc=(1.1, 0.5))
plt.show()
Using @subcircuit
lets you do hierarchical design directly in SKiDL, but SPICE has long had another option: subcircuits. These are encapsulations of device behavior stored in SPICE library files.
Many thousands of these have been created over the years, both by SPICE users and semiconductor companies.
A simulation of the NCP1117 voltage regulator
from ON Semiconductor is shown below.
In [11]:
reset()
lib_search_paths[SPICE].append('SpiceLib')
vin = V(ref='VIN', dc_value=8@u_V) # Input power supply.
vreg = Part('NCP1117', 'ncp1117_33-x') # Voltage regulator from ON Semi part lib.
print(vreg) # Print vreg pin names.
r = R(value=470 @ u_Ohm) # Load resistor on regulator output.
vreg['IN', 'OUT'] += vin['p'], r[1] # Connect vreg input to vin and output to load resistor.
gnd += vin['n'], r[2], vreg['GND'] # Ground connections for everybody.
# Simulate the voltage regulator subcircuit.
circ = generate_netlist(libs='SpiceLib') # Pass-in the library where the voltage regulator subcircuit is stored.
sim = circ.simulator()
dc_vals = sim.dc(VIN=slice(0,10,0.1)) # Ramp vin from 0->10V and observe regulator output voltage.
# Get the input and output voltages.
inp = dc_vals[node(vin['p'])]
outp = dc_vals[node(vreg['OUT'])]
# Plot the regulator output voltage vs. the input supply voltage. Note that the regulator
# starts to operate once the input exceeds 4V and the output voltage clamps at 3.3V.
figure = plt.figure(1)
plt.title('NCP1117-3.3 Regulator Output Voltage vs. Input Voltage')
plt.xlabel('Input Voltage (V)')
plt.ylabel('Output Voltage (V)')
plt.plot(inp, outp)
plt.show()
SKiDL can work with SPICE subcircuits intended for PSPICE and LTSpice. All you need to do is add the top-level directories where the subcircuit libraries are stored and SKiDL will recursively search for the library files. When it reads a subcircuit library file (indicated by a .lib
file extension), SKiDL will also look for a symbol file that provides names for the subcircuit I/O signals. For PSPICE, the symbol file has a .slb
extension while the .asy
extension is used for LTSpice.
WARNING: Even though SKiDL will read the PSPICE and LTSpice library files, that doesn't mean that ngspice can process them. Each SPICE simulator seems to support a different set of optional parameters for the various circuit elements (the nonlinear current source, G
, for example). You will probably have to modify the library file to satisfy ngspice. PSPICE libraries seem to need the least modification. I wish it was easier, but it's not.
The examples section gave you some idea of what the combination of SKiDL, PySpice, ngspice, and matplotlib can do. You should read the stand-alone documentation for each of those packages to get the full extent of their capabilities. This section will address the features and functions that come in to play at their intersection.
You may have noticed the use of units in the examples above, such as 10 @ u_kOhm
to denote a resistance of 10 K$\Omega$.
This is a feature of the PySpice package. If you want to see all the available units, just do this:
In [12]:
import PySpice.Unit
', '.join(dir(PySpice.Unit))
Out[12]:
The following units are the ones you'll probably use most:
u_TV
(terravolt), u_GV
(gigavolt), u_MV
(megavolt), u_kV
(kilovolt), u_V
(volt), u_mV
(millivolt), u_uV
(microvolt), u_nV
(nanovolt), u_pV
(picovolt).u_TA
(terraamp), u_GA
(gigaamp), u_MA
(megaamp), u_kA
(kiloamp), u_A
(amp), u_mA
(milliamp), u_uA
(microamp), u_nA
(nanoamp), u_pA
(picoamp).u_TOhm
(terraohm), u_GOhm
(gigaohm), u_MOhm
(megaohm), u_kOhm
(kiloohm), u_Ohm
(ohm), u_mOhm
(milliohm), u_uOhm
(microohm), u_nOhm
(nanoohm), u_pOhm
(picoohm).u_TF
(terrafarad) u_GF
(gigafarad), u_MF
(megafarad), u_kF
(kilofarad), u_F
(farad), u_mF
(millifarad), u_uF
(microfarad), u_nF
(nanofarad), u_pF
(picofarad).u_TH
(terrahenry), u_GH
(gigahenry), u_MH
(megahenry), u_kH
(kilohenry), u_H
(henry), u_mH
(millihenry), u_uH
(microhenry), u_nH
(nanohenry), u_pH
(picohenry).u_Ts
(terrasecond), u_Gs
(gigasecond), u_Ms
(megasecond), u_ks
(kilosecond), u_s
(second), u_ms
(millisecond), u_us
(microsecond), u_ns
(nanosecond), u_ps
(picosecond).u_THz
(terrahertz), u_GHz
(gigahertz), u_MHz
(megahertz), u_kHz
(kilohertz), u_Hz
(hertz), u_mHz
(millihertz), u_uHz
(microhertz), u_nHz
(nanohertz), u_pHz
(picohertz).The following is a list of parts (and their aliases) that are available for use in a SPICE simulation.
Many parts (like resistors) have only two pins denoted p
and n
, while some parts (like transmission lines)
have two ports composed of pins ip
, in
(the input port) and op
, on
(the output port).
The parts also have attributes that modify their characteristics.
These attributes can be set when a part is instantiated:
r = R(value=1@u_kOhm)
or they can be set after instantiation like this:
r.value = 1 @ u_kOhm
You can go here for more information about what device characteristics the attributes control.
In [13]:
from skidl.libs.pyspice_sklib import pyspice_lib
for part in pyspice_lib.parts:
print('{name} ({aliases}): {desc}\n\tPins: {pins}\n\tAttributes: {attributes}\n'.format(
name=getattr(part, 'name', ''),
aliases=', '.join(getattr(part, 'aliases','')),
desc=getattr(part, 'description'),
pins=', '.join([p.name for p in part.pins]),
attributes=', '.join([a for a in list(part.pyspice.get('pos',[])) + list(part.pyspice.get('kw',[]))]),
))
When you import the PySpice functions into SKiDL:
from skidl.pyspice import *
several things occur:
skidl.libs.pyspice.py
are imported.SKIDL
.gnd
or GND
is created.In addition, when the parts are imported, their names and aliases are instantiated in the namespace of the calling module to make it easier to create parts. So, rather than using the following standard SKiDL method of creating a part:
c = Part('pyspice', 'C', value=1@u_uF)
you can just do:
c = C(value=1@u_uF)
You can use the node()
function if you need the name of a circuit node in order to extract its data
from the results returned by a simulation. The argument to node()
can be either a pin of a part or a net:
If you need the actual SPICE deck for a circuit so you can simulate it outside of Python, just print the
Circuit object returned by generate_netlist()
or store it in a file:
In [14]:
reset()
# Create and interconnect the components.
vs = V(ref='VS', dc_value = 1 @ u_V) # Create a voltage source named "VS" with an initial value of 1 volt.
r1 = R(value = 1 @ u_kOhm) # Create a 1 Kohm resistor.
vs['p'] += r1[1] # Connect one end of the resistor to the positive terminal of the voltage source.
gnd += vs['n'], r1[2] # Connect the other end of the resistor and the negative terminal of the source to ground.
# Output the SPICE deck for the circuit.
circ = generate_netlist() # Translate the SKiDL code into a PyCircuit Circuit object.
print(circ) # Print the SPICE deck for the circuit.
There are two immediate features that need to be added:
.SUBCKT
command.Thanks to Fabrice Salvaire for inventing PySpice, and to Steve Armour whose Jupyter notebook of PySpice examples led the way.