Transient Analysis#
Time-domain simulation of an IHP circuit using ngspice.
import os
import sys
from pathlib import Path
# Set working directory to the PDK root if running from scripts/
if Path.cwd().name == "scripts":
os.chdir(Path.cwd().parent)
# Ensure ngspice shared library can be found (macOS homebrew)
if sys.platform == "darwin" and "/opt/homebrew/lib" not in os.environ.get(
"DYLD_LIBRARY_PATH", ""
):
os.environ["DYLD_LIBRARY_PATH"] = "/opt/homebrew/lib:" + os.environ.get(
"DYLD_LIBRARY_PATH", ""
)
import numpy as np
import pandas as pd
import holoviews as hv
from nyancad.watch import watch_project_dir, file_schematic
from nyancad.netlist import inspice_netlist
from nyancad.plot import timeplot
from InSpice import Simulator
hv.extension("bokeh")
Parameters#
# -- Circuit --
SCHEMATIC = "inverter" # stem of the .nyancir file
CORNER = "mos_tt"
# -- Transient parameters --
STEP_TIME = 1e-5 # time step (s)
START_TIME = 0 # start time (s)
END_TIME = 1e-3 # end time (s)
MAX_TIME_STEP = 1e-4 # max time step (s), 0 to let ngspice choose
USE_INITIAL_CONDITION = False
# -- Vectors to plot (node voltages / branch currents) --
# Leave empty to auto-select all node voltages after simulation.
VECTORS = []
Load schematic and build netlist#
project = watch_project_dir(".")
schem_data = await file_schematic(project, SCHEMATIC)
spice = await inspice_netlist(SCHEMATIC, schem_data, corner=CORNER)
print(spice)
Duplicated lib ('ihp/models/ngspice/models/cornerMOSlv.lib', 'mos_tt')
.title schematic
.lib /Users/pepijndevos/code/IHP/ihp/models/ngspice/models/cornerMOSlv.lib mos_tt
VV1 W2 GND DC 1.8
VV2 W3 GND DC 0.9 AC 1 sin(0.9 0.5 1k)
XM1 W6 W3 GND GND sg13_lv_nmos l={0.13 * 1e-6} m=1 ng=1 w={0.15 * 1e-6}
XM2 W2 W3 W6 W2 sg13_lv_pmos l={0.13 * 1e-6} m=1 ng=1 w={0.15 * 1e-6}
CC1 W6 GND 1u
print("Netlist nodes:", list(spice.node_names))
print("Netlist elements:", list(spice.element_names))
Netlist nodes: ['0', 'W2', 'GND', 'W3', 'W6']
Netlist elements: ['VV1', 'VV2', 'XM1', 'XM2', 'CC1']
Run transient simulation#
simulator = Simulator.factory(simulator="ngspice-shared")
for osdi in [
"ihp/models/ngspice/osdi/psp103.osdi",
"ihp/models/ngspice/osdi/psp103_nqs.osdi",
"ihp/models/ngspice/osdi/r3_cmc.osdi",
"ihp/models/ngspice/osdi/mosvar.osdi",
]:
simulator._ngspice_shared.exec_command(f"osdi {osdi}")
simulation = simulator.simulation(spice)
analysis = simulation.transient(
step_time=STEP_TIME,
end_time=END_TIME,
start_time=START_TIME,
max_time=MAX_TIME_STEP if MAX_TIME_STEP > 0 else None,
use_initial_condition=USE_INITIAL_CONDITION,
)
print("Transient analysis complete.")
Warning: can't find the initialization file spinit.
Newer Ngspice version that could be unsupported 46
Transient analysis complete.
Results#
available = [*analysis.nodes.keys(), *analysis.branches.keys()]
print("Available vectors:", available)
if not VECTORS:
VECTORS = list(analysis.nodes.keys())
print("Auto-selected:", VECTORS)
df = pd.DataFrame(index=np.array(analysis.time))
for vec in VECTORS:
df[vec] = np.array(analysis[vec])
df
Available vectors: ['n.xm1.nsg13_lv_nmos#SI', 'n.xm1.nsg13_lv_nmos#GP', 'n.xm1.nsg13_lv_nmos#NOI', 'n.xm2.nsg13_lv_pmos#SI', 'n.xm2.nsg13_lv_pmos#GP', 'n.xm2.nsg13_lv_pmos#NOI', 'w6', 'w3', 'w2', 'vv1', 'vv2']
Auto-selected: ['n.xm1.nsg13_lv_nmos#SI', 'n.xm1.nsg13_lv_nmos#GP', 'n.xm1.nsg13_lv_nmos#NOI', 'n.xm2.nsg13_lv_pmos#SI', 'n.xm2.nsg13_lv_pmos#GP', 'n.xm2.nsg13_lv_pmos#NOI', 'w6', 'w3', 'w2']
| n.xm1.nsg13_lv_nmos#SI | n.xm1.nsg13_lv_nmos#GP | n.xm1.nsg13_lv_nmos#NOI | n.xm2.nsg13_lv_pmos#SI | n.xm2.nsg13_lv_pmos#GP | n.xm2.nsg13_lv_pmos#NOI | w6 | w3 | w2 | |
|---|---|---|---|---|---|---|---|---|---|
| 0.000000e+00 | 0.0 | 0.900000 | 0.0 | 0.0 | 0.900000 | 0.0 | 0.162154 | 0.900000 | 1.8 |
| 1.000000e-07 | 0.0 | 0.900314 | 0.0 | 0.0 | 0.900314 | 0.0 | 0.162154 | 0.900314 | 1.8 |
| 2.000000e-07 | 0.0 | 0.900628 | 0.0 | 0.0 | 0.900628 | 0.0 | 0.162154 | 0.900628 | 1.8 |
| 4.000000e-07 | 0.0 | 0.901257 | 0.0 | 0.0 | 0.901257 | 0.0 | 0.162154 | 0.901257 | 1.8 |
| 8.000000e-07 | 0.0 | 0.902513 | 0.0 | 0.0 | 0.902513 | 0.0 | 0.162153 | 0.902513 | 1.8 |
| 1.600000e-06 | 0.0 | 0.905026 | 0.0 | 0.0 | 0.905026 | 0.0 | 0.162153 | 0.905026 | 1.8 |
| 3.200000e-06 | 0.0 | 0.910052 | 0.0 | 0.0 | 0.910052 | 0.0 | 0.162151 | 0.910052 | 1.8 |
| 6.400000e-06 | 0.0 | 0.920101 | 0.0 | 0.0 | 0.920101 | 0.0 | 0.162144 | 0.920101 | 1.8 |
| 1.280000e-05 | 0.0 | 0.940169 | 0.0 | 0.0 | 0.940169 | 0.0 | 0.162114 | 0.940169 | 1.8 |
| 2.560000e-05 | 0.0 | 0.980078 | 0.0 | 0.0 | 0.980078 | 0.0 | 0.161999 | 0.980078 | 1.8 |
| 5.120000e-05 | 0.0 | 1.058089 | 0.0 | 0.0 | 1.058089 | 0.0 | 0.161559 | 1.058089 | 1.8 |
| 1.024000e-04 | 0.0 | 1.199959 | 0.0 | 0.0 | 1.199959 | 0.0 | 0.160000 | 1.199959 | 1.8 |
| 2.024000e-04 | 0.0 | 1.377804 | 0.0 | 0.0 | 1.377804 | 0.0 | 0.155528 | 1.377804 | 1.8 |
| 3.024000e-04 | 0.0 | 1.373144 | 0.0 | 0.0 | 1.373144 | 0.0 | 0.150494 | 1.373144 | 1.8 |
| 4.024000e-04 | 0.0 | 1.187760 | 0.0 | 0.0 | 1.187760 | 0.0 | 0.146276 | 1.187760 | 1.8 |
| 5.024000e-04 | 0.0 | 0.892460 | 0.0 | 0.0 | 0.892460 | 0.0 | 0.144661 | 0.892460 | 1.8 |
| 6.024000e-04 | 0.0 | 0.600041 | 0.0 | 0.0 | 0.600041 | 0.0 | 0.147135 | 0.600041 | 1.8 |
| 7.024000e-04 | 0.0 | 0.422196 | 0.0 | 0.0 | 0.422196 | 0.0 | 0.152982 | 0.422196 | 1.8 |
| 8.024000e-04 | 0.0 | 0.426856 | 0.0 | 0.0 | 0.426856 | 0.0 | 0.159944 | 0.426856 | 1.8 |
| 9.024000e-04 | 0.0 | 0.612240 | 0.0 | 0.0 | 0.612240 | 0.0 | 0.165662 | 0.612240 | 1.8 |
| 1.000000e-03 | 0.0 | 0.900000 | 0.0 | 0.0 | 0.900000 | 0.0 | 0.167841 | 0.900000 | 1.8 |
timeplot(df).opts(responsive=True, height=500)