2021-01-14 19:54:02 -08:00
|
|
|
import os
|
|
|
|
import ltspice
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
simulations = {
|
|
|
|
"longchannel": (5.0, """
|
|
|
|
.include modelcard/1um.pm
|
|
|
|
.param supply = {}
|
|
|
|
.param ll = 1u
|
|
|
|
"""),
|
|
|
|
"50nm": (1.0, """
|
|
|
|
.include ./modelcard/50nm.pm
|
|
|
|
.param supply = {}
|
|
|
|
.param ll=50nm
|
|
|
|
"""),
|
|
|
|
"16nmlp": (0.9, """
|
|
|
|
.include ./modelcard/PTM_LP/16nm.pm
|
|
|
|
.param supply = {}
|
|
|
|
.param ll=16nm
|
|
|
|
"""),
|
|
|
|
"16nmhp": (0.7, """
|
|
|
|
.include ./modelcard/PTM_HP/16nm.pm
|
|
|
|
.param supply = {}
|
|
|
|
.param ll=16nm
|
|
|
|
""")
|
|
|
|
}
|
|
|
|
|
|
|
|
moss = {
|
2021-01-27 15:04:23 -08:00
|
|
|
"pmos": "M1 2 1 vdd vdd pmos W={10*ll} L={ll}",
|
|
|
|
"nmos": "M1 2 1 0 0 nmos W={10*ll} L= ll"
|
2021-01-14 19:54:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
script_dc = """
|
|
|
|
vdd vdd 0 {supply}
|
|
|
|
Vgs 1 0 {supply}
|
|
|
|
Vds 2 0 {supply}
|
|
|
|
.op
|
|
|
|
.dc vds 0 {supply} {supply/100} Vgs 0 {supply} {supply/12}
|
|
|
|
*.step param lambda 50n 100n 10n
|
|
|
|
.meas ix(1:D) at vgs='supply'
|
|
|
|
.save I(vds)
|
|
|
|
.end
|
|
|
|
"""
|
|
|
|
|
|
|
|
script_ring = """
|
|
|
|
.subckt nn d g s ww=100nm
|
|
|
|
mnfet d g s gnd nmos L=ll w=ww
|
|
|
|
.ends
|
|
|
|
|
|
|
|
.subckt pp d g s ww=100nm
|
|
|
|
mpfet d g s vdd pmos L=ll w=ww
|
|
|
|
.ends
|
|
|
|
|
|
|
|
|
|
|
|
.subckt inv out inn size=100n beta=2
|
|
|
|
XPP out inn vdd pp ww='size*beta/(beta+1)'
|
|
|
|
XNN out inn 0 nn ww='size/(beta+1)'
|
|
|
|
.ENDS inv
|
|
|
|
|
|
|
|
.global gnd vdd
|
|
|
|
vdd vdd 0 'supply'
|
|
|
|
*Top level
|
|
|
|
.ic v(n0)=0
|
|
|
|
X0 n1 n0 inv size = '10*ll'
|
|
|
|
X1 n2 n1 inv size = '10*ll'
|
|
|
|
X2 n3 n2 inv size = '10*ll'
|
|
|
|
X3 n4 n3 inv size = '10*ll'
|
|
|
|
X4 n0 n4 inv size = '10*ll'
|
|
|
|
.tran 0.1p 3n
|
|
|
|
"""
|
|
|
|
|
|
|
|
def find_period(ident, script, model, mos, l):
|
|
|
|
time = l.get_time()
|
|
|
|
data = l.get_data('V(n0)')
|
|
|
|
largest = max(data)
|
|
|
|
smallest = min(data)
|
|
|
|
diff = largest-smallest
|
|
|
|
nearmax = [(i, dp) for (i, dp) in enumerate(data) if abs((largest-dp)/diff) < 0.01]
|
|
|
|
nearmin = [(i, dp) for (i, dp) in enumerate(data) if abs((smallest-dp)/diff) < 0.01]
|
|
|
|
|
|
|
|
group1, group2 = [], []
|
|
|
|
# Discard near-min values
|
|
|
|
while nearmin[0][0] < nearmax[0][0]:
|
|
|
|
nearmin.pop(0)
|
|
|
|
# Group first peak
|
|
|
|
while nearmax[0][0] < nearmin[0][0]:
|
|
|
|
i, dp = nearmax.pop(0)
|
|
|
|
group1.append((dp, time[i]))
|
|
|
|
# Discard near-min values
|
|
|
|
while nearmin[0][0] < nearmax[0][0]:
|
|
|
|
nearmin.pop(0)
|
|
|
|
# Group first peak
|
|
|
|
while nearmax[0][0] < nearmin[0][0]:
|
|
|
|
i, dp = nearmax.pop(0)
|
|
|
|
group2.append((dp, time[i]))
|
|
|
|
peak1 = max(group1)[1] * 1000000000000
|
|
|
|
peak2 = max(group2)[1] * 1000000000000
|
2021-01-27 15:04:23 -08:00
|
|
|
print(ident, peak2-peak1, 1/(peak2-peak1) * 1000)
|
|
|
|
|
|
|
|
|
|
|
|
def find_table(ident, script, model, mos, l):
|
|
|
|
vds = l.get_data('vds')
|
|
|
|
current_lo = l.get_data('I(vds)', 0)
|
|
|
|
current_hi = l.get_data('I(vds)', l.case_count - 1)
|
|
|
|
if mos == "nmos":
|
|
|
|
vgs1 = l.get_data('I(vds)', l.case_count - 2)
|
|
|
|
vgs2 = l.get_data('I(vds)', l.case_count - 1)
|
|
|
|
gm = (vgs2[-1]-vgs1[-1])/(simulations[model][0]/12)
|
|
|
|
vds = l.get_data('vds', l.case_count - 1)
|
|
|
|
ids = l.get_data('I(vds)', l.case_count - 1)
|
|
|
|
ro = (vds[-5] - vds[-1])/(ids[-5] - ids[-1])
|
|
|
|
print("{} & {:.2E} & {:.2E} & {:.4} & {:.2E} & {:.2E} & {:.2E} \\\\".format(
|
|
|
|
ident[3:].replace("_", " "), -gm, -ro, gm * ro, -current_lo[-1], -current_hi[-1], current_hi[-1]/current_lo[-1]))
|
|
|
|
if mos == "pmos":
|
|
|
|
vgs1 = l.get_data('I(vds)', 0)
|
|
|
|
vgs2 = l.get_data('I(vds)', 1)
|
|
|
|
gm = (vgs2[0]-vgs1[0])/(simulations[model][0]/12)
|
|
|
|
vds = l.get_data('vds', 0)
|
|
|
|
ids = l.get_data('I(vds)', 0)
|
|
|
|
ro = (vds[50] - vds[0])/(ids[50] - ids[0])
|
|
|
|
print("{} & {:.2E} & {:.2E} & {:.4} & {:.2E} & {:.2E} & {:.2E} \\\\".format(
|
|
|
|
ident[3:].replace("_", " "), -gm, -ro, gm * ro, current_hi[0], current_lo[0], current_lo[0]/current_hi[0]))
|
2021-01-14 19:54:02 -08:00
|
|
|
|
|
|
|
def find_onoff(ident, script, model, mos, l):
|
|
|
|
vds = l.get_data('vds')
|
|
|
|
current_lo = l.get_data('I(vds)', 0)
|
|
|
|
current_hi = l.get_data('I(vds)', l.case_count - 1)
|
2021-01-18 13:56:27 -08:00
|
|
|
if mos == "nmos":
|
2021-01-27 15:04:23 -08:00
|
|
|
print("node: {}\toff: {:.2E}\ton: {:.2E}\tratio: {:.2E}".format(ident, -current_lo[-1], -current_hi[-1], current_hi[-1]/current_lo[-1]))
|
2021-01-18 13:56:27 -08:00
|
|
|
if mos == "pmos":
|
2021-01-27 15:04:23 -08:00
|
|
|
print("node: {}\toff: {:.2E}\ton: {:.2E}\tratio: {:.2E}".format(ident, current_hi[0], current_lo[0], current_lo[0]/current_hi[0]))
|
2021-01-14 19:54:02 -08:00
|
|
|
|
|
|
|
def find_gm(ident, script, model, mos, l):
|
|
|
|
vds = l.get_data('vds')
|
|
|
|
gms = []
|
2021-01-18 13:56:27 -08:00
|
|
|
if mos == "nmos":
|
|
|
|
vgs1 = l.get_data('I(vds)', l.case_count - 2)
|
|
|
|
vgs2 = l.get_data('I(vds)', l.case_count - 1)
|
|
|
|
gm = (vgs2[-1]-vgs1[-1])/(simulations[model][0]/12)
|
|
|
|
print(ident, -gm * 1000, "mA/V")
|
|
|
|
vds = l.get_data('vds', l.case_count - 1)
|
|
|
|
ids = l.get_data('I(vds)', l.case_count - 1)
|
|
|
|
ro = (vds[-5] - vds[-1])/(ids[-5] - ids[-1])
|
|
|
|
print(ident, -ro, "O")
|
|
|
|
print(ident, gm * ro)
|
|
|
|
if mos == "pmos":
|
|
|
|
vgs1 = l.get_data('I(vds)', 0)
|
|
|
|
vgs2 = l.get_data('I(vds)', 1)
|
|
|
|
gm = (vgs2[0]-vgs1[0])/(simulations[model][0]/12)
|
|
|
|
print(ident, -gm * 1000, "mA/V")
|
|
|
|
vds = l.get_data('vds', 0)
|
|
|
|
ids = l.get_data('I(vds)', 0)
|
|
|
|
ro = (vds[50] - vds[0])/(ids[50] - ids[0])
|
|
|
|
print(ident, -ro, "O")
|
|
|
|
print(ident, gm * ro)
|
2021-01-14 19:54:02 -08:00
|
|
|
|
|
|
|
def render_current(ident, script, model, mos, l):
|
|
|
|
vds = l.get_data('vds')
|
|
|
|
for i in range(l.case_count):
|
|
|
|
curr = l.get_data('I(vds)', i)
|
|
|
|
plt.plot(vds, curr)
|
|
|
|
plt.savefig(ident + "_current.png", dpi=192)
|
|
|
|
plt.close()
|
|
|
|
|
2021-01-18 13:56:27 -08:00
|
|
|
def run_sim(script, model, mos, callback, file_type = "dc"):
|
|
|
|
scriptname = "{}_{}_{}".format(file_type, model, mos)
|
2021-01-14 19:54:02 -08:00
|
|
|
with open(scriptname + ".cir", "w") as f:
|
|
|
|
f.write(simulations[model][1].format(simulations[model][0]))
|
|
|
|
if mos is not None: f.write(moss[mos])
|
|
|
|
f.write(script)
|
2021-01-14 19:57:37 -08:00
|
|
|
if not os.path.exists(scriptname + ".raw"):
|
|
|
|
sp = os.popen("wine ~/.wine/drive_c/Program\ Files/LTC/LTspiceXVII/XVIIx64.exe -b {}.cir -ascii"
|
|
|
|
.format(scriptname))
|
|
|
|
sp.read()
|
2021-01-14 19:54:02 -08:00
|
|
|
l = ltspice.Ltspice(scriptname + ".raw")
|
|
|
|
l.parse()
|
|
|
|
|
|
|
|
callback(scriptname, script, model, mos, l)
|
|
|
|
|
2021-01-27 15:04:23 -08:00
|
|
|
print(" --- LaTeX --- ")
|
|
|
|
for node in simulations.keys():
|
|
|
|
for mos in moss.keys():
|
|
|
|
run_sim(script_dc, node, mos, find_table)
|
2021-01-14 19:54:02 -08:00
|
|
|
|
2021-01-18 13:56:27 -08:00
|
|
|
print(" --- gm, ro --- ")
|
|
|
|
for mos in moss.keys():
|
|
|
|
for node in simulations.keys():
|
|
|
|
run_sim(script_dc, node, mos, find_gm)
|
|
|
|
|
|
|
|
print(" --- onoff --- ")
|
2021-01-27 15:04:23 -08:00
|
|
|
for node in simulations.keys():
|
|
|
|
for mos in moss.keys():
|
2021-01-14 19:54:02 -08:00
|
|
|
run_sim(script_dc, node, mos, find_onoff)
|
|
|
|
|
2021-01-18 13:56:27 -08:00
|
|
|
print(" --- period --- ")
|
2021-01-14 19:54:02 -08:00
|
|
|
for node in simulations.keys():
|
2021-01-18 13:56:27 -08:00
|
|
|
run_sim(script_ring, node, None, find_period, "t")
|