from itertools import product
from matplotlib import pyplot as _plt
import numpy as _np
from pdkmaster.technology import primitive as _prm
from pdkmaster.design import circuit as _ckt, library as _lbry
from c4m.pdk import sky130
prims = sky130.tech.primitives
Compiled libraries for ngspice 36 are provided compiled for either CentOS 7 or Ubuntu20.04 so simulation can be performed without needing to install ngspice. One can comment out the setting of the NGPSICE_LIBRARY_PATH
environment variable and then ngspice executable in the path will be used to find the shared library. When using own ngspice it is adviced to use version 35 or later to speed up the
Also the provided .spiceinit
file in the running directory is needed to get this speed up.
import os
os.environ["NGSPICE_LIBRARY_PATH"] = "/home/verhaegs/software/mint20/stow/ngspice-36/lib/libngspice.so"
from PySpice.Unit import *
import pya
from pdkmaster.io.klayout import export as _klexp
ktech = pya.Technology.technology_by_name("Skywater_S8")
ksaveopts = ktech.save_layout_options.dup()
ksaveopts.write_context_info = False
bg3v3_minpow = sky130.Bandgap.compute_minpower(
nmos=prims.nfet_g5v0d10v5, nmos_w=40.0, nmos_l_min=1.0, nmos_l_max=25.0, nmos_Vgs_min=1.1,
pmos=prims.pfet_g5v0d10v5, pmos_w=40.0, pmos_l_min=2.0, pmos_l_max=25.0, pmos_Vgs_min=1.3,
pnp=prims.pnp_05v5_w3u40l3u40,
resistor=prims.poly_res,
vdd_min=2.97, vdd_max=3.63, corner=("io_tt", "diode_tt", "pnp_t", "rc_tt"),
temp_min=-20, temp_max=85,
debug=True,
gmin=1e-10,
)
print(bg3v3_minpow.param_str)
Unsupported Ngspice version 36
Using temperature 32.5 First Vgs/Vds estimates: Vgs_n=1.185, Vds_n=1.185, Vgs_p=1.3, Vds_p=1.185 Computing first target current estimate... In_min=1.2112613566467846e-05, Ip_min=2.3466744207178823e-06 Computing voltage of bipolar for target current Vpnp1=0.6917801560121657 New Vgs/Vds estimates: Vgs_n=1.1391099219939171, Vds_n=1.1391099219939171, Vgs_p=1.3, Vds_p=1.1391099219939171 Computing new target current estimate... In_min=9.640194212689068e-06, Ip_min=2.344772137827066e-06 Computing transistor l WARNING: pmos too weak, taking minimum l nmos_l=25.0, nmos_w=40.0, pmos_l=2.0, pmos_w=40.0 Updating bipolar voltage... Ibr1=3.3964978425431755e-05, Vpnp1=0.765, Vpnp2=0.745 Computing r1_height... Computing r2_height... r1_height=13.85, r2_height=322.14 Updating parameters Vgs_p too low (1.1308425247641871 < 1.3) Increasing l of pmos to try to increase it's Vgs pmos_l=3.15, Vgs_p=1.1852173978483271 pmos_l=4.3, Vgs_p=1.2267873987253366 pmos_l=5.449999999999999, Vgs_p=1.260068528114698 pmos_l=6.6, Vgs_p=1.289061186245204 Vpnp1=0.726, Vpnp2=0.708 Ibr1=8.43e-06, rIbr2=1.0077798089646588, rIbr3=1.0170089912363267 Vgs_n=1.368, Vgs_p=1.315, Vds_p1=0.876, Vds_n2=0.930 Optimizing r2 height Computing first temperature sweep 0: r2_height=322.136, reldiff=0.00426, vref_mint=1.143, vref_maxt=1.138 1: r2_height=326.399, reldiff=0.00223, vref_mint=1.148, vref_maxt=1.145 2: r2_height=328.663, reldiff=0.00116, vref_mint=1.150, vref_maxt=1.149 3: r2_height=329.852, reldiff=0.00060, vref_mint=1.151, vref_maxt=1.151 Convergence criteria met Bandgap: - nmos: MOSFET(nfet_g5v0d10v5) - nmos_l: 25.0 - nmos_w: 40.0 - nmos_mult: 1 - pmos: MOSFET(pfet_g5v0d10v5) - pmos_l: 7.75 - pmos_w: 40.0 - pmos_mult: 1 - pnp: Bipolar(pnp_05v5_w3u40l3u40) - pnp_mult: 1 - pnp_ratio: 2 - resistor: Resistor(poly_res) - r1_height: 13.853961899485526 - r2_height: 329.8515595904194
sweepres3v3_minpow = bg3v3_minpow.tempsweep(vdd=3.3, corner=("io_tt", "diode_tt", "pnp_t", "rc_tt"), gmin=1e-11)
_plt.figure(figsize=(16,12))
sweepres3v3_minpow.plot_internals()
temp=slice(-20, 81, 20)
corners = tuple(
("diode_tt", *cs)
for cs in product(("io_ss", "io_ff"), ("pnp_s", "pnp_f"), ("rc_tt",))
)
vdds = (3.0, 3.15, 3.3, 3.45, 3.6)
print("Simulating corners...")
sweeprescorns3v3_minpow = bg3v3_minpow.tempsweep_corners(vdd=3.3, corners=corners, sweep=temp)
print("Simulating vdds...")
sweepresvdds3v3_minpow = bg3v3_minpow.tempsweep_vdds(
vdds=vdds, corner=("diode_tt", "io_tt", "pnp_t", "rc_tt"), sweep=temp,
)
_plt.figure(figsize=(16,8))
_plt.subplot(1, 2, 1)
sweeprescorns3v3_minpow.plot_Vref()
_plt.subplot(1, 2, 2)
sweepresvdds3v3_minpow.plot_Vref()
Simulating corners... Simulating vdds...
bg1v8lvt_minpow = sky130.Bandgap.compute_minpower(
nmos=prims.nfet_01v8_lvt, nmos_w=40.0, nmos_l_min=1.0, nmos_l_max=25.0, nmos_Vgs_min=0.0,
pmos=prims.pfet_01v8_lvt, pmos_w=40.0, pmos_l_min=1.0, pmos_l_max=25.0, pmos_Vgs_min=0.7,
pnp=prims.pnp_05v5_w3u40l3u40,
resistor=prims.poly_res,
vdd_min=1.62, vdd_max=1.98, corner=("logic_tt", "diode_tt", "pnp_t", "rc_tt"),
temp_min=-20, temp_max=85,
debug=True,
gmin=1e-10,
)
print(bg1v8lvt_minpow.param_str)
Using temperature 32.5 First Vgs/Vds estimates: Vgs_n=0.51, Vds_n=0.51, Vgs_p=0.7, Vds_p=0.51 Computing first target current estimate... In_min=2.3208725207104323e-06, Ip_min=2.0136507065860553e-06 Computing voltage of bipolar for target current Vpnp1=0.6876907198065109 New Vgs/Vds estimates: Vgs_n=0.4661546400967446, Vds_n=0.4661546400967446, Vgs_p=0.7, Vds_p=0.4661546400967446 Computing new target current estimate... In_min=1.1188427148777736e-06, Ip_min=2.0114595473338373e-06 Computing transistor l WARNING: nmos too weak, taking minimum l nmos_l=1.0, nmos_w=40.0, pmos_l=25.0, pmos_w=40.0 Updating bipolar voltage... Ibr1=2.0114613458598392e-06, Vpnp1=0.688, Vpnp2=0.669 Computing r1_height... Computing r2_height... r1_height=62.85, r2_height=1802.83 Updating parameters Vgs_p too low (0.6765322113070535 < 0.7) Increasing l of pmos to try to increase it's Vgs WARNING: max pmos l reached when incresing Vgs_p Vpnp1=0.682, Vpnp2=0.664 Ibr1=1.66e-06, rIbr2=1.0049698659854014, rIbr3=1.002099066051537 Vgs_n=0.502, Vgs_p=0.677, Vds_p1=0.436, Vds_n2=0.264 Optimizing r2 height Computing first temperature sweep 0: r2_height=1802.834, reldiff=0.09995, vref_mint=1.104, vref_maxt=1.226 1: r2_height=1470.016, reldiff=0.05955, vref_mint=1.040, vref_maxt=1.106 2: r2_height=1312.863, reldiff=0.03711, vref_mint=1.010, vref_maxt=1.049 3: r2_height=1226.718, reldiff=0.02371, vref_mint=0.994, vref_maxt=1.018 4: r2_height=1175.737, reldiff=0.01538, vref_mint=0.984, vref_maxt=1.000 5: r2_height=1144.213, reldiff=0.01007, vref_mint=0.978, vref_maxt=0.988 6: r2_height=1124.195, reldiff=0.00663, vref_mint=0.975, vref_maxt=0.981 7: r2_height=1111.269, reldiff=0.00439, vref_mint=0.972, vref_maxt=0.976 8: r2_height=1102.833, reldiff=0.00291, vref_mint=0.970, vref_maxt=0.973 9: r2_height=1097.288, reldiff=0.00193, vref_mint=0.969, vref_maxt=0.971 10: r2_height=1093.627, reldiff=0.00128, vref_mint=0.969, vref_maxt=0.970 11: r2_height=1091.203, reldiff=0.00085, vref_mint=0.968, vref_maxt=0.969 Convergence criteria met Bandgap: - nmos: MOSFET(nfet_01v8_lvt) - nmos_l: 1.0 - nmos_w: 40.0 - nmos_mult: 1 - pmos: MOSFET(pfet_01v8_lvt) - pmos_l: 25.0 - pmos_w: 40.0 - pmos_mult: 1 - pnp: Bipolar(pnp_05v5_w3u40l3u40) - pnp_mult: 1 - pnp_ratio: 2 - resistor: Resistor(poly_res) - r1_height: 62.850290742288564 - r2_height: 1091.202500323175
sweepres1v8lvt_minpow = bg1v8lvt_minpow.tempsweep(vdd=1.8, corner=("logic_tt", "diode_tt", "pnp_t", "rc_tt"), gmin=1e-11)
_plt.figure(figsize=(16,12))
sweepres1v8lvt_minpow.plot_internals()
temp=slice(-20, 81, 20)
corners = tuple(
("diode_tt", *cs)
for cs in product(("logic_ss", "logic_ff"), ("pnp_s", "pnp_f"), ("rc_tt",))
)
vdds = (1.62, 1.71, 1.8, 1.89, 1.98)
print("Simulating corners...")
sweeprescorns1v8lvt_minpow = bg1v8lvt_minpow.tempsweep_corners(vdd=1.8, corners=corners, sweep=temp, gmin=1e-9)
print("Simulating vdds...")
sweepresvdds1v8lvt_minpow = bg1v8lvt_minpow.tempsweep_vdds(
vdds=vdds, corner=("diode_tt", "logic_tt", "pnp_t", "rc_tt"), sweep=temp, gmin=1e-11,
)
_plt.figure(figsize=(16,8))
_plt.subplot(1, 2, 1)
sweeprescorns1v8lvt_minpow.plot_Vref()
_plt.subplot(1, 2, 2)
sweepresvdds1v8lvt_minpow.plot_Vref()
Simulating corners... Simulating vdds...
bg1v8_minpow = sky130.Bandgap.compute_minpower(
nmos=prims.nfet_01v8, nmos_w=40.0, nmos_l_min=1.0, nmos_l_max=25.0, nmos_Vgs_min=0.5,
pmos=prims.pfet_01v8, pmos_w=40.0, pmos_l_min=0.5, pmos_l_max=25.0, pmos_Vgs_min=0.5,
pnp=prims.pnp_05v5_w3u40l3u40,
resistor=prims.poly_res,
vdd_min=1.62, vdd_max=1.98, corner=("logic_ff", "diode_tt", "pnp_t", "rc_tt"),
temp_min=-20, temp_max=80,
debug=True,
)
print(bg1v8_minpow.param_str)
Using temperature 30.0 First Vgs/Vds estimates: Vgs_n=0.51, Vds_n=0.51, Vgs_p=0.51, Vds_p=0.51 Computing first target current estimate... In_min=3.894792035346789e-07, Ip_min=5.99872814184803e-12 Computing voltage of bipolar for target current Vpnp1=0.3423826373650004 New Vgs/Vds estimates: Vgs_n=0.6388086813174998, Vds_n=0.6388086813174998, Vgs_p=0.6388086813174998, Vds_p=0.6388086813174998 Computing new target current estimate... In_min=3.91911543910237e-06, Ip_min=1.9072453131761202e-10 Computing transistor l WARNING: pmos too weak, taking minimum l nmos_l=25.0, nmos_w=40.0, pmos_l=0.5, pmos_w=40.0 Updating bipolar voltage... Ibr1=3.361079874856215e-08, Vpnp1=0.584, Vpnp2=0.566 Computing r1_height... Computing r2_height... r1_height=32.14, r2_height=1108.15 Updating parameters Vpnp1=0.682, Vpnp2=0.666 Ibr1=1.37e-06, rIbr2=1.103133533323928, rIbr3=1.0852604953163791 Vgs_n=0.712, Vgs_p=0.838, Vds_p1=0.226, Vds_n2=0.108 Optimizing r2 height Computing first temperature sweep 0: r2_height=1108.151, reldiff=0.18558, vref_mint=0.938, vref_maxt=1.151 1: r2_height=914.927, reldiff=0.14422, vref_mint=0.907, vref_maxt=1.059 2: r2_height=796.314, reldiff=0.11416, vref_mint=0.888, vref_maxt=1.002 3: r2_height=717.024, reldiff=0.09166, vref_mint=0.875, vref_maxt=0.963 4: r2_height=660.931, reldiff=0.07441, vref_mint=0.866, vref_maxt=0.935 5: r2_height=619.633, reldiff=0.06093, vref_mint=0.859, vref_maxt=0.915 6: r2_height=588.322, reldiff=0.05023, vref_mint=0.854, vref_maxt=0.899 7: r2_height=564.049, reldiff=0.04164, vref_mint=0.850, vref_maxt=0.887 8: r2_height=544.908, reldiff=0.03467, vref_mint=0.847, vref_maxt=0.877 9: r2_height=529.606, reldiff=0.02897, vref_mint=0.844, vref_maxt=0.869 10: r2_height=517.242, reldiff=0.02427, vref_mint=0.842, vref_maxt=0.863 11: r2_height=507.164, reldiff=0.02039, vref_mint=0.841, vref_maxt=0.858 12: r2_height=498.893, reldiff=0.01718, vref_mint=0.839, vref_maxt=0.854 13: r2_height=492.057, reldiff=0.01449, vref_mint=0.838, vref_maxt=0.850 14: r2_height=486.383, reldiff=0.01223, vref_mint=0.837, vref_maxt=0.847 15: r2_height=481.657, reldiff=0.01035, vref_mint=0.836, vref_maxt=0.845 16: r2_height=477.706, reldiff=0.00876, vref_mint=0.836, vref_maxt=0.843 17: r2_height=474.393, reldiff=0.00742, vref_mint=0.835, vref_maxt=0.841 18: r2_height=471.610, reldiff=0.00629, vref_mint=0.835, vref_maxt=0.840 19: r2_height=469.267, reldiff=0.00533, vref_mint=0.834, vref_maxt=0.839 20: r2_height=467.291, reldiff=0.00453, vref_mint=0.834, vref_maxt=0.838 21: r2_height=465.622, reldiff=0.00384, vref_mint=0.834, vref_maxt=0.837 22: r2_height=464.211, reldiff=0.00326, vref_mint=0.833, vref_maxt=0.836 23: r2_height=463.017, reldiff=0.00277, vref_mint=0.833, vref_maxt=0.836 24: r2_height=462.005, reldiff=0.00236, vref_mint=0.833, vref_maxt=0.835 25: r2_height=461.148, reldiff=0.00200, vref_mint=0.833, vref_maxt=0.835 26: r2_height=460.421, reldiff=0.00170, vref_mint=0.833, vref_maxt=0.834 27: r2_height=459.804, reldiff=0.00145, vref_mint=0.833, vref_maxt=0.834 28: r2_height=459.280, reldiff=0.00123, vref_mint=0.833, vref_maxt=0.834 29: r2_height=458.835, reldiff=0.00105, vref_mint=0.833, vref_maxt=0.833 30: r2_height=458.457, reldiff=0.00089, vref_mint=0.833, vref_maxt=0.833 Convergence criteria met Bandgap: - nmos: MOSFET(nfet_01v8) - nmos_l: 25.0 - nmos_w: 40.0 - nmos_mult: 1 - pmos: MOSFET(pfet_01v8) - pmos_l: 0.5 - pmos_w: 40.0 - pmos_mult: 1 - pnp: Bipolar(pnp_05v5_w3u40l3u40) - pnp_mult: 1 - pnp_ratio: 2 - resistor: Resistor(poly_res) - r1_height: 32.1399053221926 - r2_height: 458.45723029678993
sweepres1v8_minpow = bg1v8_minpow.tempsweep(
vdd=1.8, corner=("logic_tt", "diode_tt", "pnp_t", "rc_tt"), sweep=slice(-20, 81, 20),
)
_plt.figure(figsize=(16,12))
sweepres1v8_minpow.plot_internals()
temp=slice(-20, 81, 20)
corners = tuple(
("diode_tt", *cs)
for cs in product(("logic_ss", "logic_ff"), ("pnp_s", "pnp_f"), ("rc_tt",))
)
vdds = (1.62, 1.71, 1.8, 1.89, 1.98)
print("Simulating corners...")
sweeprescorns1v8_minpow = bg1v8_minpow.tempsweep_corners(
vdd=1.8, corners=corners, sweep=temp,
)
print("Simulating vdds...")
sweepresvdds1v8_minpow = bg1v8_minpow.tempsweep_vdds(
vdds=vdds, corner=("diode_tt", "logic_tt", "pnp_t", "rc_tt"),
)
_plt.figure(figsize=(16,8))
_plt.subplot(1, 2, 1)
sweeprescorns1v8_minpow.plot_Vref()
_plt.subplot(1, 2, 2)
sweepresvdds1v8_minpow.plot_Vref()
Simulating corners... Simulating vdds...
Note: Starting dynamic gmin stepping Trying gmin = 1.0000E-03 Note: One successful gmin step Trying gmin = 1.0000E-04 Note: One successful gmin step Trying gmin = 1.0000E-05 Note: One successful gmin step Trying gmin = 1.0000E-06 Note: One successful gmin step Trying gmin = 1.0000E-07 Note: One successful gmin step Trying gmin = 1.0000E-08 Note: One successful gmin step Trying gmin = 1.0000E-09 Note: One successful gmin step Trying gmin = 1.0000E-10 Note: One successful gmin step Trying gmin = 1.0000E-11 Note: One successful gmin step Trying gmin = 1.0000E-12 Note: One successful gmin step Trying gmin = 1.0000E-12 Note: One successful gmin step Note: Dynamic gmin stepping completed
bglib = _lbry.Library(
name="bglib", tech=sky130.tech, cktfab=sky130.cktfab, layoutfab=sky130.layoutfab,
)
bgcell3v3_minpow = bg3v3_minpow.convert2cell(
lib=bglib, cell_name="Bandgap3V3", pnp_cell=sky130.macrolib.cells["PNP_05v5_W3u40L3u40"],
)
bgcell1v8lvt_minpow = bg1v8lvt_minpow.convert2cell(
lib=bglib, cell_name="Bandgap1V8lvt", pnp_cell=sky130.macrolib.cells["PNP_05v5_W3u40L3u40"],
)
bgcell1v8_minpow = bg1v8_minpow.convert2cell(
lib=bglib, cell_name="Bandgap1V8", pnp_cell=sky130.macrolib.cells["PNP_05v5_W3u40L3u40"],
)
bglib.cells += (bgcell3v3_minpow, bgcell1v8lvt_minpow, bgcell1v8_minpow)
klay1v8db = _klexp.export2db(
obj=bglib, add_pin_label=True, gds_layers=sky130.gds_layers, cell_name=None, merge=True,
)
klay1v8db.write("bglib.gds", ksaveopts)
print("bglib.gds saved")
bglib.gds saved