Operating Point Analysis#

Find the DC operating point of an IHP circuit using ngspice. Capacitors are opened and inductors are shorted.

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
from nyancad.watch import watch_project_dir, file_schematic
from nyancad.netlist import inspice_netlist
from InSpice import Simulator

Parameters#

SCHEMATIC = "inverter"  # stem of the .nyancir file
CORNER = "mos_tt"

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

Run operating point analysis#

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.operating_point()
print("Operating point analysis complete.")
Warning: can't find the initialization file spinit.
Newer Ngspice version that could be unsupported 46
Operating point analysis complete.

Results#

print("Node voltages:")
for node in analysis.nodes.keys():
    print(f"  {node} = {np.array(analysis[node]).item():.6g} V")

print("\nBranch currents:")
for branch in analysis.branches.keys():
    print(f"  {branch} = {np.array(analysis[branch]).item():.6g} A")
Node voltages:
  n.xm1.nsg13_lv_nmos#SI = 0 V
  n.xm1.nsg13_lv_nmos#GP = 0.9 V
  n.xm1.nsg13_lv_nmos#NOI = 0 V
  n.xm2.nsg13_lv_pmos#SI = 0 V
  n.xm2.nsg13_lv_pmos#GP = 0.9 V
  n.xm2.nsg13_lv_pmos#NOI = 0 V
  w6 = 0.162154 V
  w3 = 0.9 V
  w2 = 1.8 V

Branch currents:
  vv1 = -2.16629e-05 A
  vv2 = 4.20063e-14 A