From 3f11b65ecc915833f609a6d36e75cd8951d268a3 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Tue, 10 Nov 2020 17:56:55 +0100 Subject: [PATCH 01/22] Added initial script for generating plots from FET simulator Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py new file mode 100644 index 0000000..15bf9e7 --- /dev/null +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright 2020 SkyWater PDK Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import PySpice.Logging.Logging as Logging +logger = Logging.setup_logging() +from PySpice.Spice.Netlist import Circuit +from PySpice.Unit import * +import matplotlib.pyplot as plt +from pathlib import Path +import csv + + +def create_test_circuit(fet_type, iparam, fet_L, fet_W, corner_path): + c=Circuit('gm_id') + c.include(corner_path) + + # create the circuit + c.V('gg', 1, c.gnd, 0@u_V) + c.V('dd', 2, c.gnd, 1.8@u_V) + c.X('M1', fet_type, 2, 1, c.gnd, c.gnd, L=fet_L, W=fet_W, ad="'W*0.29'", pd="'2*(W+0.29)'", as_="'W*0.29'", ps="'2*(W+0.29)'", nrd="'0.29/W'", nrs="'0.29/W'", sa=0, sb=0, sd=0, nf=1, mult=1) + return c + + +def run_sim(c, iparam, fet_W): + 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_id = an.internal_parameters[iparam%'gm'].as_ndarray() / an.internal_parameters[iparam%'id'].as_ndarray() + ft = an.internal_parameters[iparam%'gm'].as_ndarray() / an.internal_parameters[iparam%'cgg'].as_ndarray() + id_W = an.internal_parameters[iparam%'id'].as_ndarray() / fet_W + gm_gds = an.internal_parameters[iparam%'gm'].as_ndarray() / an.internal_parameters[iparam%'gds'].as_ndarray() + + return gm_id, ft, id_W, gm_gds, an.nodes['v-sweep'] + + +def init_plots(): + 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): + # plot some interesting things + 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): + with open(fname, 'r') as f: + r = csv.reader(f) + return r + + +def generate_fet_plots(fet_type, corner_path, bins_csv, outdir, outprefix, only_W=None, ext='png'): + iparam = f'@m.xm1.m{fet_type}[%s]' + # fet_W and fet_L values here are only for initialization, they are + # later changed in the for loop + c = create_test_circuit(fet_type, iparam, 0.15, 1, corner_path) + f = open(bins_csv, 'r') + bins = csv.reader(f) + # skip header + next(bins) + + figs, plts = init_plots() + 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 not in only_W: + continue + c.element('XM1').parameters['W'] = fet_W + c.element('XM1').parameters['L'] = fet_L + gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fet_W) + gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts) + + figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] + for fg, name in zip(figs, figtitles): + fg.legend() + fg.tight_layout() + fg.savefig(Path(outdir) / (outprefix + f'_{name}.{ext}')) + f.close() + + +if __name__ == '__main__': + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument( + 'fet_type', + help='FET type to simulate' + ) + parser.add_argument( + 'corner_path', + help='Path to corner SPICE file containing FET definition', + type=Path + ) + parser.add_argument( + 'bins_csv', + help='Path to CSV file with fet_type, bin, fet_W and fet_L parameters', + type=Path + ) + parser.add_argument( + 'outdir', + help='Path to the directory to save the plots to', + type=Path + ) + parser.add_argument( + '--outprefix', + help='The prefix to add to plot file names' + ) + parser.add_argument( + '--only-w', + help='Simulate the FET only for a given fet_W values', + nargs='+', + type=float + ) + parser.add_argument( + '--ext', + help='The image extension to use for figures', + default='png' + ) + args = parser.parse_args() + + if args.outprefix is None: + args.outprefix = args.fet_type + + generate_fet_plots( + args.fet_type, + args.corner_path, + args.bins_csv, + args.outdir, + args.outprefix, + args.only_w, + args.ext) From 508c21f203be3d53b20e18ed4d47769f9bf35ed3 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 11 Nov 2020 01:37:04 +0100 Subject: [PATCH 02/22] Added script for generating nfet and pfet plots from libraries directory Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 19 ++++- .../fet_simulator/generate-docs-plots.py | 72 +++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 15bf9e7..d76c4ec 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -33,13 +33,18 @@ def create_test_circuit(fet_type, iparam, fet_L, fet_W, corner_path): # create the circuit c.V('gg', 1, c.gnd, 0@u_V) c.V('dd', 2, c.gnd, 1.8@u_V) - c.X('M1', fet_type, 2, 1, c.gnd, c.gnd, L=fet_L, W=fet_W, ad="'W*0.29'", pd="'2*(W+0.29)'", as_="'W*0.29'", ps="'2*(W+0.29)'", nrd="'0.29/W'", nrs="'0.29/W'", sa=0, sb=0, sd=0, nf=1, mult=1) + c.X('M1', fet_type, 2, 1, c.gnd, c.gnd, L=fet_L, W=fet_W, ad="'W*0.29'", + pd="'2*(W+0.29)'", as_="'W*0.29'", ps="'2*(W+0.29)'", nrd="'0.29/W'", + nrs="'0.29/W'", sa=0, sb=0, sd=0, nf=1, mult=1 + ) return c def run_sim(c, iparam, fet_W): sim = c.simulator() - sim.save_internal_parameters(iparam%'gm', iparam%'id', iparam%'gds', iparam%'cgg') + 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)) @@ -85,7 +90,15 @@ def read_bins(fname): return r -def generate_fet_plots(fet_type, corner_path, bins_csv, outdir, outprefix, only_W=None, ext='png'): +def generate_fet_plots( + fet_type, + corner_path, + bins_csv, + outdir, + outprefix, + only_W=None, + ext='png'): + print(f'[generate_fet_plots] {fet_type} {corner_path} {bins_csv} {outdir} {outprefix} {only_W}') iparam = f'@m.xm1.m{fet_type}[%s]' # fet_W and fet_L values here are only for initialization, they are # later changed in the for loop diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py new file mode 100644 index 0000000..3d7ec7c --- /dev/null +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright 2020 SkyWater PDK Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import argparse +from pathlib import Path + +from fet_simulator import generate_fet_plots + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + 'fd_pr_dir', + help='Path to the particular version of the primitive library', + type=Path + ) + parser.add_argument( + 'device_details_dir', + help='Path to the directory with device details to save images to', + type=Path + ) + + args = parser.parse_args() + + typicalcorner = args.fd_pr_dir / 'models/corners/tt.spice' + + fets = [ + ['esd_nfet', 'esd_nfet_01v8', None], + ['nfet_01v8', 'nfet_01v8', None], + ['nfet_01v8_lvt', 'nfet_01v8_lvt', None], + ['nfet_03v3_nvt', 'nfet_03v3_nvt', None], + ['nfet_03v3_nvt-and-nfet_05v0_nvt', 'nfet_05v0_nvt', None], + ['nfet_03v3_nvt-and-nfet_05v0_nvt', 'nfet_03v3_nvt', None], + ['nfet_05v0_nvt', 'nfet_05v0_nvt', None], + # ['nfet_20v0'], TODO provide + # ['nfet_20v0_iso', 'nfet_20v0_nvt_iso', None], TODO invalid bins.csv file + # ['nfet_20v0_nvt', 'nfet_20v0_nvt', None], TODO invalid bins.csv file + # ['nfet_20v0_zvt', 'nfet_20v0_zvt', None], TODO invalid bins.csv file + # ['nfet_g11v0d16v0'], TODO provide + ['nfet_g5v0d10v5', 'nfet_g5v0d10v5', None], + ['pfet_01v8', 'pfet_01v8', None], + ['pfet_01v8_hvt', 'pfet_01v8_hvt', None], + ['pfet_01v8_lvt', 'pfet_01v8_lvt', None], + # ['pfet_20v0', 'pfet_20v0', None], TODO some plot issues + ['pfet_g5v0d10v5', 'pfet_g5v0d10v5', None], + ['pfet_g5v0d16v0', 'pfet_g5v0d16v0', None] + ] + + for outdir, fetname, onlyw in fets: + generate_fet_plots( + f'sky130_fd_pr__{fetname}', + typicalcorner, + args.fd_pr_dir / f'cells/{fetname}/sky130_fd_pr__{fetname}.bins.csv', + args.device_details_dir / outdir, + f'sim_{fetname}_', + onlyw + ) From 5ea3ea9487d9c3dab92e45e540d20133282ca564 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Thu, 12 Nov 2020 10:11:32 +0100 Subject: [PATCH 03/22] Added grid to plots Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/fet_simulator/fet_simulator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index d76c4ec..17ea0fa 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -64,15 +64,19 @@ def init_plots(): figs[0].suptitle('Id/W vs gm/Id') plts[0].set_xlabel("gm/Id") plts[0].set_ylabel("Id/W") + plts[0].grid(True) figs[1].suptitle('fT vs gm/Id') plts[1].set_xlabel("gm/Id") plts[1].set_ylabel("f_T") + plts[1].grid(True) figs[2].suptitle('gm/gds vs gm/Id') plts[2].set_xlabel("gm/Id") plts[2].set_ylabel("gm/gds") + plts[2].grid(True) figs[3].suptitle('gm/Id vs Vgg') plts[3].set_xlabel("Vgg") plts[3].set_ylabel("gm/Id") + plts[3].grid(True) return figs, plts From 414b809a3f0c7ef9ebae9e98de3602338df9198b Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Thu, 12 Nov 2020 10:12:23 +0100 Subject: [PATCH 04/22] Added closing plots after saving to file Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/fet_simulator/fet_simulator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 17ea0fa..58e9a90 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -128,6 +128,7 @@ def generate_fet_plots( fg.tight_layout() fg.savefig(Path(outdir) / (outprefix + f'_{name}.{ext}')) f.close() + plt.close('all') if __name__ == '__main__': From 3e57ce2ecc56c592de8d66a759201b30dda9f284 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Thu, 12 Nov 2020 10:14:06 +0100 Subject: [PATCH 05/22] Comment out not working fet Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/fet_simulator/generate-docs-plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index 3d7ec7c..6b5f44f 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -58,7 +58,7 @@ if __name__ == '__main__': ['pfet_01v8_lvt', 'pfet_01v8_lvt', None], # ['pfet_20v0', 'pfet_20v0', None], TODO some plot issues ['pfet_g5v0d10v5', 'pfet_g5v0d10v5', None], - ['pfet_g5v0d16v0', 'pfet_g5v0d16v0', None] + # ['pfet_g5v0d16v0', 'pfet_g5v0d16v0', None] TODO invalid bins.csv file ] for outdir, fetname, onlyw in fets: From c64020cb8bd4855207cc0fe1166981cfc2fcdd96 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Thu, 12 Nov 2020 11:16:49 +0100 Subject: [PATCH 06/22] FET simulator code cleanup Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 35 +++++++++++-------- .../fet_simulator/generate-docs-plots.py | 6 ++-- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 58e9a90..ae2b521 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -18,42 +18,48 @@ # SPDX-License-Identifier: Apache-2.0 import PySpice.Logging.Logging as Logging -logger = Logging.setup_logging() from PySpice.Spice.Netlist import Circuit -from PySpice.Unit import * +from PySpice.Unit import u_V import matplotlib.pyplot as plt from pathlib import Path import csv +logger = Logging.setup_logging() + def create_test_circuit(fet_type, iparam, fet_L, fet_W, corner_path): - c=Circuit('gm_id') + c = Circuit('gm_id') c.include(corner_path) # create the circuit c.V('gg', 1, c.gnd, 0@u_V) c.V('dd', 2, c.gnd, 1.8@u_V) - c.X('M1', fet_type, 2, 1, c.gnd, c.gnd, L=fet_L, W=fet_W, ad="'W*0.29'", - pd="'2*(W+0.29)'", as_="'W*0.29'", ps="'2*(W+0.29)'", nrd="'0.29/W'", + c.X( + 'M1', fet_type, 2, 1, c.gnd, c.gnd, L=fet_L, W=fet_W, ad="'W*0.29'", + pd="'2*(W+0.29)'", as_="'W*0.29'", ps="'2*(W+0.29)'", nrd="'0.29/W'", nrs="'0.29/W'", sa=0, sb=0, sd=0, nf=1, mult=1 ) - return c + return c def run_sim(c, iparam, fet_W): sim = c.simulator() sim.save_internal_parameters( - iparam%'gm', iparam%'id', iparam%'gds', iparam%'cgg' + 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_id = an.internal_parameters[iparam%'gm'].as_ndarray() / an.internal_parameters[iparam%'id'].as_ndarray() - ft = an.internal_parameters[iparam%'gm'].as_ndarray() / an.internal_parameters[iparam%'cgg'].as_ndarray() - id_W = an.internal_parameters[iparam%'id'].as_ndarray() / fet_W - gm_gds = an.internal_parameters[iparam%'gm'].as_ndarray() / an.internal_parameters[iparam%'gds'].as_ndarray() + # calculate needed values..need as_ndarray() since most of these have None + # as the unit and that causes an error + gm_id = (an.internal_parameters[iparam % 'gm'].as_ndarray() / + an.internal_parameters[iparam % 'id'].as_ndarray()) + ft = (an.internal_parameters[iparam % 'gm'].as_ndarray() / + an.internal_parameters[iparam % 'cgg'].as_ndarray()) + id_W = (an.internal_parameters[iparam % 'id'].as_ndarray() / fet_W) + gm_gds = (an.internal_parameters[iparam % 'gm'].as_ndarray() / + an.internal_parameters[iparam % 'gds'].as_ndarray()) return gm_id, ft, id_W, gm_gds, an.nodes['v-sweep'] @@ -101,8 +107,9 @@ def generate_fet_plots( outdir, outprefix, only_W=None, - ext='png'): - print(f'[generate_fet_plots] {fet_type} {corner_path} {bins_csv} {outdir} {outprefix} {only_W}') + ext='svg'): + print(f'[generate_fet_plots] {fet_type} {corner_path} {bins_csv}' + + f'{outdir} {outprefix} {only_W}') iparam = f'@m.xm1.m{fet_type}[%s]' # fet_W and fet_L values here are only for initialization, they are # later changed in the for loop diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index 6b5f44f..4636cce 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -48,7 +48,7 @@ if __name__ == '__main__': ['nfet_03v3_nvt-and-nfet_05v0_nvt', 'nfet_03v3_nvt', None], ['nfet_05v0_nvt', 'nfet_05v0_nvt', None], # ['nfet_20v0'], TODO provide - # ['nfet_20v0_iso', 'nfet_20v0_nvt_iso', None], TODO invalid bins.csv file + # ['nfet_20v0_iso', 'nfet_20v0_nvt_iso', None], TODO invalid bins.csv # ['nfet_20v0_nvt', 'nfet_20v0_nvt', None], TODO invalid bins.csv file # ['nfet_20v0_zvt', 'nfet_20v0_zvt', None], TODO invalid bins.csv file # ['nfet_g11v0d16v0'], TODO provide @@ -65,8 +65,8 @@ if __name__ == '__main__': generate_fet_plots( f'sky130_fd_pr__{fetname}', typicalcorner, - args.fd_pr_dir / f'cells/{fetname}/sky130_fd_pr__{fetname}.bins.csv', - args.device_details_dir / outdir, + args.fd_pr_dir/f'cells/{fetname}/sky130_fd_pr__{fetname}.bins.csv', + args.device_details_dir/outdir, f'sim_{fetname}_', onlyw ) From 2aed534b466b637f8d24af0274beb13f86e88630 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Mon, 16 Nov 2020 15:27:40 +0100 Subject: [PATCH 07/22] Added ngspice to environment.yml and PySpice to requirements.txt Signed-off-by: Grzegorz Latosinski --- environment.yml | 2 ++ requirements.txt | 3 +++ 2 files changed, 5 insertions(+) diff --git a/environment.yml b/environment.yml index ee13527..a6e5861 100644 --- a/environment.yml +++ b/environment.yml @@ -17,9 +17,11 @@ name: skywater-pdk-scripts channels: - symbiflow - defaults +- conda-forge dependencies: - python=3.8 - pip +- ngspice # Packages installed from PyPI - pip: - -r file:requirements.txt diff --git a/requirements.txt b/requirements.txt index fe8a2c1..58a4641 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,8 @@ flake8 # previews. rst_include +# the PySpice module for rendering FET bins plots +PySpice + # The Python API for the SkyWater PDK. -e scripts/python-skywater-pdk From ee63e6cb8736baca3fff94b12db953efab975083 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Mon, 16 Nov 2020 15:29:01 +0100 Subject: [PATCH 08/22] Added grouping FET plots by W Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 70 ++++++++++++------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index ae2b521..0f8f806 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -23,6 +23,7 @@ from PySpice.Unit import u_V import matplotlib.pyplot as plt from pathlib import Path import csv +from collections import defaultdict logger = Logging.setup_logging() @@ -64,22 +65,22 @@ def run_sim(c, iparam, fet_W): return gm_id, ft, id_W, gm_gds, an.nodes['v-sweep'] -def init_plots(): +def init_plots(fet_name, W): figs = [plt.figure(), plt.figure(), plt.figure(), plt.figure()] plts = [f.subplots() for f in figs] - figs[0].suptitle('Id/W vs gm/Id') + figs[0].suptitle(f'{fet_name} Id/W vs gm/Id (W = {W})') plts[0].set_xlabel("gm/Id") plts[0].set_ylabel("Id/W") plts[0].grid(True) - figs[1].suptitle('fT vs gm/Id') + figs[1].suptitle(f'{fet_name} fT vs gm/Id (W = {W})') plts[1].set_xlabel("gm/Id") plts[1].set_ylabel("f_T") plts[1].grid(True) - figs[2].suptitle('gm/gds vs gm/Id') + figs[2].suptitle(f'{fet_name} gm/gds vs gm/Id (W = {W})') plts[2].set_xlabel("gm/Id") plts[2].set_ylabel("gm/gds") plts[2].grid(True) - figs[3].suptitle('gm/Id vs Vgg') + figs[3].suptitle(f'{fet_name} gm/Id vs Vgg (W = {W})') plts[3].set_xlabel("Vgg") plts[3].set_ylabel("gm/Id") plts[3].grid(True) @@ -97,7 +98,12 @@ def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): def read_bins(fname): with open(fname, 'r') as f: r = csv.reader(f) - return r + # drop CSV header + next(r) + res = [] + for line in r: + res.append(line) + return res def generate_fet_plots( @@ -114,28 +120,40 @@ def generate_fet_plots( # fet_W and fet_L values here are only for initialization, they are # later changed in the for loop c = create_test_circuit(fet_type, iparam, 0.15, 1, corner_path) - f = open(bins_csv, 'r') - bins = csv.reader(f) - # skip header - next(bins) + + bins = read_bins(bins_csv) - figs, plts = init_plots() - 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 not in only_W: - continue - c.element('XM1').parameters['W'] = fet_W - c.element('XM1').parameters['L'] = fet_L - gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fet_W) - gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts) + bins_by_W = defaultdict(list) + # group bins by W + for line in bins: + bins_by_W[line[2]].append(line) + + Ws = only_W if only_W is not None else list(bins_by_W.keys()) - figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] - for fg, name in zip(figs, figtitles): - fg.legend() - fg.tight_layout() - fg.savefig(Path(outdir) / (outprefix + f'_{name}.{ext}')) - f.close() - plt.close('all') + for W in Ws: + figs, plts = init_plots(fet_type, W) + for dev, bin, fet_W, fet_L in bins_by_W[W]: + fet_W, fet_L = float(fet_W), float(fet_L) + if only_W is not None and fet_W not in only_W: + continue + c.element('XM1').parameters['W'] = fet_W + c.element('XM1').parameters['L'] = fet_L + gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fet_W) + gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts) + + figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] + for fg, name in zip(figs, figtitles): + lg = fg.legend( + bbox_to_anchor=(1.05, 1), + loc='upper left' + ) + fg.tight_layout() + fg.savefig( + Path(outdir) / (outprefix + f'_{name}_W{str(W).replace(".", "_")}.{ext}'), + bbox_extra_artists=(lg,), + bbox_inches='tight' + ) + plt.close('all') if __name__ == '__main__': From c8b18d8ad40d922f8ac983ad3fa5aa402964169b Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Mon, 16 Nov 2020 15:29:28 +0100 Subject: [PATCH 09/22] Changed extension for FET plots from PNG to SVG Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/fet_simulator/fet_simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 0f8f806..853a825 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -192,7 +192,7 @@ if __name__ == '__main__': parser.add_argument( '--ext', help='The image extension to use for figures', - default='png' + default='svg' ) args = parser.parse_args() From f177fa7797d79f09806e3bc822adc1cdaca9a6d0 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Mon, 16 Nov 2020 17:38:07 +0100 Subject: [PATCH 10/22] Wrapped script in main function Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 20 +++++++++++++------ .../fet_simulator/generate-docs-plots.py | 13 +++++++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 853a825..5af84a2 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -24,6 +24,7 @@ import matplotlib.pyplot as plt from pathlib import Path import csv from collections import defaultdict +import sys logger = Logging.setup_logging() @@ -120,14 +121,14 @@ def generate_fet_plots( # fet_W and fet_L values here are only for initialization, they are # later changed in the for loop c = create_test_circuit(fet_type, iparam, 0.15, 1, corner_path) - + bins = read_bins(bins_csv) bins_by_W = defaultdict(list) # group bins by W for line in bins: bins_by_W[line[2]].append(line) - + Ws = only_W if only_W is not None else list(bins_by_W.keys()) for W in Ws: @@ -149,17 +150,18 @@ def generate_fet_plots( ) fg.tight_layout() fg.savefig( - Path(outdir) / (outprefix + f'_{name}_W{str(W).replace(".", "_")}.{ext}'), + Path(outdir) / ( + outprefix + f'_{name}_W{str(W).replace(".", "_")}.{ext}'), bbox_extra_artists=(lg,), bbox_inches='tight' ) plt.close('all') -if __name__ == '__main__': +def main(argv): import argparse - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog=argv[0]) parser.add_argument( 'fet_type', help='FET type to simulate' @@ -194,7 +196,7 @@ if __name__ == '__main__': help='The image extension to use for figures', default='svg' ) - args = parser.parse_args() + args = parser.parse_args(argv[1:]) if args.outprefix is None: args.outprefix = args.fet_type @@ -207,3 +209,9 @@ if __name__ == '__main__': args.outprefix, args.only_w, args.ext) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index 4636cce..feb830c 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -19,11 +19,13 @@ import argparse from pathlib import Path +import sys from fet_simulator import generate_fet_plots -if __name__ == '__main__': - parser = argparse.ArgumentParser() + +def main(argv): + parser = argparse.ArgumentParser(prog=argv[0]) parser.add_argument( 'fd_pr_dir', help='Path to the particular version of the primitive library', @@ -35,7 +37,7 @@ if __name__ == '__main__': type=Path ) - args = parser.parse_args() + args = parser.parse_args(argv[1:]) typicalcorner = args.fd_pr_dir / 'models/corners/tt.spice' @@ -70,3 +72,8 @@ if __name__ == '__main__': f'sim_{fetname}_', onlyw ) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) From 222572af4d5311dad32d03dc8cfcd6f8c7ac1585 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Tue, 17 Nov 2020 16:13:44 +0100 Subject: [PATCH 11/22] Added fetching FETs by regular expression Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 33 +++--- .../fet_simulator/generate-docs-plots.py | 103 ++++++++++++------ 2 files changed, 82 insertions(+), 54 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 5af84a2..528a8e1 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -108,32 +108,35 @@ def read_bins(fname): def generate_fet_plots( - fet_type, corner_path, bins_csv, outdir, outprefix, only_W=None, ext='svg'): - print(f'[generate_fet_plots] {fet_type} {corner_path} {bins_csv}' + + print(f'[generate_fet_plots] {corner_path} {bins_csv}' + f'{outdir} {outprefix} {only_W}') - iparam = f'@m.xm1.m{fet_type}[%s]' - # fet_W and fet_L values here are only for initialization, they are - # later changed in the for loop - c = create_test_circuit(fet_type, iparam, 0.15, 1, corner_path) bins = read_bins(bins_csv) bins_by_W = defaultdict(list) # group bins by W for line in bins: - bins_by_W[line[2]].append(line) + bins_by_W[(line[0], float(line[2]))].append(line) - Ws = only_W if only_W is not None else list(bins_by_W.keys()) + Ws = [key for key in bins_by_W.keys() if only_W is None or key[1] in only_W] + + for fet_type, W in Ws: + if outprefix is None: + outprefix = fet_type + print(f'======> {fet_type}: {W}') + iparam = f'@m.xm1.m{fet_type}[%s]' + # fet_W and fet_L values here are only for initialization, they are + # later changed in the for loop + c = create_test_circuit(fet_type, iparam, 0.15, 1, corner_path) - for W in Ws: figs, plts = init_plots(fet_type, W) - for dev, bin, fet_W, fet_L in bins_by_W[W]: + for dev, bin, fet_W, fet_L in bins_by_W[(fet_type, W)]: fet_W, fet_L = float(fet_W), float(fet_L) if only_W is not None and fet_W not in only_W: continue @@ -151,7 +154,7 @@ def generate_fet_plots( fg.tight_layout() fg.savefig( Path(outdir) / ( - outprefix + f'_{name}_W{str(W).replace(".", "_")}.{ext}'), + outprefix + f'_{fet_type}_{name}_W{str(W).replace(".", "_")}.{ext}'), bbox_extra_artists=(lg,), bbox_inches='tight' ) @@ -162,10 +165,6 @@ def main(argv): import argparse parser = argparse.ArgumentParser(prog=argv[0]) - parser.add_argument( - 'fet_type', - help='FET type to simulate' - ) parser.add_argument( 'corner_path', help='Path to corner SPICE file containing FET definition', @@ -198,11 +197,7 @@ def main(argv): ) args = parser.parse_args(argv[1:]) - if args.outprefix is None: - args.outprefix = args.fet_type - generate_fet_plots( - args.fet_type, args.corner_path, args.bins_csv, args.outdir, diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index feb830c..68b5d73 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -20,6 +20,8 @@ import argparse from pathlib import Path import sys +import contextlib +import traceback from fet_simulator import generate_fet_plots @@ -27,51 +29,82 @@ from fet_simulator import generate_fet_plots def main(argv): parser = argparse.ArgumentParser(prog=argv[0]) parser.add_argument( - 'fd_pr_dir', - help='Path to the particular version of the primitive library', + 'libraries_dir', + help='Path to the libraries directory of skywater-pdk', type=Path ) parser.add_argument( - 'device_details_dir', - help='Path to the directory with device details to save images to', + 'corner_file', + help='Path to the corner SPICE file', + type=Path + ) + parser.add_argument( + 'output_dir', + help='Path to the output directory', + type=Path + ) + parser.add_argument( + '--libname', + help='Library name to generate the Symbolator diagrams for', + type=str + ) + parser.add_argument( + '--version', + help='Version for which the Symbolator diagrams should be generated', + type=str + ) + parser.add_argument( + '--create-dirs', + help='Create directories for output when not present', + action='store_true' + ) + parser.add_argument( + '--failed-inputs', + help='Path to files for which Symbolator failed to generate diagram', type=Path ) args = parser.parse_args(argv[1:]) - typicalcorner = args.fd_pr_dir / 'models/corners/tt.spice' + fetbins = list(args.libraries_dir.rglob('*fet*bins.csv')) - fets = [ - ['esd_nfet', 'esd_nfet_01v8', None], - ['nfet_01v8', 'nfet_01v8', None], - ['nfet_01v8_lvt', 'nfet_01v8_lvt', None], - ['nfet_03v3_nvt', 'nfet_03v3_nvt', None], - ['nfet_03v3_nvt-and-nfet_05v0_nvt', 'nfet_05v0_nvt', None], - ['nfet_03v3_nvt-and-nfet_05v0_nvt', 'nfet_03v3_nvt', None], - ['nfet_05v0_nvt', 'nfet_05v0_nvt', None], - # ['nfet_20v0'], TODO provide - # ['nfet_20v0_iso', 'nfet_20v0_nvt_iso', None], TODO invalid bins.csv - # ['nfet_20v0_nvt', 'nfet_20v0_nvt', None], TODO invalid bins.csv file - # ['nfet_20v0_zvt', 'nfet_20v0_zvt', None], TODO invalid bins.csv file - # ['nfet_g11v0d16v0'], TODO provide - ['nfet_g5v0d10v5', 'nfet_g5v0d10v5', None], - ['pfet_01v8', 'pfet_01v8', None], - ['pfet_01v8_hvt', 'pfet_01v8_hvt', None], - ['pfet_01v8_lvt', 'pfet_01v8_lvt', None], - # ['pfet_20v0', 'pfet_20v0', None], TODO some plot issues - ['pfet_g5v0d10v5', 'pfet_g5v0d10v5', None], - # ['pfet_g5v0d16v0', 'pfet_g5v0d16v0', None] TODO invalid bins.csv file - ] + nc = contextlib.nullcontext() + + with open(args.failed_inputs, 'w') if args.failed_inputs else nc as err: + for fetbin in fetbins: + outdir = (args.output_dir / + fetbin.resolve() + .relative_to(args.libraries_dir.resolve())) + if args.libname and args.libname != outdir.parts[0]: + continue + if args.version and args.version != outdir.parts[1]: + continue + print(f'===> {str(fetbin)}') + try: + if not outdir.exists(): + if args.create_dirs: + outdir.mkdir(parents=True) + else: + print('The output directory {str(outdir)} is missing') + print('Run the script with --create-dirs') + return errno.ENOENT + + prefix = fetbin.name.replace('.bins.csv', '') + generate_fet_plots( + args.corner_file, + fetbin, + outdir, + f'{prefix}_', + ext='sim.svg' + ) + except Exception: + print( + f'Failed to generate FET plot for {str(fetbin)}', + file=sys.stderr + ) + traceback.print_exc() + err.write(f'{fetbin}\n') - for outdir, fetname, onlyw in fets: - generate_fet_plots( - f'sky130_fd_pr__{fetname}', - typicalcorner, - args.fd_pr_dir/f'cells/{fetname}/sky130_fd_pr__{fetname}.bins.csv', - args.device_details_dir/outdir, - f'sim_{fetname}_', - onlyw - ) return 0 From 7c4d2fa0f33377e367b6c9e4c1f5c5a5629af038 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Tue, 17 Nov 2020 18:53:48 +0100 Subject: [PATCH 12/22] Added proper closing of plots Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 33 ++++++++++++------- .../fet_simulator/generate-docs-plots.py | 11 ++++--- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 528a8e1..efaffa0 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -96,6 +96,11 @@ def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): plts[3].plot(vsweep, gm_id, label=f'W {fet_W} x L {fet_L}') +def close_plots(figs): + for f in figs: + plt.close(f) + + def read_bins(fname): with open(fname, 'r') as f: r = csv.reader(f) @@ -124,7 +129,8 @@ def generate_fet_plots( for line in bins: bins_by_W[(line[0], float(line[2]))].append(line) - Ws = [key for key in bins_by_W.keys() if only_W is None or key[1] in only_W] + Ws = [key for key in bins_by_W.keys() + if only_W is None or key[1] in only_W] for fet_type, W in Ws: if outprefix is None: @@ -136,14 +142,18 @@ def generate_fet_plots( c = create_test_circuit(fet_type, iparam, 0.15, 1, corner_path) figs, plts = init_plots(fet_type, W) - for dev, bin, fet_W, fet_L in bins_by_W[(fet_type, W)]: - fet_W, fet_L = float(fet_W), float(fet_L) - if only_W is not None and fet_W not in only_W: - continue - c.element('XM1').parameters['W'] = fet_W - c.element('XM1').parameters['L'] = fet_L - gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fet_W) - gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts) + try: + for dev, bin, fet_W, fet_L in bins_by_W[(fet_type, W)]: + fet_W, fet_L = float(fet_W), float(fet_L) + if only_W is not None and fet_W not in only_W: + continue + c.element('XM1').parameters['W'] = fet_W + c.element('XM1').parameters['L'] = fet_L + gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fet_W) + gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts) + except Exception: + close_plots(figs) + raise figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] for fg, name in zip(figs, figtitles): @@ -154,11 +164,12 @@ def generate_fet_plots( fg.tight_layout() fg.savefig( Path(outdir) / ( - outprefix + f'_{fet_type}_{name}_W{str(W).replace(".", "_")}.{ext}'), + outprefix + + f'_{fet_type}_{name}_W{str(W).replace(".", "_")}.{ext}'), bbox_extra_artists=(lg,), bbox_inches='tight' ) - plt.close('all') + close_plots(figs) def main(argv): diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index 68b5d73..52d0076 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -22,6 +22,7 @@ from pathlib import Path import sys import contextlib import traceback +import errno from fet_simulator import generate_fet_plots @@ -73,11 +74,13 @@ def main(argv): with open(args.failed_inputs, 'w') if args.failed_inputs else nc as err: for fetbin in fetbins: outdir = (args.output_dir / - fetbin.resolve() - .relative_to(args.libraries_dir.resolve())) - if args.libname and args.libname != outdir.parts[0]: + fetbin.resolve() + .relative_to(args.libraries_dir.resolve())) + library = outdir.relative_to(args.output_dir).parts[0] + ver = outdir.relative_to(args.output_dir).parts[1] + if args.libname and args.libname != library: continue - if args.version and args.version != outdir.parts[1]: + if args.version and args.version != ver: continue print(f'===> {str(fetbin)}') try: From 3f0a64be4a8b6b0d2e7c10a8b564c12634d5152b Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 18 Nov 2020 11:02:41 +0100 Subject: [PATCH 13/22] Fixed directory generation Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/fet_simulator/generate-docs-plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index 52d0076..2cf7dd8 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -74,7 +74,7 @@ def main(argv): with open(args.failed_inputs, 'w') if args.failed_inputs else nc as err: for fetbin in fetbins: outdir = (args.output_dir / - fetbin.resolve() + fetbin.parent.resolve() .relative_to(args.libraries_dir.resolve())) library = outdir.relative_to(args.output_dir).parts[0] ver = outdir.relative_to(args.output_dir).parts[1] From 1c0eeda6c6fc026be32de8627359b43fac081bca Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 18 Nov 2020 16:13:56 +0100 Subject: [PATCH 14/22] Added releasing memory after plotting the FET graphs Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index efaffa0..0a68df5 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -50,18 +50,26 @@ def run_sim(c, iparam, fet_W): iparam % 'gm', iparam % 'id', iparam % 'gds', iparam % 'cgg' ) - # run the dc simulation - an = sim.dc(Vgg=slice(0, 1.8, 0.01)) + try: + # 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_id = (an.internal_parameters[iparam % 'gm'].as_ndarray() / - an.internal_parameters[iparam % 'id'].as_ndarray()) - ft = (an.internal_parameters[iparam % 'gm'].as_ndarray() / - an.internal_parameters[iparam % 'cgg'].as_ndarray()) - id_W = (an.internal_parameters[iparam % 'id'].as_ndarray() / fet_W) - gm_gds = (an.internal_parameters[iparam % 'gm'].as_ndarray() / - an.internal_parameters[iparam % 'gds'].as_ndarray()) + # calculate needed values..need as_ndarray() since most of these have + # None as the unit and that causes an error + gm_id = (an.internal_parameters[iparam % 'gm'].as_ndarray() / + an.internal_parameters[iparam % 'id'].as_ndarray()) + ft = (an.internal_parameters[iparam % 'gm'].as_ndarray() / + an.internal_parameters[iparam % 'cgg'].as_ndarray()) + id_W = (an.internal_parameters[iparam % 'id'].as_ndarray() / fet_W) + gm_gds = (an.internal_parameters[iparam % 'gm'].as_ndarray() / + an.internal_parameters[iparam % 'gds'].as_ndarray()) + except Exception: + sim.ngspice.destroy('all') + sim.ngspice.reset() + raise + + sim.ngspice.destroy('all') + sim.ngspice.reset() return gm_id, ft, id_W, gm_gds, an.nodes['v-sweep'] From 6709fdfc1142c67f3ca5ce740bd13951e3f910ab Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 18 Nov 2020 16:16:29 +0100 Subject: [PATCH 15/22] Added not failing on invalid bins.csv file Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/fet_simulator/generate-docs-plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index 2cf7dd8..c68736a 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -100,7 +100,7 @@ def main(argv): f'{prefix}_', ext='sim.svg' ) - except Exception: + except (Exception, IndexError): print( f'Failed to generate FET plot for {str(fetbin)}', file=sys.stderr From 7f0f405c2b77c948a0d9751952562029f388d90f Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 18 Nov 2020 16:16:48 +0100 Subject: [PATCH 16/22] Added notification on successful finish of the script Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/fet_simulator/generate-docs-plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py index c68736a..e037fc4 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py @@ -108,6 +108,7 @@ def main(argv): traceback.print_exc() err.write(f'{fetbin}\n') + print('Finished generating FET plots') return 0 From 8d734f0c91678112704e65d71d7a91a2ccaacb31 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Tue, 24 Nov 2020 18:07:51 +0100 Subject: [PATCH 17/22] fet_simulator: added docstrings Signed-off-by: Grzegorz Latosinski --- .../fet_simulator/fet_simulator.py | 66 ++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py index 0a68df5..e1c05fd 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py +++ b/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py @@ -29,7 +29,18 @@ import sys logger = Logging.setup_logging() -def create_test_circuit(fet_type, iparam, fet_L, fet_W, corner_path): +def create_test_circuit(fet_type, fet_L, fet_W, corner_path): + """ + Creates a simple test circuit that contains only given FET. + + Parameters + ---------- + fet_type: name of the FET model + fet_L: FET length + fet_W: FET width + corner_path: + path to the spice corner file with model definitions and parameters + """ c = Circuit('gm_id') c.include(corner_path) @@ -45,6 +56,17 @@ def create_test_circuit(fet_type, iparam, fet_L, fet_W, corner_path): def run_sim(c, iparam, fet_W): + """ + Runs simulation for a given circuit with FET cell. + + Parameters + ---------- + c: circuit to simulate + iparam: + template string for creating internal parameter names (gm, id, gds, + cgg) for a given FET + fet_W: FET width + """ sim = c.simulator() sim.save_internal_parameters( iparam % 'gm', iparam % 'id', iparam % 'gds', iparam % 'cgg' @@ -75,6 +97,14 @@ def run_sim(c, iparam, fet_W): def init_plots(fet_name, W): + """ + Initializes plots for FET bins simulation. + + Parameters + ---------- + fet_name: name of the current FET cell + W: FET width + """ figs = [plt.figure(), plt.figure(), plt.figure(), plt.figure()] plts = [f.subplots() for f in figs] figs[0].suptitle(f'{fet_name} Id/W vs gm/Id (W = {W})') @@ -97,6 +127,20 @@ def init_plots(fet_name, W): def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): + """ + Generates plot lines for FET bins simulation parameters. + + Parameters + ---------- + gm_id: gm/Id values + id_W: Id/W values + ft: f_T values + gm_gds: gm/gds values + vsweep: v-sweep values + fet_W: FET width + fet_L: FET length + plts: plots on which plot lines should be drawn + """ # plot some interesting things 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}') @@ -105,11 +149,17 @@ def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): def close_plots(figs): + """ + Closes plots. + """ for f in figs: plt.close(f) def read_bins(fname): + """ + Reads bins CSV file. + """ with open(fname, 'r') as f: r = csv.reader(f) # drop CSV header @@ -127,6 +177,18 @@ def generate_fet_plots( outprefix, only_W=None, ext='svg'): + """ + Generates FET bins plots. + + Parameters + ---------- + corner_path: Path to FET model definitions and parameters + bins_csv: Path to the CSV file with bin parameters and FET model names + outdir: Directory where outputs should be stored + outprefix: Prefix for the output plot images + only_W: List of FET widths for which the plots should be generated + ext: extension for plot files + """ print(f'[generate_fet_plots] {corner_path} {bins_csv}' + f'{outdir} {outprefix} {only_W}') @@ -147,7 +209,7 @@ def generate_fet_plots( iparam = f'@m.xm1.m{fet_type}[%s]' # fet_W and fet_L values here are only for initialization, they are # later changed in the for loop - c = create_test_circuit(fet_type, iparam, 0.15, 1, corner_path) + c = create_test_circuit(fet_type, 0.15, 1, corner_path) figs, plts = init_plots(fet_type, W) try: From d760ac4b820e1614b2be1f8ac2c8381591c42a84 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 25 Nov 2020 11:17:00 +0100 Subject: [PATCH 18/22] fet_simulator: moved fet modules to simulation/analog Signed-off-by: Grzegorz Latosinski --- .../fet_simulator.py => simulation/analog/fet.py} | 0 .../analog/generate-fet-bins-plots.py} | 7 ++++--- 2 files changed, 4 insertions(+), 3 deletions(-) rename scripts/python-skywater-pdk/skywater_pdk/{fet_simulator/fet_simulator.py => simulation/analog/fet.py} (100%) rename scripts/python-skywater-pdk/skywater_pdk/{fet_simulator/generate-docs-plots.py => simulation/analog/generate-fet-bins-plots.py} (94%) diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py similarity index 100% rename from scripts/python-skywater-pdk/skywater_pdk/fet_simulator/fet_simulator.py rename to scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py diff --git a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/generate-fet-bins-plots.py similarity index 94% rename from scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py rename to scripts/python-skywater-pdk/skywater_pdk/simulation/analog/generate-fet-bins-plots.py index e037fc4..018f943 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/fet_simulator/generate-docs-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/generate-fet-bins-plots.py @@ -24,7 +24,7 @@ import contextlib import traceback import errno -from fet_simulator import generate_fet_plots +from fet import generate_fet_plots def main(argv): @@ -88,7 +88,7 @@ def main(argv): if args.create_dirs: outdir.mkdir(parents=True) else: - print('The output directory {str(outdir)} is missing') + print(f'The output directory {str(outdir)} is missing') print('Run the script with --create-dirs') return errno.ENOENT @@ -106,7 +106,8 @@ def main(argv): file=sys.stderr ) traceback.print_exc() - err.write(f'{fetbin}\n') + if err: + err.write(f'{fetbin}\n') print('Finished generating FET plots') return 0 From 77bad0df65ce04d83456b9cabda0f9d1f4a9eade Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 25 Nov 2020 11:24:37 +0100 Subject: [PATCH 19/22] fet_simulator: used more elaborate variable names Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/simulation/analog/fet.py | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py index e1c05fd..6926cd0 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py +++ b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py @@ -106,27 +106,27 @@ def init_plots(fet_name, W): W: FET width """ figs = [plt.figure(), plt.figure(), plt.figure(), plt.figure()] - plts = [f.subplots() for f in figs] + plots = [f.subplots() for f in figs] figs[0].suptitle(f'{fet_name} Id/W vs gm/Id (W = {W})') - plts[0].set_xlabel("gm/Id") - plts[0].set_ylabel("Id/W") - plts[0].grid(True) + plots[0].set_xlabel("gm/Id") + plots[0].set_ylabel("Id/W") + plots[0].grid(True) figs[1].suptitle(f'{fet_name} fT vs gm/Id (W = {W})') - plts[1].set_xlabel("gm/Id") - plts[1].set_ylabel("f_T") - plts[1].grid(True) + plots[1].set_xlabel("gm/Id") + plots[1].set_ylabel("f_T") + plots[1].grid(True) figs[2].suptitle(f'{fet_name} gm/gds vs gm/Id (W = {W})') - plts[2].set_xlabel("gm/Id") - plts[2].set_ylabel("gm/gds") - plts[2].grid(True) + plots[2].set_xlabel("gm/Id") + plots[2].set_ylabel("gm/gds") + plots[2].grid(True) figs[3].suptitle(f'{fet_name} gm/Id vs Vgg (W = {W})') - plts[3].set_xlabel("Vgg") - plts[3].set_ylabel("gm/Id") - plts[3].grid(True) - return figs, plts + plots[3].set_xlabel("Vgg") + plots[3].set_ylabel("gm/Id") + plots[3].grid(True) + return figs, plots -def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): +def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plots): """ Generates plot lines for FET bins simulation parameters. @@ -139,21 +139,21 @@ def gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts): vsweep: v-sweep values fet_W: FET width fet_L: FET length - plts: plots on which plot lines should be drawn + plots: plots on which plot lines should be drawn """ # plot some interesting things - 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}') + plots[0].plot(gm_id, id_W, label=f'W {fet_W} x L {fet_L}') + plots[1].plot(gm_id, ft, label=f'W {fet_W} x L {fet_L}') + plots[2].plot(gm_id, gm_gds, label=f'W {fet_W} x L {fet_L}') + plots[3].plot(vsweep, gm_id, label=f'W {fet_W} x L {fet_L}') -def close_plots(figs): +def close_plots(figures): """ Closes plots. """ - for f in figs: - plt.close(f) + for figure in figures: + plt.close(figure) def read_bins(fname): @@ -211,7 +211,7 @@ def generate_fet_plots( # later changed in the for loop c = create_test_circuit(fet_type, 0.15, 1, corner_path) - figs, plts = init_plots(fet_type, W) + figures, plots = init_plots(fet_type, W) try: for dev, bin, fet_W, fet_L in bins_by_W[(fet_type, W)]: fet_W, fet_L = float(fet_W), float(fet_L) @@ -220,26 +220,26 @@ def generate_fet_plots( c.element('XM1').parameters['W'] = fet_W c.element('XM1').parameters['L'] = fet_L gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fet_W) - gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plts) + gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plots) except Exception: - close_plots(figs) + close_plots(figures) raise figtitles = ['Id_w', 'fT', 'gm_gds', 'gm_id'] - for fg, name in zip(figs, figtitles): - lg = fg.legend( + for figure, name in zip(figures, figtitles): + lg = figure.legend( bbox_to_anchor=(1.05, 1), loc='upper left' ) - fg.tight_layout() - fg.savefig( + figure.tight_layout() + figure.savefig( Path(outdir) / ( outprefix + f'_{fet_type}_{name}_W{str(W).replace(".", "_")}.{ext}'), bbox_extra_artists=(lg,), bbox_inches='tight' ) - close_plots(figs) + close_plots(figures) def main(argv): From ee006e4a3f726c527cacf0e5fd9c56d21cc61d79 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 25 Nov 2020 18:53:14 +0100 Subject: [PATCH 20/22] base: added method for parsing bins.csv files Signed-off-by: Grzegorz Latosinski --- .../python-skywater-pdk/skywater_pdk/base.py | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/base.py b/scripts/python-skywater-pdk/skywater_pdk/base.py index bcfc7e8..bbc4d00 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/base.py +++ b/scripts/python-skywater-pdk/skywater_pdk/base.py @@ -22,12 +22,16 @@ import os from dataclasses import dataclass from dataclasses_json import dataclass_json from enum import Enum -from typing import Optional, Union, Tuple +from typing import Optional, Union, Tuple, List +from collections import namedtuple +from pathlib import Path +import csv from .utils import comparable_to_none from .utils import dataclass_json_passthru_config as dj_pass_cfg +FETBin = namedtuple('FETBin', ['device', 'bin', 'w', 'l']) LibraryOrCell = Union['Library', 'Cell'] @@ -545,6 +549,24 @@ class Cell: self)) return "{}__{}".format(self.library.fullname, self.name) + @classmethod + def parse_bins(cls, binsfile) -> List[FETBin]: + """ + Parse bins.csv file. + + Parameters + ---------- + binsfile: path to the bins.csv file + """ + with open(binsfile, 'r') as f: + reader = csv.reader(f) + next(reader) + res = [] + for line in reader: + res.append(FETBin(line[0], int(line[1]), float(line[2]), float(line[3]))) + return res + + @classmethod def parse(cls, s): kw = {} @@ -555,7 +577,6 @@ class Cell: return cls(**kw) - if __name__ == "__main__": import doctest doctest.testmod() From a4f7529f756923aa3baf3cebf405f09c82e7b51c Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 25 Nov 2020 18:55:05 +0100 Subject: [PATCH 21/22] fet: added using bins.csv parser from skywater_pdk.base Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/simulation/__init__.py | 0 .../simulation/analog/__init__.py | 0 .../skywater_pdk/simulation/analog/fet.py | 38 +++++++------------ 3 files changed, 14 insertions(+), 24 deletions(-) create mode 100644 scripts/python-skywater-pdk/skywater_pdk/simulation/__init__.py create mode 100644 scripts/python-skywater-pdk/skywater_pdk/simulation/analog/__init__.py diff --git a/scripts/python-skywater-pdk/skywater_pdk/simulation/__init__.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/__init__.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py index 6926cd0..12e863a 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py +++ b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py @@ -24,8 +24,13 @@ import matplotlib.pyplot as plt from pathlib import Path import csv from collections import defaultdict +import os import sys +sys.path.insert(0, os.path.abspath(__file__ + '/../../../../')) + +from skywater_pdk.base import Cell + logger = Logging.setup_logging() @@ -156,20 +161,6 @@ def close_plots(figures): plt.close(figure) -def read_bins(fname): - """ - Reads bins CSV file. - """ - with open(fname, 'r') as f: - r = csv.reader(f) - # drop CSV header - next(r) - res = [] - for line in r: - res.append(line) - return res - - def generate_fet_plots( corner_path, bins_csv, @@ -192,12 +183,12 @@ def generate_fet_plots( print(f'[generate_fet_plots] {corner_path} {bins_csv}' + f'{outdir} {outprefix} {only_W}') - bins = read_bins(bins_csv) + bins = Cell.parse_bins(bins_csv) bins_by_W = defaultdict(list) # group bins by W - for line in bins: - bins_by_W[(line[0], float(line[2]))].append(line) + for fetbin in bins: + bins_by_W[(fetbin.device, fetbin.w)].append(fetbin) Ws = [key for key in bins_by_W.keys() if only_W is None or key[1] in only_W] @@ -213,14 +204,13 @@ def generate_fet_plots( figures, plots = init_plots(fet_type, W) try: - for dev, bin, fet_W, fet_L in bins_by_W[(fet_type, W)]: - fet_W, fet_L = float(fet_W), float(fet_L) - if only_W is not None and fet_W not in only_W: + for fetbin in bins_by_W[(fet_type, W)]: + if only_W is not None and fetbin.w not in only_W: continue - c.element('XM1').parameters['W'] = fet_W - c.element('XM1').parameters['L'] = fet_L - gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fet_W) - gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fet_W, fet_L, plots) + c.element('XM1').parameters['W'] = fetbin.w + c.element('XM1').parameters['L'] = fetbin.l + gm_id, ft, id_W, gm_gds, vsweep = run_sim(c, iparam, fetbin.w) + gen_plots(gm_id, id_W, ft, gm_gds, vsweep, fetbin.w, fetbin.l, plots) except Exception: close_plots(figures) raise From a512ddf81a587ae51a0a3edb929436786321a9f8 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 25 Nov 2020 19:18:13 +0100 Subject: [PATCH 22/22] fet: added docstrings for modules Signed-off-by: Grzegorz Latosinski --- .../skywater_pdk/simulation/analog/fet.py | 19 ++++++++++++++++++- .../analog/generate-fet-bins-plots.py | 18 ++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py index 12e863a..c197d21 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py +++ b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/fet.py @@ -17,6 +17,19 @@ # # SPDX-License-Identifier: Apache-2.0 + +"""Module for creating FET characteristics plots from bins.csv files. + +This module allows simulating FET cells and creating: + +* Id/W vs gm/Id +* fT vs gm/Id +* gm/gds vs gm/Id +* gm/Id vs Vgg + +plots based on different FET length and width values from bins.csv file. +""" + import PySpice.Logging.Logging as Logging from PySpice.Spice.Netlist import Circuit from PySpice.Unit import u_V @@ -235,7 +248,11 @@ def generate_fet_plots( def main(argv): import argparse - parser = argparse.ArgumentParser(prog=argv[0]) + parser = argparse.ArgumentParser( + prog=argv[0], + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter + ) parser.add_argument( 'corner_path', help='Path to corner SPICE file containing FET definition', diff --git a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/generate-fet-bins-plots.py b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/generate-fet-bins-plots.py index 018f943..391de39 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/generate-fet-bins-plots.py +++ b/scripts/python-skywater-pdk/skywater_pdk/simulation/analog/generate-fet-bins-plots.py @@ -17,6 +17,14 @@ # # SPDX-License-Identifier: Apache-2.0 + +"""Creates plots for FET characteristics for FET cells in Skywater libraries. + +This script scans for FET cells in the Skywater PDK libraries and generates +the FET cells using methods from the fet submodule. +""" + + import argparse from pathlib import Path import sys @@ -28,7 +36,11 @@ from fet import generate_fet_plots def main(argv): - parser = argparse.ArgumentParser(prog=argv[0]) + parser = argparse.ArgumentParser( + prog=argv[0], + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter + ) parser.add_argument( 'libraries_dir', help='Path to the libraries directory of skywater-pdk', @@ -61,7 +73,9 @@ def main(argv): ) parser.add_argument( '--failed-inputs', - help='Path to files for which Symbolator failed to generate diagram', + help=('Path to an output file which will store all input filenames ' + + 'for which ngspice failed to simulate' + ), type=Path )