Labs/Lab2/process.py

148 lines
3.9 KiB
Python

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 = {
"nmos": "M1 2 1 0 0 nmos W={10*ll} L= ll",
"pmos": "M1 2 1 vdd vdd pmos W={10*ll} L={ll}"
}
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
print(ident, peak2-peak1)
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)
print(ident, current_lo[-5], current_hi[-5])
def find_gm(ident, script, model, mos, l):
vds = l.get_data('vds')
gms = []
for i in range(l.case_count):
if i == 0: continue
max_diff = max(
[ abs(curr - prev) for (curr, prev) in
zip(l.get_data('I(vds)', i), l.get_data('I(vds)', i-1)) ])
gms.append(max_diff/(simulations[model][0]/12))
print(ident, max(gms) * 10000)
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()
def run_sim(script, model, mos, callback):
scriptname = "t_{}_{}".format(model, mos)
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)
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()
l = ltspice.Ltspice(scriptname + ".raw")
l.parse()
callback(scriptname, script, model, mos, l)
for mos in moss.keys():
for node in simulations.keys():
run_sim(script_dc, node, mos, find_onoff)
for node in simulations.keys():
run_sim(script_ring, node, None, find_period)