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 = { "pmos": "M1 2 1 vdd vdd pmos W={10*ll} L={ll}", "nmos": "M1 2 1 0 0 nmos 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, 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])) 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) if mos == "nmos": print("node: {}\toff: {:.2E}\ton: {:.2E}\tratio: {:.2E}".format(ident, -current_lo[-1], -current_hi[-1], current_hi[-1]/current_lo[-1])) if mos == "pmos": print("node: {}\toff: {:.2E}\ton: {:.2E}\tratio: {:.2E}".format(ident, current_hi[0], current_lo[0], current_lo[0]/current_hi[0])) def find_gm(ident, script, model, mos, l): vds = l.get_data('vds') gms = [] 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) 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, file_type = "dc"): scriptname = "{}_{}_{}".format(file_type, 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) print(" --- LaTeX --- ") for node in simulations.keys(): for mos in moss.keys(): run_sim(script_dc, node, mos, find_table) print(" --- gm, ro --- ") for mos in moss.keys(): for node in simulations.keys(): run_sim(script_dc, node, mos, find_gm) print(" --- onoff --- ") for node in simulations.keys(): for mos in moss.keys(): run_sim(script_dc, node, mos, find_onoff) print(" --- period --- ") for node in simulations.keys(): run_sim(script_ring, node, None, find_period, "t")