From c0b8eacd7b54e2d5cd3ad19a5a620b228cd42431 Mon Sep 17 00:00:00 2001 From: leochand101 Date: Thu, 8 Sep 2022 20:46:49 -0700 Subject: [PATCH 1/5] Added simple and an advanced (gmID) examples on sky130 Addressing possibly issue #327 --- examples/transistor/gmID.py | 225 ++++++++++++++++++ examples/transistor/gm_id_01v8.csv | 3 + examples/transistor/nmos-transistor_sky130.py | 100 ++++++++ 3 files changed, 328 insertions(+) create mode 100644 examples/transistor/gmID.py create mode 100644 examples/transistor/gm_id_01v8.csv create mode 100644 examples/transistor/nmos-transistor_sky130.py diff --git a/examples/transistor/gmID.py b/examples/transistor/gmID.py new file mode 100644 index 000000000..18f0dfbdd --- /dev/null +++ b/examples/transistor/gmID.py @@ -0,0 +1,225 @@ +# Adapted and modified from https://github.com/tclarke/sky130radio/tree/4eca853b7e4fd6bc0d69998f65c04f97e73bee84/utils +# Thanks to T Clarke https://github.com/tclarke + +import PySpice.Logging.Logging as Logging +from PySpice.Spice.Netlist import Circuit +from PySpice.Unit import * +import matplotlib.pyplot as plt +import numpy as np +import os.path +import csv +import h5py +import sys +import pandas as pd +import argparse +# Set Logging +logger = Logging.setup_logging() +# Set Defaults If you want to adapt for different technologies +_SKY130_defaults = { + 'w': 1, + 'l': 0.15, + 'nf': 1 +} +_plot_defaults = { + 'id_W_vs_gm_id': ['gm_id', 'id_W'], + 'ft_vs_gm_id': ['gm_id', 'ft'], + 'gm_gds_vs_gm_id': ['gm_id', 'gm_gds'], + 'gm_id_vs_vgg': ['v-sweep', 'gm_id'], +} + + +def create_test_circuit(libpath, fet_typ='sky130_fd_pr__nfet_01v8', w=1, l=0.15, nf=1, corner='tt'): + """ + Create the test gmID circuit and instantiate the liberty file in a particular corner + Fet W and L are instantiated + """ + ckt =Circuit('gm_id') + ckt.lib(libpath, section=corner) + + # create the circuit + ckt.V('gg', 1, ckt.gnd, 0@u_V) + ckt.V('dd', 2, ckt.gnd, 1.8@u_V) + ckt.X('M1', fet_typ, 2, 1, ckt.gnd, ckt.gnd, L=l, W=w, nf=1) + return ckt + + +def run_sim(c, iparam, w, l): + """ + Simulation Method which runs DC sim to get the values needed for gm-ID analysis + Parameters id, gm, gds and cgg + :param c: Circuit Object + :param iparam: Mosfet identifier string + :param w: + :return: Values of gm_id, ft, id_W, gm_gds, vgs, gm, id, cgg, gds + """ + sim = c.simulator() + sim.save_internal_parameters(iparam%'gm', iparam%'id', iparam%'gds', iparam%'cgg') + # run the dc simulation + an = sim.dc(Vgg=slice(0, 1.8, 0.01)) +# calculate needed values..need as_ndarray() since most of these have None as the unit and that causes an error + gm = an.internal_parameters[iparam%'gm'].as_ndarray() + id = an.internal_parameters[iparam%'id'].as_ndarray() + gm_id = gm / id + cgg = an.internal_parameters[iparam%'cgg'].as_ndarray() + ft = gm / cgg + id_W = id / w + gds = an.internal_parameters[iparam%'gds'].as_ndarray() + gm_gds = gm / gds + w_arr, l_arr = np.empty(len(gm)), np.empty(len(gm)) + for i in range(len(gm)): + w_arr[i] = w + l_arr[i] = l + gmid_dict = { + 'w': list(w_arr), + 'l': list(l_arr), + 'id_W': list(id_W), + 'gm_id': list(gm_id), + 'ft': list(ft), + 'gm_gds': list(gm_gds), + 'v-sweep': list(an.nodes['v-sweep']), + 'gm': list(gm), + 'id': list(id), + 'cgg': list(cgg), + 'gds': list(gds), + } + return gmid_dict +# #return id_W, gm_id, ft, gm_gds, an.nodes['v-sweep'], gm, id, cgg, gds + + +def init_plots(): + ''' + Plot the figures with the given + :return: + ''' + figs, plts = [], [] + for loopnum, plotname in enumerate(_plot_defaults.keys()): + figs.append(plt.figure()) + plts.append(figs[loopnum].subplots()) + figs[loopnum].suptitle(plotname) + plts[loopnum].set_xlabel(_plot_defaults[plotname][0]) + plts[loopnum].set_ylabel(_plot_defaults[plotname][1]) + return plts, figs +# #figs = [plt.figure(), plt.figure(), plt.figure(), plt.figure()] +# #plts = [f.subplots() for f in figs] +# #figs[0].suptitle('Id/W vs gm/Id') +# #plts[0].set_xlabel("gm/Id") +# #plts[0].set_ylabel("Id/W") +# #figs[1].suptitle('fT vs gm/Id') +# #plts[1].set_xlabel("gm/Id") +# #plts[1].set_ylabel("f_T") +# #figs[2].suptitle('gm/gds vs gm/Id') +# #plts[2].set_xlabel("gm/Id") +# #plts[2].set_ylabel("gm/gds") +# #figs[3].suptitle('gm/Id vs Vgg') +# #plts[3].set_xlabel("Vgg") +# #plts[3].set_ylabel("gm/Id") +# #return figs, plts + + +# def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): +def gen_plots(df_gmid, plts): + # plot some interesting things + for loopnum, plotname in enumerate(_plot_defaults.keys()): + for (w, l) in np.unique(df_gmid.index.values): + curr_x = df_gmid.loc[(w, l)][_plot_defaults[plotname][0]] + curr_y = df_gmid.loc[(w, l)][_plot_defaults[plotname][1]] + plts[loopnum].plot(curr_x, curr_y, label= f'W_{w} x L_{l}') +# #plts[0].plot(gm_id, id_W, label=f'W {fet_W} x L {fet_L}') +# #plts[1].plot(gm_id, ft, label=f'W {fet_W} x L {fet_L}') +# #plts[2].plot(gm_id, gm_gds, label=f'W {fet_W} x L {fet_L}') +# #plts[3].plot(vsweep, gm_id, label=f'W {fet_W} x L {fet_L}') + + +def read_bins(fname): + r = csv.reader(open(fname, 'r')) + return r + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--fet_types", required=True, help="Provide the FET Name list",nargs='+', default=[]) + parser.add_argument("--WL_csv", required=True, help="Provide the WL bin") + parser.add_argument("--plot_dir", required=False, help="Provide the WL bin", default="gmid_plots") + parser.add_argument("--hdf_db", required=True, help="hdf5 database", default="gmid_plots") + parser.add_argument("--SingleW", required=False, help="Provide Single W for run on the W", default=None) + parser.add_argument("--libpath", required=False, help="path to library", + default="/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice") + parser.add_argument("--corner", required=False, help="Corner to run the sims on", default='tt') + + + args = parser.parse_args() +# #if len(sys.argv) < 4: + # print(f'{sys.argv[0]} [width]') + # print(' is a template with 1 \%s which will contain the plot name. 4 are generated per LxW combo.') + # print('If [width] is specified, only W/L pairs for that width are processed.') + # sys.exit(0) + fet_types = args.fet_types + bins_fname = args.WL_csv + figname = args.plot_dir + only_W = args.SingleW + for fet_type in fet_types: + print(f'Simulating {fet_type} with bins {bins_fname}') + # Fet Identifier to pass + iparam = f'@m.xm1.m{fet_type}[%s]' + c = create_test_circuit(args.libpath, fet_type, 0.15, 1, args.corner) + bins = read_bins(bins_fname) + next(bins) +# #figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] +# #figs, plts = init_plots() +# #h5name = os.path.splitext(figname % 'data')[0] + '.h5' + h5name = f'{fet_type}.h5' + + out = h5py.File(h5name, "w") + bins_d = out.create_dataset('bins', (0, 2), maxshape=(None,2)) + gm_d = out.create_dataset('gm', (0, 0), maxshape=(None,None)) + id_d = out.create_dataset('id', (0, 0), maxshape=(None,None)) + cgg_d = out.create_dataset('cgg', (0, 0), maxshape=(None,None)) + gds_d = out.create_dataset('gds', (0, 0), maxshape=(None,None)) + vsweep_d = out.create_dataset('vsweep', (0,0), maxshape=(None,None)) + idx = 0 + # loop W and L + run_dict = {} + for dev, bin, fet_W, fet_L in bins: + fet_W, fet_L = float(fet_W), float(fet_L) + if only_W is not None and fet_W != only_W: + continue + print(f'{bin}: {dev} W {fet_W} x L {fet_L}') + # Update parameters in the loop before runing simulations + c.element('XM1').parameters['W'] = fet_W + c.element('XM1').parameters['L'] = fet_L + # Run Simulations +# #id_W, gm_id, ft, gm_gds, vsweep, gm, id, cgg, gds = run_sim(c, iparam, fet_W, fet_L) + curr_dict = run_sim(c, iparam, fet_W, fet_L) + for k, v in curr_dict.items(): + if k in run_dict.keys(): + run_dict[k] = run_dict[k]+curr_dict[k] + else: + run_dict[k] = v + gmid_df = pd.DataFrame.from_dict(run_dict) +# #if idx == 0: +# # gm_d.resize(len(gm_id), 1) +# # id_d.resize(len(id_W), 1) +# # cgg_d.resize(len(ft), 1) +# # gds_d.resize(len(gm_gds), 1) +# # vsweep_d.resize(len(vsweep), 1) +# #bins_d.resize(idx+1, 0) +# #gm_d.resize(idx+1, 0) +# #id_d.resize(idx+1, 0) +# #cgg_d.resize(idx+1, 0) +# #gds_d.resize(idx+1, 0) +# #vsweep_d.resize(idx+1, 0) +# #bins_d[idx,:] = [fet_W, fet_L] +# #gm_d[idx, :] = gm +# #id_d[idx, :] = id +# #cgg_d[idx, :] = cgg +# #gds_d[idx, :] = gds +# #vsweep_d[idx, :] = vsweep # should be the same for every row +# #idx += 1 + gmid_df.to_csv(f'{fet_type}.csv') + gmid_df.set_index(['w', 'l'], inplace=True) + plts, figs = init_plots() + gen_plots(gmid_df, plts) + for f, nm in zip(figs, _plot_defaults.keys()): + f.legend() + f.tight_layout() + f.savefig(f'{fet_type}_{nm}') \ No newline at end of file diff --git a/examples/transistor/gm_id_01v8.csv b/examples/transistor/gm_id_01v8.csv new file mode 100644 index 000000000..ea3dee98d --- /dev/null +++ b/examples/transistor/gm_id_01v8.csv @@ -0,0 +1,3 @@ +bin,dev, W, L +0,nfet1v8,1.0,1.0 +1,nfet1v8,1.0,0.5 diff --git a/examples/transistor/nmos-transistor_sky130.py b/examples/transistor/nmos-transistor_sky130.py new file mode 100644 index 000000000..47d63836d --- /dev/null +++ b/examples/transistor/nmos-transistor_sky130.py @@ -0,0 +1,100 @@ +#r# ===================== +#r# n-MOSFET Transistor +#r# ===================== + +#r# This example shows how to simulate the characteristic curves of an nmos transistor. + +#################################################################################################### +import os +import pdb + +import matplotlib.pyplot as plt +from pint import UnitRegistry +#################################################################################################### + +import PySpice.Logging.Logging as Logging +logger = Logging.setup_logging() +from PySpice.Spice.Netlist import Circuit, SubCircuit, SubCircuitFactory +#################################################################################################### + +from PySpice.Doc.ExampleTools import find_libraries +from PySpice.Probe.Plot import plot +from PySpice.Spice.Library import SpiceLibrary +from PySpice.Spice.Netlist import Circuit +from PySpice.Unit import * + +#################################################################################################### + +libraries_path = find_libraries() +library_sky130 = os.path.abspath("/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/") +#spice_library = SpiceLibrary(library_sky130, recurse=True) +#for i in spice_library.__dict__['_subcircuits'].keys(): +# print(i) +#################################################################################################### + +#Unit Registry +u = UnitRegistry() +class SkyNmosNfet1p8(SubCircuitFactory): + NAME = 'sky130_fd_pr__nfet_01v8 NMOS' + NODES = ('d', 'g', 's', 'b') + + def __init__(self, w=1*u.m, l=0.15*u.m, nf=1): + super().__init__() + self.nameid = 'nfet_01v8' + # Figure out the allowable ranges sot that assertions can be added to error our for wrong + self.W = w + self.L = l + self.nf = nf + w_sky, l_sky = self.transform_unit(1e6) + self.X(f'{self.nameid}_1', 'sky130_fd_pr__nfet_01v8' 'd', 'g', 's', 'b', w_sky, l_sky, self.nf) + + def transform_unit(self, mult: float): + """Transforming the unit to the model specific unit by applying a multiplier""" + w_trans = self.W*mult + L_trans = self.L*mult + return w_trans, L_trans +#r# We define a basic circuit to drive an nmos transistor using two voltage sources. +#r# The nmos transistor demonstrated in this example is a low-level device description. + +#?# TODO: Write the : circuit_macros('nmos_transistor.m4') + +circuit = Circuit('NMOS Transistor') +#circuit.include(spice_library['ptm65nm_nmos']) +#circuit.include(spice_library['sky130_fd_pr__nfet_01v8']) +#ngspice = NgSpiceShared.new_instance() +# Define the DC supply voltage value +Vdd = 1.1 +circuit.lib("/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice",section="tt") +# Instanciate circuit elements +Vgate = circuit.V('gate', 'gatenode', circuit.gnd, 0@u_V) +Vdrain = circuit.V('drain', 'vdd', circuit.gnd, u_V(Vdd)) +circuit.X(1, 'sky130_fd_pr__nfet_01v8', 'vdd', 'gatenode', circuit.gnd, circuit.gnd, W=4.8, L=0.15, nf=1) + +#print(circuit) +#exit() +#circuit.raw_spice = """ +#.title NMOS Transistor +#.lib /usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice tt +#Vgate gatenode 0 0V +#Vdrain vdd 0 1.1V +##X1 vdd gatenode 0 0 sky130_fd_pr__nfet_01v8 L=0.15 W=4.8 nf=1 +#""" +print(circuit) + +#r# We plot the characteristics :math:`Id = f(Vgs)` using a DC sweep simulation. +#print(circuit) +simulator = circuit.simulator(temperature=25, nominal_temperature=25) +analysis = simulator.dc(Vgate=slice(0, Vdd, .1)) + +figure, ax = plt.subplots(figsize=(20, 10)) +#print(analysis.parameters) +ax.plot(analysis['gatenode'], u_mA(-analysis.Vdrain)) +ax.legend('NMOS characteristic') +ax.grid() +ax.set_xlabel('Vgs [V]') +ax.set_ylabel('Id [mA]') + +plt.tight_layout() +plt.show() + +#f# save_figure('figure', 'transistor-nmos-plot.png') From c27b82fdf99c3c20a6386b55b399638be16d3e35 Mon Sep 17 00:00:00 2001 From: leochand101 Date: Fri, 9 Sep 2022 14:06:56 -0700 Subject: [PATCH 2/5] Updated Simple (examples/transistor/transistor_sky130.py) and advanced usage Addressing #327 --- examples/advanced-usages/gmID.py | 225 ++++++++++++++++++ examples/advanced-usages/gm_id_01v8.csv | 3 + examples/transistor/nmos-transistor_sky130.py | 138 +++++------ examples/transistor/transistor_sky130.py | 105 ++++++++ 4 files changed, 404 insertions(+), 67 deletions(-) create mode 100644 examples/advanced-usages/gmID.py create mode 100644 examples/advanced-usages/gm_id_01v8.csv create mode 100644 examples/transistor/transistor_sky130.py diff --git a/examples/advanced-usages/gmID.py b/examples/advanced-usages/gmID.py new file mode 100644 index 000000000..18f0dfbdd --- /dev/null +++ b/examples/advanced-usages/gmID.py @@ -0,0 +1,225 @@ +# Adapted and modified from https://github.com/tclarke/sky130radio/tree/4eca853b7e4fd6bc0d69998f65c04f97e73bee84/utils +# Thanks to T Clarke https://github.com/tclarke + +import PySpice.Logging.Logging as Logging +from PySpice.Spice.Netlist import Circuit +from PySpice.Unit import * +import matplotlib.pyplot as plt +import numpy as np +import os.path +import csv +import h5py +import sys +import pandas as pd +import argparse +# Set Logging +logger = Logging.setup_logging() +# Set Defaults If you want to adapt for different technologies +_SKY130_defaults = { + 'w': 1, + 'l': 0.15, + 'nf': 1 +} +_plot_defaults = { + 'id_W_vs_gm_id': ['gm_id', 'id_W'], + 'ft_vs_gm_id': ['gm_id', 'ft'], + 'gm_gds_vs_gm_id': ['gm_id', 'gm_gds'], + 'gm_id_vs_vgg': ['v-sweep', 'gm_id'], +} + + +def create_test_circuit(libpath, fet_typ='sky130_fd_pr__nfet_01v8', w=1, l=0.15, nf=1, corner='tt'): + """ + Create the test gmID circuit and instantiate the liberty file in a particular corner + Fet W and L are instantiated + """ + ckt =Circuit('gm_id') + ckt.lib(libpath, section=corner) + + # create the circuit + ckt.V('gg', 1, ckt.gnd, 0@u_V) + ckt.V('dd', 2, ckt.gnd, 1.8@u_V) + ckt.X('M1', fet_typ, 2, 1, ckt.gnd, ckt.gnd, L=l, W=w, nf=1) + return ckt + + +def run_sim(c, iparam, w, l): + """ + Simulation Method which runs DC sim to get the values needed for gm-ID analysis + Parameters id, gm, gds and cgg + :param c: Circuit Object + :param iparam: Mosfet identifier string + :param w: + :return: Values of gm_id, ft, id_W, gm_gds, vgs, gm, id, cgg, gds + """ + sim = c.simulator() + sim.save_internal_parameters(iparam%'gm', iparam%'id', iparam%'gds', iparam%'cgg') + # run the dc simulation + an = sim.dc(Vgg=slice(0, 1.8, 0.01)) +# calculate needed values..need as_ndarray() since most of these have None as the unit and that causes an error + gm = an.internal_parameters[iparam%'gm'].as_ndarray() + id = an.internal_parameters[iparam%'id'].as_ndarray() + gm_id = gm / id + cgg = an.internal_parameters[iparam%'cgg'].as_ndarray() + ft = gm / cgg + id_W = id / w + gds = an.internal_parameters[iparam%'gds'].as_ndarray() + gm_gds = gm / gds + w_arr, l_arr = np.empty(len(gm)), np.empty(len(gm)) + for i in range(len(gm)): + w_arr[i] = w + l_arr[i] = l + gmid_dict = { + 'w': list(w_arr), + 'l': list(l_arr), + 'id_W': list(id_W), + 'gm_id': list(gm_id), + 'ft': list(ft), + 'gm_gds': list(gm_gds), + 'v-sweep': list(an.nodes['v-sweep']), + 'gm': list(gm), + 'id': list(id), + 'cgg': list(cgg), + 'gds': list(gds), + } + return gmid_dict +# #return id_W, gm_id, ft, gm_gds, an.nodes['v-sweep'], gm, id, cgg, gds + + +def init_plots(): + ''' + Plot the figures with the given + :return: + ''' + figs, plts = [], [] + for loopnum, plotname in enumerate(_plot_defaults.keys()): + figs.append(plt.figure()) + plts.append(figs[loopnum].subplots()) + figs[loopnum].suptitle(plotname) + plts[loopnum].set_xlabel(_plot_defaults[plotname][0]) + plts[loopnum].set_ylabel(_plot_defaults[plotname][1]) + return plts, figs +# #figs = [plt.figure(), plt.figure(), plt.figure(), plt.figure()] +# #plts = [f.subplots() for f in figs] +# #figs[0].suptitle('Id/W vs gm/Id') +# #plts[0].set_xlabel("gm/Id") +# #plts[0].set_ylabel("Id/W") +# #figs[1].suptitle('fT vs gm/Id') +# #plts[1].set_xlabel("gm/Id") +# #plts[1].set_ylabel("f_T") +# #figs[2].suptitle('gm/gds vs gm/Id') +# #plts[2].set_xlabel("gm/Id") +# #plts[2].set_ylabel("gm/gds") +# #figs[3].suptitle('gm/Id vs Vgg') +# #plts[3].set_xlabel("Vgg") +# #plts[3].set_ylabel("gm/Id") +# #return figs, plts + + +# def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): +def gen_plots(df_gmid, plts): + # plot some interesting things + for loopnum, plotname in enumerate(_plot_defaults.keys()): + for (w, l) in np.unique(df_gmid.index.values): + curr_x = df_gmid.loc[(w, l)][_plot_defaults[plotname][0]] + curr_y = df_gmid.loc[(w, l)][_plot_defaults[plotname][1]] + plts[loopnum].plot(curr_x, curr_y, label= f'W_{w} x L_{l}') +# #plts[0].plot(gm_id, id_W, label=f'W {fet_W} x L {fet_L}') +# #plts[1].plot(gm_id, ft, label=f'W {fet_W} x L {fet_L}') +# #plts[2].plot(gm_id, gm_gds, label=f'W {fet_W} x L {fet_L}') +# #plts[3].plot(vsweep, gm_id, label=f'W {fet_W} x L {fet_L}') + + +def read_bins(fname): + r = csv.reader(open(fname, 'r')) + return r + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--fet_types", required=True, help="Provide the FET Name list",nargs='+', default=[]) + parser.add_argument("--WL_csv", required=True, help="Provide the WL bin") + parser.add_argument("--plot_dir", required=False, help="Provide the WL bin", default="gmid_plots") + parser.add_argument("--hdf_db", required=True, help="hdf5 database", default="gmid_plots") + parser.add_argument("--SingleW", required=False, help="Provide Single W for run on the W", default=None) + parser.add_argument("--libpath", required=False, help="path to library", + default="/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice") + parser.add_argument("--corner", required=False, help="Corner to run the sims on", default='tt') + + + args = parser.parse_args() +# #if len(sys.argv) < 4: + # print(f'{sys.argv[0]} [width]') + # print(' is a template with 1 \%s which will contain the plot name. 4 are generated per LxW combo.') + # print('If [width] is specified, only W/L pairs for that width are processed.') + # sys.exit(0) + fet_types = args.fet_types + bins_fname = args.WL_csv + figname = args.plot_dir + only_W = args.SingleW + for fet_type in fet_types: + print(f'Simulating {fet_type} with bins {bins_fname}') + # Fet Identifier to pass + iparam = f'@m.xm1.m{fet_type}[%s]' + c = create_test_circuit(args.libpath, fet_type, 0.15, 1, args.corner) + bins = read_bins(bins_fname) + next(bins) +# #figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] +# #figs, plts = init_plots() +# #h5name = os.path.splitext(figname % 'data')[0] + '.h5' + h5name = f'{fet_type}.h5' + + out = h5py.File(h5name, "w") + bins_d = out.create_dataset('bins', (0, 2), maxshape=(None,2)) + gm_d = out.create_dataset('gm', (0, 0), maxshape=(None,None)) + id_d = out.create_dataset('id', (0, 0), maxshape=(None,None)) + cgg_d = out.create_dataset('cgg', (0, 0), maxshape=(None,None)) + gds_d = out.create_dataset('gds', (0, 0), maxshape=(None,None)) + vsweep_d = out.create_dataset('vsweep', (0,0), maxshape=(None,None)) + idx = 0 + # loop W and L + run_dict = {} + for dev, bin, fet_W, fet_L in bins: + fet_W, fet_L = float(fet_W), float(fet_L) + if only_W is not None and fet_W != only_W: + continue + print(f'{bin}: {dev} W {fet_W} x L {fet_L}') + # Update parameters in the loop before runing simulations + c.element('XM1').parameters['W'] = fet_W + c.element('XM1').parameters['L'] = fet_L + # Run Simulations +# #id_W, gm_id, ft, gm_gds, vsweep, gm, id, cgg, gds = run_sim(c, iparam, fet_W, fet_L) + curr_dict = run_sim(c, iparam, fet_W, fet_L) + for k, v in curr_dict.items(): + if k in run_dict.keys(): + run_dict[k] = run_dict[k]+curr_dict[k] + else: + run_dict[k] = v + gmid_df = pd.DataFrame.from_dict(run_dict) +# #if idx == 0: +# # gm_d.resize(len(gm_id), 1) +# # id_d.resize(len(id_W), 1) +# # cgg_d.resize(len(ft), 1) +# # gds_d.resize(len(gm_gds), 1) +# # vsweep_d.resize(len(vsweep), 1) +# #bins_d.resize(idx+1, 0) +# #gm_d.resize(idx+1, 0) +# #id_d.resize(idx+1, 0) +# #cgg_d.resize(idx+1, 0) +# #gds_d.resize(idx+1, 0) +# #vsweep_d.resize(idx+1, 0) +# #bins_d[idx,:] = [fet_W, fet_L] +# #gm_d[idx, :] = gm +# #id_d[idx, :] = id +# #cgg_d[idx, :] = cgg +# #gds_d[idx, :] = gds +# #vsweep_d[idx, :] = vsweep # should be the same for every row +# #idx += 1 + gmid_df.to_csv(f'{fet_type}.csv') + gmid_df.set_index(['w', 'l'], inplace=True) + plts, figs = init_plots() + gen_plots(gmid_df, plts) + for f, nm in zip(figs, _plot_defaults.keys()): + f.legend() + f.tight_layout() + f.savefig(f'{fet_type}_{nm}') \ No newline at end of file diff --git a/examples/advanced-usages/gm_id_01v8.csv b/examples/advanced-usages/gm_id_01v8.csv new file mode 100644 index 000000000..ea3dee98d --- /dev/null +++ b/examples/advanced-usages/gm_id_01v8.csv @@ -0,0 +1,3 @@ +bin,dev, W, L +0,nfet1v8,1.0,1.0 +1,nfet1v8,1.0,0.5 diff --git a/examples/transistor/nmos-transistor_sky130.py b/examples/transistor/nmos-transistor_sky130.py index 47d63836d..53e86b76e 100644 --- a/examples/transistor/nmos-transistor_sky130.py +++ b/examples/transistor/nmos-transistor_sky130.py @@ -1,15 +1,17 @@ #r# ===================== -#r# n-MOSFET Transistor +#r# Skywater 130 n-MOSFET Transistor #r# ===================== -#r# This example shows how to simulate the characteristic curves of an nmos transistor. +#r# This example shows how to simulate the characteristic curves of an nmos transistor for skywater130 Technology. #################################################################################################### import os +import argparse import pdb - +import numpy as np import matplotlib.pyplot as plt from pint import UnitRegistry +import re #################################################################################################### import PySpice.Logging.Logging as Logging @@ -23,78 +25,80 @@ from PySpice.Spice.Netlist import Circuit from PySpice.Unit import * + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--fet_types", required=True, help="Provide the FET Name list",nargs='+', default=[]) + parser.add_argument("--libpath", required=False, help="path to library", + default="/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice") + parser.add_argument("--corner", required=False, help="Corner to run the sims on", default='tt') + args = parser.parse_args() + + #################################################################################################### libraries_path = find_libraries() -library_sky130 = os.path.abspath("/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/") -#spice_library = SpiceLibrary(library_sky130, recurse=True) -#for i in spice_library.__dict__['_subcircuits'].keys(): -# print(i) +library_sky130 = os.path.abspath(args.libpath) #################################################################################################### #Unit Registry u = UnitRegistry() -class SkyNmosNfet1p8(SubCircuitFactory): - NAME = 'sky130_fd_pr__nfet_01v8 NMOS' - NODES = ('d', 'g', 's', 'b') - - def __init__(self, w=1*u.m, l=0.15*u.m, nf=1): - super().__init__() - self.nameid = 'nfet_01v8' - # Figure out the allowable ranges sot that assertions can be added to error our for wrong - self.W = w - self.L = l - self.nf = nf - w_sky, l_sky = self.transform_unit(1e6) - self.X(f'{self.nameid}_1', 'sky130_fd_pr__nfet_01v8' 'd', 'g', 's', 'b', w_sky, l_sky, self.nf) - - def transform_unit(self, mult: float): - """Transforming the unit to the model specific unit by applying a multiplier""" - w_trans = self.W*mult - L_trans = self.L*mult - return w_trans, L_trans -#r# We define a basic circuit to drive an nmos transistor using two voltage sources. -#r# The nmos transistor demonstrated in this example is a low-level device description. - -#?# TODO: Write the : circuit_macros('nmos_transistor.m4') + +#?# TODO: Extend to search for all possible allowable W and L ranges circuit = Circuit('NMOS Transistor') -#circuit.include(spice_library['ptm65nm_nmos']) -#circuit.include(spice_library['sky130_fd_pr__nfet_01v8']) -#ngspice = NgSpiceShared.new_instance() # Define the DC supply voltage value -Vdd = 1.1 -circuit.lib("/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice",section="tt") -# Instanciate circuit elements +Vdd = 1.2 +circuit.lib(args.libpath, args.corner) +# Instantiate circuit elements and transistor Vgate = circuit.V('gate', 'gatenode', circuit.gnd, 0@u_V) -Vdrain = circuit.V('drain', 'vdd', circuit.gnd, u_V(Vdd)) -circuit.X(1, 'sky130_fd_pr__nfet_01v8', 'vdd', 'gatenode', circuit.gnd, circuit.gnd, W=4.8, L=0.15, nf=1) - -#print(circuit) -#exit() -#circuit.raw_spice = """ -#.title NMOS Transistor -#.lib /usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice tt -#Vgate gatenode 0 0V -#Vdrain vdd 0 1.1V -##X1 vdd gatenode 0 0 sky130_fd_pr__nfet_01v8 L=0.15 W=4.8 nf=1 -#""" -print(circuit) - -#r# We plot the characteristics :math:`Id = f(Vgs)` using a DC sweep simulation. -#print(circuit) -simulator = circuit.simulator(temperature=25, nominal_temperature=25) -analysis = simulator.dc(Vgate=slice(0, Vdd, .1)) - -figure, ax = plt.subplots(figsize=(20, 10)) -#print(analysis.parameters) -ax.plot(analysis['gatenode'], u_mA(-analysis.Vdrain)) -ax.legend('NMOS characteristic') -ax.grid() -ax.set_xlabel('Vgs [V]') -ax.set_ylabel('Id [mA]') - -plt.tight_layout() -plt.show() - -#f# save_figure('figure', 'transistor-nmos-plot.png') +Vdrain = circuit.V('drain', 'vdrain', circuit.gnd, u_V(Vdd)) +Vvdd = circuit.V('vdd', 'vdd', circuit.gnd, u_V(Vdd)) + +gate_range = np.arange(0, Vdd+0.1, .1) +drain_range = np.arange(0.3, Vdd+0.1, .1) + +for num, fet in enumerate(args.fet_types): + if re.search("nfet", fet): + circuit.X(num, fet, 'vdrain', 'gatenode', circuit.gnd, circuit.gnd, W=4.8, L=0.15, nf=1) + elif re.search("pfet", fet): + circuit.X(num, fet, 'vdrain', 'gatenode', 'vdd', 'vdd', W=4.8, L=0.15, nf=1) + + # Raw Spice Equivalent + #circuit.raw_spice = """ + #.title NMOS Transistor + #.lib /usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice tt + #Vgate gatenode 0 0V + #Vdrain vdrain 0 1.2V + #Vvdd vdd 0 1.2V + #X0 vdrain gatenode 0 0 sky130_fd_pr__nfet_01v8 L=0.15 W=4.8 nf=1 + #X1 vdrain gatenode vdd vdd sky130_fd_pr__pfet_01v8 L=0.15 W=4.8 nf=1 + #""" + +# Simulation and Plotting + simulator = circuit.simulator(temperature=25, nominal_temperature=25) + # simulator = circuit_raw_spice.simulator(temperature=25, nominal_temperature=25) + # Passing Slices to the voltages are non-intuitive, need to dig deeper, cant pass ndarrays # TODO Can we pass lists ? + if re.search("nfet", fet): + analysis = simulator.dc(Vgate=slice(0, Vdd, 0.1), Vdrain=slice(0.3, Vdd, 0.1)) + elif re.search("pfet", fet): + analysis = simulator.dc(Vgate=slice(Vdd, 0, -0.1), Vdrain=slice(Vdd-0.3, 0.0, -0.1)) + figure, ax = plt.subplots(figsize=(20, 10)) + # Plotting by slicing the sweep elements on Vsrc2 or Vdrain, Need a better way to access both dimensions + # Ideally the simulation object saves the object in different indexed arrays for vsrc2 + for num, loopvar in enumerate(range(0, len(drain_range))): + if re.search("nfet", fet): + plt.plot(analysis['gatenode'][num*len(gate_range):(num*len(gate_range) + len(gate_range))], + u_mA(-analysis._branches['vdrain'])[num*len(gate_range):(num*len(gate_range) + len(gate_range))], + label=f'vds={drain_range[loopvar]:.2f}') + elif re.search("pfet", fet): + plt.plot(analysis['gatenode'][num*len(gate_range):(num*len(gate_range) + len(gate_range))], + u_mA(analysis._branches['vdrain'])[num*len(gate_range):(num*len(gate_range) + len(gate_range))], + label=f'vds={drain_range[loopvar]:.2f}') + ax.set_xlabel('Vgs [V]') + ax.set_ylabel('Id [mA]') + plt.title(label=f'{fet} id vgs for vds') + plt.legend() + plt.tight_layout() + plt.savefig(f'{fet}_igvgs.png') + #plt.show() \ No newline at end of file diff --git a/examples/transistor/transistor_sky130.py b/examples/transistor/transistor_sky130.py new file mode 100644 index 000000000..4ded98528 --- /dev/null +++ b/examples/transistor/transistor_sky130.py @@ -0,0 +1,105 @@ +#r# ===================== +#r# Skywater 130 n-MOSFET Transistor +#r# ===================== + +#r# This example shows how to simulate the characteristic curves of an nmos transistor for skywater130 Technology. + +#################################################################################################### +import os +import argparse +import pdb +import numpy as np +import matplotlib.pyplot as plt +from pint import UnitRegistry +import re +#################################################################################################### + +import PySpice.Logging.Logging as Logging +logger = Logging.setup_logging() +from PySpice.Spice.Netlist import Circuit, SubCircuit, SubCircuitFactory +#################################################################################################### + +from PySpice.Doc.ExampleTools import find_libraries +from PySpice.Probe.Plot import plot +from PySpice.Spice.Library import SpiceLibrary +from PySpice.Spice.Netlist import Circuit +from PySpice.Unit import * + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Example python3.8 ./examples/transistor/nmos-transistor_sky130.py" + " --fet_types sky130_fd_pr__nfet_01v8 sky130_fd_pr__pfet_01v8") + parser.add_argument("--fet_types", required=True, help="Provide the FET Name list",nargs='+', default=[]) + parser.add_argument("--libpath", required=False, help="path to library", + default="/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice") + parser.add_argument("--corner", required=False, help="Corner to run the sims on", default='tt') + args = parser.parse_args() + + +#################################################################################################### + +libraries_path = find_libraries() +library_sky130 = os.path.abspath(args.libpath) +#################################################################################################### + +#Unit Registry +u = UnitRegistry() + +#?# TODO: Extend to search for all possible allowable W and L ranges + +circuit = Circuit('NMOS Transistor') +# Define the DC supply voltage value +Vdd = 1.2 +circuit.lib(args.libpath, args.corner) +# Instantiate circuit elements and transistor +Vgate = circuit.V('gate', 'gatenode', circuit.gnd, 0@u_V) +Vdrain = circuit.V('drain', 'vdrain', circuit.gnd, u_V(Vdd)) +Vvdd = circuit.V('vdd', 'vdd', circuit.gnd, u_V(Vdd)) + +gate_range = np.arange(0, Vdd+0.1, .1) +drain_range = np.arange(0.3, Vdd+0.1, .1) + +for num, fet in enumerate(args.fet_types): + if re.search("nfet", fet): + circuit.X(num, fet, 'vdrain', 'gatenode', circuit.gnd, circuit.gnd, W=4.8, L=0.15, nf=1) + elif re.search("pfet", fet): + circuit.X(num, fet, 'vdrain', 'gatenode', 'vdd', 'vdd', W=4.8, L=0.15, nf=1) + + # Raw Spice Equivalent + #circuit.raw_spice = """ + #.title NMOS Transistor + #.lib /usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice tt + #Vgate gatenode 0 0V + #Vdrain vdrain 0 1.2V + #Vvdd vdd 0 1.2V + #X0 vdrain gatenode 0 0 sky130_fd_pr__nfet_01v8 L=0.15 W=4.8 nf=1 + #X1 vdrain gatenode vdd vdd sky130_fd_pr__pfet_01v8 L=0.15 W=4.8 nf=1 + #""" + +# Simulation and Plotting + simulator = circuit.simulator(temperature=25, nominal_temperature=25) + # simulator = circuit_raw_spice.simulator(temperature=25, nominal_temperature=25) + # Passing Slices to the voltages are non-intuitive, need to dig deeper, cant pass ndarrays # TODO Can we pass lists ? + if re.search("nfet", fet): + analysis = simulator.dc(Vgate=slice(0, Vdd, 0.1), Vdrain=slice(0.3, Vdd, 0.1)) + elif re.search("pfet", fet): + analysis = simulator.dc(Vgate=slice(Vdd, 0, -0.1), Vdrain=slice(Vdd-0.3, 0.0, -0.1)) + figure, ax = plt.subplots(figsize=(20, 10)) + # Plotting by slicing the sweep elements on Vsrc2 or Vdrain, Need a better way to access both dimensions + # Ideally the simulation object saves the object in different indexed arrays for vsrc2 + for num, loopvar in enumerate(range(0, len(drain_range))): + if re.search("nfet", fet): + plt.plot(analysis['gatenode'][num*len(gate_range):(num*len(gate_range) + len(gate_range))], + u_mA(-analysis._branches['vdrain'])[num*len(gate_range):(num*len(gate_range) + len(gate_range))], + label=f'vds={drain_range[loopvar]:.2f}') + elif re.search("pfet", fet): + plt.plot(analysis['gatenode'][num*len(gate_range):(num*len(gate_range) + len(gate_range))], + u_mA(analysis._branches['vdrain'])[num*len(gate_range):(num*len(gate_range) + len(gate_range))], + label=f'vds={drain_range[loopvar]:.2f}') + ax.set_xlabel('Vgs [V]') + ax.set_ylabel('Id [mA]') + plt.title(label=f'{fet} id vgs for vds') + plt.legend() + plt.tight_layout() + plt.savefig(f'{fet}_igvgs.png') + #plt.show() \ No newline at end of file From 1df1288f13a163a758bae697e474426e8a592aaa Mon Sep 17 00:00:00 2001 From: leochand101 Date: Fri, 9 Sep 2022 14:10:41 -0700 Subject: [PATCH 3/5] Updated Simple (examples/transistor/transistor_sky130.py) and advanced usage Addressing #327 --- examples/transistor/nmos-transistor_sky130.py | 104 ------------------ 1 file changed, 104 deletions(-) delete mode 100644 examples/transistor/nmos-transistor_sky130.py diff --git a/examples/transistor/nmos-transistor_sky130.py b/examples/transistor/nmos-transistor_sky130.py deleted file mode 100644 index 53e86b76e..000000000 --- a/examples/transistor/nmos-transistor_sky130.py +++ /dev/null @@ -1,104 +0,0 @@ -#r# ===================== -#r# Skywater 130 n-MOSFET Transistor -#r# ===================== - -#r# This example shows how to simulate the characteristic curves of an nmos transistor for skywater130 Technology. - -#################################################################################################### -import os -import argparse -import pdb -import numpy as np -import matplotlib.pyplot as plt -from pint import UnitRegistry -import re -#################################################################################################### - -import PySpice.Logging.Logging as Logging -logger = Logging.setup_logging() -from PySpice.Spice.Netlist import Circuit, SubCircuit, SubCircuitFactory -#################################################################################################### - -from PySpice.Doc.ExampleTools import find_libraries -from PySpice.Probe.Plot import plot -from PySpice.Spice.Library import SpiceLibrary -from PySpice.Spice.Netlist import Circuit -from PySpice.Unit import * - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("--fet_types", required=True, help="Provide the FET Name list",nargs='+', default=[]) - parser.add_argument("--libpath", required=False, help="path to library", - default="/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice") - parser.add_argument("--corner", required=False, help="Corner to run the sims on", default='tt') - args = parser.parse_args() - - -#################################################################################################### - -libraries_path = find_libraries() -library_sky130 = os.path.abspath(args.libpath) -#################################################################################################### - -#Unit Registry -u = UnitRegistry() - -#?# TODO: Extend to search for all possible allowable W and L ranges - -circuit = Circuit('NMOS Transistor') -# Define the DC supply voltage value -Vdd = 1.2 -circuit.lib(args.libpath, args.corner) -# Instantiate circuit elements and transistor -Vgate = circuit.V('gate', 'gatenode', circuit.gnd, 0@u_V) -Vdrain = circuit.V('drain', 'vdrain', circuit.gnd, u_V(Vdd)) -Vvdd = circuit.V('vdd', 'vdd', circuit.gnd, u_V(Vdd)) - -gate_range = np.arange(0, Vdd+0.1, .1) -drain_range = np.arange(0.3, Vdd+0.1, .1) - -for num, fet in enumerate(args.fet_types): - if re.search("nfet", fet): - circuit.X(num, fet, 'vdrain', 'gatenode', circuit.gnd, circuit.gnd, W=4.8, L=0.15, nf=1) - elif re.search("pfet", fet): - circuit.X(num, fet, 'vdrain', 'gatenode', 'vdd', 'vdd', W=4.8, L=0.15, nf=1) - - # Raw Spice Equivalent - #circuit.raw_spice = """ - #.title NMOS Transistor - #.lib /usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice tt - #Vgate gatenode 0 0V - #Vdrain vdrain 0 1.2V - #Vvdd vdd 0 1.2V - #X0 vdrain gatenode 0 0 sky130_fd_pr__nfet_01v8 L=0.15 W=4.8 nf=1 - #X1 vdrain gatenode vdd vdd sky130_fd_pr__pfet_01v8 L=0.15 W=4.8 nf=1 - #""" - -# Simulation and Plotting - simulator = circuit.simulator(temperature=25, nominal_temperature=25) - # simulator = circuit_raw_spice.simulator(temperature=25, nominal_temperature=25) - # Passing Slices to the voltages are non-intuitive, need to dig deeper, cant pass ndarrays # TODO Can we pass lists ? - if re.search("nfet", fet): - analysis = simulator.dc(Vgate=slice(0, Vdd, 0.1), Vdrain=slice(0.3, Vdd, 0.1)) - elif re.search("pfet", fet): - analysis = simulator.dc(Vgate=slice(Vdd, 0, -0.1), Vdrain=slice(Vdd-0.3, 0.0, -0.1)) - figure, ax = plt.subplots(figsize=(20, 10)) - # Plotting by slicing the sweep elements on Vsrc2 or Vdrain, Need a better way to access both dimensions - # Ideally the simulation object saves the object in different indexed arrays for vsrc2 - for num, loopvar in enumerate(range(0, len(drain_range))): - if re.search("nfet", fet): - plt.plot(analysis['gatenode'][num*len(gate_range):(num*len(gate_range) + len(gate_range))], - u_mA(-analysis._branches['vdrain'])[num*len(gate_range):(num*len(gate_range) + len(gate_range))], - label=f'vds={drain_range[loopvar]:.2f}') - elif re.search("pfet", fet): - plt.plot(analysis['gatenode'][num*len(gate_range):(num*len(gate_range) + len(gate_range))], - u_mA(analysis._branches['vdrain'])[num*len(gate_range):(num*len(gate_range) + len(gate_range))], - label=f'vds={drain_range[loopvar]:.2f}') - ax.set_xlabel('Vgs [V]') - ax.set_ylabel('Id [mA]') - plt.title(label=f'{fet} id vgs for vds') - plt.legend() - plt.tight_layout() - plt.savefig(f'{fet}_igvgs.png') - #plt.show() \ No newline at end of file From d00a59c68663c1ce1743ee675ad3416177a4a32a Mon Sep 17 00:00:00 2001 From: leochand101 Date: Fri, 9 Sep 2022 14:12:20 -0700 Subject: [PATCH 4/5] Updated Simple (examples/transistor/transistor_sky130.py) and advanced usage Addressing #327 --- examples/transistor/gmID.py | 225 ----------------------------- examples/transistor/gm_id_01v8.csv | 3 - 2 files changed, 228 deletions(-) delete mode 100644 examples/transistor/gmID.py delete mode 100644 examples/transistor/gm_id_01v8.csv diff --git a/examples/transistor/gmID.py b/examples/transistor/gmID.py deleted file mode 100644 index 18f0dfbdd..000000000 --- a/examples/transistor/gmID.py +++ /dev/null @@ -1,225 +0,0 @@ -# Adapted and modified from https://github.com/tclarke/sky130radio/tree/4eca853b7e4fd6bc0d69998f65c04f97e73bee84/utils -# Thanks to T Clarke https://github.com/tclarke - -import PySpice.Logging.Logging as Logging -from PySpice.Spice.Netlist import Circuit -from PySpice.Unit import * -import matplotlib.pyplot as plt -import numpy as np -import os.path -import csv -import h5py -import sys -import pandas as pd -import argparse -# Set Logging -logger = Logging.setup_logging() -# Set Defaults If you want to adapt for different technologies -_SKY130_defaults = { - 'w': 1, - 'l': 0.15, - 'nf': 1 -} -_plot_defaults = { - 'id_W_vs_gm_id': ['gm_id', 'id_W'], - 'ft_vs_gm_id': ['gm_id', 'ft'], - 'gm_gds_vs_gm_id': ['gm_id', 'gm_gds'], - 'gm_id_vs_vgg': ['v-sweep', 'gm_id'], -} - - -def create_test_circuit(libpath, fet_typ='sky130_fd_pr__nfet_01v8', w=1, l=0.15, nf=1, corner='tt'): - """ - Create the test gmID circuit and instantiate the liberty file in a particular corner - Fet W and L are instantiated - """ - ckt =Circuit('gm_id') - ckt.lib(libpath, section=corner) - - # create the circuit - ckt.V('gg', 1, ckt.gnd, 0@u_V) - ckt.V('dd', 2, ckt.gnd, 1.8@u_V) - ckt.X('M1', fet_typ, 2, 1, ckt.gnd, ckt.gnd, L=l, W=w, nf=1) - return ckt - - -def run_sim(c, iparam, w, l): - """ - Simulation Method which runs DC sim to get the values needed for gm-ID analysis - Parameters id, gm, gds and cgg - :param c: Circuit Object - :param iparam: Mosfet identifier string - :param w: - :return: Values of gm_id, ft, id_W, gm_gds, vgs, gm, id, cgg, gds - """ - sim = c.simulator() - sim.save_internal_parameters(iparam%'gm', iparam%'id', iparam%'gds', iparam%'cgg') - # run the dc simulation - an = sim.dc(Vgg=slice(0, 1.8, 0.01)) -# calculate needed values..need as_ndarray() since most of these have None as the unit and that causes an error - gm = an.internal_parameters[iparam%'gm'].as_ndarray() - id = an.internal_parameters[iparam%'id'].as_ndarray() - gm_id = gm / id - cgg = an.internal_parameters[iparam%'cgg'].as_ndarray() - ft = gm / cgg - id_W = id / w - gds = an.internal_parameters[iparam%'gds'].as_ndarray() - gm_gds = gm / gds - w_arr, l_arr = np.empty(len(gm)), np.empty(len(gm)) - for i in range(len(gm)): - w_arr[i] = w - l_arr[i] = l - gmid_dict = { - 'w': list(w_arr), - 'l': list(l_arr), - 'id_W': list(id_W), - 'gm_id': list(gm_id), - 'ft': list(ft), - 'gm_gds': list(gm_gds), - 'v-sweep': list(an.nodes['v-sweep']), - 'gm': list(gm), - 'id': list(id), - 'cgg': list(cgg), - 'gds': list(gds), - } - return gmid_dict -# #return id_W, gm_id, ft, gm_gds, an.nodes['v-sweep'], gm, id, cgg, gds - - -def init_plots(): - ''' - Plot the figures with the given - :return: - ''' - figs, plts = [], [] - for loopnum, plotname in enumerate(_plot_defaults.keys()): - figs.append(plt.figure()) - plts.append(figs[loopnum].subplots()) - figs[loopnum].suptitle(plotname) - plts[loopnum].set_xlabel(_plot_defaults[plotname][0]) - plts[loopnum].set_ylabel(_plot_defaults[plotname][1]) - return plts, figs -# #figs = [plt.figure(), plt.figure(), plt.figure(), plt.figure()] -# #plts = [f.subplots() for f in figs] -# #figs[0].suptitle('Id/W vs gm/Id') -# #plts[0].set_xlabel("gm/Id") -# #plts[0].set_ylabel("Id/W") -# #figs[1].suptitle('fT vs gm/Id') -# #plts[1].set_xlabel("gm/Id") -# #plts[1].set_ylabel("f_T") -# #figs[2].suptitle('gm/gds vs gm/Id') -# #plts[2].set_xlabel("gm/Id") -# #plts[2].set_ylabel("gm/gds") -# #figs[3].suptitle('gm/Id vs Vgg') -# #plts[3].set_xlabel("Vgg") -# #plts[3].set_ylabel("gm/Id") -# #return figs, plts - - -# def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): -def gen_plots(df_gmid, plts): - # plot some interesting things - for loopnum, plotname in enumerate(_plot_defaults.keys()): - for (w, l) in np.unique(df_gmid.index.values): - curr_x = df_gmid.loc[(w, l)][_plot_defaults[plotname][0]] - curr_y = df_gmid.loc[(w, l)][_plot_defaults[plotname][1]] - plts[loopnum].plot(curr_x, curr_y, label= f'W_{w} x L_{l}') -# #plts[0].plot(gm_id, id_W, label=f'W {fet_W} x L {fet_L}') -# #plts[1].plot(gm_id, ft, label=f'W {fet_W} x L {fet_L}') -# #plts[2].plot(gm_id, gm_gds, label=f'W {fet_W} x L {fet_L}') -# #plts[3].plot(vsweep, gm_id, label=f'W {fet_W} x L {fet_L}') - - -def read_bins(fname): - r = csv.reader(open(fname, 'r')) - return r - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("--fet_types", required=True, help="Provide the FET Name list",nargs='+', default=[]) - parser.add_argument("--WL_csv", required=True, help="Provide the WL bin") - parser.add_argument("--plot_dir", required=False, help="Provide the WL bin", default="gmid_plots") - parser.add_argument("--hdf_db", required=True, help="hdf5 database", default="gmid_plots") - parser.add_argument("--SingleW", required=False, help="Provide Single W for run on the W", default=None) - parser.add_argument("--libpath", required=False, help="path to library", - default="/usr/bin/miniconda3/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice") - parser.add_argument("--corner", required=False, help="Corner to run the sims on", default='tt') - - - args = parser.parse_args() -# #if len(sys.argv) < 4: - # print(f'{sys.argv[0]} [width]') - # print(' is a template with 1 \%s which will contain the plot name. 4 are generated per LxW combo.') - # print('If [width] is specified, only W/L pairs for that width are processed.') - # sys.exit(0) - fet_types = args.fet_types - bins_fname = args.WL_csv - figname = args.plot_dir - only_W = args.SingleW - for fet_type in fet_types: - print(f'Simulating {fet_type} with bins {bins_fname}') - # Fet Identifier to pass - iparam = f'@m.xm1.m{fet_type}[%s]' - c = create_test_circuit(args.libpath, fet_type, 0.15, 1, args.corner) - bins = read_bins(bins_fname) - next(bins) -# #figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] -# #figs, plts = init_plots() -# #h5name = os.path.splitext(figname % 'data')[0] + '.h5' - h5name = f'{fet_type}.h5' - - out = h5py.File(h5name, "w") - bins_d = out.create_dataset('bins', (0, 2), maxshape=(None,2)) - gm_d = out.create_dataset('gm', (0, 0), maxshape=(None,None)) - id_d = out.create_dataset('id', (0, 0), maxshape=(None,None)) - cgg_d = out.create_dataset('cgg', (0, 0), maxshape=(None,None)) - gds_d = out.create_dataset('gds', (0, 0), maxshape=(None,None)) - vsweep_d = out.create_dataset('vsweep', (0,0), maxshape=(None,None)) - idx = 0 - # loop W and L - run_dict = {} - for dev, bin, fet_W, fet_L in bins: - fet_W, fet_L = float(fet_W), float(fet_L) - if only_W is not None and fet_W != only_W: - continue - print(f'{bin}: {dev} W {fet_W} x L {fet_L}') - # Update parameters in the loop before runing simulations - c.element('XM1').parameters['W'] = fet_W - c.element('XM1').parameters['L'] = fet_L - # Run Simulations -# #id_W, gm_id, ft, gm_gds, vsweep, gm, id, cgg, gds = run_sim(c, iparam, fet_W, fet_L) - curr_dict = run_sim(c, iparam, fet_W, fet_L) - for k, v in curr_dict.items(): - if k in run_dict.keys(): - run_dict[k] = run_dict[k]+curr_dict[k] - else: - run_dict[k] = v - gmid_df = pd.DataFrame.from_dict(run_dict) -# #if idx == 0: -# # gm_d.resize(len(gm_id), 1) -# # id_d.resize(len(id_W), 1) -# # cgg_d.resize(len(ft), 1) -# # gds_d.resize(len(gm_gds), 1) -# # vsweep_d.resize(len(vsweep), 1) -# #bins_d.resize(idx+1, 0) -# #gm_d.resize(idx+1, 0) -# #id_d.resize(idx+1, 0) -# #cgg_d.resize(idx+1, 0) -# #gds_d.resize(idx+1, 0) -# #vsweep_d.resize(idx+1, 0) -# #bins_d[idx,:] = [fet_W, fet_L] -# #gm_d[idx, :] = gm -# #id_d[idx, :] = id -# #cgg_d[idx, :] = cgg -# #gds_d[idx, :] = gds -# #vsweep_d[idx, :] = vsweep # should be the same for every row -# #idx += 1 - gmid_df.to_csv(f'{fet_type}.csv') - gmid_df.set_index(['w', 'l'], inplace=True) - plts, figs = init_plots() - gen_plots(gmid_df, plts) - for f, nm in zip(figs, _plot_defaults.keys()): - f.legend() - f.tight_layout() - f.savefig(f'{fet_type}_{nm}') \ No newline at end of file diff --git a/examples/transistor/gm_id_01v8.csv b/examples/transistor/gm_id_01v8.csv deleted file mode 100644 index ea3dee98d..000000000 --- a/examples/transistor/gm_id_01v8.csv +++ /dev/null @@ -1,3 +0,0 @@ -bin,dev, W, L -0,nfet1v8,1.0,1.0 -1,nfet1v8,1.0,0.5 From d2825cebf09933b7bb23f06b8388d66f27e394ae Mon Sep 17 00:00:00 2001 From: leochand101 Date: Fri, 9 Sep 2022 14:23:35 -0700 Subject: [PATCH 5/5] Updated Simple (examples/transistor/transistor_sky130.py) and advanced usage Addressing #327 + png files --- .../sky130_fd_pr__nfet_01v8_ft_vs_gm_id.png | Bin 0 -> 24586 bytes ...sky130_fd_pr__nfet_01v8_gm_gds_vs_gm_id.png | Bin 0 -> 31723 bytes .../sky130_fd_pr__nfet_01v8_gm_id_vs_vgg.png | Bin 0 -> 29542 bytes .../sky130_fd_pr__nfet_01v8_id_W_vs_gm_id.png | Bin 0 -> 26901 bytes .../sky130_fd_pr__pfet_01v8_ft_vs_gm_id.png | Bin 0 -> 23015 bytes ...sky130_fd_pr__pfet_01v8_gm_gds_vs_gm_id.png | Bin 0 -> 28024 bytes .../sky130_fd_pr__pfet_01v8_gm_id_vs_vgg.png | Bin 0 -> 30072 bytes .../sky130_fd_pr__pfet_01v8_id_W_vs_gm_id.png | Bin 0 -> 28234 bytes .../sky130_fd_pr__nfet_01v8_igvgs.png | Bin 0 -> 189632 bytes .../sky130_fd_pr__pfet_01v8_igvgs.png | Bin 0 -> 183750 bytes 10 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/advanced-usages/sky130_fd_pr__nfet_01v8_ft_vs_gm_id.png create mode 100644 examples/advanced-usages/sky130_fd_pr__nfet_01v8_gm_gds_vs_gm_id.png create mode 100644 examples/advanced-usages/sky130_fd_pr__nfet_01v8_gm_id_vs_vgg.png create mode 100644 examples/advanced-usages/sky130_fd_pr__nfet_01v8_id_W_vs_gm_id.png create mode 100644 examples/advanced-usages/sky130_fd_pr__pfet_01v8_ft_vs_gm_id.png create mode 100644 examples/advanced-usages/sky130_fd_pr__pfet_01v8_gm_gds_vs_gm_id.png create mode 100644 examples/advanced-usages/sky130_fd_pr__pfet_01v8_gm_id_vs_vgg.png create mode 100644 examples/advanced-usages/sky130_fd_pr__pfet_01v8_id_W_vs_gm_id.png create mode 100644 examples/transistor/sky130_fd_pr__nfet_01v8_igvgs.png create mode 100644 examples/transistor/sky130_fd_pr__pfet_01v8_igvgs.png diff --git a/examples/advanced-usages/sky130_fd_pr__nfet_01v8_ft_vs_gm_id.png b/examples/advanced-usages/sky130_fd_pr__nfet_01v8_ft_vs_gm_id.png new file mode 100644 index 0000000000000000000000000000000000000000..8275b32c623f148e13322512a09450842323eab8 GIT binary patch literal 24586 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 literal 0 HcmV?d00001 diff --git a/examples/advanced-usages/sky130_fd_pr__nfet_01v8_id_W_vs_gm_id.png b/examples/advanced-usages/sky130_fd_pr__nfet_01v8_id_W_vs_gm_id.png new file mode 100644 index 0000000000000000000000000000000000000000..d8d0e8358b60e41ebe66d5a969bf426158107334 GIT binary patch literal 26901 zcmb?@1yohr`|Um)x=~7`xq>1BA}u9~C`yWeNUNZ9gXEzEK|)bM0YO000O@W~>F$v3 zkd%=3ZMeVxc>nRndt!R82*Zf|Q9AK@f`T*RE(F2p$eW zFr&nT@Qp~@-~{|j!cjrT@s^FLql>Zq14P-_(bm$&(ejZAyYmBkhetNn7x+c^FYvKH zbab?JkQ5NG`mYB3Huh!$O=pYR;U*-u*X}qV2$eDVHzrLs^$~(R^u2!NlBR3y;*h(Z zrqf^9m7c-C0djU1g4&bAdBW6A3NI--cGB}o%wLyjND4 z53CKyCyMR`o`47(6jlE zN7zkb4!JAvhyU`@3|9ysayK{Rd=RWI~z_3ssKYp)AS2C%w@n-2-^TjZJ zzaU9;Xz`HKw4@L7bW64*pRr0a!DyDaEa%^;u;}66n^pCefBaaYy`#e_e{rzrxE~SK zlc=aDeT&&=!b}vJyswfP*b^zy;YK>k6mJsyOg2O%-7T=Tb#YPD(_;=}x!Aoh9tL;C ziL7*Gq@`8*V2Ny}<6III!&yZKe*gZxGXJGQX;&mABZIWOygd4XnM$0Mf`US;d7kS+ zc73vwMfb-l90m)+PRYi`M$RU~d{Wf%VfTpR)| zL&Z)SCA&){RPWxs6L(o+wyxNBc6D=0wCMS)Q{sFM8U6m8i&4y)SxQQZnw52UK@zvK zjFEC)VEh((l1NNU%yGV(q9ICP+L5y}<;9B^qmz>Xb93g8^siqh(d4E37RF-HA$sZ* z0nBr%YP|J&p-siUD11v6r-IH@b#*m&SB~?duH*7Bi|!}-wzjt35*HhXzp>o2!(|t> zKIwKPdbY$}47?u6T{Sesg27;1_g3qz%1fR!^F;ozG!Q7?HVJ8_5=4%vW8XcVI>C20 zpZxL@@+x>cm>sv#?z=V$j~+eB3F}Bwr1JOoH=1saYcKZP-K@GVWIXZxIirMqvNG~< z@r#>ehRU!mk0IoCd16wP+y+7*ak+w*0F+ZVX z3C_*UMYeEj&4yGMh~uL@UKD9$YDNUSQoK}#=S z@a$l3TGn+juSeHOFY)BlkdVtTx|_=*0-tvOOvl46_0P=}*3{IzHXj)oNkdQnhbUU=Zz|Hrd?iT@Cup; zb*9#*7v0d?^VWmn7`f7ezq{=_Fvw)cSO9}TL^mBREio)e2nMzFY~FmIWbMZM5%y!p zkH_3rb#*Olk`1p5VIV^|&z)1&jf#mmyY5!D-Vzmi_wnN*aj(6?(*go6j%S*VF*EaN zebnLb^cs80Q|n7WcBR5=|BAi+*Icj0=<`i~M z6s}zhQnu>h6%b&6*u=0l)fCwsC+VaFdnwpu#IvAk%|9?OMBC6a?}zv0Pd4R$u701i zHx^_}c7p#-fqj|y^=rHJw8er;Evp=7#&{r`7w*UX>O_SQ%Ow)MQdhf>Ffq6FhcWK^ z&Wrs4&$$%wkgK6g0Wb%#QjazI>1k+uA|fKV*}Mk}?N-*-YG8YQa3UZe=*_n}nW7xS z{b>#MeE=Clmd!{x{XPqlrO8(hrhXCmOocGAudn+@=XTI?_=`9 zMa{5y|KZ`e5f6*;yz_!i?YD1A^9>CR&tJcO0zZ{_tKv3DcxPv4gTlk3x_?aC*Vfl_ ziHPJ4d*;7UGP9?LA7<#M{G@4#mDw<~9`@2<9zrQtNv&D4l)~)joTiNjm36