diff --git a/.gitignore b/.gitignore index e4df3b01..b6e28286 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ modules/module_3_8_bit_SAR_ADC/part_2_digital_comps/bootstrap_switch/python/ *.so *.out *.vcd +*.npz diff --git a/modules/module_0_foundations/gmid_repo/README.md b/modules/module_0_foundations/gmid_repo/README.md deleted file mode 100644 index 2e0ecc33..00000000 --- a/modules/module_0_foundations/gmid_repo/README.md +++ /dev/null @@ -1,431 +0,0 @@ -# Modified gmid Script for IHP Open PDK - -This repository contains a modified version of the original [gmid](https://github.com/medwatt/gmid) script, adapted specifically for use with the IHP Open PDK. - -## Acknowledgements - -We extend our thanks to [medwatt](https://github.com/medwatt) for providing a robust environment for generating GmID lookup tables with NgSpice. - -## Credits - -All credit for the original repository and script goes to [medwatt](https://github.com/medwatt). - -## Changes and Enhancements - -- **Terminal GUI**: A terminal-based GUI was developed to facilitate easy selection of MOSFETs in the IHP Open PDK for generating LUTs. -- **LUT Class**: The lookup table generator was modified to ensure compatibility with the IHP Open PDK netlisting format. -- **Automation Handling**: A dedicated handling script was created to automate the GUI process for improved workflow. - - - - -# MOSFET Characterization in Python - -## Motivation - -This tool has the following goals: - -1. Provide an easy way of creating plots of MOSFET parameters, such as those - used in the gm/ID design methodology. - -2. Provide a tool that does not depend on any proprietary software or require - licensing fees. - -3. Open source so that it can be easily modified/extended by the user. - -## Installation - -### Requirements - -This tools is written in Python and requires the following: - -- `Numpy`, `Scipy`, and `Matplotlib` for data analysis and plotting. - -- [`ngspice`](https://ngspice.sourceforge.io/) or `hspice` for generating the - lookup table. - - -### Installation - -- Clone this repository: `git clone https://github.com/medwatt/gmid.git`. - -- Inside the directory, invoke: `pip install .`. - - -## Generating a Lookup Table - -Before any plots can be made, a lookup table of all the relevant parameters -must first be created. This is done by instantiating an object from the -`LookupTableGenerator` and then building the table with the `build` method. An -example is given below. - -```python -from mosplot import LookupTableGenerator - -obj = LookupTableGenerator( - description="freepdk 45nm ngspice", - simulator="ngspice", - simulator_path="/usr/bin/ngspice", # optional - model_paths=[ - "/home/username/gmid/models/NMOS_VTH.lib", - "/home/username/gmid/models/PMOS_VTH.lib", - ], - model_names={ - "nmos": "NMOS_VTH", - "pmos": "PMOS_VTH", - }, - vsb=(0, 1.0, 0.1), - vgs=(0, 1.0, 0.01), - vds=(0, 1.0, 0.01), - width=10e-6, - lengths=[50e-9, 100e-9, 200e-9, 400e-9, 800e-9, 1.6e-6, 3.2e-6, 6.4e-6], -) -obj.build("/home/username/gmid/lookup_tables/freepdk_45nm_ngspice.npy") -``` - -A summary of some of the parameters is given below: - -- The simulator used is specified with the `simulator` parameter. At the - moment, only `ngspice` and `hspice` are supported. If you're using windows or - some linux distribution where `ngspice` and `hspice` are named differently, - you will have to pass the full path to the binary to the `simulator_path` - variable. - -- The lookup_table will be generated for a specific transistor model. Provide - the location of the model files as a list using the `model_paths` parameter. - Since it is possible to have more than one model definition inside a file, - you need to specify the model name. This is done via the `model_names` - parameter, where the keys are always `"nmos"` and `"pmos` and their values - are the names of the models to be used. - -- If there's a specific need to pass in some custom SPICE commands, these - should be done via the `raw_spice` parameter (not shown in the example above). - -- To generate a lookup table, the bulk, gate, and drain voltages relative to - the source have to be swept over a range of voltages. Specify the range in - the form `(start, stop, step)`. The smaller the step size, the bigger is the - size of the lookup table. - -- The `lengths` can be provided as a list of discrete values or a 1-dimensional - `numpy` array. - -- Only a single `width` should be provided. The assumption here is that the - parameters of the MOSFET scale linearly with the width. Because of this - assumption, all parameters that are width-dependent must be de-normalized - with respect to the current or width that you're working with. - -- The directory where the generated lookup table is saved is passed directly to - the `build` method. - -## Using the Tool - -Because of the interactive nature of designing analog circuits, using this -script within a `jupyter` notebook is highly recommended. - -### Imports - -We begin by making the following imports: - -```python -import numpy as np -from mosplot import load_lookup_table, LoadMosfet -``` - -The `load_lookup_table` function loads a lookup table such as the one generated -in the previous section. - -```python -lookup_table = load_lookup_table("path/to/lookup-table.npy") -``` - -The `LoadMosfet` class contains methods that can be used to generate plots -seamlessly. If you plan to modify the style of the plots or plot things -differently, you will also have to import `matplotlib`. - -```python -import matplotlib.pyplot as plt -plt.style.use('path/to/style') -``` - -### Making Simple Plots - -We start by creating an object called `nmos` that selects the NMOS -from the lookup table and sets the source-bulk and drain-source voltages to -some fixed values. Since the data is 4-dimensional, it is necessary to fix two -of the variables at a time to enable 2-dimensional plotting. - -```python -nmos = LoadMosfet(lookup_table=lookup_table, mos="nmos", vsb=0.0, vds=0.5, vgs=(0.3, 1)) -``` - -The above code filters the table at `vsb=0.0` and `vds=0.5` for all `lengths` -and for `vgs` values between `(0.3, 1)`. You can also include a step such as -`(0.3, 1, 0.02)`. If you want all values of `vgs`, either set it to `None` or -don't include it. - -Methods are available to create the most commonly-used plots in the gm/ID -methodology so that you don't have to type them. These are: - -- `current_density_plot()`: this plots $I_{D}/W$ vs $g_{m}/I_{D}$. -- `gain_plot()`: this plots $g_m / g_{ds}$ vs $g_{m}/I_{D}$. -- `transit_frequency_plot()`: this plots $f_{T}$ vs $g_{m}/I_{D}$. -- `early_voltage_plot()`: this plots $V_{A}$, vs $g_{m}/I_{D}$. - -For example, the plot of $I_{D}/W$ vs $g_{m}/I_{D}$ is shown below. - -```python -nmos.current_density_plot() -``` - -![current density plot](./figures/nmos_current_density.svg) - -When the lookup table includes a lot of lengths, the plot can become crowded. -You can pass a list of lengths to plot with the `length` parameter. - -Use `nmos.lengths` to get a list of all the lengths in the lookup table. - -``` -array([5.0e-08, 1.0e-07, 2.0e-07, 4.0e-07, 8.0e-07, 1.6e-06, 3.2e-06, - 6.4e-06]) -``` - -Pass a filtered list to the `current_density_plot` method. - -```python -nmos.current_density_plot( - lengths = [5.0e-08, 1.0e-07, 2.0e-07] -) -``` - -![current density plot](./figures/nmos_current_density_filtered.svg) - -Note that the tool does its best to determine how to scale the axes. For -example, in the last plot, a `log` scale was chosen for the y-axis. We can -easily overwrite that, as well as other things. - -```python -nmos.current_density_plot( - lengths = [5.0e-08, 1.0e-07, 2.0e-07], - y_scale = 'linear', - x_limit = (5, 20), - y_limit = (0, 300), - save_fig="path/to/save/figure/with/extension" -) -``` - -![current density plot](./figures/nmos_current_density_options.svg) - -### Plotting by Expression - -Now, suppose we want to plot something completely custom. The example below -shows how. - -```python -nmos.plot_by_expression( - x_expression = nmos.vgs_expression, - y_expression = { - "variables": ["id", "gds"], - "function": lambda x, y: x / y, - "label": "$I_D / g_{ds} (A/S)$" - }, -) -``` - -![custom expression](./figures/nmos_custom_expression_1.svg) - -For this example, we want $V_{\mathrm{GS}}$ on the x-axis. Since $V_{\mathrm{GS}}$ is such a -commonly-used expression, it is already defined in the code. Other -commonly-used expressions are also defined, such as: - -- `gmid_expression` -- `vgs_expression` -- `vds_expression` -- `vsb_expression` -- `gain_expression` -- `current_density_expression` -- `transist_frequency_expression` -- `early_voltage_expression` - -For the y-axis, we want a custom expression that uses the parameters $I_D$ and -$g_{\mathrm{ds}}$. This can be done by defining a dictionary that specifies the -variables needed and how to calculate the required parameter. The `label` field -is optional. The function field is also optional if we want to just plot the -parameter, as shown in the example below. - -```python -nmos.plot_by_expression( - x_expression = nmos.vgs_expression, - # y_expression = nmos.id_expression, ## same as below - y_expression = { - "variables": ["id"], - "label": "$I_D (A)$" - } -) -``` - -![custom expression](./figures/nmos_custom_expression_2.svg) - -## Looking Up Values - -While having plots is a good way to visualize trends, we might also just be -interested in the raw value. - -![gain expression](./figures/nmos_gain_plot.svg) - -Looking at the figure above, it's hard to read the exact value on the y-axis -for a particular value on the x-axis, especially more so when the scale is -logarithmic. Also, what if we need to read the value for a length that -is not defined in our lookup table? - -There are two ways to go about this: - -- Zoom in and click on the plot. This prints out the `x` and `y` - coordinates. Note, in jupyter notebooks, you need to execute `%matplotlib - widget` or `%matplotlib qt` to interact with the plot. - -- Use a lookup method to get a more precise value. - -### Lookup Using Interpolation - -The snippet below shows how we can lookup the `gain` given the `length` and -`gmid`. The returned value is calculated using interpolation from the available -data. The accuracy of the result depends on how far the points are from those -defined in the table. - -```python -x = nmos.interpolate( - x_expression=nmos.lengths_expression, - x_value=100e-9, - y_expression=nmos.gmid_expression, - y_value=15, - z_expression=nmos.gain_expression, -) -``` - -The above code evaluates the `gain` at a single point. Suppose we want to know -the `gmid` or `length` for which `0.08 <= vdsat < 0.12` and `1e6 <= gds < 4e-6`. -The snippet below shows how. - -```python -x = nmos.interpolate( - x_expression=nmos.vdsat_expression, - x_value=(0.08, 0.12, 0.01), - y_expression=nmos.gds_expression, - y_value=(1e-6, 4e-6, 1e-6), - z_expression=nmos.gmid_expression, - # z_expression=nmos.length_expression, -) - # 1e-6 -array([[17.95041245, 17.89435802, 17.47526426], # 0.08 - [16.87609489, 16.76595338, 16.53927928], - [14.77585736, 15.09803158, 14.9483348 ], - [14.12540234, 14.05481451, 14.04265227]]) -``` - -### Lookup By Expression - -`lookup_expression_from_table()` simply looks up an expression from the -table. It doesn't use any interpolation. So, make sure that the values you -are looking up are present in the table. - -```python -x = nmos.lookup_expression_from_table( - lengths=100e-9, - vsb=0, - vds=(0.0, 1, 0.01), - vgs=(0.0, 1.01, 0.2), - primary="vds", - expression=nmos.current_density_expression, -) -``` - -## Plotting Methods - -### Plot by Sweep - -The `plot_by_sweep` method is extremely flexible and can be used to create -all sorts of plots. For example, the snippet below shows how to plot the -traditional output characteristic plot of a MOSFET. - -```python -nmos.plot_by_sweep( - lengths=180e-9, - vsb = 0, - vds = (0.0, 1, 0.01), # you can also set to `None` - vgs = (0.0, 1.01, 0.2), - x_expression_expression = nmos.vds_expression, - y_expression_expression = nmos.id_expression, - primary = "vds", - x_eng_format=True, - y_eng_format=True, - y_scale='linear', -) -``` - -![output characteristic](./figures/nmos_output_characteristics.svg) - -### Quick Plot - -Let's say we want to see how $V_{\mathrm{DS}_{\mathrm{SAT}}}$ (the drain-source -voltage required to enter saturation) compares with $V_{\mathrm{OV}}$ and -$V^{\star} = \frac{2}{g_m / I_D}$ in a single plot. We can generate each of -these plots individually, as we did before, but ask the method to return the -plot data so that we can combine them in a single plot. Note that you can also -use `lookup_expression_from_table()` to return the required data if you don't -want to see the plot. - -```python -vdsat = nmos.plot_by_expression( - lengths=[45e-9], - x_expression = nmos.vgs_expression, - y_expression = nmos.vdsat_expression, - return_result = True, -) - -vov = nmos.plot_by_expression( - lengths=[45e-9], - x_expression = nmos.vgs_expression, - y_expression = { - "variables": ["vgs", "vth"], - "function": lambda x, y: x - y, - }, - return_result = True, -) - -vstar = nmos.plot_by_expression( - lengths=[45e-9], - x_expression = nmos.vgs_expression, - y_expression = { - "variables": ["gm", "id"], - "function": lambda x, y: 2 / (x/y), - }, - return_result=True, -) -``` - -The result is returned in a tuple in the form `(x_data, y_data)`. We can then -make any custom plot using `matplotlib`. Nevertheless, there's a method called -`quick_plot()` that formats the plot in the same way as the generated plots. -`quick_plot()` accepts `numpy` arrays, or a list of `x` and `y` values, as -shown in the example below. - -```python -nmos.quick_plot( - x = [vdsat[0], vstar[0], vov[0]], - y = [vdsat[1], vstar[1], vov[1]], - legend = ["$V_{\\mathrm{DS}_{\\mathrm{SAT}}}$", "$V^{\\star}$", "$V_{\\mathrm{OV}}$"], - x_limit = (0.1, 1), - y_limit = (0, 0.6), - x_label = "$V_{\\mathrm{GS}}$", - y_label = "$V$", -) -``` - -![qucik plot](./figures/nmos_quick_plot.svg) - -# Acknowledgment - -- Parsing the output from `hspice` is done using - [this](https://github.com/HMC-ACE/hspiceParser) script. - -- If you find this tool useful, it would be nice if you cite it. diff --git a/modules/module_0_foundations/gmid_repo/__pycache__/gmid_launcher.cpython-310.pyc b/modules/module_0_foundations/gmid_repo/__pycache__/gmid_launcher.cpython-310.pyc deleted file mode 100644 index be42a3a4..00000000 Binary files a/modules/module_0_foundations/gmid_repo/__pycache__/gmid_launcher.cpython-310.pyc and /dev/null differ diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_current_density.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_current_density.svg deleted file mode 100644 index 4794aa66..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_current_density.svg +++ /dev/null @@ -1,2262 +0,0 @@ - - - - - - - - 2023-11-07T10:57:11.324023 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_filtered.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_filtered.svg deleted file mode 100644 index 15ea90a8..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_filtered.svg +++ /dev/null @@ -1,2522 +0,0 @@ - - - - - - - - 2023-11-07T10:58:38.762276 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_options.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_options.svg deleted file mode 100644 index dba28229..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_options.svg +++ /dev/null @@ -1,2330 +0,0 @@ - - - - - - - - 2023-11-07T11:00:16.184541 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_1.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_1.svg deleted file mode 100644 index c6b9fabc..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_1.svg +++ /dev/null @@ -1,2973 +0,0 @@ - - - - - - - - 2023-11-07T11:01:12.285212 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_2.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_2.svg deleted file mode 100644 index 53e2efd7..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_2.svg +++ /dev/null @@ -1,2404 +0,0 @@ - - - - - - - - 2023-11-07T11:07:25.240509 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_gain_plot.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_gain_plot.svg deleted file mode 100644 index 543bc867..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_gain_plot.svg +++ /dev/null @@ -1,2439 +0,0 @@ - - - - - - - - 2023-11-09T23:43:09.194887 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_output_characteristics.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_output_characteristics.svg deleted file mode 100644 index 2aadfbdc..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_output_characteristics.svg +++ /dev/null @@ -1,2045 +0,0 @@ - - - - - - - - 2023-11-02T16:08:38.335943 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_plot_by_sweep.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_plot_by_sweep.svg deleted file mode 100644 index 79d30548..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_plot_by_sweep.svg +++ /dev/null @@ -1,1739 +0,0 @@ - - - - - - - - 2023-11-02T15:41:01.451027 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot.svg deleted file mode 100644 index d25d864b..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot.svg +++ /dev/null @@ -1,2371 +0,0 @@ - - - - - - - - 2023-11-02T14:58:52.693797 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot2.svg b/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot2.svg deleted file mode 100644 index 4a8724bd..00000000 --- a/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot2.svg +++ /dev/null @@ -1,1309 +0,0 @@ - - - - - - - - 2023-11-02T15:45:04.279117 - image/svg+xml - - - Matplotlib v3.8.0, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/module_0_foundations/gmid_repo/gmid_launcher.py b/modules/module_0_foundations/gmid_repo/gmid_launcher.py deleted file mode 100644 index 4c0b84c9..00000000 --- a/modules/module_0_foundations/gmid_repo/gmid_launcher.py +++ /dev/null @@ -1,38 +0,0 @@ -from mosplot import LookupTableGenerator -from mosplot.src.manu_man import description_menu, sweeping_menu, input_selection, transistor_menu -import os - -def main(): - pdk_type = input_selection() - model, transistor_model = transistor_menu() # model is a dictionary - description, simulator, model_path = description_menu(model) - vsb, vgs, vds, width, lengths = sweeping_menu() - - # Create the LookupTableGenerator object with all parameters - obj = LookupTableGenerator( - simdisc=transistor_model, - description=description, - simulator=simulator, - model_paths=model_path, - model_names=model, # Directly use the model dictionary - vsb=vsb, - vgs=vgs, - vds=vds, - width=width, - lengths=lengths, - ) - - - - file_name = f"{description}" - file_path = os.path.join('LUTs', file_name) - - - os.makedirs('LUTs', exist_ok=True) - - obj.build(file_path) - - -if __name__ == "__main__": - main() - diff --git a/modules/module_0_foundations/gmid_repo/matplotlib_style/style.mplstyle b/modules/module_0_foundations/gmid_repo/matplotlib_style/style.mplstyle deleted file mode 100644 index e6816080..00000000 --- a/modules/module_0_foundations/gmid_repo/matplotlib_style/style.mplstyle +++ /dev/null @@ -1,55 +0,0 @@ -# Matplotlib style for scientific plotting -# This is the base style for "SciencePlots" -# see: https://github.com/garrettj403/SciencePlots - -# Set color cycle: blue, green, yellow, red, violet, gray -axes.prop_cycle : cycler('color', ['0C5DA5', '00B945', 'FF9500', 'FF2C00', '845B97', '474747', '9e9e9e']) - -# Set default figure size -figure.figsize : 3.5, 2.625 - -# Set x axis -xtick.direction : in -xtick.major.size : 3 -xtick.major.width : 0.5 -xtick.minor.size : 1.5 -xtick.minor.width : 0.5 -xtick.minor.visible : True -xtick.top : True - -# Set y axis -ytick.direction : in -ytick.major.size : 3 -ytick.major.width : 0.5 -ytick.minor.size : 1.5 -ytick.minor.width : 0.5 -ytick.minor.visible : True -ytick.right : True - -# Set line widths -axes.linewidth : 0.5 -grid.linewidth : 0.5 -lines.linewidth : 1. - -# Remove legend frame -legend.frameon : False - -# Always save as 'tight' -savefig.bbox : tight -savefig.pad_inches : 0.05 -savefig.transparent : True - -# Increase global font size -font.size : 14 - -# Use sans-serif fonts -font.sans-serif : cm -font.family : sans-serif - -# Use CM Sans Serif for math text -axes.formatter.use_mathtext : True -mathtext.fontset : cm - -# Use LaTeX for math formatting -text.usetex : True -text.latex.preamble : \usepackage{amsmath} \usepackage{amssymb} diff --git a/modules/module_0_foundations/gmid_repo/mosplot/__init__.py b/modules/module_0_foundations/gmid_repo/mosplot/__init__.py deleted file mode 100644 index 4117c367..00000000 --- a/modules/module_0_foundations/gmid_repo/mosplot/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .src.main import LoadMosfet, load_lookup_table -from .src.lookup_table_generator import LookupTableGenerator - -__all__ = ['LoadMosfet', 'load_lookup_table', 'LookupTableGenerator'] diff --git a/modules/module_0_foundations/gmid_repo/mosplot/__pycache__/__init__.cpython-310.pyc b/modules/module_0_foundations/gmid_repo/mosplot/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 352e607a..00000000 Binary files a/modules/module_0_foundations/gmid_repo/mosplot/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/hspice_parser.cpython-310.pyc b/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/hspice_parser.cpython-310.pyc deleted file mode 100644 index fc9f6666..00000000 Binary files a/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/hspice_parser.cpython-310.pyc and /dev/null differ diff --git a/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/ngspice_parser.cpython-310.pyc b/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/ngspice_parser.cpython-310.pyc deleted file mode 100644 index c75925d7..00000000 Binary files a/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/ngspice_parser.cpython-310.pyc and /dev/null differ diff --git a/modules/module_0_foundations/gmid_repo/mosplot/parsers/hspice_parser.py b/modules/module_0_foundations/gmid_repo/mosplot/parsers/hspice_parser.py deleted file mode 100644 index 242d284b..00000000 --- a/modules/module_0_foundations/gmid_repo/mosplot/parsers/hspice_parser.py +++ /dev/null @@ -1,615 +0,0 @@ -""" -Author: Raphael Gonzalez (RAffA), Mathew Spencer -github: RaffaGonzo - - -Copyright 2021 Raphael Gonzalez, Mathew Spencer - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" -import struct -from os.path import basename, split, join -from os import mkdir -from shutil import rmtree -import pickle -import sys - -COUNT = 0 - -def read_data_block(data_block, vrsn): - data_format = {'9601': (' 1 - assert COUNT > 0 # for debugging - - func_dict = {'tr': general_make_dict, - 'sw': general_make_dict, - 'ac': ac_make_dict} - - return func_dict[ext](var_lst, data_lst, header_str, MULTI_SWEEP_FLAG) - -def write_to_file(write_path, file_content, ext=None): - if type(file_content) == str: - with open(write_path, 'w+') as file: - file.write(file_content) - elif ext == 'mat': - sio.savemat(write_path, file_content) - else: - with open(write_path, 'wb') as file: - pickle.dump(file_content, file, protocol=pickle.HIGHEST_PROTOCOL) - -def get_outfile_name(path, ext=None): - file_name = basename(path) - outfile_name = file_name.replace('.', '_') - if ext is not None: - outfile_name += f'.{ext}' - return outfile_name - -def import_export_binary(path, ext, from_ext): - """ - this function will import *.tr*, *.sw*, and *.ac* binary and put a similarly named csv, matlab *.m, or python.pickle file/ folder of files in the same directory - :param path: (str) path to the *.tr* binary to be imported - :param ext: (str) the extension of the output file [only options *.m, *.csv, and *.pickle] - :return: None - """ - dict_to_pickle_dict = lambda dict_data: dict_data - - ext_dict = {'m': dict_to_matlab_str, - 'csv': dict_to_csv_str, - 'pickle': dict_to_pickle_dict, - 'mat': dict_to_mat_obj} - try: - header, data = read_binary_signal_file(path) - main_dict, sweep_flag, sweep_var_name, sweep_values = write_to_dict(data, header, from_ext) - # use sweep_flag to tell if it should be a folder of csvs or single file - if sweep_flag and ext == 'csv': - content = '' - fpath = path + '_' + sweep_var_name + '(' - outfile_name = get_outfile_name(path, ext) - folder_name = get_outfile_name(outfile_name) # gets rid of dot - og_path = str(split(fpath)[0]) - path = join(og_path, folder_name) - try: - mkdir(path) - except FileExistsError: # this will overwrite an existing folder with the same name - rmtree(path) - mkdir(path) - - for i in range(len(list(main_dict.values())[0])): - temp_dict = {} - for key in main_dict.keys(): - temp_dict[key] = [main_dict[key][i]] - file_name = get_outfile_name(fpath) + str(sweep_values[i]) + ').csv' - full_path = join(path, file_name) - content = dict_to_csv_str(temp_dict) - write_to_file(full_path, content) - else: - outfile_name = get_outfile_name(path, ext) - dir_path, _ = split(path) - file_path = join(str(dir_path), outfile_name) - content = ext_dict[ext](main_dict) - write_to_file(file_path, content, ext) - return main_dict, content - - except KeyError: - raise ValueError('the extension must NOT have the "." in it ex: "csv" | the only extension options are m, csv, and pickle') - except FileNotFoundError as err: - print(err) - -def import_export_ascii(path, ext): - dict_to_pickle_dict = lambda dict_data: dict_data - - ext_dict = {'m': dict_to_matlab_str, - 'csv': dict_to_csv_str, - 'pickle': dict_to_pickle_dict, - 'mat': dict_to_mat_obj} - try: - main_dict = signal_file_ascii_read(path) - content_obj = ext_dict[ext](main_dict) - sweep_flag = len(list(main_dict.values())[0]) > 1 - sweep_var_name = '' - sweep_values = [] - for key, val in main_dict.items(): - if len(val[0]) == 1 and sweep_flag: - sweep_var_name = key - sweep_values = sum(val, []) - break - if sweep_flag and ext == 'csv': - fpath = f'{path}_{sweep_var_name}(' - outfile_name = get_outfile_name(path, ext) - folder_name = get_outfile_name(outfile_name) # gets rid of dot - og_path = str(split(fpath)[0]) - path = join(og_path, folder_name) - try: - mkdir(path) - except FileExistsError: # this will overwrite an existing folder with the same name - rmtree(path) - mkdir(path) - - for i in range(len(list(main_dict.values())[0])): - temp_dict = {} - for key in main_dict.keys(): - temp_dict[key] = [main_dict[key][i]] - file_name = get_outfile_name(fpath) + str(sweep_values[i]) + ').csv' - full_path = join(path, file_name) - content = dict_to_csv_str(temp_dict) - write_to_file(full_path, content) - else: - outfile_name = get_outfile_name(path, ext) - dir_path, _ = split(path) - file_path = join(str(dir_path), outfile_name) - write_to_file(file_path, content_obj, ext) - return main_dict, content_obj - except KeyError: - raise ValueError('the only extension options are "m," "csv," or "pickle"') - except FileNotFoundError as err: - print(err) - -def usage(): - usage_str = """Usage: - python Hspice_parse - - *** note: This parser only supports the 2001, 9601 formats - designated by ".option post_version=9601" for example - - supported input file types are: - - AC simulations *.ac* - - DC sweeps *.sw* - - Transient simulations *.tr* - - supported output file types are: - - CSV note that if there are multiple sweeps in the input - file this option will output a folder of CSVs - - pickle - - matlab's *.m - - -h or --help to get this message - """ - print(usage_str) - -def get_from_ext(path): # this function is for auto detecting the input file extention - file_name = basename(path) - name_components = file_name.split('.') - entire_ext = name_components[-1] - return entire_ext[:2] # returns the first two chars of the extension i.e. "ac" "sw" "tr" and ignores the numbers after - -def reformat(sweep_lst): - """ - This function will change the original output of signal_file_ascii_read() to the format that - the write to dict function makes. - :param sweep_lst: list of dictionaries - :return: dictionary of 2D lists - """ - rtn_dict = {key: [] for key in sweep_lst[0].keys()} - for sweep in sweep_lst: - for key, value in sweep.items(): - rtn_dict[key].append(value) - return rtn_dict - -###################### ascii parsing ################### -# author: Mathew Spencer - -# class simulation_result(): -# """All the results germane to a single hSpice simulation including -# results, measures, parameters, temperature and alter number. -# -# Contains dicts called 'results' 'measures' 'parameters' and -# 'misc' each of which is keyed with named values and has values -# corresponding to the results. -# """ -# pass - - -# class sweep_result(): -# """Aggregates the results of individual simulations -# """ -# pass - -def signal_file_ascii_read(post2file): - """Accepts a raw sweep or measure file from spice in ASCII format - (.tr*, .sw*, .mt*, etc.) and returns a list of dicts containing the - signal and parameter names as keys. Signal name keys have an array - containing the simulation results as values. Parameter name keys have - the parameter as a value. There is one dict in the array for each - simulation sweep point.""" - - ## Preamble part of header - f = open(post2file) - l = f.readline() # 1st line contains string w/ # of variables - nauto = int(l[0:4]) - nprobe = int(l[4:8]) - nsweepparam = int(l[8:12]) - l = f.readline() # 2nd and 3rd lines are copyright and stuff - l = f.readline() - ndataset = int(l.split()[-1]) # 3rd line ends with the number of data sets - - ## Number and name part of header - l = f.readline() # 4th line+ useful, but may be wrapped - while l.find('$&%#') == -1: - l = l + f.readline() - l = l.replace('\n', '') - simparams = l.split()[:-1] # Throw away terminator string - datatypes = simparams[0:nauto + nprobe] # NOTUSED - varnames = simparams[nauto + nprobe:2 * (nauto + nprobe)] - paramnames = simparams[2 * (nauto + nprobe):] - - # Transform varnames and paramnames for Matlab - varnames = [x.partition('(')[0] if x.startswith('x') else x for x in varnames] - varnames = [x.replace('(', '_') for x in varnames] - varnames = [x.replace('.', '_') for x in varnames] - varnames = [x.replace(':', '_') for x in varnames] - paramnames = [x.replace(':', '_') for x in paramnames] - paramnames = ['param_' + x for x in paramnames] - - # Read data block - all_sweep_results = [] - l = f.readline().strip() - while l: - # Set up data storage structure - this_sweep_result = {} - for name in varnames: - this_sweep_result[name] = [] - for name in paramnames: - this_sweep_result[name] = 0 - - ## Data block for one sweep - numbers = [] - fieldwidth = l.find('E') + 4 - while True: - lastrow = l.find('0.1000000E+31') != -1 - while l: - numbers.append(float(l[0:fieldwidth])) - l = l[fieldwidth:] - if lastrow: - break - else: - l = f.readline().strip() - numbers = numbers[:-1] # throw away terminator - params = numbers[:nsweepparam] - for index in range(len(varnames)): - this_sweep_result[varnames[index]] = numbers[nsweepparam + index::nauto + nprobe] - this_sweep_result.update(zip(paramnames, [[x] for x in params])) - all_sweep_results.append(this_sweep_result) - l = f.readline().strip() - - f.close() - return reformat(all_sweep_results) - -def signal_array_to_matlab_string(signal_array, accum=True): # Todo: This should be obsolete now! - """ Takes in a signal array produced by signal_file_read and returns a string that - is suitable for inclusion in a .m file to be loaded into matlab. Makes 2D arrays - for DC and AC sims. 1D arrays numbered by sweep for transients b/c length uneven. - """ - matlab_string = '' - if accum: - # first, slice across the signal array to gather like signals into 2D arrays - signal_accumulation = signal_array[0] - for name, signal in signal_accumulation.items(): - signal_accumulation[name] = ['%.5e' % x for x in signal] - for simulation_result in signal_array[1:]: - for name, signal in simulation_result.items(): - signal_accumulation[name].extend([';'] + ['%.5e' % x for x in signal]) - # Second write out to a .m file string - for name, signal in signal_accumulation.items(): - matlab_string = matlab_string + name + '= [' + ' '.join(signal) + '];\n' - else: - # some cases, esp transient sims, can't be accumulated b/c different sizes - matlab_string = '' - signal_accumulation = signal_array[0] - for name, signal in signal_accumulation.items(): - signal_accumulation[name] = [['%.5e' % x for x in signal]] - for simulation_result in signal_array[1:]: - for name, signal in simulation_result.items(): - signal_accumulation[name].append(['%.5e' % x for x in signal]) - for name, signal_list in signal_accumulation.items(): - matlab_string = matlab_string + name + '={' - for signal in signal_list: - matlab_string = matlab_string + '[ ' + ' '.join(signal) + '];\n' - matlab_string = matlab_string + '};' - return matlab_string - -def measure_file_read(measure_file): - """Reads a measure file and resturns a dict keyed on the names of the columns - with values that are a list of results""" - f = open(measure_file) - l = f.readline().strip() # 1st line auto generated - param_count = int(l.split('PARAM_COUNT=')[-1].strip()) - l = f.readline().strip() # 2nd line is the comment - l = f.readline().strip() # 3rd line starts var names, last is alter# - while (l.find('alter#') == -1): - l = l + ' ' + f.readline().strip() - l = l.replace('#', '') - varnames = l.split() - varnum = len(varnames) - measure_result = {name: [] for name in varnames} - l = f.readline().strip() # 4th line starts data clumps to EOF - - while l: - vals = l.split() - if len(vals) != varnum: # accumulate results over multiple lines - l = l + ' ' + f.readline().strip() - else: # then write to measure dict - for name, val in zip(varnames, vals): - if val == 'failed': # skip over failed measures - val = 0 # silent failures are the best kind! - measure_result[name].append([float(val)]) # todo that I added [] to make the lists 2D not sure if necessary - l = f.readline().strip() - f.close() - return measure_result - -def measure_result_to_matlab_string(measure_result): # this should be obsolete now - matlab_string = '' - for name, signal in measure_result.items(): - matlab_string = matlab_string + name + '= [' + ' '.join(['%.5e' % x for x in signal]) + '];\n' - return matlab_string -# end of Spencer's (lightly modified) code - -def is_binary(file_path): - with open(file_path) as file: - try: - file.readline() - return False # this means that it is ascii - except UnicodeDecodeError: - return True # this means that it is binary - -def import_export(path, ext): - if is_binary(path): - from_ext = get_from_ext(path) - import_export_binary(path, ext, from_ext) - else: - import_export_ascii(path, ext) - -if __name__ == '__main__': - import sys - try: - if sys.argv[1] == '-h' or sys.argv[1] == '--help' or sys.argv[1] == '-help': - usage() - else: - path = sys.argv[1] - extension = sys.argv[2] - if extension == 'mat': - try: - import scipy.io as sio - import numpy as np - except ImportError: - print("To use Matlab's .mat extension, you must have Scipy and Numpy installed on your machine. See https://www.scipy.org/install.html and https://numpy.org/install for details.") - exit(1) - import_export(path, extension) - except: - usage() - resp_str = '\n\n This command needs the path to the file for import and the extension of the file being output.\n The extension can be either m, csv, or pickle. No other extensions are supported with this file at this time. If you are seeing this message, then something went wrong.' - print(resp_str) diff --git a/modules/module_0_foundations/gmid_repo/mosplot/parsers/ngspice_parser.py b/modules/module_0_foundations/gmid_repo/mosplot/parsers/ngspice_parser.py deleted file mode 100644 index a285e037..00000000 --- a/modules/module_0_foundations/gmid_repo/mosplot/parsers/ngspice_parser.py +++ /dev/null @@ -1,63 +0,0 @@ -import numpy as np - -class NgspiceRawFileReader: - def __init__(self): - self.bsize_sp = 512 - self.mdata_list = [ - b"title", - b"date", - b"plotname", - b"flags", - b"no. variables", - b"no. points", - b"dimensions", - b"command", - b"option", - ] - - def read_file(self, fname): - """Read ngspice binary raw files. Return tuple of the data, and the - plot metadata. The dtype of the data contains field names. This is - not very robust yet, and only supports ngspice. - >>> darr, mdata = rawread('test.py') - >>> darr.dtype.names - >>> plot(np.real(darr['frequency']), np.abs(darr['v(out)'])) - """ - with open(fname, "rb") as fp: - plot = {} - arrs = [] - plots = [] - while True: - mdata = fp.readline(self.bsize_sp).split(b":", maxsplit=1) - if len(mdata) == 2: - key = mdata[0].lower() - if key in self.mdata_list: - plot[key] = mdata[1].strip() - elif key == b"variables": - nvars = int(plot[b"no. variables"]) - npoints = int(plot[b"no. points"]) - varspecs = [ - fp.readline(self.bsize_sp).strip().decode("ascii").split() - for _ in range(nvars) - ] - plot["varnames"], plot["varunits"] = zip( - *[(spec[1], spec[2]) for spec in varspecs] - ) - elif key == b"binary": - rowdtype = np.dtype( - { - "names": plot["varnames"], - "formats": [ - np.complex_ - if b"complex" in plot[b"flags"] - else np.float_ - ] - * nvars, - } - ) - arrs.append(np.fromfile(fp, dtype=rowdtype, count=npoints)) - plots.append(plot.copy()) - fp.readline() # Read to the end of line - else: - break - return (arrs, plots) diff --git a/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/lookup_table_generator.cpython-310.pyc b/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/lookup_table_generator.cpython-310.pyc deleted file mode 100644 index ae9e0e2c..00000000 Binary files a/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/lookup_table_generator.cpython-310.pyc and /dev/null differ diff --git a/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/main.cpython-310.pyc b/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/main.cpython-310.pyc deleted file mode 100644 index b0996d4a..00000000 Binary files a/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/main.cpython-310.pyc and /dev/null differ diff --git a/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/manu_man.cpython-310.pyc b/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/manu_man.cpython-310.pyc deleted file mode 100644 index 0a6e1cda..00000000 Binary files a/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/manu_man.cpython-310.pyc and /dev/null differ diff --git a/modules/module_0_foundations/gmid_repo/mosplot/src/lookup_table_generator.py b/modules/module_0_foundations/gmid_repo/mosplot/src/lookup_table_generator.py deleted file mode 100644 index d4596c18..00000000 --- a/modules/module_0_foundations/gmid_repo/mosplot/src/lookup_table_generator.py +++ /dev/null @@ -1,336 +0,0 @@ -# -----------------------------------------------------------------------------# -# Author: Mohamed Watfa -# URL: https://github.com/medwatt/ -# -----------------------------------------------------------------------------# - -import os -import shutil -import pickle -import tempfile -import subprocess -from pick import pick -import numpy as np -from mosplot.src.manu_man import transistor_menu - -from ..parsers.ngspice_parser import NgspiceRawFileReader -from ..parsers.hspice_parser import import_export - -################################################################################ - -def range_to_arr(r): - start, stop, step = r - return np.arange(start, stop + step, step) - -class LookupTableGenerator: - def __init__( - self, - identifier=None, - simdisc="*n.xm1.nsg13_lv_nmos", - vgs=(0, 1, 0.01), - vds=(0, 1, 0.01), - vsb=(0, 1, 0.1), - width=10e-6, - lengths=[500e-9, 600e-9], - simulator="ngspice", - simulator_path=None, - temp=27, - model_paths=[], - model_names={"pmos": "NMOS_VTH"}, - description="gmid lookup table", - raw_spice="", - ): - self.simdis = simdisc - self.vgs = np.array(vgs) - self.vds = np.array(vds) - self.vsb = np.array(vsb) - self.width = width - self.lengths = np.array(lengths) - self.simulator = simulator - self.simulator_path = simulator_path - self.temp = temp - self.model_paths = model_paths - self.model_names = model_names - self.description = description - self.raw_spice = raw_spice - self.lookup_table = {} - self.validate_paths() - self.setup_simulator() - - ################################################################################ - def validate_paths(self): - path_list = self.model_paths[::] - if self.simulator_path is not None: - path_list.append(self.simulator_path) - for path in path_list: - if not os.path.exists(path): - continue - - def setup_simulator(self): - def check_for_binary(binary_name): - if shutil.which(binary_name) is None: - raise ValueError(f"The binary '{binary_name}' is not accessible.") - if self.simulator_path is None: - check_for_binary(self.simulator) - self.simulator_path = self.simulator - - ################################################################################ - def __make_tmp_files(self): - self.input_file_path = tempfile.NamedTemporaryFile(delete=False).name - self.log_file_path = tempfile.NamedTemporaryFile(delete=False).name - self.output_file_path = tempfile.NamedTemporaryFile(delete=False).name - - def __remove_tmp_files(self): - os.remove(self.input_file_path) - os.remove(self.log_file_path) - os.remove(self.output_file_path) - - ################################################################################ - # NGSPICE # - ################################################################################ - - - def __ngspice_parameters(self): - transistor_model = self.simdis[1:] - self.parameter_table = { - # parameter name : [name recognized by simulator, name used in the output file], - "id" : ["save i(vds)" , "i(i_vds)"], - "vth" : [f"save @{transistor_model}[vth]" , f"v(@{transistor_model}[vth])"], - "vdsat": [f"save @{transistor_model}[vdsat]", f"v(@{transistor_model}[vdsat])"], - "gm" : [f"save @{transistor_model}[gm]" , f"@{transistor_model}[gm]"], - "gmbs" : [f"save @{transistor_model}[gmbs]" , f"@{transistor_model}[gmbs]"], - "gds" : [f"save @{transistor_model}[gds]" , f"@{transistor_model}[gds]"], - "cgg" : [f"save @{transistor_model}[cgg]" , f"@{transistor_model}[cgg]"], - "cgs" : [f"save @{transistor_model}[cgs]" , f"@{transistor_model}[cgs]"], - "cbg" : [f"save @{transistor_model}[cbg]" , f"@{transistor_model}[cbg]"], - "cgd" : [f"save @{transistor_model}[cgd]" , f"@{transistor_model}[cgd]"], - "cdd" : [f"save @{transistor_model}[cdd]" , f"@{transistor_model}[cdd]"], - } - self.save_internal_parameters = "\n".join([values[0] for values in self.parameter_table.values()]) - - def __ngspice_simulator_setup(self): - vgs_start, vgs_stop, vgs_step = self.vgs * self.r - vds_start, vds_stop, vds_step = self.vds * self.r - analysis_string = f"dc VDS {vds_start} {vds_stop} {vds_step} VGS {vgs_start} {vgs_stop} {vgs_step}" - - simulator = [ - f".options TEMP = {self.temp}", - f".options TNOM = {self.temp}", - ".control", - self.save_internal_parameters, - analysis_string, - "let i_vds = abs(i(vds))", - f"write {self.output_file_path} all", - ".endc", - ".end", - ] - return simulator - - def __run_ngspice(self, circuit): - with open(self.input_file_path, "w") as file: - file.write("\n".join(circuit)) - ngspice_command = f"{self.simulator_path} -b -o {self.log_file_path} {self.input_file_path}" - subprocess.run(ngspice_command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - - def __parse_ngspice_output(self): - ars, _ = NgspiceRawFileReader().read_file(self.output_file_path) - return ars - - def __save_ngspice_parameters(self, analysis, mos, length, vsb): - column_names = analysis[0].dtype.names - data = analysis[0] - - for p in self.parameter_table.keys(): - col_name = self.parameter_table[p][1] - if col_name in column_names: - res = np.array(data[col_name]) - self.lookup_table[self.identifier][p][length][vsb] = res.reshape(self.n_vgs, self.n_vds) - - ################################################################################ - # HSPICE # - ################################################################################ - def __hspice_parameters(self): - self.parameter_table = { - # parameter name : [name recognized by simulator, name used in the output file], - "id" : [".probe DC m_id = par('abs(i(vds))')" , "m_id"], - "vth" : [".probe DC m_vth = par('vth(m1)')" , "m_vth"], - "vdsat": [".probe DC m_vdsat = par('vdsat(m1)')" , "m_vdsat"], - "gm" : [".probe DC m_gm = par('gmo(m1)')" , "m_gm"], - "gmbs" : [".probe DC m_gmb = par('gmbso(m1)')" , "m_gmb"], - "gds" : [".probe DC m_gds = par('gdso(m1)')" , "m_gds"], - "cgg" : [".probe DC m_cgg = par('cggbo(m1)')" , "m_cgg"], - "cgs" : [".probe DC m_cgs = par('-cgsbo(m1)')" , "m_cgs"], - "cgd" : [".probe DC m_cgd = par('-cgdbo(m1)')" , "m_cgd"], - "cgb" : [".probe DC m_cgb = par('cggbo(m1)-(-cgsbo(m1))-(-cgdbo(m1))')", "m_cgb"], - "cdd" : [".probe DC m_cdd = par('cddbo(m1)')" , "m_cdd"], - "css" : [".probe DC m_css = par('-cgsbo(m1)-cbsbo(m1)')" , "m_css"], - } - self.save_internal_parameters = "\n".join([values[0] for values in self.parameter_table.values()]) - - def __hspice_simulator_setup(self): - vgs_start, vgs_stop, vgs_step = self.vgs * self.r - vds_start, vds_stop, vds_step = self.vds * self.r - analysis_string = f".dc VGS {vgs_start} {vgs_stop} {vgs_step} VDS {vds_start} {vds_stop} {vds_step}" - - simulator = [ - f".TEMP = {self.temp}", - ".options probe dccap brief accurate", - ".option POST=2", - self.save_internal_parameters, - analysis_string, - ".end", - ] - return simulator - - def __run_hspice(self, circuit): - with open(self.input_file_path, "w") as file: - file.write("\n".join(circuit)) - hspice_command = f"{self.simulator_path} -i {self.input_file_path} -o {tempfile.gettempdir()}" - subprocess.run(hspice_command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - - def __parse_hspice_output(self): - import_export(self.input_file_path + ".sw0", "pickle") - with open(self.input_file_path + "_sw0.pickle", 'rb') as file: - loaded_data = pickle.load(file) - return loaded_data - - def __save_hspice_parameters(self, analysis, mos, length, vsb): - for p in self.parameter_table.keys(): - col_name = self.parameter_table[p][1] - if col_name in analysis.keys(): - res = np.array(analysis[col_name]).T - self.lookup_table[self.identifier][p][length][vsb] = res - - ################################################################################ - # Shared Methods # - ################################################################################ - def __initalize(self): - self.n_lengths = len(self.lengths) - self.n_vsb = round((self.vsb[1] - self.vsb[0]) / self.vsb[2]) + 1 - self.n_vds = round((self.vds[1] - self.vds[0]) / self.vds[2]) + 1 - self.n_vgs = round((self.vgs[1] - self.vgs[0]) / self.vgs[2]) + 1 - - self.lookup_table[self.identifier] = {} - for p in self.parameter_table: - self.lookup_table[self.identifier][p] = np.zeros(shape=(self.n_lengths, self.n_vsb, self.n_vgs, self.n_vds)) - - # choose right simulator - if self.simulator == "ngspice": - self.__ngspice_parameters() - self.simulator_setup = self.__ngspice_simulator_setup - self.run = self.__run_ngspice - self.parse = self.__parse_ngspice_output - self.save = self.__save_ngspice_parameters - elif self.simulator == "hspice": - self.__hspice_parameters() - self.simulator_setup = self.__hspice_simulator_setup - self.run = self.__run_hspice - self.parse = self.__parse_hspice_output - self.save = self.__save_hspice_parameters - - def __generate_netlist(self, length, vsb): - if self.identifier in self.model_names: - model_name = self.model_names[self.identifier] - else: - model_name = self.model_names.get("pmos") - if model_name is None: - raise ValueError(" Neither 'nmos' nor 'pmos' is avaliable in the model_name") - if self.model_paths: - include_string = "\n".join([f".lib {path}" for path in self.model_paths]) - else: - include_string = "" - - - circuit = [ - "* Lookup Table Generation *", - include_string, - "VGS NG 0 DC=0", - f"VBS NB 0 DC={-vsb * self.r}", - "VDS ND 0 DC=0", - f"XM1 ND NG 0 NB {model_name} l={length} w={self.width}", - self.raw_spice, - ] - return circuit - - def __generate_loopkup_table(self, mos): - self.__initalize() - for idx, length in enumerate(self.lengths): - print(f"-- length={length}") - for idy, vsb in enumerate(np.linspace(self.vsb[0], self.vsb[1], self.n_vsb)): - circuit = self.__generate_netlist(length, vsb) - simulator = self.simulator_setup() - circuit.extend(simulator) - self.run(circuit) - analysis = self.parse() - self.save(analysis, mos, idx, idy) - - def __save_to_dictionary(self): - self.lookup_table["description"] = self.description - self.lookup_table["parameter_names"] = list(self.parameter_table.keys()) - self.lookup_table["width"] = self.width - self.lookup_table["lengths"] = self.lengths - - if "nmos" in self.model_names: - self.lookup_table["nmos"]["vgs"] = range_to_arr(self.vgs) - self.lookup_table["nmos"]["vds"] = range_to_arr(self.vds) - self.lookup_table["nmos"]["vsb"] = range_to_arr(self.vsb) - self.lookup_table["nmos"]["model_name"] = self.model_names["nmos"] - self.lookup_table["nmos"]["width"] = self.width - self.lookup_table["nmos"]["lengths"] = self.lengths - self.lookup_table["nmos"]["parameter_names"] = list(self.parameter_table.keys()) - - if "pmos" in self.model_names: - self.lookup_table["pmos"]["vgs"] = -range_to_arr(self.vgs) - self.lookup_table["pmos"]["vds"] = -range_to_arr(self.vds) - self.lookup_table["pmos"]["vsb"] = -range_to_arr(self.vsb) - self.lookup_table["pmos"]["model_name"] = self.model_names["pmos"] - self.lookup_table["pmos"]["width"] = self.width - self.lookup_table["pmos"]["lengths"] = self.lengths - self.lookup_table["pmos"]["parameter_names"] = list(self.parameter_table.keys()) - - def __print_netlist(self): - self.r = 1 - self.identifier = "nmos" - circuit = self.__generate_netlist(self.lengths[0], 0) - if self.simulator == "ngspice": - self.__ngspice_parameters() - simulator = self.__ngspice_simulator_setup() - elif self.simulator == "hspice": - self.__hspice_parameters() - simulator = self.__hspice_simulator_setup() - circuit.extend(simulator) - print("---------------------------------------------------") - print("----- This is the netlist that gets simulated -----") - print("---------------------------------------------------") - print("\n".join(circuit)) - print("---------------------------------------------------") - print("") - - ################################################################################ - - def build(self, filepath): - self.__make_tmp_files() - self.__print_netlist() - - if "nmos" in self.model_names: - print("Generating lookup table for NMOS") - self.r = 1 - self.identifier = "nmos" - self.__generate_loopkup_table(self.identifier) - - if "pmos" in self.model_names: - print("Generating lookup table for PMOS") - self.r = -1 - self.identifier = "pmos" - self.__generate_loopkup_table(self.identifier) - - # Save results to file - print("Saving to file") - self.__save_to_dictionary() - np.save(filepath, self.lookup_table, allow_pickle=True) - - # Remove tmp files - self.__remove_tmp_files() - print("Done") - - - diff --git a/modules/module_0_foundations/gmid_repo/mosplot/src/main.py b/modules/module_0_foundations/gmid_repo/mosplot/src/main.py deleted file mode 100644 index 93b80afa..00000000 --- a/modules/module_0_foundations/gmid_repo/mosplot/src/main.py +++ /dev/null @@ -1,706 +0,0 @@ -# -----------------------------------------------------------------------------# -# Author: Mohamed Watfa -# URL: https://github.com/medwatt/ -# -----------------------------------------------------------------------------# - -import numpy as np -from scipy.interpolate import interpn, griddata -import matplotlib as mpl -import matplotlib.pyplot as plt -from matplotlib.ticker import EngFormatter -from matplotlib.widgets import Cursor - -# don't warn user about bad divisions -np.seterr(divide="ignore", invalid="ignore") - -################################################################################ -# Helper Functions # -################################################################################ -# load the generated lookup table -def load_lookup_table(path: str): - return np.load(path, allow_pickle=True).tolist() - -# tile one array so that it has the same shape as the other -def tile_arrays(A, B): - if A.ndim == 1 and B.ndim == 2: - if A.shape[0] == B.shape[0]: - return np.tile(A, (B.shape[1], 1)).T, B - elif A.shape[0] == B.shape[1]: - return np.tile(A, (B.shape[0], 1)), B - elif B.ndim == 1 and A.ndim == 2: - if B.shape[0] == A.shape[0]: - return A, np.tile(B, (A.shape[1], 1)).T - elif B.shape[0] == A.shape[1]: - return A, np.tile(B, (A.shape[0], 1)) - return A, B - -################################################################################ -# Override Plot Settings During Runtime # -################################################################################ -FIG_SIZE = (8, 4) -LINE_WIDTH = 1.5 -GRID_COLOR = "0.9" - -PLOT_SETTINGS = { - "FIG_SIZE": FIG_SIZE, - "LINE_WIDTH": LINE_WIDTH, - "GRID_COLOR": GRID_COLOR, -} - -def set_plot_settings(var_name, new_value): - global FIG_SIZE, LINE_WIDTH, GRID_COLOR - if var_name in PLOT_SETTINGS: - globals()[var_name] = new_value - -################################################################################ -# Matplotlib Plot Interraction # -################################################################################ -dots = [] -annotations = [] - -def on_canvas_click(event, fig, ax): - if fig.canvas.toolbar.mode != "": - return - - x = event.xdata - y = event.ydata - - if x is None or y is None: - return - - print(f"x={x}, y={y}") - - (dot,) = ax.plot(x, y, "ro") - dots.append(dot) - - formatter = EngFormatter() - x_eng = formatter(x) - y_eng = formatter(y) - annotation = ax.annotate( - f"({x_eng}, {y_eng})", - (x, y), - textcoords="offset points", - xytext=(0, 10), - ha="center", - ) - annotations.append(annotation) - fig.canvas.draw() - - -def clear_annotations_and_dots(fig): - for dot in dots: - dot.remove() - for annotation in annotations: - annotation.remove() - fig.canvas.draw() - dots.clear() - annotations.clear() - -################################################################################ -# GMID # -################################################################################ -class LoadMosfet: - def __init__( self, *, lookup_table, mos, lengths=None, vsb=None, vgs=None, vds=None, primary=None): - """ - Initialize a mosfet object. - Two of `lengths, vsb, vgs, vds` must be fixed at any time. - - Args: - lookup_table (dict): dictionary of mosfet parameters - mos (str): type of mosfet: "nmos" or "pmos" - lengths (float, list, ndarray): length(s) of the mosfet - vsb (float, tuple): source-body voltage: tuple of the form (start, stop, step) - vgs (float, tuple): gate-source voltage: tuple of the form (start, stop, step) - vds (float, tuple): drain-source voltage: tuple of the form (start, stop, step) - primary (str): name of the primary sweep source - - Example: - nmos = LoadMosfet(lookup_table=lookup_table, mos="nmos", vsb=0.0, vds=0.5, vgs=(0.3, 1)) - """ - # extract data from lookup table - self.mos = mos - self.lookup_table = lookup_table - self.width = lookup_table[mos]["width"] - self.lengths = lookup_table[mos]["lengths"] - self.parameters = lookup_table[mos]["parameter_names"] - - # extract a 2d table of the parameters - self.secondary_variable_idx, self.filtered_variables, self.extracted_table = \ - self.extract_2d_table(lookup_table=self.lookup_table[self.mos], primary=primary, lengths=lengths, vsb=vsb, vgs=vgs, vds=vds) - self.lengths, self.vsb, self.vgs, self.vds = self.filtered_variables - - # define commonly-used expressions to avoid typing them every time - self.__common_expressions() - self.__common_plot_methods() - - def extract_2d_table(self, *, lookup_table, parameters=None, lengths=None, vsb=None, vgs=None, vds=None, primary=None): - """ - Filter the lookup table based - - Args: - lookup_table (dict): dictionary of parameters of one of the mosfets - lengths (float, list, ndarray): length(s) of the mosfet - vsb (float, tuple): source-body voltage: tuple of the form (start, stop, step) - vgs (float, tuple): gate-source voltage: tuple of the form (start, stop, step) - vds (float, tuple): drain-source voltage: tuple of the form (start, stop, step) - primary (str): name of the primary sweep source - - Returns: - secondary_idx: index of the secondary sweep variable in `lengths, vsb, vgs, vds` - filter_values: filtered values of `lengths, vsb, vgs, vds` - extracted_table: filtered values of parameters - """ - # check that at least two parameters are provided - params = [lengths is not None, vsb is not None, vgs is not None, vds is not None] - if sum(params) < 2: - raise ValueError("Please provide at least two parameters.") - - def get_indices(var, target): - data = lookup_table[var] - if isinstance(target, tuple): # when `vsb, vgs, vds` is a range - start, end = target[:2] - indices = np.where((data >= start) & (data <= end))[0] - if len(target) == 3: # if it contains a step - step = int(target[2] / (data[1] - data[0])) - indices = indices[::step] - # lengths must be handled separately since they are provided as list of values - elif var == "lengths" and isinstance(target, (list, np.ndarray)): - # filter by lengths - mask = np.isin(self.lookup_table["lengths"], np.array(target)) - indices = np.nonzero(mask)[0] - indices = np.array(indices, dtype=int) - else: - # by eliminating all float variables, we will be left with one variable - # that is not a float, and that will be the secondary variable - variables[var] = True - index = (np.abs(data - target)).argmin() - return np.array([index]), data[index] - - return indices, data[indices] - - secondary_idx = None - variables = {"lengths": False, "vsb": False, "vgs": False, "vds": False} - if primary: - variables[primary] = True - - indices_and_values = { - "lengths": get_indices("lengths", lengths) if lengths is not None else (slice(None), lookup_table["lengths"]), - "vsb": get_indices("vsb", vsb) if vsb is not None else (slice(None), lookup_table["vsb"]), - "vgs": get_indices("vgs", vgs) if vgs is not None else (slice(None), lookup_table["vgs"]), - "vds": get_indices("vds", vds) if vds is not None else (slice(None), lookup_table["vds"]), - } - - slice_indices = [] - filter_values = [] - for idx, item in enumerate(variables.keys()): - slice_indices.append(indices_and_values[item][0]) - filter_values.append(indices_and_values[item][1]) - slice_indices = tuple(slice_indices) - - def slice_me(a, slices): - x = a[slices[0], :, :, :] - x = x[:, slices[1], :, :] - x = x[:, :, slices[2], :] - x = x[:, :, :, slices[3]] - return x - - # extract the data based on the indices - extracted_table = {} - if not parameters: - parameters = lookup_table["parameter_names"] - - for p in parameters: - if p in lookup_table: - x = np.squeeze(slice_me(lookup_table[p], slice_indices)) - if x.ndim > 1 and x.shape[0] > x.shape[1]: - extracted_table[p] = x.T - else: - extracted_table[p] = x - - one_key = next(iter(extracted_table)) - extracted_table["width"] = np.array(self.width) - extracted_table["lengths"], _ = tile_arrays(filter_values[0], extracted_table[one_key]) - extracted_table["vsb"], _ = tile_arrays(filter_values[1], extracted_table[one_key]) - extracted_table["vgs"], _ = tile_arrays(filter_values[2], extracted_table[one_key]) - extracted_table["vds"], _ = tile_arrays(filter_values[3], extracted_table[one_key]) - - if primary and secondary_idx: - secondary_idx = list(variables.values()).index(False) - - return secondary_idx, filter_values, extracted_table - - ################################################################################ - # Plotting Methods # - ################################################################################ - def __generate_plot_labels(self): - variables_labels = ["lengths", "vsb", "vgs", "vds"] - model_name = self.lookup_table[self.mos]["model_name"] - title_label = [] - - for i, v in enumerate(self.filtered_variables): - if not isinstance(v, np.ndarray): - label = variables_labels[i] - title_label.append(f"V_{{ \\mathrm{{ { (label[1:]).upper() } }} }}") - title_label.append(v) - - self.plot_labels = {} - self.plot_labels["title"] = f"{model_name}, " + "$%s=%.2f$, $%s=%.2f$" % tuple(title_label) - - legend_formatter = EngFormatter(unit="m") - self.plot_labels["lengths"] = [legend_formatter.format_eng(sw) for sw in self.lengths] - - def __plot_settings( - self, - y: np.ndarray, - x_limit: tuple = (), - y_limit: tuple = (), - x_scale: str = "", - y_scale: str = "", - x_eng_format: bool = False, - y_eng_format: bool = False, - x_label: str = "", - y_label: str = "", - title: str = "", - save_fig: str = "", - ): - fig, ax = plt.subplots(1, 1, figsize=FIG_SIZE, tight_layout=True) - fig.canvas.mpl_connect( - "button_press_event", - lambda event: on_canvas_click(event, fig, ax), - ) - fig.canvas.mpl_connect( - "key_press_event", - lambda event: clear_annotations_and_dots(fig) if event.key == "d" else None, - ) - - ax.set_title(title) - ax.grid(True, which="both", ls="--", color=GRID_COLOR) - ax.set_xlabel(x_label) - ax.set_ylabel(y_label) - - if x_limit: - ax.set_xlim(*x_limit) - - if y_limit: - ax.set_ylim(*y_limit) - - if x_scale: - ax.set_xscale(x_scale) - - if y_scale: - ax.set_yscale(y_scale) - else: - # TODO: this might not always give expected result - if np.max(y) / np.min(y) > 1000: - ax.set_yscale("log") - - # set engineering format if specified - if y_eng_format: - ax.yaxis.set_major_formatter(EngFormatter(unit="")) - if x_eng_format: - ax.xaxis.set_major_formatter(EngFormatter(unit="")) - - return fig, ax - - def __plot(self, x, y, fig, ax, legend, save_fig): - if isinstance(x, np.ndarray) and isinstance(y, np.ndarray) and x.ndim == y.ndim: - ax.plot(x.T, y.T, lw=LINE_WIDTH, picker=True) - - elif isinstance(x, (list, tuple)) and isinstance(y, (list, tuple)): - for x_, y_ in zip(x, y): - if x_.ndim == 1 and x_.shape[0] != y_.shape[0]: - ax.plot(x_, y_.T, lw=LINE_WIDTH, picker=True) - else: - ax.plot(x_, y_, lw=LINE_WIDTH, picker=True) - - elif x.ndim == 1: - if x.shape[0] != y.shape[0]: - ax.plot(x, y.T, lw=LINE_WIDTH, picker=True) - else: - ax.plot(x, y, lw=LINE_WIDTH, picker=True) - - elif y.ndim == 1: - if y.shape[0] != x.shape[0]: - ax.plot(x.T, y, lw=LINE_WIDTH, picker=True) - else: - ax.plot(x, y, lw=LINE_WIDTH, picker=True) - - - if legend: - ax.legend(legend, loc="center left", bbox_to_anchor=(1, 0.5)) - - if save_fig: - ax.figure.savefig(save_fig, bbox_inches="tight") - - def plot_by_expression( - self, - *, - x_expression: dict, - y_expression: dict, - lengths: tuple = (), - x_limit: tuple = (), - y_limit: tuple = (), - x_scale: str = "", - y_scale: str = "", - x_eng_format: bool = False, - y_eng_format: bool = False, - title: str = None, - save_fig: str = "", - return_result: bool = False, - ): - extracted_table = self.extracted_table - - # plot labels - self.__generate_plot_labels() - if title is not None: - self.plot_labels["title"] = title - - # filter by lengths - mask = np.isin(self.lengths, np.array(lengths)) - indices = np.nonzero(mask)[0] - length_indices = np.array(indices, dtype=int) - - if length_indices.size > 0: - legend = [self.plot_labels["lengths"][i] for i in length_indices] - else: - legend = self.plot_labels["lengths"] - - x, x_label = self.__calculate_from_expression(x_expression, extracted_table, length_indices) - y, y_label = self.__calculate_from_expression(y_expression, extracted_table, length_indices) - - fig, ax = self.__plot_settings( - y, - x_limit, - y_limit, - x_scale, - y_scale, - x_eng_format, - y_eng_format, - x_label, - y_label, - self.plot_labels["title"], - save_fig, - ) - - self.__plot(x, y, fig, ax, legend, save_fig) - - if return_result: - return x, y - - def plot_by_sweep( - self, - *, - lengths, - vsb, - vgs, - vds, - primary, - x_expression, - y_expression, - title: str = "", - x_limit: tuple = (), - y_limit: tuple = (), - x_scale: str = "", - y_scale: str = "", - x_eng_format: bool = False, - y_eng_format: bool = False, - save_fig: str = "", - return_result: bool = False, - ): - secondary_variable_idx, filtered_variables, extracted_table = self.extract_2d_table( - lookup_table=self.lookup_table[self.mos], lengths=lengths, vsb=vsb, vgs=vgs, vds=vds, primary=primary - ) - - x, x_label = self.__calculate_from_expression(x_expression, extracted_table) - y, y_label = self.__calculate_from_expression(y_expression, extracted_table) - - fig, ax = self.__plot_settings( - y, - x_limit, - y_limit, - x_scale, - y_scale, - x_eng_format, - y_eng_format, - x_label, - y_label, - title, - save_fig, - ) - - if secondary_variable_idx: - legend_formatter = EngFormatter(unit="") - legend = [legend_formatter.format_eng(sw) for sw in filtered_variables[secondary_variable_idx]] - else: - legend = None - - self.__plot(x, y, fig, ax, legend, save_fig) - - if return_result: - return x, y - - def quick_plot( - self, - x: np.ndarray | list | tuple, - y: np.ndarray | list | tuple, - *, - x_label: str = "", - y_label: str = "", - x_limit: tuple = (), - y_limit: tuple = (), - x_scale: str = "", - y_scale: str = "", - x_eng_format: bool = False, - y_eng_format: bool = False, - legend: list = [], - title: str = None, - save_fig: str = "", - ): - """ - Make quick plots. As a reminder, when `x` and `y` are of size m x n, pass - them to this function as x.T and y.T - """ - fig, ax = self.__plot_settings( - y, - x_limit, - y_limit, - x_scale, - y_scale, - x_eng_format, - y_eng_format, - x_label, - y_label, - title, - save_fig, - ) - - self.__plot(x, y, fig, ax, legend, save_fig) - - # }}} - - ################################################################################ - # Expression Handling # - ################################################################################ - def __calculate_from_expression( - self, - expression: dict, - table: dict, - filter_by_rows: np.ndarray = np.array([]), - ): - if isinstance(expression, dict): - var_list = [] - for v in expression["variables"]: - var = table[v] - if filter_by_rows.size > 0 and var.ndim > 1: - var_list.append((np.take(var, filter_by_rows, 0))) - else: - var_list.append(var) - - if "function" in expression: - values = expression["function"](*var_list) - else: - values = var_list[0] - - try: - return values, expression["label"] - except KeyError: - return values, "" - else: - return expression, None - - def __common_expressions(self): - # create attributes for parameters from the lookup table - LABEL_TABLE = { - "lengths": ["\\mathrm{Length}", "m"], - "vsb": ["V_{\\mathrm{SB}}", "V"], - "vgs": ["V_{\\mathrm{GS}}", "V"], - "vds": ["V_{\\mathrm{DS}}", "V"], - "id": ["I_{D}", "A"], - "vth": ["V_{\\mathrm{TH}}", "V"], - "vdsat": ["V_{\\mathrm{DS_{\\mathrm{SAT}}}}", "V"], - "gm": ["g_{m}", "S"], - "gmbs": ["g_{\\mathrm{mbs}}", "S"], - "gds": ["g_{\\mathrm{ds}}", "S"], - "cgg": ["c_{\\mathrm{gg}}", "F"], - "cgs": ["c_{\\mathrm{gs}}", "F"], - "cbg": ["c_{\\mathrm{bg}}", "F"], - "cgd": ["c_{\\mathrm{gd}}", "F"], - "cdd": ["c_{\\mathrm{dd}}", "F"], - } - for parameter, (label, unit) in LABEL_TABLE.items(): - if parameter in self.parameters or parameter in ["lengths", "vsb", "vgs", "vds"]: - setattr( - self, - f"{parameter}_expression", - {"variables": [parameter], "label": f"${label}\ ({unit})$"}, - ) - - self.gmid_expression = { - "variables": ["gm", "id"], - "function": lambda x, y: x / y, - "label": "$g_m/I_D (S/A)$", - } - self.vstar_expression = { - "variables": ["gm", "id"], - "function": lambda x, y: (2 * y) / x, - "label": "$V^{\\star} (V)$", - } - self.gain_expression = { - "variables": ["gm", "gds"], - "function": lambda x, y: x / y, - "label": "$g_{m}/g_{\\mathrm{ds}}$", - } - self.current_density_expression = { - "variables": ["id", "width"], - "function": lambda x, y: x / y, - "label": "$I_{D}/W (A/m)$", - } - self.transist_frequency_expression = { - "variables": ["gm", "cgg"], - "function": lambda x, y: x / (2 * np.pi * y), - "label": "$f_{T} (\\mathrm{Hz})$", - } - self.early_voltage_expression = { - "variables": ["id", "gds"], - "function": lambda x, y: x / y, - "label": "$V_{A} (V)$", - } - self.rds_expression = { - "variables": ["gds"], - "function": lambda x: 1 / x, - "label": "$r_{\\mathrm{ds}} (\\Omega)$", - } - - def __common_plot_methods(self): - PLOT_METHODS = { - "current_density_plot": [self.gmid_expression, self.current_density_expression], - "gain_plot": [self.gmid_expression, self.gain_expression], - "transit_frequency_plot": [self.gmid_expression, self.transist_frequency_expression], - "early_voltage_plot": [self.gmid_expression, self.early_voltage_expression], - } - - # This is not ideal since I need to keep track of the signature of `plot_by_expression`. - # I can use `partial` from `functools` here, but it doesn't hide the bound variables, which I don't like. - def create_plot_method(self, x_expression, y_expression): - def plot_method( - self, - lengths: tuple = (), - x_limit: tuple = (), - y_limit: tuple = (), - x_scale: str = "", - y_scale: str = "", - x_eng_format: bool = False, - y_eng_format: bool = False, - title: str = None, - save_fig: str = "", - return_result: bool = False, - ): - return self.plot_by_expression( - x_expression=x_expression, - y_expression=y_expression, - lengths=lengths, - x_scale=x_scale, - y_scale=y_scale, - x_limit=x_limit, - y_limit=y_limit, - x_eng_format=x_eng_format, - y_eng_format=y_eng_format, - title=title, - save_fig=save_fig, - return_result=return_result, - ) - - return plot_method - - for method_name, (x, y) in PLOT_METHODS.items(): - setattr(LoadMosfet, method_name, create_plot_method(self, x_expression=x, y_expression=y)) - - ################################################################################ - # Lookup Methods # - ################################################################################ - def interpolate(self, *, x_expression, x_value, y_expression, y_value, z_expression): - """ - Given (1) a value from x_expression, - (2) a value from y_expression, - find value of z_expression using interpolation. - - Args: - x_expression (dict): expression of how to calculate the points on the x-axis - x_value (float, dict): value(s) inside the domain of x_expression - y_expression (dict): expression of how to calculate the points on the y-axis - y_value (float, dict): value(s) inside the domain of y_expression - z_expression (dict): expression of how to calculate the value you're looking for - - Returns: - value of expression you're looking for - - Example: - x = nmos.interpolate( - x_expression=nmos.vgs_expression, - x_value=0.65, - y_expression=nmos.gmid_expression, - y_value=15, - z_expression=nmos.lengths_expression, - ) - """ - x_array, _ = self.__calculate_from_expression(x_expression, self.extracted_table) - y_aray, _ = self.__calculate_from_expression(y_expression, self.extracted_table) - z_array, _ = self.__calculate_from_expression(z_expression, self.extracted_table) - - points = np.column_stack((x_array.ravel(), y_aray.ravel())) - - if isinstance(x_value, (tuple, np.ndarray)) and isinstance(y_value, (int, float)): - if isinstance(x_value, tuple): - x = np.arange(*x_value) - else: - x = x_value - evaluate_at = np.column_stack((x, np.full(x.shape, y_value))) - elif isinstance(y_value, (tuple, np.ndarray)) and isinstance(x_value, (int, float)): - if isinstance(y_value, tuple): - y = np.arange(*y_value) - else: - y = y_value - evaluate_at = np.column_stack((np.full(y.shape, x_value), y)) - elif isinstance(x_value, tuple) and isinstance(y_value, tuple): - x = np.arange(*x_value) - y = np.arange(*y_value) - X, Y = np.meshgrid(x, y) - evaluate_at = np.dstack((X, Y)).transpose(1, 0, 2) - else: - evaluate_at = np.array([x_value, y_value]) - - z_value = griddata(points, z_array.ravel(), evaluate_at, method='cubic', rescale=True) - return z_value - - - def lookup_expression_from_table(self, *, lengths, vsb, vgs, vds, primary, expression): - """ - Calculate a parameter using the entire table. - No interpolation is used. - - Args: - lengths (float, list, ndarray): length(s) of the mosfet - vsb (float, tuple): source-body voltage, tuple of the form (start, stop, step) - vgs (float, tuple): gate-source voltage, tuple of the form (start, stop, step) - vds (float, tuple): drain-source voltage, tuple of the form (start, stop, step) - primary (str): name of the primary sweep source: "lengths", "vsb", "vgs", or "vds" - expression(dict): expression of how to calculate the value you're looking for - - Example: - x = nmos.lookup_expression_from_table( - lengths=100e-9, - vsb=0, - vds=(0.0, 1, 0.01), - vgs=(0.0, 1.01, 0.2), - primary="vds", - expression=nmos.current_density_expression, - ) - """ - parameters = expression["variables"].copy() - remove_from_parameters = ["width", "length"] - for item in remove_from_parameters: - if item in parameters: - parameters.remove(item) - _, _, extracted_table = self.extract_2d_table(lookup_table=self.lookup_table[self.mos], parameters=parameters, lengths=lengths, vsb=vsb, vgs=vgs, vds=vds, primary=primary) - x, _ = self.__calculate_from_expression(expression, extracted_table) - return x diff --git a/modules/module_0_foundations/gmid_repo/mosplot/src/manu_man.py b/modules/module_0_foundations/gmid_repo/mosplot/src/manu_man.py deleted file mode 100644 index 8e23d954..00000000 --- a/modules/module_0_foundations/gmid_repo/mosplot/src/manu_man.py +++ /dev/null @@ -1,140 +0,0 @@ -from pick import pick - -def description_menu(model): - Options_1 = ["Corner lib typical", "Corner lib fast", "Corner lib slow", "Exit"] - title_1 = "Please choose the corner lib: " - discription = input("Please enter a discription: ") - simulator = "ngspice" - _, index = pick(Options_1, title_1) - if index == 0: - print("You have chosen Corner lib typical") - print(model) - if any('lv' in value for value in model.values()): - model_path = ["cornerMOSlv.lib mos_tt",] - else: - model_path = ["cornerMOShv.lib mos_tt",] - elif index == 1: - print("You have chosen Corner lib fast") - if any('lv' in value for value in model.values()): - model_path = ["cornerMOSlv.lib mos_ff",] - else: - model_path = ["cornerMOShv.lib mos_ff",] - elif index == 2: - print("You have chosen Corner lib slow") - if any('lv' in value for value in model.values()): - model_path = ["cornerMOSlv.lib mos_ss",] - else: - model_path = ["cornerMOShv.lib mos_ss",] - elif index == 3: - print("You have chosen to exit") - exit() - - return discription, simulator, model_path - -def sweeping_menu(): - def get_floats(prompt): - while True: - user_input = input(prompt) - if user_input.lower() == 'exit': - return 'exit' - try: - return tuple(map(float, user_input.split(','))) - except ValueError: - print("Invalid input format. Please enter three numbers separated by commas (e.g., 0, 1, 0.01).") - - # Prompt for VSB - while True: - vsb = get_floats("Enter VSB (start, stop, step) or type 'exit' to quit: ") - if vsb == 'exit': - return 'Exited' - elif len(vsb) == 3: - break - - # Prompt for VGS - while True: - vgs = get_floats("Enter VGS (start, stop, step) or type 'exit' to quit: ") - if vgs == 'exit': - return 'Exited' - elif len(vgs) == 3: - break - - # Prompt for VDS - while True: - vds = get_floats("Enter VDS (start, stop, step) or type 'exit' to quit: ") - if vds == 'exit': - return 'Exited' - elif len(vds) == 3: - break - - # Prompt for width - while True: - width_input = input("Enter width value (e.g., 10e-6) or type 'exit' to quit: ") - if width_input.lower() == 'exit': - return 'Exited' - try: - width = float(width_input) - break - except ValueError: - print("Invalid width input. Please enter a valid number (e.g., 10e-6).") - - # Prompt for lengths or auto-generate - while True: - lengths_input = input("Enter lengths (comma-separated, e.g., 500e-9, 600e-9), or type 'auto' for auto-generation: ") - if lengths_input.lower() == 'exit': - return 'Exited' - elif lengths_input.lower() == 'auto': - try: - start = float(input("Enter start value for lengths: ")) - increment = float(input("Enter increment value: ")) - count = int(input("Enter number of increments: ")) - lengths = [start + i * increment for i in range(count)] - break - except ValueError: - print("Invalid input for auto-generation. Try again.") - else: - try: - lengths = [float(i) for i in lengths_input.split(',')] - break - except ValueError: - print("Invalid lengths input. Please enter comma-separated numbers.") - - return vsb, vgs, vds, width, lengths - - - - -def input_selection(): - # Simply return "ihp_open_pdk" without asking for input - return "ihp_open_pdk" - - - -def transistor_menu(): - Options = ["NMOS_LV", "PMOS_LV", "NMOS_HV", "PMOS_HV", "Exit"] - title = "Please choose the transistor model: " - option, index = pick(Options, title) - - if index == 0: - print("You have chosen NMOS_LV") - model = {"nmos": "sg13_lv_nmos"} - simulation_description = "*n.xm1.nsg13_lv_nmos" - return model, simulation_description - elif index == 1: - print("You have chosen PMOS_LV") - model = {"pmos": "sg13_lv_pmos"} - simulation_description = "*n.xm1.nsg13_lv_pmos" - return model, simulation_description - elif index == 2: - print("You have chosen NMOS_HV") - model = {"nmos": "sg13_hv_nmos"} - simulation_description = "*n.xm1.nsg13_hv_nmos" - return model, simulation_description - elif index == 3: - print("You have chosen PMOS_HV") - model = {"pmos": "sg13_hv_pmos"} - simulation_description = "*n.xm1.nsg13_hv_pmos" - return model, simulation_description - elif index == 4: - print("You have chosen to exit") - exit() - return model, simulation_description diff --git a/modules/module_0_foundations/gmid_repo/setup.py b/modules/module_0_foundations/gmid_repo/setup.py deleted file mode 100644 index 9f5bbf2b..00000000 --- a/modules/module_0_foundations/gmid_repo/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup - -setup( - name='mosplot', - version='0.1.0', - description='A python tool for making plots of mosfet parameters.', - author='Mohamed Watfa', - author_email='medwatt@hotmail.com', - install_requires=[ - 'numpy', - 'matplotlib', - 'scipy' - ], -) diff --git a/modules/module_0_foundations/scripting/gmid_test.ipynb b/modules/module_0_foundations/scripting/gmid_test.ipynb index b6103dc5..0447ad1d 100644 --- a/modules/module_0_foundations/scripting/gmid_test.ipynb +++ b/modules/module_0_foundations/scripting/gmid_test.ipynb @@ -2,39 +2,14 @@ "cells": [ { "cell_type": "code", - "execution_count": 8, - "id": "fe26dc38-6623-48db-820a-cf131ac9a268", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Successfully imported from src.main!\n" - ] - } - ], - "source": [ - "import os\n", - "import sys\n", - "sys.path.append(os.path.abspath('../gmid_repo/mosplot/src'))\n", - "\n", - "try:\n", - " from main import load_lookup_table, LoadMosfet\n", - " print(\"Successfully imported from src.main!\")\n", - "except ImportError as e:\n", - " print(f\"Error importing directly from src.main: {e}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, + "execution_count": 101, "id": "9f325daf-eb3b-4cf7-8ffe-b9b94e7f66ea", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", + "from mosplot.plot import load_lookup_table, Mosfet, Expression\n", "import ipywidgets as widgets\n", "from ipywidgets import interactive\n", "from ipywidgets import interactive_output, HBox, VBox\n", @@ -43,33 +18,52 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 102, "id": "b5b31aca-47bf-4461-8e50-16c20f03b337", "metadata": {}, "outputs": [], "source": [ - "nmos_lv_path = '../gmid_repo/LUTs/nmos_lv_lut_tt.npy'\n", - "pmos_lv_path = '../gmid_repo/LUTs/pmos_lv_lut_tt.npy'\n", - "\n", - "\n", - "lookup_table_nmos = load_lookup_table(nmos_lv_path)\n", - "lookup_table_pmos = load_lookup_table(pmos_lv_path)" + "lookup_table_nmos = load_lookup_table('../sg13_nmos_lv_LUT.npz')\n", + "lookup_table_pmos = load_lookup_table('../sg13_pmos_lv_LUT.npz')" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 103, + "id": "a03cd944-2432-457c-9b88-486ab781fde6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['sg13_lv_nmos ', 'sg13_lv_pmos', 'description', 'simulator', 'parameter_names', 'device_parameters'])\n" + ] + } + ], + "source": [ + "print(lookup_table.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 108, "id": "743dc381-0d35-4aa9-847c-c42c80c17786", "metadata": {}, "outputs": [], "source": [ - "nmos = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0.0, vds=0.6)\n", - "pmos = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=0.0, vds=-0.6, vgs=(-1.2, -0.15))\n" + "nmos = Mosfet(lookup_table=lookup_table_nmos, mos=\"sg13_lv_nmos \", vbs=0.0, vds=0.6)\n", + "pmos = Mosfet(lookup_table=lookup_table_pmos, mos=\"sg13_lv_pmos\", vbs=0.0, vds=-0.6, vgs=(-1.2, -0.15))\n", + "\n", + "rows_0, cols_0 = np.shape(nmos.extracted_table['gm']) # just for getting the shape of the data\n", + "rows_1, cols_1 = np.shape(pmos.extracted_table['gm']) # just for getting the shape of the data\n", + "reshaped_lengths_nmos = np.tile(nmos.length[:, np.newaxis], (1, cols_0))\n", + "reshaped_lengths_pmos = np.tile(pmos.length[:, np.newaxis], (1, cols_1))" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 109, "id": "b27d5fca-3436-4df7-895f-f6a4bbd7a80d", "metadata": { "jupyter": { @@ -263,19 +257,19 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 110, "id": "b7cc630f-b385-47a6-a6f9-ac0d10effffe", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "29f64ef5153445daad7e300c3c91e7f1", + "model_id": "f4bd29dd4a254e499496ceb2f8444c8f", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.20 μm', '…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -283,13 +277,13 @@ } ], "source": [ - "width_values = nmos.extracted_table['width']\n", + "width_values = nmos.width\n", "id_values = nmos.extracted_table['id']\n", "gm_values = nmos.extracted_table['gm']\n", "gds_values = nmos.extracted_table['gds']\n", "vgs_values= nmos.extracted_table['vgs']\n", "\n", - "plot_data_vs_data(gm_values/id_values, gm_values/gds_values, vgs_values, nmos.extracted_table['lengths'], 'gm/id', 'gm/gds')" + "plot_data_vs_data(gm_values/id_values, gm_values/gds_values, vgs_values, reshaped_lengths_nmos, 'gm/id', 'gm/gds')" ] }, { @@ -302,19 +296,19 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 111, "id": "3727c42d-a4bf-4eb0-bc11-6e859ae41324", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9d62db56b7f240f9bd379086729554c4", + "model_id": "25e14d69e1084f71a4f21a94fe991a02", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.20 μm', '…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -322,38 +316,14 @@ } ], "source": [ - "width_values = pmos.extracted_table['width']\n", + "width_values = pmos.width\n", "id_values = pmos.extracted_table['id']\n", "gm_values = pmos.extracted_table['gm']\n", "gds_values = pmos.extracted_table['gds']\n", "vgs_values= pmos.extracted_table['vgs']\n", "\n", - "plot_data_vs_data(gm_values/id_values, gm_values/gds_values, vgs_values, pmos.extracted_table['lengths'], 'gm/id', 'gm/gds')" + "plot_data_vs_data(gm_values/id_values, gm_values/gds_values, vgs_values, reshaped_lengths_pmos, 'gm/id', 'gm/gds')" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "efd31595-ac9a-4bcd-aa34-8ed796fb0eb6", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5707b58d-d36f-44c6-af55-238127aea52d", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fcc3617a-3c04-448c-a988-c30ea2603d7f", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/modules/module_0_foundations/sg13_nmos_lv.py b/modules/module_0_foundations/sg13_nmos_lv.py new file mode 100644 index 00000000..a6a32a21 --- /dev/null +++ b/modules/module_0_foundations/sg13_nmos_lv.py @@ -0,0 +1,58 @@ +from mosplot.lookup_table_generator.simulators import NgspiceSimulator, HspiceSimulator +from mosplot.lookup_table_generator import LookupTableGenerator, TransistorSweep +# One of `include_paths` or `lib_mappings` must be specified. +# The rest are optional. + +ngspice = NgspiceSimulator( + # Provide path to simulator if simulator is not in system path. + simulator_path="ngspice", + + # Default simulation temperature. Override if needed. + temperature=27, + + # All parameters are saved by default. Override if needed. + parameters_to_save=["id", "vth", "vdsat", "gm","gds","vgs"], + + + # Files to include with `.LIB`. + lib_mappings = [ + ("/home/pedersen/IHP-Open-PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerMOSlv.lib", " mos_tt") + ], + + # If the transistor is defined inside a subcircuit in + # the library files, you must specify the symbol used (first entry) + # and the hierarchical name (second entry). Override if needed. + mos_spice_symbols = ("XM1", "n.xm1.nsg13_lv_nmos"), + + + + # Specify the width. For devices that do not take a width, + # you can specify other parameters such as the number of fingers. + # The keys are exactly those recognized by the model. + device_parameters = { + "w": 10e-6, + } +) + +nmos_sweep = TransistorSweep( + mos_type="nmos", + vgs=(0, 1.2, 0.01), + vds=(0, 1.2, 0.01), + vbs=(0, -1.2, -0.1), + length = [130e-9, 260e-9, 390e-9, 520e-9, 650e-9, 780e-9, 910e-9, 1040e-9, 1170e-9, 1300e-9, 1430e-9, 1560e-9, 1690e-9, 1820e-9, 1950e-9, 2080e-9, 2210e-9, 2340e-9, 2470e-9, 2600e-9, 2730e-9, 2860e-9, 2990e-9, 3120e-9, 3250e-9, 3380e-9, 3510e-9, 3640e-9, 3770e-9, 3900e-9, 4030e-9, 4160e-9, 4290e-9, 4420e-9, 4550e-9, 4680e-9, 4810e-9, 4940e-9, 5070e-9, 5200e-9, 5330e-9, 5460e-9, 5590e-9, 5720e-9, 5850e-9, 5980e-9, 6110e-9, 6240e-9, 6370e-9, 6500e-9, 6630e-9, 6760e-9, 6890e-9, 7020e-9, 7150e-9, 7280e-9, 7410e-9, 7540e-9, 7670e-9, 7800e-9, 7930e-9, 8060e-9, 8190e-9, 8320e-9, 8450e-9, 8580e-9, 8710e-9, 8840e-9, 8970e-9, 9100e-9, 9230e-9, 9360e-9, 9490e-9, 9620e-9, 9750e-9, 9880e-9] +) + + +obj = LookupTableGenerator( + description="sg13_nmos_lv", + simulator=ngspice, + model_sweeps={ + "sg13_lv_nmos ": nmos_sweep, + }, + n_process=2, +) + +# obj.op_simulation() +obj.build("./sg13_nmos_lv_LUT") + + diff --git a/modules/module_0_foundations/sg13_pmos_lv.py b/modules/module_0_foundations/sg13_pmos_lv.py new file mode 100644 index 00000000..6346516e --- /dev/null +++ b/modules/module_0_foundations/sg13_pmos_lv.py @@ -0,0 +1,58 @@ +from mosplot.lookup_table_generator.simulators import NgspiceSimulator, HspiceSimulator +from mosplot.lookup_table_generator import LookupTableGenerator, TransistorSweep +# One of `include_paths` or `lib_mappings` must be specified. +# The rest are optional. + +ngspice = NgspiceSimulator( + # Provide path to simulator if simulator is not in system path. + simulator_path="ngspice", + + # Default simulation temperature. Override if needed. + temperature=27, + + # All parameters are saved by default. Override if needed. + parameters_to_save=["id", "vth", "vdsat", "gm","gds","vgs"], + + + # Files to include with `.LIB`. + lib_mappings = [ + ("/home/pedersen/IHP-Open-PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerMOSlv.lib", " mos_tt") + ], + + # If the transistor is defined inside a subcircuit in + # the library files, you must specify the symbol used (first entry) + # and the hierarchical name (second entry). Override if needed. + mos_spice_symbols = ("XM1", "n.xm1.nsg13_lv_pmos"), + + + + # Specify the width. For devices that do not take a width, + # you can specify other parameters such as the number of fingers. + # The keys are exactly those recognized by the model. + device_parameters = { + "w": 10e-6, + } +) + +# Define a sweep object for PMOS transistors. +pmos_sweep = TransistorSweep( + mos_type="pmos", + vgs=(0, -1.2, -0.01), + vds=(0, -1.2, -0.01), + vbs=(0, 1.2, 0.1), + length = [130e-9, 260e-9, 390e-9, 520e-9, 650e-9, 780e-9, 910e-9, 1040e-9, 1170e-9, 1300e-9, 1430e-9, 1560e-9, 1690e-9, 1820e-9, 1950e-9, 2080e-9, 2210e-9, 2340e-9, 2470e-9, 2600e-9, 2730e-9, 2860e-9, 2990e-9, 3120e-9, 3250e-9, 3380e-9, 3510e-9, 3640e-9, 3770e-9, 3900e-9, 4030e-9, 4160e-9, 4290e-9, 4420e-9, 4550e-9, 4680e-9, 4810e-9, 4940e-9, 5070e-9, 5200e-9, 5330e-9, 5460e-9, 5590e-9, 5720e-9, 5850e-9, 5980e-9, 6110e-9, 6240e-9, 6370e-9, 6500e-9, 6630e-9, 6760e-9, 6890e-9, 7020e-9, 7150e-9, 7280e-9, 7410e-9, 7540e-9, 7670e-9, 7800e-9, 7930e-9, 8060e-9, 8190e-9, 8320e-9, 8450e-9, 8580e-9, 8710e-9, 8840e-9, 8970e-9, 9100e-9, 9230e-9, 9360e-9, 9490e-9, 9620e-9, 9750e-9, 9880e-9] +) + +obj = LookupTableGenerator( + description="sg13_omos_lv", + simulator=ngspice, + model_sweeps={ + "sg13_lv_pmos" : pmos_sweep, + }, + n_process=2, +) + +# obj.op_simulation() +obj.build("./sg13_pmos_lv_LUT") + + diff --git a/modules/module_1_bandgap_reference/part_1_OTA/scripting/OTA_low_gain.ipynb b/modules/module_1_bandgap_reference/part_1_OTA/scripting/OTA_low_gain.ipynb index 1219acbf..58874e6b 100644 --- a/modules/module_1_bandgap_reference/part_1_OTA/scripting/OTA_low_gain.ipynb +++ b/modules/module_1_bandgap_reference/part_1_OTA/scripting/OTA_low_gain.ipynb @@ -2,37 +2,13 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Successfully imported from src.main!\n" - ] - } - ], - "source": [ - "import os\n", - "import sys\n", - "sys.path.append(os.path.abspath('../../../module_0_foundations/gmid_repo/mosplot/src'))\n", - "\n", - "try:\n", - " from main import load_lookup_table, LoadMosfet\n", - " print(\"Successfully imported from src.main!\")\n", - "except ImportError as e:\n", - " print(f\"Error importing directly from src.main: {e}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", + "from mosplot.plot import load_lookup_table, Mosfet, Expression\n", "import ipywidgets as widgets\n", "from ipywidgets import interactive\n", "from ipywidgets import interactive_output, HBox, VBox\n", @@ -41,31 +17,54 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ - "nmos_lv_path = '../../../module_0_foundations/gmid_repo/LUTs/nmos_lv_lut_tt.npy'\n", - "pmos_lv_path = '../../../module_0_foundations/gmid_repo/LUTs/pmos_lv_lut_tt.npy'\n", + "lookup_table_nmos = load_lookup_table('../../../module_0_foundations/sg13_nmos_lv_LUT.npz')\n", + "lookup_table_pmos = load_lookup_table('../../../module_0_foundations/sg13_pmos_lv_LUT.npz')" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['sg13_lv_nmos ', 'description', 'simulator', 'parameter_names', 'device_parameters'])\n" + ] + } + ], + "source": [ + "print(lookup_table_nmos.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "nmos = Mosfet(lookup_table=lookup_table_nmos, mos=\"sg13_lv_nmos \", vbs=0.0, vds=0.6)\n", + "pmos = Mosfet(lookup_table=lookup_table_pmos, mos=\"sg13_lv_pmos\", vbs=0.0, vds=-0.6, vgs=(-1.2, -0.15))\n", "\n", - "lookup_table_pmos = load_lookup_table(pmos_lv_path)\n", - "lookup_table_nmos = load_lookup_table(nmos_lv_path)" + "rows_0, cols_0 = np.shape(nmos.extracted_table['gm']) # just for getting the shape of the data\n", + "rows_1, cols_1 = np.shape(pmos.extracted_table['gm']) # just for getting the shape of the data\n", + "reshaped_lengths_nmos = np.tile(nmos.length[:, np.newaxis], (1, cols_0))\n", + "reshaped_lengths_pmos = np.tile(pmos.length[:, np.newaxis], (1, cols_1))" ] }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "nmos = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0.0, vds=0.6)\n", - "pmos = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=-0.2, vds=-0.6, vgs=(-1.2, -0.1))" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, + "execution_count": 24, + "metadata": { + "jupyter": { + "source_hidden": true + } + }, "outputs": [], "source": [ "def plot_data_vs_data(x_values, y_values, z_values, length, x_axis_name, y_axis_name='y', y_multiplier=1, log=False):\n", @@ -265,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -279,7 +278,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -289,18 +288,18 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c19c949de81145fbb8f56ff8c2747ab1", + "model_id": "ea4f549168444a6da3039c78cb0ef5f0", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.20 μm', '…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -308,19 +307,22 @@ } ], "source": [ - "nmos_M6 = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0, vds=0.6)\n", - "width_values_M6 = nmos_M6.extracted_table['width']\n", + "nmos_M6 = Mosfet(lookup_table=lookup_table_nmos, mos=\"sg13_lv_nmos \", vbs=0, vds=0.6)\n", + "rows_0, cols_0 = np.shape(nmos_M6.extracted_table['gm'])\n", + "reshaped_lengths_nmos_M6 = np.tile(nmos_M6.length[:, np.newaxis], (1, cols_0))\n", + "\n", + "width_values_M6 = nmos_M6.width\n", "id_values_M6 = nmos_M6.extracted_table['id']\n", "gm_values_M6 = nmos_M6.extracted_table['gm']\n", "gds_values_M6 = nmos_M6.extracted_table['gds']\n", "vgs_values_M6 = nmos_M6.extracted_table['vgs']\n", "\n", - "plot_data_vs_data(gm_values_M6/id_values_M6, gm_values_M6/gds_values_M6, vgs_values_M6, nmos_M6.extracted_table['lengths'], 'gm/id', 'gm/gds')" + "plot_data_vs_data(gm_values_M6/id_values_M6, gm_values_M6/gds_values_M6, vgs_values_M6, reshaped_lengths_nmos_M6, 'gm/id', 'gm/gds')" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -329,7 +331,7 @@ "7.949913192125772e-05" ] }, - "execution_count": 9, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -346,7 +348,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -369,18 +371,18 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1fec87aafa2d4d009fc5065b0c919616", + "model_id": "c13371af34b44b4fbcd6d05b532d361f", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -388,19 +390,24 @@ } ], "source": [ - "pmos_M7 = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=0, vds=-0.6, vgs=(-1.2, -0.1))\n", - "width_values_M7 = pmos_M7.extracted_table['width']\n", + "pmos_M7 = Mosfet(lookup_table=lookup_table_pmos, mos=\"sg13_lv_pmos\", vbs=0, vds=-0.6, vgs=(-1.2, -0.1))\n", + "rows_0, cols_0 = np.shape(pmos_M7.extracted_table['gm'])\n", + "reshaped_lengths_pmos_M7 = np.tile(pmos_M7.length[:, np.newaxis], (1, cols_0))\n", + "\n", + "width_values_M7 = pmos_M7.width\n", "id_values_M7 = pmos_M7.extracted_table['id']\n", "gm_values_M7 = pmos_M7.extracted_table['gm']\n", "gds_values_M7 = pmos_M7.extracted_table['gds']\n", "vgs_values_M7 = pmos_M7.extracted_table['vgs']\n", "\n", - "plot_data_vs_data(gm_values_M7/id_values_M7, gm_values_M7/gds_values_M7, vgs_values_M7, pmos_M7.extracted_table['lengths'], 'gm/id', 'gds')\n" + "plot_data_vs_data(gm_values_M7/id_values_M7, gm_values_M7/gds_values_M7, vgs_values_M7, reshaped_lengths_pmos_M7, 'gm/id', 'gds')\n", + "\n", + "\n" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -411,7 +418,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -434,18 +441,18 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d9ae4f5537e44eb98cb8f43a99ace9e6", + "model_id": "2eca17df295841629e74fb72e1cff63f", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.30 μm', '0.40 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -454,12 +461,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "86826b2d41334c9f97fc4c71228b844b", + "model_id": "88996c8fb302472b8fb5add0523f26dc", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -467,13 +474,13 @@ } ], "source": [ - "plot_data_vs_data(gm_values_M6/id_values_M6,id_values_M6/width_values_M6, vgs_values_M6, nmos_M6.extracted_table['lengths'], 'gm/id', 'M6 id/W', log=True)\n", - "plot_data_vs_data(gm_values_M7/id_values_M7, id_values_M7/width_values_M7,vgs_values_M7, pmos_M7.extracted_table['lengths'], 'gm/id', 'M7 id/W', log=True)" + "plot_data_vs_data(gm_values_M6/id_values_M6,id_values_M6/width_values_M6, vgs_values_M6, reshaped_lengths_nmos_M6, 'gm/id', 'M6 id/W', log=True)\n", + "plot_data_vs_data(gm_values_M7/id_values_M7, id_values_M7/width_values_M7,vgs_values_M7, reshaped_lengths_pmos_M7, 'gm/id', 'M7 id/W', log=True)" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -543,7 +550,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -552,7 +559,7 @@ "2.0004294796936965e-06" ] }, - "execution_count": 16, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -570,7 +577,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 44, "metadata": {}, "outputs": [ { @@ -590,7 +597,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 45, "metadata": {}, "outputs": [ { @@ -618,18 +625,18 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "653d5df7f39548198c9e9ad5b226e0ed", + "model_id": "5d7b576ad312473eaeab532b93f3e92f", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -638,12 +645,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "45eaee19c87c4d629180ab7b63eae94a", + "model_id": "e116eacdd44e4571b3f183da0118a4fa", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.30 μm', '0.40 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -651,8 +658,13 @@ } ], "source": [ - "pmos_M12 = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=-0.2, vds=-0.6, vgs=(-1.2, -0.1))\n", - "nmos_M34 = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0, vds=0.6)\n", + "pmos_M12 = Mosfet(lookup_table=lookup_table_pmos, mos=\"sg13_lv_pmos\", vbs=-0.2, vds=-0.6, vgs=(-1.2, -0.1))\n", + "nmos_M34 = Mosfet(lookup_table=lookup_table_nmos, mos=\"sg13_lv_nmos \", vbs=0, vds=0.6)\n", + "rows_0, cols_0 = np.shape(nmos_M34.extracted_table['gm']) # just for getting the shape of the data\n", + "rows_1, cols_1 = np.shape(pmos_M12.extracted_table['gm']) # just for getting the shape of the data\n", + "reshaped_lengths_nmos_M34 = np.tile(nmos_M34.length[:, np.newaxis], (1, cols_0))\n", + "reshaped_lengths_pmos_M12 = np.tile(pmos_M12.length[:, np.newaxis], (1, cols_1))\n", + "\n", "\n", "id_values_M12 = pmos_M12.extracted_table['id']\n", "gm_values_M12 = pmos_M12.extracted_table['gm']\n", @@ -664,13 +676,13 @@ "gds_values_M34 = nmos_M34.extracted_table['gds']\n", "vgs_values_M34 = nmos_M34.extracted_table['vgs']\n", "\n", - "plot_data_vs_data(gm_values_M12/id_values_M12, gm_values_M12/gds_values_M12, vgs_values_M12, pmos_M12.extracted_table['lengths'], 'gm/id', 'm12 gm/gds')\n", - "plot_data_vs_data(gm_values_M34/id_values_M34, gm_values_M34/gds_values_M34, vgs_values_M34, nmos_M34.extracted_table['lengths'], 'gm/id', 'm34 gm/gds')" + "plot_data_vs_data(gm_values_M12/id_values_M12, gm_values_M12/gds_values_M12, vgs_values_M12, reshaped_lengths_pmos_M12, 'gm/id', 'm12 gm/gds')\n", + "plot_data_vs_data(gm_values_M34/id_values_M34, gm_values_M34/gds_values_M34, vgs_values_M34, reshaped_lengths_nmos_M34, 'gm/id', 'm34 gm/gds')" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 48, "metadata": {}, "outputs": [ { @@ -697,18 +709,18 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "777232b20ad64f47aaa584c273a69fe7", + "model_id": "4cca567128c14cb68100041e1a57c919", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -717,12 +729,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7e6503685d4f4eadb9a22f5e2fd505c1", + "model_id": "6c92cedcecfb47f491e2b860b0c65a31", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.30 μm', '0.40 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…" ] }, "metadata": {}, @@ -730,16 +742,16 @@ } ], "source": [ - "width_values_M12 = pmos_M12.extracted_table['width']\n", - "width_values_M34 = nmos_M34.extracted_table['width']\n", + "width_values_M12 = pmos_M12.width\n", + "width_values_M34 = nmos_M34.width\n", "\n", - "plot_data_vs_data(gm_values_M12/id_values_M12,id_values_M12/width_values_M12, vgs_values_M12, pmos_M12.extracted_table['lengths'], 'gm/id', 'M12 id/W', log=True)\n", - "plot_data_vs_data(gm_values_M34/id_values_M34, id_values_M34/width_values_M34,vgs_values_M34, nmos_M34.extracted_table['lengths'], 'gm/id', 'M34 id/W', log=True)\n" + "plot_data_vs_data(gm_values_M12/id_values_M12,id_values_M12/width_values_M12, vgs_values_M12, reshaped_lengths_pmos_M12, 'gm/id', 'M12 id/W', log=True)\n", + "plot_data_vs_data(gm_values_M34/id_values_M34, id_values_M34/width_values_M34,vgs_values_M34, reshaped_lengths_nmos_M34, 'gm/id', 'M34 id/W', log=True)\n" ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 51, "metadata": {}, "outputs": [ { @@ -792,7 +804,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 52, "metadata": {}, "outputs": [ { @@ -894,6 +906,13 @@ "\"\"\"\n", "print(summary)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/modules/module_1_bandgap_reference/part_1_OTA/testbenches/ota_testbench.sch b/modules/module_1_bandgap_reference/part_1_OTA/testbenches/ota_testbench.sch index ad4c3d64..6c472580 100644 --- a/modules/module_1_bandgap_reference/part_1_OTA/testbenches/ota_testbench.sch +++ b/modules/module_1_bandgap_reference/part_1_OTA/testbenches/ota_testbench.sch @@ -12,7 +12,7 @@ ypos2=2 divy=5 subdivy=4 unity=1 -x1=-1.75 +x1=-0.22184875 divx=5 subdivx=8 @@ -38,16 +38,16 @@ y2=33.964 y1=-136.006 color=4 node=ph(vout) -x2=5.25} +x2=-0.22184875} B 2 680 -1295 1480 -895 {flags=graph -y1=33 -y2=74 +y1=27 +y2=71 ypos1=0 ypos2=2 divy=5 subdivy=1 unity=1 -x1=-1.75 +x1=-0.22184875 divx=5 subdivx=8 @@ -59,7 +59,7 @@ dataset=-1 unitx=1 logx=1 logy=0 -x2=5.25} +x2=-0.22184875} B 2 1535 -1295 2335 -895 {flags=graph y1=37 y2=86 @@ -68,7 +68,7 @@ ypos2=2 divy=5 subdivy=1 unity=1 -x1=-1.75 +x1=-0.22184875 divx=5 subdivx=8 @@ -80,18 +80,18 @@ dataset=-1 unitx=1 logx=1 logy=0 -x2=5.25 +x2=-0.22184875 color=4 node=cmrr} B 2 1525 -875 2325 -475 {flags=graph -y1=0.05 -y2=28 +y1=0.055 +y2=30 ypos1=0 ypos2=2 divy=5 subdivy=1 unity=1 -x1=-1.75 +x1=-0.22184875 divx=5 subdivx=8 @@ -103,7 +103,7 @@ dataset=-1 unitx=1 logx=1 logy=0 -x2=5.25 +x2=-0.22184875 color=4 node=psrr} N 775 -265 775 -235 { @@ -235,7 +235,7 @@ footprint=1206 device="ceramic capacitor"} C {gnd.sym} 530 -335 0 0 {name=l5 lab=GND} C {iopin.sym} 620 -410 0 0 {name=p7 lab=vout} -C {devices/code_shown.sym} -445 -290 0 0 {name=MODEL only_toplevel=false +C {devices/code_shown.sym} -415 -290 0 0 {name=MODEL only_toplevel=false format="tcleval( @value )" value=" .lib $::SG13G2_MODELS/cornerCAP.lib cap_typ