Pole-Zero Analysis#
Compute poles and zeros of the small-signal transfer function.
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"
# -- Pole-zero parameters --
INPUT_NODE1 = "W3" # input positive node
INPUT_NODE2 = "GND" # input negative node
OUTPUT_NODE1 = "W6" # output positive node
OUTPUT_NODE2 = "GND" # output negative node
TF_TYPE = "vol" # transfer function type: "vol" or "cur"
PZ_TYPE = "pz" # analysis type: "pol", "zer", or "pz"
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))
Netlist nodes: ['0', 'W2', 'GND', 'W3', 'W6']
Run pole-zero 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.polezero(
node1=INPUT_NODE1,
node2=INPUT_NODE2,
node3=OUTPUT_NODE1,
node4=OUTPUT_NODE2,
tf_type=TF_TYPE,
pz_type=PZ_TYPE,
)
print("Pole-zero analysis complete.")
Warning: can't find the initialization file spinit.
Newer Ngspice version that could be unsupported 46
Pole-zero analysis complete.
Results#
print("Poles:")
for key in analysis.nodes.keys():
if "pole" in key.lower():
val = np.array(analysis[key]).item()
print(f" {key} = {val:.6g}")
print("\nZeros:")
for key in analysis.nodes.keys():
if "zero" in key.lower():
val = np.array(analysis[key]).item()
print(f" {key} = {val:.6g}")
Poles:
pole(5) = -71.7487+0j
pole(4) = -5.03165e+12+0j
pole(3) = -1.23475e+13+0j
pole(2) = -1.7598e+13+0j
pole(1) = -1.53426e+14+0j
Zeros:
zero(4) = 6.86471e+11+0j
zero(3) = -1.23475e+13+0j
zero(2) = -1.28779e+13+0j
zero(1) = -1.7598e+13+0j