diff --git a/modules/module_0_foundations/gm_id_test/nmos_gmid.sch b/modules/module_0_foundations/gm_id_test/nmos_gmid.sch deleted file mode 100644 index c05355a2..00000000 --- a/modules/module_0_foundations/gm_id_test/nmos_gmid.sch +++ /dev/null @@ -1,56 +0,0 @@ -v {xschem version=3.4.5 file_version=1.2 -} -G {} -K {} -V {} -S {} -E {} -N 190 -550 190 -390 { -lab=#net1} -N 190 -320 190 -300 { -lab=GND} -N 190 -360 200 -360 { -lab=GND} -N 200 -360 200 -320 { -lab=GND} -N 190 -320 200 -320 { -lab=GND} -N 190 -330 190 -320 { -lab=GND} -N 90 -480 90 -460 { -lab=GND} -N 90 -550 90 -540 { -lab=#net1} -N 90 -550 190 -550 { -lab=#net1} -N 90 -280 90 -260 { -lab=GND} -N 90 -360 90 -340 { -lab=#net2} -N 90 -360 150 -360 { -lab=#net2} -C {devices/code_shown.sym} -280 -270 0 0 {name=MODEL only_toplevel=true -format="tcleval( @value )" -value=" -.lib cornerMOSlv.lib mos_tt -"} -C {devices/code_shown.sym} -290 -420 0 0 {name=NGSPICE only_toplevel=true -value=" -.control -op -write output_file.raw -.endc -"} -C {sg13g2_pr/sg13_lv_nmos.sym} 170 -360 2 1 {name=M3 -l=3.25u -w=3.33u -ng=1 -m=1 -model=sg13_lv_nmos -spiceprefix=X -} -C {devices/gnd.sym} 190 -300 0 0 {name=l2 lab=GND} -C {devices/vsource.sym} 90 -510 0 0 {name=Vdd1 value= 0.6} -C {devices/gnd.sym} 90 -460 0 0 {name=l3 lab=GND} -C {devices/vsource.sym} 90 -310 0 0 {name=Vdd2 value= 0.27} -C {devices/gnd.sym} 90 -260 0 0 {name=l4 lab=GND} diff --git a/modules/module_0_foundations/gmid_repo/README.md b/modules/module_0_foundations/gmid_repo/README.md new file mode 100644 index 00000000..18062593 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/README.md @@ -0,0 +1,410 @@ +# 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 new file mode 100644 index 00000000..be42a3a4 Binary files /dev/null and b/modules/module_0_foundations/gmid_repo/__pycache__/gmid_launcher.cpython-310.pyc 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 new file mode 100644 index 00000000..4794aa66 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_current_density.svg @@ -0,0 +1,2262 @@ + + + + + + + + 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 new file mode 100644 index 00000000..15ea90a8 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_filtered.svg @@ -0,0 +1,2522 @@ + + + + + + + + 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 new file mode 100644 index 00000000..dba28229 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_current_density_options.svg @@ -0,0 +1,2330 @@ + + + + + + + + 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 new file mode 100644 index 00000000..c6b9fabc --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_1.svg @@ -0,0 +1,2973 @@ + + + + + + + + 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 new file mode 100644 index 00000000..53e2efd7 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_custom_expression_2.svg @@ -0,0 +1,2404 @@ + + + + + + + + 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 new file mode 100644 index 00000000..543bc867 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_gain_plot.svg @@ -0,0 +1,2439 @@ + + + + + + + + 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 new file mode 100644 index 00000000..2aadfbdc --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_output_characteristics.svg @@ -0,0 +1,2045 @@ + + + + + + + + 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 new file mode 100644 index 00000000..79d30548 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_plot_by_sweep.svg @@ -0,0 +1,1739 @@ + + + + + + + + 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 new file mode 100644 index 00000000..d25d864b --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot.svg @@ -0,0 +1,2371 @@ + + + + + + + + 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 new file mode 100644 index 00000000..4a8724bd --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/figures/nmos_quick_plot2.svg @@ -0,0 +1,1309 @@ + + + + + + + + 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 new file mode 100644 index 00000000..4c0b84c9 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/gmid_launcher.py @@ -0,0 +1,38 @@ +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 new file mode 100644 index 00000000..e6816080 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/matplotlib_style/style.mplstyle @@ -0,0 +1,55 @@ +# 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 new file mode 100644 index 00000000..4117c367 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/mosplot/__init__.py @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000..352e607a Binary files /dev/null and b/modules/module_0_foundations/gmid_repo/mosplot/__pycache__/__init__.cpython-310.pyc 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 new file mode 100644 index 00000000..fc9f6666 Binary files /dev/null and b/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/hspice_parser.cpython-310.pyc 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 new file mode 100644 index 00000000..c75925d7 Binary files /dev/null and b/modules/module_0_foundations/gmid_repo/mosplot/parsers/__pycache__/ngspice_parser.cpython-310.pyc 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 new file mode 100644 index 00000000..242d284b --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/mosplot/parsers/hspice_parser.py @@ -0,0 +1,615 @@ +""" +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 new file mode 100644 index 00000000..a285e037 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/mosplot/parsers/ngspice_parser.py @@ -0,0 +1,63 @@ +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 new file mode 100644 index 00000000..ae9e0e2c Binary files /dev/null and b/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/lookup_table_generator.cpython-310.pyc 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 new file mode 100644 index 00000000..b0996d4a Binary files /dev/null and b/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/main.cpython-310.pyc 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 new file mode 100644 index 00000000..0a6e1cda Binary files /dev/null and b/modules/module_0_foundations/gmid_repo/mosplot/src/__pycache__/manu_man.cpython-310.pyc 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 new file mode 100644 index 00000000..d4596c18 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/mosplot/src/lookup_table_generator.py @@ -0,0 +1,336 @@ +# -----------------------------------------------------------------------------# +# 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 new file mode 100644 index 00000000..93b80afa --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/mosplot/src/main.py @@ -0,0 +1,706 @@ +# -----------------------------------------------------------------------------# +# 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 new file mode 100644 index 00000000..8e23d954 --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/mosplot/src/manu_man.py @@ -0,0 +1,140 @@ +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 new file mode 100644 index 00000000..9f5bbf2b --- /dev/null +++ b/modules/module_0_foundations/gmid_repo/setup.py @@ -0,0 +1,14 @@ +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_commonsource.ipynb b/modules/module_0_foundations/scripting/gmid_test.ipynb similarity index 52% rename from modules/module_0_foundations/scripting/gmid_commonsource.ipynb rename to modules/module_0_foundations/scripting/gmid_test.ipynb index ae204ee4..d15abd5a 100644 --- a/modules/module_0_foundations/scripting/gmid_commonsource.ipynb +++ b/modules/module_0_foundations/scripting/gmid_test.ipynb @@ -1,27 +1,40 @@ { "cells": [ { - "cell_type": "markdown", - "id": "96c73c5c-d26f-4710-9dce-8b67c625438e", + "cell_type": "code", + "execution_count": 1, + "id": "fe26dc38-6623-48db-820a-cf131ac9a268", "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Successfully imported from src.main!\n" + ] + } + ], "source": [ - "# Simple setup for using gm/id with IHP open PDK" + "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": 4, - "id": "93924bd4-4ba6-4275-8643-a10c59a209e7", + "execution_count": 2, + "id": "9f325daf-eb3b-4cf7-8ffe-b9b94e7f66ea", "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", - "#for windows\n", - "#import sys\n", - "#sys.path.append(r'C:\\Users\\....\\gmid') # path to gmid repository\n", - "# ------\n", "import matplotlib.pyplot as plt\n", - "from mosplot import load_lookup_table, LoadMosfet # make sure that mosplot can be found in the python path\n", + "import numpy as np\n", "import ipywidgets as widgets\n", "from ipywidgets import interactive\n", "from ipywidgets import interactive_output, HBox, VBox\n", @@ -30,41 +43,34 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "16b9f998-3107-436e-842d-c588dc4cbfee", + "execution_count": 3, + "id": "b5b31aca-47bf-4461-8e50-16c20f03b337", "metadata": {}, "outputs": [], "source": [ - "pmos_lv_path = '/home/pedersen/projects/IHP-AnalogAcademy/modules/module_0_foundations/gmid_sweeps/pmos_lv_sweep.npy'\n", - "nmos_lv_path ='/home/pedersen/projects/IHP-AnalogAcademy/modules/module_0_foundations/gmid_sweeps/nmos_lv_sweep.npy'\n", + "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", - "lookup_table_pmos = load_lookup_table(pmos_lv_path)\n", - "lookup_table_nmos = load_lookup_table(nmos_lv_path)" + "\n", + "lookup_table_nmos = load_lookup_table(nmos_lv_path)\n", + "lookup_table_pmos = load_lookup_table(pmos_lv_path)" ] }, { "cell_type": "code", - "execution_count": 6, - "id": "1eae8452-b1d5-4611-bb86-4f2f222b2929", + "execution_count": null, + "id": "743dc381-0d35-4aa9-847c-c42c80c17786", "metadata": {}, "outputs": [], "source": [ - "nmos = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0.0, vds=0.4)\n", - "pmos = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=0, vds=-0.6, vgs=(-1.2, -0.1))" - ] - }, - { - "cell_type": "markdown", - "id": "576b86e1-caae-496f-a644-584ff4fb5dd6", - "metadata": {}, - "source": [ - "# Function Definitions" + "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" ] }, { "cell_type": "code", - "execution_count": 7, - "id": "7abd7d3e-cf3d-4643-b5b3-d35f2362f7b7", + "execution_count": null, + "id": "b27d5fca-3436-4df7-895f-f6a4bbd7a80d", "metadata": { "jupyter": { "source_hidden": true @@ -86,16 +92,16 @@ " unique_lengths_in_micro = unique_lengths * 1e6\n", "\n", " def update_plot(selected_length, x_value=None, y_value=None):\n", - " plt.figure(figsize=(8, 6)) # Ensure the plot is drawn fresh for each update\n", + " plt.figure(figsize=(12, 8)) # Make the figure wider (adjust as needed)\n", "\n", " if selected_length == \"Show All\":\n", " mask = np.ones_like(length_flat, dtype=bool)\n", " else:\n", " selected_length_in_micro = float(selected_length.replace(' μm', ''))\n", - " tolerance = 0.1\n", - " # Recalculate the mask with matching shapes\n", + " tolerance = 0.01 # Tighten the tolerance to avoid unwanted data points\n", " mask = np.abs(length_flat * 1e6 - selected_length_in_micro) < tolerance\n", "\n", + " # Apply the mask to the data\n", " x_values_for_length = x_values_flat[mask]\n", " y_values_for_length = y_values_flat[mask] * y_multiplier\n", " z_values_for_length = z_values_flat[mask]\n", @@ -150,30 +156,38 @@ " length_widget = widgets.Dropdown(\n", " options=dropdown_options,\n", " value=dropdown_options[0],\n", - " description='Select Length:',\n", + " description='Length:',\n", + " layout=widgets.Layout(width='500px') # Make the dropdown wider\n", " )\n", "\n", " x_value_widget = widgets.FloatText(\n", " value=np.mean(x_values_flat),\n", - " description=f\"Select {x_axis_name}:\",\n", - " disabled=False\n", + " description=f\"{x_axis_name}:\",\n", + " disabled=False,\n", + " layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n", + " description_width='150px' # Smaller description width\n", " )\n", "\n", " y_value_widget = widgets.FloatText(\n", " value=None,\n", - " description=f\"Set {y_axis_name}:\",\n", - " disabled=True\n", + " description=f\"{y_axis_name}:\",\n", + " disabled=True,\n", + " layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n", + " description_width='150px' # Smaller description width\n", " )\n", "\n", " z_value_widget = widgets.FloatText(\n", " value=None,\n", - " description=f\"Corresponding z value:\",\n", - " disabled=True\n", + " description=f\" Vgs:\",\n", + " disabled=True,\n", + " layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n", + " description_width='150px' # Smaller description width\n", " )\n", "\n", " select_x_or_y_widget = widgets.Checkbox(\n", " value=True,\n", - " description=f\"Select {x_axis_name} (uncheck for {y_axis_name})\",\n", + " description=f\"{x_axis_name} (uncheck for {y_axis_name})\",\n", + " layout=widgets.Layout(width='300px') # Make the checkbox wider\n", " )\n", "\n", " def toggle_x_or_y(change):\n", @@ -193,8 +207,7 @@ " })\n", "\n", " display(VBox([length_widget, select_x_or_y_widget, HBox([x_value_widget, y_value_widget]), z_value_widget, output]))\n", - "\n", - "\n", + " \n", "def display_resistance(ro_value):\n", " \"\"\"Determine the resistance value and its unit.\"\"\"\n", " if ro_value < 1e3:\n", @@ -242,261 +255,56 @@ }, { "cell_type": "markdown", - "id": "57bc7b38-a0e8-40c3-90c5-bd7c2bf03df4", + "id": "2fc675aa-6d59-4d74-83e2-18c56353db0d", "metadata": {}, "source": [ - "# Plotting examples\n", - "\n", - "Note: In my cases i will be using my own defined functions to plot more interatively. You can refer to the standard repository to see how to plot using the native functions in the repo..." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "cdfafca1-e7c0-4491-8a38-5785f38c8d60", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b02c4d3ead7440c2803417b6fcc005cf", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "03c6dd08648e483ea151c95baae95aa4", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Lets start by plotting the intrinsic gain of both pmos and nmos as a function of gm/id. We start by setting the data arrays\n", - "\n", - "id_values_nmos = nmos.extracted_table['id']\n", - "gm_values_nmos = nmos.extracted_table['gm']\n", - "gds_values_nmos = nmos.extracted_table['gds']\n", - "vgs_values_nmos = nmos.extracted_table['vgs']\n", - "\n", - "id_values_pmos = pmos.extracted_table['id']\n", - "gm_values_pmos = pmos.extracted_table['gm']\n", - "gds_values_pmos = pmos.extracted_table['gds']\n", - "vgs_values_pmos = pmos.extracted_table['vgs']\n", - "\n", - "\n", - "plot_data_vs_data(gm_values_nmos/id_values_nmos, gm_values_nmos/gds_values_nmos, vgs_values_nmos, nmos.extracted_table['lengths'], 'gm/id', 'gds') # plotting nmos data\n", - "plot_data_vs_data(gm_values_pmos/id_values_pmos, gm_values_pmos/gds_values_pmos, vgs_values_pmos, pmos.extracted_table['lengths'], 'gm/id', 'gds') # plotting pmos data" - ] - }, - { - "cell_type": "markdown", - "id": "1cd1cfe6-8260-4b9f-8166-f953758c0563", - "metadata": {}, - "source": [ - "# Verifying the models\n", - "To ensure that the gmid library is working properly we will simply set the dimensions for a nmos a varify in xschem" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "7dd17dfa-abd2-4c76-9fd4-1f7824fdaa58", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0 μA\n" - ] - } - ], - "source": [ - "gmid = 18 #moderate inversion\n", - "id = 1e-6\n", - "gm = gmid*id\n", - "display_id, unit_id = display_current(id)\n", - "print(f'{display_id} {unit_id}')" - ] - }, - { - "cell_type": "markdown", - "id": "c5edba7b-f8ec-4405-a7b3-908489a7c47f", - "metadata": {}, - "source": [ - "# Choosing the channel length by quick overview" - ] - }, - { - "cell_type": "markdown", - "id": "5a570be2-c30c-456e-ad10-f18870d1d27e", - "metadata": {}, - "source": [ - "Here the focus is on getting a high intrinsic gain" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "7fd6ceee-3d40-460f-82d6-6d672d0d337d", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "82d7c57bbc6f44a79891a51bcd92c82e", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_data_vs_data(gm_values_nmos/id_values_nmos, gm_values_nmos/gds_values_nmos, vgs_values_nmos, nmos.extracted_table['lengths'], 'gm/id', 'gds') # plotting nmos data" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "3fa1a702-34dd-43de-a95e-a6654fea16eb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "R_load = 2.25 MΩ\n" - ] - } - ], - "source": [ - "# By sweeping through the lenghts for a fixed gm/id we see that the lengths is approximatly 3.25e-6\n", - "Lnmos = 3.25e-6\n", - "gmro = 40.44\n", - "ro = gmro/gm\n", - "display_ro, unit_ro = display_resistance(ro)\n", - "print(f'R_load = {display_ro:.2f} {unit_ro}')" - ] - }, - { - "cell_type": "markdown", - "id": "20cf0ad2-022a-45d6-8a1f-7c41fd2e26c7", - "metadata": {}, - "source": [ - "# Now we want to find the Corresponding width" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "eb54a54a-491b-4c94-8d34-d14457474b25", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9056d3790ad7459bbf57e878d655cce1", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "width_nmos = nmos.extracted_table['width']\n", - "plot_data_vs_data(gm_values_nmos/id_values_nmos, id_values_nmos/width_nmos, vgs_values_nmos, nmos.extracted_table['lengths'], 'gm/id', 'id/W', log=True) # plotting nmos data" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "e6e2b885-e999-4e29-9a07-d9183db4c021", - "metadata": {}, - "outputs": [], - "source": [ - "# We see that for a gm/id of 18 and length of 3.25 our, id/W is given as 0.3\n", - "id_over_width_nmos = 0.3\n", - "Wnmos = id/id_over_width_nmos" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "9ff5d012-44c7-4acb-b7e2-19ff15312594", - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Width and Length for NMOS\n", - " W = 3.33 um\n", - " L = 3.25 um\n", - "Inversion Region for NMOS: Moderate Inversion\n", - "\n", - "Bias Current:\n", - " Id = 1.00μA\n", - "Parameters to check\n", - " gm/gds = 40.44\n", - " gm = 0.018 mS\n", - " ro = 2.25MΩ\n", - "\n", - "\n" - ] - } - ], - "source": [ - "# Now we can summarize everything\n", - "\n", - "single_transistor_summary = f\"\"\"\n", - "Width and Length for NMOS\n", - " W = {Wnmos*1e6:.2f} um\n", - " L = {Lnmos*1e6:.2f} um\n", - "Inversion Region for NMOS: {determine_inversion_region(gmid, 'nmos')}\n", - "\n", - "Bias Current:\n", - " Id = {display_id:.2f}{unit_id}\n", - "Parameters to check\n", - " gm/gds = {gmro:.2f}\n", - " gm = {gm*1e3:.2} mS\n", - " ro = {display_ro:.2f}{unit_ro}\n", - "\n", - "\"\"\"\n", - "print(single_transistor_summary)" + "# NMOS GMID" ] }, { "cell_type": "code", "execution_count": null, - "id": "5a0c63c5-99f4-46ae-a827-bb7525950798", + "id": "b7cc630f-b385-47a6-a6f9-ac0d10effffe", + "metadata": {}, + "outputs": [], + "source": [ + "width_values = nmos.extracted_table['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')" + ] + }, + { + "cell_type": "markdown", + "id": "e847c359-b57e-4e84-b0dc-93616d575efd", + "metadata": {}, + "source": [ + "# PMOS GMID" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3727c42d-a4bf-4eb0-bc11-6e859ae41324", + "metadata": {}, + "outputs": [], + "source": [ + "width_values = pmos.extracted_table['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')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efd31595-ac9a-4bcd-aa34-8ed796fb0eb6", "metadata": {}, "outputs": [], "source": [] @@ -504,7 +312,15 @@ { "cell_type": "code", "execution_count": null, - "id": "16b8b4f2-807a-4f75-8bc1-cf12a7d1ec0f", + "id": "5707b58d-d36f-44c6-af55-238127aea52d", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcc3617a-3c04-448c-a988-c30ea2603d7f", "metadata": {}, "outputs": [], "source": [] 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 0a4caac0..1219acbf 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,17 +2,37 @@ "cells": [ { "cell_type": "code", - "execution_count": 122, + "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, "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", - "#for windows\n", - "import sys\n", - "sys.path.append(r'C:\\Users\\pedersen\\gmid') # path to gmid repository\n", - "# ------\n", "import matplotlib.pyplot as plt\n", - "from mosplot import load_lookup_table, LoadMosfet # make sure that mosplot can be found in the python path\n", + "import numpy as np\n", "import ipywidgets as widgets\n", "from ipywidgets import interactive\n", "from ipywidgets import interactive_output, HBox, VBox\n", @@ -21,12 +41,12 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "pmos_lv_path = r'C:\\Users\\pedersen\\Desktop\\OpenDesingCourse/no_touch_files\\gmoveridpypmos_gmid_final.npy'\n", - "nmos_lv_path = r'C:\\Users\\pedersen\\Desktop\\OpenDesingCourse/no_touch_files\\gmoveridpynmos_final_gmid.npy'\n", + "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", "\n", "lookup_table_pmos = load_lookup_table(pmos_lv_path)\n", "lookup_table_nmos = load_lookup_table(nmos_lv_path)" @@ -34,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -44,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -62,16 +82,16 @@ " unique_lengths_in_micro = unique_lengths * 1e6\n", "\n", " def update_plot(selected_length, x_value=None, y_value=None):\n", - " plt.figure(figsize=(8, 6)) # Ensure the plot is drawn fresh for each update\n", + " plt.figure(figsize=(12, 8)) # Make the figure wider (adjust as needed)\n", "\n", " if selected_length == \"Show All\":\n", " mask = np.ones_like(length_flat, dtype=bool)\n", " else:\n", " selected_length_in_micro = float(selected_length.replace(' μm', ''))\n", - " tolerance = 0.1\n", - " # Recalculate the mask with matching shapes\n", + " tolerance = 0.01 # Tighten the tolerance to avoid unwanted data points\n", " mask = np.abs(length_flat * 1e6 - selected_length_in_micro) < tolerance\n", "\n", + " # Apply the mask to the data\n", " x_values_for_length = x_values_flat[mask]\n", " y_values_for_length = y_values_flat[mask] * y_multiplier\n", " z_values_for_length = z_values_flat[mask]\n", @@ -126,30 +146,38 @@ " length_widget = widgets.Dropdown(\n", " options=dropdown_options,\n", " value=dropdown_options[0],\n", - " description='Select Length:',\n", + " description='Length:',\n", + " layout=widgets.Layout(width='500px') # Make the dropdown wider\n", " )\n", "\n", " x_value_widget = widgets.FloatText(\n", " value=np.mean(x_values_flat),\n", - " description=f\"Select {x_axis_name}:\",\n", - " disabled=False\n", + " description=f\"{x_axis_name}:\",\n", + " disabled=False,\n", + " layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n", + " description_width='150px' # Smaller description width\n", " )\n", "\n", " y_value_widget = widgets.FloatText(\n", " value=None,\n", - " description=f\"Set {y_axis_name}:\",\n", - " disabled=True\n", + " description=f\"{y_axis_name}:\",\n", + " disabled=True,\n", + " layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n", + " description_width='150px' # Smaller description width\n", " )\n", "\n", " z_value_widget = widgets.FloatText(\n", " value=None,\n", - " description=f\"Corresponding z value:\",\n", - " disabled=True\n", + " description=f\" Vgs:\",\n", + " disabled=True,\n", + " layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n", + " description_width='150px' # Smaller description width\n", " )\n", "\n", " select_x_or_y_widget = widgets.Checkbox(\n", " value=True,\n", - " description=f\"Select {x_axis_name} (uncheck for {y_axis_name})\",\n", + " description=f\"{x_axis_name} (uncheck for {y_axis_name})\",\n", + " layout=widgets.Layout(width='300px') # Make the checkbox wider\n", " )\n", "\n", " def toggle_x_or_y(change):\n", @@ -237,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -251,7 +279,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -261,18 +289,18 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7d85aaf18b7e4afb84f5464597cbeb06", + "model_id": "c19c949de81145fbb8f56ff8c2747ab1", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.20 μm', '…" ] }, "metadata": {}, @@ -292,7 +320,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -301,7 +329,7 @@ "7.949913192125772e-05" ] }, - "execution_count": 129, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -318,7 +346,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -341,18 +369,18 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "833bb160c6a040f1aab4ff0562870238", + "model_id": "1fec87aafa2d4d009fc5065b0c919616", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" ] }, "metadata": {}, @@ -372,7 +400,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -383,7 +411,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -406,18 +434,18 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f844ac0b2e5c48ef9c44cf4f63247c08", + "model_id": "d9ae4f5537e44eb98cb8f43a99ace9e6", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.30 μm', '0.40 μm', '0…" ] }, "metadata": {}, @@ -426,12 +454,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "cf6424ea716549f4ad1dfc93487e5e34", + "model_id": "86826b2d41334c9f97fc4c71228b844b", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" ] }, "metadata": {}, @@ -445,7 +473,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -515,7 +543,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -524,7 +552,7 @@ "2.0004294796936965e-06" ] }, - "execution_count": 136, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -542,7 +570,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -562,7 +590,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -590,18 +618,18 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "fa44bbefc0154f809fc62bb1f4a41d7e", + "model_id": "653d5df7f39548198c9e9ad5b226e0ed", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" ] }, "metadata": {}, @@ -610,12 +638,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c792d4dd57d0406fa39ab7b9779bdb9c", + "model_id": "45eaee19c87c4d629180ab7b63eae94a", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.30 μm', '0.40 μm', '0…" ] }, "metadata": {}, @@ -642,7 +670,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -669,18 +697,18 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8cf6156d8983439b9e36b85a04dd0a8b", + "model_id": "777232b20ad64f47aaa584c273a69fe7", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.25 μm', '0.30 μm', '0…" ] }, "metadata": {}, @@ -689,12 +717,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a53493719c6f4501acf45f7f50192f97", + "model_id": "7e6503685d4f4eadb9a22f5e2fd505c1", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…" + "VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.20 μm', '0.30 μm', '0.40 μm', '0…" ] }, "metadata": {}, @@ -711,7 +739,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -764,7 +792,7 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -870,7 +898,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -884,7 +912,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.5" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/bandgap_core/bandgap_core.gds b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/bandgap_core/bandgap_core.gds deleted file mode 100644 index 786f5e73..00000000 Binary files a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/bandgap_core/bandgap_core.gds and /dev/null differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/bandgap_core/schematic_mod/bandgap_reference.sch b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/bandgap_core/schematic_mod/bandgap_reference.sch deleted file mode 100644 index 431326f8..00000000 --- a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/bandgap_core/schematic_mod/bandgap_reference.sch +++ /dev/null @@ -1,201 +0,0 @@ -v {xschem version=3.4.5 file_version=1.2 -} -G {} -K {} -V {} -S {} -E {} -N 370 -560 390 -560 { -lab=#net1} -N 370 -620 370 -560 { -lab=#net1} -N 370 -620 430 -620 { -lab=#net1} -N 130 -560 130 -460 { -lab=GND} -N 280 -460 430 -460 { -lab=GND} -N 130 -740 230 -740 { -lab=v-} -N 280 -965 280 -940 { -lab=vdd} -N 130 -940 280 -940 { -lab=vdd} -N 130 -840 130 -740 { -lab=v-} -N 430 -750 430 -740 { -lab=v+} -N 430 -740 430 -710 { -lab=v+} -N 430 -560 430 -460 { -lab=GND} -N 430 -620 430 -590 { -lab=#net1} -N 430 -650 430 -620 { -lab=#net1} -N 280 -460 280 -430 { -lab=GND} -N 130 -460 280 -460 { -lab=GND} -N 170 -870 390 -870 { -lab=Vo1} -N 595 -900 595 -870 { -lab=Vo1} -N 430 -750 595 -750 { -lab=v+} -N 980 -770 1030 -770 { -lab=VBG} -N 635 -840 635 -770 { -lab=VBG} -N 315 -740 430 -740 { -lab=v+} -N 430 -840 430 -750 { -lab=v+} -N 595 -750 595 -710 { -lab=v+} -N 430 -460 595 -460 { -lab=GND} -N 595 -650 595 -460 { -lab=GND} -N 430 -940 635 -940 { -lab=vdd} -N 980 -655 980 -460 { -lab=GND} -N 800 -460 980 -460 { -lab=GND} -N 980 -770 980 -715 { -lab=VBG} -N 800 -770 980 -770 { -lab=VBG} -N 635 -940 635 -900 { -lab=vdd} -N 635 -870 725 -870 { -lab=vdd} -N 430 -940 430 -900 { -lab=vdd} -N 280 -940 430 -940 { -lab=vdd} -N 430 -870 495 -870 { -lab=vdd} -N 130 -940 130 -900 { -lab=vdd} -N 70 -870 130 -870 { -lab=vdd} -N 130 -740 130 -590 { -lab=v-} -N 170 -560 315 -560 { -lab=v+} -N 315 -740 315 -560 { -lab=v+} -N 800 -770 800 -715 { -lab=VBG} -N 635 -770 800 -770 { -lab=VBG} -N 800 -655 800 -460 { -lab=GND} -N 595 -460 800 -460 { -lab=GND} -N 1195 -730 1195 -700 { -lab=bulk7} -N 1195 -640 1195 -600 { -lab=vdd} -N 1290 -640 1290 -605 { -lab=vss} -N 1290 -725 1290 -700 { -lab=sub!} -C {sg13g2_pr/sg13_lv_nmos.sym} 150 -560 2 0 {name=M1 -l=5u -w=7.14u -ng=4 -m=1 -model=sg13_lv_nmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_nmos.sym} 410 -560 2 1 {name=M2 -l=5u -w=21u -ng=8 -m=1 -model=sg13_lv_nmos -spiceprefix=X -} -C {gnd.sym} 280 -430 0 0 {name=l3 lab=GND} -C {sg13g2_pr/sg13_lv_pmos.sym} 150 -870 0 1 {name=M3 -l=5u -w=15u -ng=8 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_pmos.sym} 410 -870 0 0 {name=M4 -l=5u -w=15u -ng=8 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 280 -965 0 1 {name=p2 sig_type=std_logic lab=vdd} -C {lab_pin.sym} 230 -740 3 0 {name=p3 sig_type=std_logic lab=v-} -C {lab_pin.sym} 315 -740 0 0 {name=p8 sig_type=std_logic lab=v+} -C {lab_pin.sym} 285 -870 1 1 {name=p9 sig_type=std_logic lab=Vo1} -C {sg13g2_pr/sg13_lv_pmos.sym} 615 -870 0 0 {name=M5 -l=5u -w=16u -ng=8 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {opin.sym} 1030 -770 0 0 {name=p11 lab=VBG} -C {lab_pin.sym} 595 -900 2 1 {name=p12 sig_type=std_logic lab=Vo1} -C {lab_pin.sym} 70 -870 0 0 {name=p13 sig_type=std_logic lab=vdd} -C {lab_pin.sym} 495 -870 0 1 {name=p14 sig_type=std_logic lab=vdd} -C {lab_pin.sym} 725 -870 0 1 {name=p15 sig_type=std_logic lab=vdd} -C {sg13g2_pr/cap_cmim.sym} 980 -685 0 0 {name=C3 -model=cap_cmim -w=72.965e-6 -l=72.965e-6 -m=1 -spiceprefix=X} -C {sg13g2_pr/rppd.sym} 430 -680 0 0 {name=R3 -w=0.5e-6 -l=194.345e-6 -model=rppd -spiceprefix=X -b=0 -m=1 -} -C {sg13g2_pr/rppd.sym} 595 -680 0 0 {name=R1 -w=0.6e-6 -l=194.345e-6 -model=rppd -spiceprefix=X -b=0 -m=1 -} -C {sg13g2_pr/rppd.sym} 800 -685 0 0 {name=R2 -w=0.5e-6 -l=192.395e-6 -model=rppd -spiceprefix=X -b=0 -m=1 -} -C {lab_pin.sym} 1195 -730 0 1 {name=p67 sig_type=std_logic lab=bulk7} -C {lab_pin.sym} 1195 -600 0 0 {name=p68 sig_type=std_logic lab=vdd} -C {sg13g2_pr/ntap1.sym} 1195 -670 0 0 {name=R27 -model=ntap1 -spiceprefix=X -R=8.463 -Imax=0.3e-6 -} -C {sg13g2_pr/ptap1.sym} 1290 -670 0 0 {name=R29 -model=ptap1 -spiceprefix=X -w=10e-6 -l=1.0e-6 -} -C {lab_pin.sym} 1290 -605 0 0 {name=p81 sig_type=std_logic lab=vss} -C {lab_pin.sym} 1290 -725 0 1 {name=p82 sig_type=std_logic lab=sub!} diff --git a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout.gds b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout.gds index 4342a090..4b32e67f 100644 Binary files a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout.gds and b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout.gds differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads_filled.gds b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_filled.gds similarity index 86% rename from modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads_filled.gds rename to modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_filled.gds index ab9931b9..b026795d 100644 Binary files a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads_filled.gds and b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_filled.gds differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads.gds b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads.gds index 039ad30e..5cdf02bb 100644 Binary files a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads.gds and b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads.gds differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads_filler_os.gds b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads_filler_os.gds deleted file mode 100644 index d2e3c2f6..00000000 Binary files a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/full_bandgap_layout_pads_filler_os.gds and /dev/null differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/schematic_mod/full_bandgap.sch b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/schematic_mod/full_bandgap.sch index 1c48b8e2..7d3294c6 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/schematic_mod/full_bandgap.sch +++ b/modules/module_1_bandgap_reference/part_3_layout/BGR_layout/final_bandgapreference/schematic_mod/full_bandgap.sch @@ -1,5 +1,4 @@ -v {xschem version=3.4.5 file_version=1.2 -} +v {xschem version=3.4.6 file_version=1.2} G {} K {} V {} @@ -53,7 +52,7 @@ N 450 -305 450 -245 { lab=#net3} N 450 -305 510 -305 { lab=#net3} -N 510 -145 675 -145 { +N 675 -145 880 -145 { lab=vss} N 210 -760 310 -760 { lab=v-} @@ -283,32 +282,26 @@ N 510 -585 510 -565 { lab=#net6} N 510 -670 510 -645 { lab=#net7} -N 675 -350 675 -145 { -lab=vss} -N 675 -425 675 -410 { -lab=#net8} N 675 -500 675 -485 { -lab=#net9} +lab=#net8} N 675 -585 675 -560 { -lab=#net10} +lab=#net9} N 675 -670 675 -645 { -lab=#net11} +lab=#net10} N 880 -240 880 -145 { lab=vss} -N 675 -145 880 -145 { -lab=vss} N 880 -675 880 -660 { -lab=#net12} +lab=#net11} N 880 -600 880 -590 { -lab=#net13} +lab=#net12} N 880 -530 880 -520 { -lab=#net14} +lab=#net13} N 880 -460 880 -445 { -lab=#net15} +lab=#net14} N 880 -385 880 -375 { -lab=#net16} +lab=#net15} N 880 -315 880 -300 { -lab=#net17} +lab=#net16} N 210 -215 210 -145 { lab=vss} N 250 -245 395 -245 { @@ -329,86 +322,6 @@ N 1755 -620 1755 -590 { lab=bulk7} N 1755 -530 1755 -490 { lab=vdd} -N 1690 -855 1690 -785 { -lab=vdd} -N 1690 -855 1730 -855 { -lab=vdd} -N 1730 -855 1730 -815 { -lab=vdd} -N 1690 -785 1730 -785 { -lab=vdd} -N 1825 -855 1825 -785 { -lab=vdd} -N 1825 -855 1865 -855 { -lab=vdd} -N 1865 -855 1865 -815 { -lab=vdd} -N 1825 -785 1865 -785 { -lab=vdd} -N 1695 -770 1695 -700 { -lab=vdd} -N 1695 -770 1735 -770 { -lab=vdd} -N 1735 -770 1735 -730 { -lab=vdd} -N 1695 -700 1735 -700 { -lab=vdd} -N 1830 -770 1830 -700 { -lab=vdd} -N 1830 -770 1870 -770 { -lab=vdd} -N 1870 -770 1870 -730 { -lab=vdd} -N 1830 -700 1870 -700 { -lab=vdd} -N 1950 -855 1950 -785 { -lab=vdd} -N 1950 -855 1990 -855 { -lab=vdd} -N 1990 -855 1990 -815 { -lab=vdd} -N 1950 -785 1990 -785 { -lab=vdd} -N 1950 -775 1950 -705 { -lab=vdd} -N 1950 -775 1990 -775 { -lab=vdd} -N 1990 -775 1990 -735 { -lab=vdd} -N 1950 -705 1990 -705 { -lab=vdd} -N 2075 -775 2075 -705 { -lab=vdd} -N 2075 -775 2115 -775 { -lab=vdd} -N 2115 -775 2115 -735 { -lab=vdd} -N 2075 -705 2115 -705 { -lab=vdd} -N 2075 -855 2075 -785 { -lab=vdd} -N 2075 -855 2115 -855 { -lab=vdd} -N 2115 -855 2115 -815 { -lab=vdd} -N 2075 -785 2115 -785 { -lab=vdd} -N 2200 -775 2200 -705 { -lab=vdd} -N 2200 -775 2240 -775 { -lab=vdd} -N 2240 -775 2240 -735 { -lab=vdd} -N 2200 -705 2240 -705 { -lab=vdd} -N 2200 -855 2200 -785 { -lab=vdd} -N 2200 -855 2240 -855 { -lab=vdd} -N 2240 -855 2240 -815 { -lab=vdd} -N 2200 -785 2240 -785 { -lab=vdd} N 1355 -365 1355 -330 { lab=vss} N 1355 -450 1355 -425 { @@ -429,13 +342,12 @@ N 1670 -360 1670 -325 { lab=vss} N 1670 -445 1670 -420 { lab=sub!} -C {devices/code_shown.sym} 1110 -215 0 0 {name=MODEL only_toplevel=true -format="tcleval( @value )" -value=" -.lib $::SG13G2_MODELS/cornerCAP.lib cap_typ -.lib $::SG13G2_MODELS/cornerRES.lib res_typ -.lib cornerMOSlv.lib mos_tt -"} +N 1887.5 -607.5 1887.5 -585 {lab=vdd} +N 1887.5 -527.5 1887.5 -507.5 {lab=vss} +N 675 -340 675 -145 {lab=vss} +N 510 -145 675 -145 { +lab=vss} +N 675 -425 675 -400 {lab=#net17} C {sg13g2_pr/sg13_lv_nmos.sym} 1520 -1320 2 0 {name=M8 l=10u w=150n @@ -471,7 +383,7 @@ spiceprefix=X C {lab_pin.sym} 1735 -1535 0 1 {name=p5 sig_type=std_logic lab=vdd} C {lab_pin.sym} 1690 -1485 0 1 {name=p4 sig_type=std_logic lab=bulk6} C {lab_pin.sym} 1440 -1485 0 0 {name=p17 sig_type=std_logic lab=bulk6} -C {sg13g2_pr/sg13_lv_nmos.sym} 230 -245 2 0 {name=M1 +C {sg13g2_pr/sg13_lv_nmos.sym} 230 -245 0 1 {name=M1 l=5u w=7.14u ng=4 @@ -479,7 +391,7 @@ m=1 model=sg13_lv_nmos spiceprefix=X } -C {sg13g2_pr/sg13_lv_nmos.sym} 490 -245 2 1 {name=M2 +C {sg13g2_pr/sg13_lv_nmos.sym} 490 -245 0 0 {name=M2 l=5u w=21u ng=8 @@ -591,7 +503,7 @@ spiceprefix=X C {lab_pin.sym} 1255 -865 0 0 {name=p50 sig_type=std_logic lab=vdd} C {lab_pin.sym} 1295 -785 0 0 {name=p53 sig_type=std_logic lab=dn2} C {lab_pin.sym} 1490 -785 0 0 {name=p54 sig_type=std_logic lab=dn2} -C {sg13g2_pr/ntap1.sym} 1300 -560 0 0 {name=R4 +C {sg13g2_pr/ntap1.sym} 1300 -560 2 0 {name=R4 model=ntap1 spiceprefix=X w=13e-6 @@ -615,7 +527,7 @@ spiceprefix=X C {iopin.sym} 505 -1070 1 1 {name=p30 lab=vss} C {iopin.sym} 505 -1580 1 1 {name=p31 lab=vdd} C {lab_pin.sym} 695 -1130 0 1 {name=p32 sig_type=std_logic lab=sub!} -C {sg13g2_pr/ptap1.sym} 1275 -400 0 0 {name=R5 +C {sg13g2_pr/ptap1.sym} 1275 -400 2 0 {name=R5 model=ptap1 spiceprefix=X w=10e-6 @@ -625,7 +537,7 @@ C {lab_pin.sym} 1275 -335 0 0 {name=p33 sig_type=std_logic lab=vss} C {lab_pin.sym} 1275 -455 0 1 {name=p34 sig_type=std_logic lab=sub!} C {lab_pin.sym} 1415 -495 0 0 {name=p35 sig_type=std_logic lab=vdd} C {lab_pin.sym} 1415 -615 0 1 {name=p36 sig_type=std_logic lab=bulk1} -C {sg13g2_pr/ntap1.sym} 1415 -560 0 0 {name=R6 +C {sg13g2_pr/ntap1.sym} 1415 -560 2 0 {name=R6 model=ntap1 spiceprefix=X w=0.78e-6 @@ -686,7 +598,7 @@ C {lab_pin.sym} 75 -1530 0 0 {name=p46 sig_type=std_logic lab=bulk4} C {lab_pin.sym} 950 -1525 2 0 {name=p47 sig_type=std_logic lab=bulk4} C {lab_pin.sym} 1520 -620 0 1 {name=p48 sig_type=std_logic lab=bulk4} C {lab_pin.sym} 1520 -490 0 0 {name=p51 sig_type=std_logic lab=vdd} -C {sg13g2_pr/ntap1.sym} 1520 -560 0 0 {name=R8 +C {sg13g2_pr/ntap1.sym} 1520 -560 2 0 {name=R8 model=ntap1 spiceprefix=X R=262.847.0 @@ -701,7 +613,7 @@ C {lab_pin.sym} 675 -1385 0 1 {name=p19 sig_type=std_logic lab=v+} C {lab_pin.sym} 1810 -1205 3 0 {name=p1 sig_type=std_logic lab=v-} C {lab_pin.sym} 1675 -620 0 1 {name=p7 sig_type=std_logic lab=bulk6} C {lab_pin.sym} 1675 -490 0 0 {name=p16 sig_type=std_logic lab=vdd} -C {sg13g2_pr/ntap1.sym} 1675 -560 0 0 {name=R9 +C {sg13g2_pr/ntap1.sym} 1675 -560 2 0 {name=R9 model=ntap1 spiceprefix=X R=8.463 @@ -749,14 +661,6 @@ spiceprefix=X b=0 m=1 } -C {sg13g2_pr/rppd.sym} 675 -700 0 0 {name=R1 -w=0.5e-6 -l=38.65e-6 -model=rppd -spiceprefix=X -b=0 -m=1 -} C {sg13g2_pr/rppd.sym} 675 -615 0 0 {name=R16 w=0.5e-6 l=38.65e-6 @@ -781,7 +685,7 @@ spiceprefix=X b=0 m=1 } -C {sg13g2_pr/rppd.sym} 675 -380 0 0 {name=R19 +C {sg13g2_pr/rppd.sym} 675 -370 0 0 {name=R19 w=3e-6 l=38.65e-6 model=rppd @@ -842,7 +746,7 @@ C {lab_pin.sym} 1435 -1320 0 0 {name=p63 sig_type=std_logic lab=sub!} C {lab_pin.sym} 1800 -1405 0 1 {name=p64 sig_type=std_logic lab=bulk5} C {lab_pin.sym} 1600 -620 0 1 {name=p65 sig_type=std_logic lab=bulk5} C {lab_pin.sym} 1600 -490 0 0 {name=p66 sig_type=std_logic lab=vdd} -C {sg13g2_pr/ntap1.sym} 1600 -560 0 0 {name=R26 +C {sg13g2_pr/ntap1.sym} 1600 -560 2 0 {name=R26 model=ntap1 spiceprefix=X R=8.463 @@ -850,103 +754,13 @@ Imax=0.3e-6 } C {lab_pin.sym} 1755 -620 0 1 {name=p67 sig_type=std_logic lab=bulk7} C {lab_pin.sym} 1755 -490 0 0 {name=p68 sig_type=std_logic lab=vdd} -C {sg13g2_pr/ntap1.sym} 1755 -560 0 0 {name=R27 +C {sg13g2_pr/ntap1.sym} 1755 -560 2 0 {name=R27 model=ntap1 spiceprefix=X R=8.463 Imax=0.3e-6 } -C {sg13g2_pr/sg13_lv_pmos.sym} 1710 -815 0 0 {name=M22 -l=5u -w=10u -ng=4 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 1690 -855 0 0 {name=p69 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 1845 -815 0 0 {name=M23 -l=5u -w=10u -ng=4 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 1825 -855 0 0 {name=p70 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 1715 -730 0 0 {name=M24 -l=5u -w=10u -ng=4 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 1695 -770 0 0 {name=p71 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 1850 -730 0 0 {name=M25 -l=5u -w=10u -ng=4 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 1830 -770 0 0 {name=p72 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 1970 -815 0 0 {name=M26 -l=5u -w=10u -ng=4 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 1950 -855 0 0 {name=p73 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 1970 -735 0 0 {name=M27 -l=4u -w=5u -ng=2 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 1950 -775 0 0 {name=p74 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 2095 -735 0 0 {name=M28 -l=4u -w=5u -ng=2 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 2075 -775 0 0 {name=p75 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 2095 -815 0 0 {name=M29 -l=4u -w=5u -ng=2 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 2075 -855 0 0 {name=p76 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 2220 -735 0 0 {name=M30 -l=4u -w=5u -ng=2 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 2200 -775 0 0 {name=p77 sig_type=std_logic lab=vdd} -C {sg13g2_pr/sg13_lv_pmos.sym} 2220 -815 0 0 {name=M31 -l=4u -w=5u -ng=2 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 2200 -855 0 0 {name=p78 sig_type=std_logic lab=vdd} -C {sg13g2_pr/ptap1.sym} 1355 -395 0 0 {name=R7 +C {sg13g2_pr/ptap1.sym} 1355 -395 2 0 {name=R7 model=ptap1 spiceprefix=X w=10e-6 @@ -954,7 +768,7 @@ l=1.0e-6 } C {lab_pin.sym} 1355 -330 0 0 {name=p42 sig_type=std_logic lab=vss} C {lab_pin.sym} 1355 -450 0 1 {name=p44 sig_type=std_logic lab=sub!} -C {sg13g2_pr/ptap1.sym} 1430 -395 0 0 {name=R10 +C {sg13g2_pr/ptap1.sym} 1430 -395 2 0 {name=R10 model=ptap1 spiceprefix=X w=10e-6 @@ -962,7 +776,7 @@ l=1.0e-6 } C {lab_pin.sym} 1430 -330 0 0 {name=p58 sig_type=std_logic lab=vss} C {lab_pin.sym} 1430 -450 0 1 {name=p59 sig_type=std_logic lab=sub!} -C {sg13g2_pr/ptap1.sym} 1510 -395 0 0 {name=R11 +C {sg13g2_pr/ptap1.sym} 1510 -395 2 0 {name=R11 model=ptap1 spiceprefix=X w=10e-6 @@ -970,7 +784,7 @@ l=1.0e-6 } C {lab_pin.sym} 1510 -330 0 0 {name=p60 sig_type=std_logic lab=vss} C {lab_pin.sym} 1510 -450 0 1 {name=p61 sig_type=std_logic lab=sub!} -C {sg13g2_pr/ptap1.sym} 1590 -395 0 0 {name=R28 +C {sg13g2_pr/ptap1.sym} 1590 -395 2 0 {name=R28 model=ptap1 spiceprefix=X w=10e-6 @@ -978,7 +792,7 @@ l=1.0e-6 } C {lab_pin.sym} 1590 -330 0 0 {name=p79 sig_type=std_logic lab=vss} C {lab_pin.sym} 1590 -450 0 1 {name=p80 sig_type=std_logic lab=sub!} -C {sg13g2_pr/ptap1.sym} 1670 -390 0 0 {name=R29 +C {sg13g2_pr/ptap1.sym} 1670 -390 2 0 {name=R29 model=ptap1 spiceprefix=X w=10e-6 @@ -986,3 +800,19 @@ l=1.0e-6 } C {lab_pin.sym} 1670 -325 0 0 {name=p81 sig_type=std_logic lab=vss} C {lab_pin.sym} 1670 -445 0 1 {name=p82 sig_type=std_logic lab=sub!} +C {sg13g2_pr/cap_cmim.sym} 1887.5 -555 0 0 {name=C3 +model=cap_cmim +w=5e-6 +l=5e-6 +m=15 +spiceprefix=X} +C {lab_pin.sym} 1887.5 -607.5 0 1 {name=p83 sig_type=std_logic lab=vdd} +C {lab_pin.sym} 1887.5 -507.5 0 1 {name=p84 sig_type=std_logic lab=vss} +C {sg13g2_pr/rppd.sym} 675 -700 0 0 {name=R31 +w=0.5e-6 +l=38.65e-6 +model=rppd +spiceprefix=X +b=0 +m=1 +} diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/simulations/two_stage_OTA_layout.cdl b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/simulations/two_stage_OTA_layout.cdl index 629d2aef..48a8d155 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/simulations/two_stage_OTA_layout.cdl +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/simulations/two_stage_OTA_layout.cdl @@ -17,7 +17,7 @@ M5 vout iout vdd bulk4 sg13_lv_pmos l=2.08u w=75u ng=8 m=1 M6 vout dn4 vss bulk3 sg13_lv_nmos l=9.75u w=28.8u ng=4 m=1 M9 iout iout vdd bulk4 sg13_lv_pmos l=2.08u w=75u ng=8 m=1 C2 dn4 vout cap_cmim w=22.295e-6 l=22.295e-6 m=1 -R4 vss bulk2 ptap1 A = 68.4036e-12, P = 0.24264e-3 -R5 vdd bulk3 ntap1 A = 95.5222e-12, P = 0.21292e-3 +R4 vss bulk2 ptap1 A = 9.64655e-12, P = 0.18e-3 +R5 vdd bulk3 ntap1 A = 27.435e-12, P = 0.177e-3 .ends .end diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/two_stage_OTA_layout.sch b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/two_stage_OTA_layout.sch index b835777c..d46f94ac 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/two_stage_OTA_layout.sch +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/schematic_mod/two_stage_OTA_layout.sch @@ -1,5 +1,4 @@ -v {xschem version=3.4.5 file_version=1.2 -} +v {xschem version=3.4.6 file_version=1.2} G {} K {} V {} @@ -197,7 +196,7 @@ N 660 -305 660 -255 { lab=vss} N 450 -305 450 -260 { lab=vss} -C {sg13g2_pr/sg13_lv_nmos.sym} 640 -365 2 1 {name=M4 +C {sg13g2_pr/sg13_lv_nmos.sym} 640 -365 0 0 {name=M4 l=9.75u w=720n ng=1 @@ -205,7 +204,7 @@ m=1 model=sg13_lv_nmos spiceprefix=X } -C {sg13g2_pr/sg13_lv_nmos.sym} 470 -365 2 0 {name=M3 +C {sg13g2_pr/sg13_lv_nmos.sym} 470 -365 0 1 {name=M3 l=9.75u w=720n ng=1 @@ -245,7 +244,7 @@ m=1 model=sg13_lv_pmos spiceprefix=X } -C {sg13g2_pr/sg13_lv_nmos.sym} 840 -400 2 1 {name=M6 +C {sg13g2_pr/sg13_lv_nmos.sym} 840 -400 0 0 {name=M6 l=9.75u w=28.8u ng=4 diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout.gds b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout.gds index 06b0de6a..464cc63d 100644 Binary files a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout.gds and b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout.gds differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout_extracted.cir b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout_extracted.cir index 7b3c80a0..bf5b4138 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout_extracted.cir +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/full_OTA/two_stage_OTA_layout_extracted.cir @@ -1,4 +1,4 @@ -* Extracted by KLayout with SG13G2 LVS runset on : 03/03/2025 11:24 +* Extracted by KLayout with SG13G2 LVS runset on : 28/03/2025 08:06 .SUBCKT two_stage_OTA_layout vss vdd dn3 iout vout dn2 v+ v\x2d vss$1 dn4 M$1 vss dn3 dn3 vss sg13_lv_nmos L=9.75u W=0.72u AS=0.2448p AD=0.2448p PS=2.12u diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid.gds b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid.gds index 3177ec86..3b7a7d08 100644 Binary files a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid.gds and b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid.gds differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid_extracted.cir b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid_extracted.cir index 037513ec..da1f12c5 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid_extracted.cir +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/input_common_centroid_extracted.cir @@ -1,14 +1,15 @@ -* Extracted by KLayout with SG13G2 LVS runset on : 18/03/2025 08:57 +* Extracted by KLayout with SG13G2 LVS runset on : 27/03/2025 15:48 -.SUBCKT input_common_centroid v+ v\x2d vdd dn4 dn3 -M$1 vdd vdd \$6 \$27 sg13_lv_pmos L=3.7u W=14.56u AS=4.9504p AD=4.9504p +.SUBCKT input_common_centroid vdd dn3 v+ dn4 v\x2d +M$1 vdd vdd \$9 \$2 sg13_lv_pmos L=3.7u W=14.56u AS=4.9504p AD=4.9504p + PS=31.84u PD=31.84u -M$2 \$6 v+ dn4 \$27 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p PS=15.92u +M$3 \$9 v+ dn4 \$2 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p PS=15.92u + PD=15.92u -M$3 dn4 vdd vdd \$27 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p -+ PS=15.92u PD=15.92u -M$5 \$6 v\x2d dn3 \$27 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p -+ PS=15.92u PD=15.92u -M$6 dn3 vdd vdd \$27 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p +M$4 \$9 v\x2d dn3 \$2 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p + PS=15.92u PD=15.92u +M$5 dn4 vdd vdd \$2 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p PS=15.92u ++ PD=15.92u +M$6 dn3 vdd vdd \$2 sg13_lv_pmos L=3.7u W=7.28u AS=2.4752p AD=2.4752p PS=15.92u ++ PD=15.92u +R$13 \$2 vdd ntap1 A=28.7556p P=185.52u .ENDS input_common_centroid diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/schematic_mod/input_common_centroid.sch b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/schematic_mod/input_common_centroid.sch index e74cafda..06a0352c 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/schematic_mod/input_common_centroid.sch +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/schematic_mod/input_common_centroid.sch @@ -1,5 +1,4 @@ -v {xschem version=3.4.5 file_version=1.2 -} +v {xschem version=3.4.6 file_version=1.2} G {} K {} V {} @@ -121,7 +120,7 @@ spiceprefix=X C {lab_pin.sym} 55 -445 0 0 {name=p50 sig_type=std_logic lab=vdd} C {lab_pin.sym} 95 -365 0 0 {name=p53 sig_type=std_logic lab=dn2} C {lab_pin.sym} 290 -365 0 0 {name=p54 sig_type=std_logic lab=dn2} -C {sg13g2_pr/ntap1.sym} 650 -240 0 0 {name=R1 +C {sg13g2_pr/ntap1.sym} 650 -240 2 0 {name=R1 model=ntap1 spiceprefix=X w=13e-6 diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/schematic_mod/input_common_centroid_alternative.sch b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/schematic_mod/input_common_centroid_alternative.sch deleted file mode 100644 index 6d6713ee..00000000 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_pair/schematic_mod/input_common_centroid_alternative.sch +++ /dev/null @@ -1,283 +0,0 @@ -v {xschem version=3.4.5 file_version=1.2 -} -G {} -K {} -V {} -S {} -E {} -N 450 -495 450 -395 { -lab=dn3} -N 450 -525 660 -525 { -lab=bulk} -N 450 -575 450 -555 { -lab=dn2} -N 660 -575 660 -555 { -lab=dn2} -N 560 -575 660 -575 { -lab=dn2} -N 380 -525 410 -525 { -lab=v-} -N 700 -525 730 -525 { -lab=v+} -N 560 -595 560 -575 { -lab=dn2} -N 450 -575 560 -575 { -lab=dn2} -N 660 -495 660 -400 { -lab=dn4} -N 660 -335 715 -335 { -lab=bulk} -N 620 -365 620 -335 { -lab=#net1} -N 660 -305 660 -285 { -lab=dn4} -N 425 -345 480 -345 { -lab=bulk} -N 385 -375 385 -345 { -lab=#net2} -N 425 -315 425 -295 { -lab=dn3} -N 620 -365 660 -365 { -lab=#net1} -N 385 -375 425 -375 { -lab=#net2} -N 420 -645 475 -645 { -lab=bulk} -N 380 -675 380 -645 { -lab=#net3} -N 420 -615 420 -595 { -lab=dn2} -N 655 -655 710 -655 { -lab=bulk} -N 615 -685 615 -655 { -lab=#net4} -N 655 -625 655 -605 { -lab=dn2} -N 380 -675 420 -675 { -lab=#net3} -N 615 -685 655 -685 { -lab=#net4} -N 120 -375 120 -340 { -lab=bulk} -N 120 -285 120 -245 { -lab=vdd} -N 1170 -365 1225 -365 { -lab=bulk} -N 1130 -395 1130 -365 { -lab=#net5} -N 1170 -335 1170 -315 { -lab=dn4} -N 885 -355 940 -355 { -lab=bulk} -N 845 -385 845 -355 { -lab=#net6} -N 885 -325 885 -305 { -lab=dn3} -N 1130 -395 1170 -395 { -lab=#net5} -N 845 -385 885 -385 { -lab=#net6} -N 1090 -655 1145 -655 { -lab=bulk} -N 1050 -685 1050 -655 { -lab=#net7} -N 1090 -625 1090 -605 { -lab=dn2} -N 925 -645 980 -645 { -lab=bulk} -N 885 -675 885 -645 { -lab=#net8} -N 925 -615 925 -595 { -lab=dn2} -N 1050 -685 1090 -685 { -lab=#net7} -N 885 -675 925 -675 { -lab=#net8} -N 890 -495 890 -395 { -lab=dn3} -N 890 -525 1100 -525 { -lab=bulk} -N 890 -575 890 -555 { -lab=dn2} -N 1100 -575 1100 -555 { -lab=dn2} -N 1000 -575 1100 -575 { -lab=dn2} -N 820 -525 850 -525 { -lab=v-} -N 1140 -525 1170 -525 { -lab=v+} -N 1000 -595 1000 -575 { -lab=dn2} -N 890 -575 1000 -575 { -lab=dn2} -N 1100 -495 1100 -400 { -lab=dn4} -C {sg13g2_pr/sg13_lv_pmos.sym} 430 -525 0 0 {name=M1 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_pmos.sym} 680 -525 0 1 {name=M2 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {iopin.sym} 380 -525 0 1 {name=p10 lab=v- -m=1} -C {iopin.sym} 730 -525 0 0 {name=p11 lab=v+ -m=1} -C {iopin.sym} 120 -245 2 1 {name=p1 lab=vdd} -C {lab_pin.sym} 450 -455 0 0 {name=p9 sig_type=std_logic lab=dn3 -m=1} -C {lab_pin.sym} 660 -455 0 0 {name=p12 sig_type=std_logic lab=dn4 -m=1} -C {sg13g2_pr/sg13_lv_pmos.sym} 640 -335 0 0 {name=M8 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_pmos.sym} 405 -345 0 0 {name=M10 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 120 -375 0 0 {name=p30 sig_type=std_logic lab=bulk} -C {lab_pin.sym} 560 -525 3 0 {name=p2 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 480 -345 0 1 {name=p32 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 715 -335 0 1 {name=p33 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 560 -585 0 1 {name=p40 sig_type=std_logic lab=dn2 -m=1} -C {lab_pin.sym} 425 -295 0 0 {name=p43 sig_type=std_logic lab=dn3 -m=1} -C {lab_pin.sym} 660 -285 0 0 {name=p18 sig_type=std_logic lab=dn4 -m=1} -C {sg13g2_pr/sg13_lv_pmos.sym} 400 -645 0 0 {name=M15 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_pmos.sym} 635 -655 0 0 {name=M16 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 710 -655 0 1 {name=p51 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 475 -645 0 1 {name=p52 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 655 -605 0 0 {name=p53 sig_type=std_logic lab=dn2 -m=1} -C {lab_pin.sym} 420 -595 0 0 {name=p54 sig_type=std_logic lab=dn2 -m=1} -C {sg13g2_pr/ntap1.sym} 120 -310 2 0 {name=R1 -model=ntap1 -spiceprefix=X -w=13e-6 -l=34e-6 -} -C {sg13g2_pr/sg13_lv_pmos.sym} 1150 -365 0 0 {name=M3 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_pmos.sym} 865 -355 0 0 {name=M4 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 940 -355 0 1 {name=p5 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 1225 -365 0 1 {name=p6 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 885 -305 0 0 {name=p7 sig_type=std_logic lab=dn3 -m=1} -C {lab_pin.sym} 1170 -315 0 0 {name=p8 sig_type=std_logic lab=dn4 -m=1} -C {sg13g2_pr/sg13_lv_pmos.sym} 1070 -655 0 0 {name=M5 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_pmos.sym} 905 -645 0 0 {name=M6 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {lab_pin.sym} 980 -645 0 1 {name=p16 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 1145 -655 0 1 {name=p19 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 925 -595 0 0 {name=p20 sig_type=std_logic lab=dn2 -m=1} -C {lab_pin.sym} 1090 -605 0 0 {name=p21 sig_type=std_logic lab=dn2 -m=1} -C {sg13g2_pr/sg13_lv_pmos.sym} 870 -525 0 0 {name=M7 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {sg13g2_pr/sg13_lv_pmos.sym} 1120 -525 0 1 {name=M9 -l=3.7u -w=3.64u -ng=1 -m=1 -model=sg13_lv_pmos -spiceprefix=X -} -C {iopin.sym} 820 -525 0 1 {name=p22 lab=v- -m=1} -C {iopin.sym} 1170 -525 0 0 {name=p23 lab=v+ -m=1} -C {lab_pin.sym} 890 -455 0 0 {name=p24 sig_type=std_logic lab=dn3 -m=1} -C {lab_pin.sym} 1100 -455 0 0 {name=p25 sig_type=std_logic lab=dn4 -m=1} -C {lab_pin.sym} 1000 -525 3 0 {name=p26 sig_type=std_logic lab=bulk -m=1} -C {lab_pin.sym} 1000 -585 0 1 {name=p27 sig_type=std_logic lab=dn2 -m=1} -C {iopin.sym} 385 -375 2 0 {name=p3 lab=vdd} -C {iopin.sym} 620 -360 2 0 {name=p4 lab=vdd} -C {iopin.sym} 845 -380 2 0 {name=p13 lab=vdd} -C {iopin.sym} 1130 -390 2 0 {name=p14 lab=vdd} -C {iopin.sym} 1050 -675 2 0 {name=p15 lab=vdd} -C {iopin.sym} 885 -665 2 0 {name=p17 lab=vdd} -C {iopin.sym} 615 -680 2 0 {name=p28 lab=vdd} -C {iopin.sym} 380 -675 2 0 {name=p29 lab=vdd} diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage.gds b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage.gds index 10ce5e65..4c109dfd 100644 Binary files a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage.gds and b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage.gds differ diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage_extracted.cir b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage_extracted.cir index 72fa0bcc..c7ecbd41 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage_extracted.cir +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/input_stage_extracted.cir @@ -1,10 +1,12 @@ -* Extracted by KLayout with SG13G2 LVS runset on : 13/02/2025 13:49 +* Extracted by KLayout with SG13G2 LVS runset on : 27/03/2025 16:03 -.SUBCKT input_stage dn3|dn4|vss vdd dn2 iout -M$1 dn3|dn4|vss dn3|dn4|vss dn3|dn4|vss \$1 sg13_lv_nmos L=9.75u W=1.44u -+ AS=0.4896p AD=0.4896p PS=4.24u PD=4.24u -M$3 dn2 iout vdd \$6 sg13_lv_pmos L=1.95u W=5.3u AS=1.802p AD=1.802p PS=11.28u +.SUBCKT input_stage vss dn4 dn3 vdd dn2 iout +M$1 vss dn3 dn4 \$1 sg13_lv_nmos L=9.75u W=0.72u AS=0.2448p AD=0.2448p PS=2.12u ++ PD=2.12u +M$2 vss dn3 dn3 \$1 sg13_lv_nmos L=9.75u W=0.72u AS=0.2448p AD=0.2448p PS=2.12u ++ PD=2.12u +M$3 dn2 iout vdd \$8 sg13_lv_pmos L=1.95u W=5.3u AS=1.802p AD=1.802p PS=11.28u + PD=11.28u -R$4 \$6 vdd ntap1 A=21.5031p P=43.5u -R$5 \$1 dn3|dn4|vss ptap1 A=15.54345p P=53.44u +R$4 \$8 vdd ntap1 A=7.5826p P=48.92u +R$5 \$1 vss ptap1 A=7.0975p P=56.78u .ENDS input_stage diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/input_stage.sch b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/input_stage.sch index 1c60f8af..30fe20c1 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/input_stage.sch +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/input_stage.sch @@ -1,5 +1,4 @@ -v {xschem version=3.4.5 file_version=1.2 -} +v {xschem version=3.4.6 file_version=1.2} G {} K {} V {} @@ -29,7 +28,7 @@ lab=vss} N 265 -295 265 -260 { lab=vss} N 265 -380 265 -355 { -lab=bulk2} +lab=sub!} N 865 -245 955 -245 { lab=sub!} N 655 -215 655 -185 { @@ -38,9 +37,9 @@ N 560 -245 655 -245 { lab=sub!} N 865 -215 865 -185 { lab=vss} -N 385 -290 385 -255 { +N 425 -290 425 -255 { lab=vdd} -N 385 -375 385 -350 { +N 425 -375 425 -350 { lab=bulk1} N 765 -580 765 -560 { lab=vdd} @@ -57,7 +56,7 @@ spiceprefix=X C {iopin.sym} 765 -185 1 1 {name=p16 lab=vss} C {iopin.sym} 765 -580 1 1 {name=p19 lab=vdd} C {lab_pin.sym} 955 -245 0 1 {name=p1 sig_type=std_logic lab=sub!} -C {sg13g2_pr/ptap1.sym} 265 -325 0 0 {name=R2 +C {sg13g2_pr/ptap1.sym} 265 -325 2 0 {name=R2 model=ptap1 spiceprefix=X w=10e-6 @@ -69,9 +68,9 @@ C {iopin.sym} 865 -375 2 1 {name=p2 lab=dn4} C {iopin.sym} 655 -375 2 1 {name=p5 lab=dn3} C {iopin.sym} 765 -455 2 1 {name=p6 lab=dn2} C {iopin.sym} 595 -530 2 0 {name=p7 lab=iout} -C {lab_pin.sym} 385 -255 0 0 {name=p8 sig_type=std_logic lab=vdd} -C {lab_pin.sym} 385 -375 0 1 {name=p10 sig_type=std_logic lab=bulk1} -C {sg13g2_pr/ntap1.sym} 385 -320 0 0 {name=R3 +C {lab_pin.sym} 425 -255 0 0 {name=p8 sig_type=std_logic lab=vdd} +C {lab_pin.sym} 425 -375 0 1 {name=p10 sig_type=std_logic lab=bulk1} +C {sg13g2_pr/ntap1.sym} 425 -320 2 0 {name=R3 model=ntap1 spiceprefix=X w=0.78e-6 diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/simulations/input_stage.cdl b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/simulations/input_stage.cdl deleted file mode 100644 index b1fc8e54..00000000 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/simulations/input_stage.cdl +++ /dev/null @@ -1,12 +0,0 @@ -** sch_path: /home/pedersen/projects/IHP-AnalogAcademy/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/input_stage/schematic_mod/input_stage.sch -.subckt input_stage vss vdd dn4 dn3 dn2 iout -*.PININFO vss:B vdd:B dn4:B dn3:B dn2:B iout:B -M7 dn2 iout vdd bulk1 sg13_lv_pmos l=1.95u w=5.3u ng=1 m=1 -R2 vss bulk2 ptap1 A = 7.0975e-12 P=56.78e-6 -R3 vdd bulk1 ntap1 A = 7.5826e-12 P = 48.92e-6 -M1 dn3 dn3 vss bulk2 sg13_lv_nmos l=9.75u w=720n ng=1 m=1 -M2 dn4 dn3 vss bulk2 sg13_lv_nmos l=9.75u w=720n ng=1 m=1 -.ends -.end - - diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/output_stage_extracted.cir b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/output_stage_extracted.cir index a576515e..930500f2 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/output_stage_extracted.cir +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/output_stage_extracted.cir @@ -1,13 +1,13 @@ -* Extracted by KLayout with SG13G2 LVS runset on : 14/01/2025 15:57 +* Extracted by KLayout with SG13G2 LVS runset on : 27/03/2025 16:05 -.SUBCKT output_stage vout dn4 vdd iout vout|vss vout$1 dn4$1 -M$1 vout|vss dn4$1 vout|vss \$1 sg13_lv_nmos L=9.75u W=28.8u AS=6.552p -+ AD=6.552p PS=37.82u PD=37.82u +.SUBCKT output_stage vout dn4 vdd iout vss vout$1 vout$2 dn4$1 +M$1 vss dn4$1 vout$2 \$1 sg13_lv_nmos L=9.75u W=28.8u AS=6.552p AD=6.552p ++ PS=37.82u PD=37.82u M$5 vdd iout iout \$4 sg13_lv_pmos L=2.08u W=75u AS=15.65625p AD=15.65625p + PS=87.715u PD=87.715u M$13 vdd iout vout$1 \$4 sg13_lv_pmos L=2.08u W=75u AS=15.65625p AD=15.65625p + PS=87.715u PD=87.715u C$21 dn4 vout cap_cmim w=22.295u l=22.295u A=497.067025p P=89.18u m=1 -R$22 \$4 vdd ntap1 A=95.5222p P=212.92u -R$23 \$1 vout|vss ptap1 A=68.4036p P=242.64u +R$22 \$4 vdd ntap1 A=27.435p P=177u +R$23 \$1 vss ptap1 A=25.19p P=201.52u .ENDS output_stage diff --git a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/schematic_mod/output_stage.sch b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/schematic_mod/output_stage.sch index 5e9b94db..05af6888 100644 --- a/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/schematic_mod/output_stage.sch +++ b/modules/module_1_bandgap_reference/part_3_layout/OTA_layout/output_stage/schematic_mod/output_stage.sch @@ -1,5 +1,4 @@ -v {xschem version=3.4.5 file_version=1.2 -} +v {xschem version=3.4.6 file_version=1.2} G {} K {} V {} @@ -40,7 +39,7 @@ lab=vout} N 860 -375 860 -305 { lab=vss} N 1265 -385 1265 -355 { -lab=bulk3} +lab=sub!} N 1265 -295 1265 -255 { lab=vss} N 860 -400 940 -400 { @@ -98,7 +97,7 @@ C {lab_pin.sym} 1265 -385 0 0 {name=p23 sig_type=std_logic lab=sub! } C {lab_pin.sym} 1265 -255 2 0 {name=p24 sig_type=std_logic lab=vss} C {lab_pin.sym} 940 -400 0 1 {name=p27 sig_type=std_logic lab=sub!} -C {sg13g2_pr/ptap1.sym} 1265 -325 0 0 {name=R2 +C {sg13g2_pr/ptap1.sym} 1265 -325 2 0 {name=R2 model=ptap1 spiceprefix=X R=262.847.0 @@ -108,7 +107,7 @@ C {lab_pin.sym} 505 -625 0 0 {name=p36 sig_type=std_logic lab=bulk4} C {lab_pin.sym} 930 -625 2 0 {name=p37 sig_type=std_logic lab=bulk4} C {lab_pin.sym} 1455 -385 0 0 {name=p38 sig_type=std_logic lab=bulk4} C {lab_pin.sym} 1455 -255 0 0 {name=p39 sig_type=std_logic lab=vdd} -C {sg13g2_pr/ntap1.sym} 1455 -325 0 0 {name=R5 +C {sg13g2_pr/ntap1.sym} 1455 -325 2 0 {name=R5 model=ntap1 spiceprefix=X R=262.847.0 diff --git a/modules/module_1_bandgap_reference/part_3_layout/pycell_macro/generator.py b/modules/module_1_bandgap_reference/part_3_layout/pycell_macro/generator.py deleted file mode 100644 index 79b72203..00000000 --- a/modules/module_1_bandgap_reference/part_3_layout/pycell_macro/generator.py +++ /dev/null @@ -1,379 +0,0 @@ -"""Module to automatically generate a gallery of devices out of spice netlist creating a new GDS file. Can be used in -Klayout's batch mode. For example: -klayout -n sg13g2 -zz -r generator.py -rd netlist=netlist.spice -rd output=macros/gallery.gds - -""" -import pathlib -import sys -import re -import argparse -from typing import List, Dict -import pya -import klayout.db - -LIB = 'SG13_dev' - -def parse_netlist(netlist: str) -> List[Dict[str, str]]: - # Define regular expression patterns for each component type - patterns = { - 'ipin': re.compile(r"^\*\.ipin\s+(\S+)\s*$", re.MULTILINE), - 'opin': re.compile(r"^\*\.opin\s+(\S+)\s*$", re.MULTILINE), - 'iopin': re.compile(r"^\*\.iopin\s+(\S+)\s*$", re.MULTILINE), - 'bjt': re.compile(r"^XQ(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+Nx=([\d\.]+)(?:\s+le=([\d\.e-]+))?\s*$", re.MULTILINE), - 'capacitor': re.compile(r"^XC(\S+)\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.e-]+)\s+l=([\d\.e-]+)\s+m=([\d\.]+)\s*$", re.MULTILINE), - 'diode': re.compile(r"^XD(\S+)\s+\S+\s+\S+\s+(\S+)\s+l=([\d\.a-zA-Z]+)\s+w=([\d\.a-zA-Z]+)\s*$", re.MULTILINE), - 'resistor': re.compile(r"^XR(\S+)\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.e-]+)\s+l=([\d\.e-]+)\s+m=([\d\.]+)\s+b=([\d\.]+)\s*$", re.MULTILINE), - 'mosfet': re.compile(r"^XM(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.u-]+)\s+l=([\d\.u-]+)\s+ng=([\d\.]+)\s+m=([\d\.]+)(?:\s+rfmode=([\d\.]+))?\s*$", re.MULTILINE), - 'tap': re.compile(r"^XR(\S+)\s+\S+\s+\S+\s+(\S+)\s*$", re.MULTILINE) - } - - components = [] - - # Parse pins - for pin_type in ['ipin', 'opin', 'iopin']: - for match in patterns[pin_type].finditer(netlist): - components.append({ - 'name': match.group(1), - 'model': pin_type - }) - - # Parse BJTs - for match in patterns['bjt'].finditer(netlist): - components.append({ - 'name': f"XQ{match.group(1)}", - 'model': match.group(2), - 'Nx': match.group(3), - 'le': match.group(4) if match.group(4) else None - }) - - # Parse capacitors - for match in patterns['capacitor'].finditer(netlist): - components.append({ - 'name': f"XC{match.group(1)}", - 'model': match.group(2), - 'w': match.group(3), - 'l': match.group(4), - 'm': match.group(5) - }) - - # Parse diodes - for match in patterns['diode'].finditer(netlist): - components.append({ - 'name': f"XD{match.group(1)}", - 'model': match.group(2), - 'l': match.group(3), - 'w': match.group(4) - }) - - # Parse resistors - for match in patterns['resistor'].finditer(netlist): - components.append({ - 'name': f"XR{match.group(1)}", - 'model': match.group(2), - 'w': match.group(3), - 'l': match.group(4), - 'm': match.group(5), - 'b': match.group(6) - }) - - # Parse MOSFETs - for match in patterns['mosfet'].finditer(netlist): - components.append({ - 'name': f"XM{match.group(1)}", - 'model': match.group(2), - 'w': match.group(3), - 'l': match.group(4), - 'ng': match.group(5), - 'm': match.group(6), - 'rfmode': match.group(7) if match.group(7) else None - }) - - #Parse ntap - for match in patterns['tap'].finditer(netlist): - components.append({ - 'name': f"XR{match.group(1)}", - 'model': match.group(2) # In this case, group 4 is the ntap model - }) - - return components - - -def generate_devices(netlist: str, output: str): - - components = parse_netlist(netlist) - lib = pya.Library.library_by_name(LIB) - - layout = klayout.db.Layout(True) - layout.dbu = 0.001 - top_cell = layout.cell(layout.add_cell('gallery')) - - res = find_instances_by_model(components, 'rppd') - - xstep = 0 - cell_w = 0 - y_offset = 100 - hmax = 0 - for item in res: - width = float(item['w'])*1e6 - length = float(item['l'])*1e6 - bends = int(item['b']) - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('rppd') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'b' : f'{bends}'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep,y_offset )))) - cell_w = bbox.width() - cell_h = bbox.height() - if cell_h > hmax: - hmax = cell_h - # print(f'w: {cell_w}, h: {cell_h}, xstep: {xstep}') - - - res = find_instances_by_model(components, 'rhigh') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - for item in res: - width = float(item['w'])*1e6 - length = float(item['l'])*1e6 - bends = int(item['b']) - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('rhigh') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'b' : f'{bends}'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - res = find_instances_by_model(components, 'rsil') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - for item in res: - width = float(item['w'])*1e6 - length = float(item['l'])*1e6 - bends = int(item['b']) - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('rsil') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'b' : f'{bends}'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep,y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - - mos = find_instances_by_model(components, 'sg13_lv_nmos') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - for item in mos: - width = float(item['w'][:-1]) - length = float(item['l'][:-1]) - ng = int(item['ng']) - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('nmos') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - - - mos = find_instances_by_model(components, 'sg13_hv_nmos') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - - for item in mos: - width = float(item['w'][:-1]) - length = float(item['l'][:-1]) - ng = int(item['ng']) - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('nmosHV') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - mos = find_instances_by_model(components, 'sg13_lv_pmos') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - for item in mos: - name = item['name'] - width = float(item['w'][:-1]) - length = float(item['l'][:-1]) - ng = int(item['ng']) - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('pmos') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - mos = find_instances_by_model(components, 'sg13_hv_pmos') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - for item in mos: - name = item['name'] - width = float(item['w'][:-1]) - length = float(item['l'][:-1]) - ng = int(item['ng']) - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('pmosHV') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - cmim = find_instances_by_model(components, 'cap_cmim') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - - for item in cmim: - width = float(item['w'])*1e6 - length = float(item['l'])*1e6 - m = int(item['m']) - for i in range(1,m+1): - pcell_decl = lib.layout().pcell_declaration('cmim') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - dant = find_instances_by_model(components, 'dantenna') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - for item in dant: - name = item['name'] - model = item['model'] - width = float(item['w'][:-1]) - length = float(item['l'][:-1]) - pcell_decl = lib.layout().pcell_declaration('dantenna') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - dant = find_instances_by_model(components, 'dpantenna') - xstep = 0 - cell_w = 0 - y_offset = y_offset + hmax - hmax = 0 - for item in dant: - name = item['name'] - model = item['model'] - width = float(item['w'][:-1]) - length = float(item['l'][:-1]) - pcell_decl = lib.layout().pcell_declaration('dpantenna') - params = pcell_decl.params_as_hash(pcell_decl.get_parameters()) - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u'}) - cell=layout.cell(pcell) - bbox=cell.bbox() - xstep = xstep + 100 + cell_w - top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset)))) - cell_w=bbox.width() - cell_h=bbox.height() - if cell_h > hmax: - hmax = cell_h - - - pathlib.Path(output).parent.mkdir(parents=True, exist_ok=True) - layout.write(output) - - -def find_instances_by_model(components, model_name): - return [item for item in components if item['model'] == model_name] - -try: - netlist -except NameError: - print("Missing netlist argument. Please define '-rd netlsit='") - sys.exit(1) -# sys.exit(1) -try: - output -except NameError: - print("Missing output argument. Please define '-rd output='") - sys.exit(1) - - -def main(): - - with open(netlist, 'r') as file: - netlist_content = file.read() - - generate_devices(netlist_content, output) # pylint: disable=undefined-variable - print(f'GDS created, open it using: ->klayout {output}') - -if __name__ == "__main__": - main() - - diff --git a/modules/module_1_bandgap_reference/part_3_layout/pycell_macro/generator_modified.py b/modules/module_1_bandgap_reference/part_3_layout/pycell_macro/generator_modified.py deleted file mode 100644 index 3eb564c4..00000000 --- a/modules/module_1_bandgap_reference/part_3_layout/pycell_macro/generator_modified.py +++ /dev/null @@ -1,282 +0,0 @@ -""" -Module to automatically generate a gallery of devices out of a SPICE netlist by creating a new GDS file. -Can be used in Klayout's batch mode. For example: -klayout -n sg13g2 -zz -r generator.py -rd netlist=netlist.spice -rd output=macros/gallery.gds -""" - -import pathlib -import sys -import re -from typing import List, Dict -import pya -import klayout.db - -LIB = 'SG13_dev' - -def parse_netlist(netlist: str) -> List[Dict[str, str]]: - print("[DEBUG] Starting to parse netlist...") - - # Define regular expression patterns for each component type - patterns = { - 'ipin': re.compile(r"^\*\.ipin\s+(\S+)\s*$", re.MULTILINE), - 'opin': re.compile(r"^\*\.opin\s+(\S+)\s*$", re.MULTILINE), - 'iopin': re.compile(r"^\*\.iopin\s+(\S+)\s*$", re.MULTILINE), - 'bjt': re.compile( - r"^XQ(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+Nx=([\d\.]+)(?:\s+le=([\d\.e-]+))?\s*$", - re.MULTILINE - ), - 'capacitor': re.compile( - r"^XC(\S+)\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.e-]+)\s+l=([\d\.e-]+)\s+m=([\d\.]+)\s*$", - re.MULTILINE - ), - 'diode': re.compile( - r"^XD(\S+)\s+\S+\s+\S+\s+(\S+)\s+l=([\d\.a-zA-Z]+)\s+w=([\d\.a-zA-Z]+)\s*$", - re.MULTILINE - ), - 'resistor': re.compile( - r"^XR(\S+)\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.e-]+)\s+l=([\d\.e-]+)\s+m=([\d\.]+)\s+b=([\d\.]+)\s*$", - re.MULTILINE - ), - 'mosfet': re.compile( - r"^XM(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.u-]+)\s+l=([\d\.u-]+)\s+ng=([\d\.]+)\s+m=([\d\.]+)(?:\s+rfmode=([\d\.]+))?\s*$", - re.MULTILINE - ), - 'tap': re.compile( - r"^XR(\S+)\s+\S+\s+\S+\s+(\S+)\s*$", - re.MULTILINE - ) - } - - components = [] - - # Parse each type of component - for component_type, pattern in patterns.items(): - matches = list(pattern.finditer(netlist)) - print(f"[DEBUG] Found {len(matches)} '{component_type}' components.") - for match in matches: - component = {'type': component_type} - if component_type in ['ipin', 'opin', 'iopin']: - component['name'] = match.group(1) - component['model'] = component_type - elif component_type == 'bjt': - component['name'] = f"XQ{match.group(1)}" - component['model'] = match.group(2) - component['Nx'] = match.group(3) - component['le'] = match.group(4) if match.group(4) else None - elif component_type == 'capacitor': - component['name'] = f"XC{match.group(1)}" - component['model'] = match.group(2) - component['w'] = match.group(3) - component['l'] = match.group(4) - component['m'] = match.group(5) - elif component_type == 'diode': - component['name'] = f"XD{match.group(1)}" - component['model'] = match.group(2) - component['l'] = match.group(3) - component['w'] = match.group(4) - elif component_type == 'resistor': - component['name'] = f"XR{match.group(1)}" - component['model'] = match.group(2) - component['w'] = match.group(3) - component['l'] = match.group(4) - component['m'] = match.group(5) - component['b'] = match.group(6) - elif component_type == 'mosfet': - component['name'] = f"XM{match.group(1)}" - component['model'] = match.group(2) - component['w'] = match.group(3) - component['l'] = match.group(4) - component['ng'] = match.group(5) - component['m'] = match.group(6) - component['rfmode'] = match.group(7) if match.group(7) else None - elif component_type == 'tap': - component['name'] = f"XR{match.group(1)}" - component['model'] = match.group(2) # Assuming group 2 is the model - else: - print(f"[WARNING] Unknown component type: {component_type}") - - components.append(component) - print(f"[DEBUG] Parsed component: {component}") - - print(f"[DEBUG] Total components parsed: {len(components)}") - return components - -def find_instances_by_model(components: List[Dict[str, str]], model_name: str) -> List[Dict[str, str]]: - print(f"[DEBUG] Filtering components for model: '{model_name}'") - filtered = [comp for comp in components if comp.get('model') == model_name] - print(f"[DEBUG] Found {len(filtered)} instances of model: '{model_name}'") - return filtered - -def generate_devices(netlist: str, output: str): - print("[DEBUG] Starting device generation...") - components = parse_netlist(netlist) - print("[DEBUG] Completed netlist parsing.") - - lib = pya.Library.library_by_name(LIB) - if lib is None: - print(f"[ERROR] Library '{LIB}' not found. Please ensure it is loaded in KLayout.") - sys.exit(1) - print(f"[DEBUG] Using library: '{LIB}'") - - layout = klayout.db.Layout(True) - layout.dbu = 0.001 - print(f"[DEBUG] Layout created with dbu={layout.dbu}") - - top_cell = layout.cell(layout.add_cell('gallery')) - print("[DEBUG] Created top-level cell 'gallery'.") - - # Define the order and models to process - models_to_process = [ - 'rppd', 'rhigh', 'rsil', - 'sg13_lv_nmos', 'sg13_hv_nmos', - 'sg13_lv_pmos', 'sg13_hv_pmos', - 'cap_cmim', 'dantenna', 'dpantenna' - ] - - # Initialize positioning variables - xstep = 0 - y_offset = 100 # Starting y offset - hmax = 0 # Maximum height of the current row - - for model_name in models_to_process: - print(f"[DEBUG] Processing model: '{model_name}'") - res = find_instances_by_model(components, model_name) - if not res: - print(f"[DEBUG] No instances found for model: '{model_name}'. Skipping.") - continue - - for item in res: - print(f"[DEBUG] Processing item: {item}") - try: - # Handle different models with varying parameters - if model_name in ['rppd', 'rhigh', 'rsil']: - width = float(item['w']) * 1e6 - length = float(item['l']) * 1e6 - bends = int(item['b']) - m = int(item['m']) - pcell_type = model_name - params = { - 'w': f'{width}u', - 'l': f'{length}u', - 'b': f'{bends}' - } - elif model_name in ['sg13_lv_nmos', 'sg13_hv_nmos', 'sg13_lv_pmos', 'sg13_hv_pmos']: - width = float(item['w'].rstrip('u')) # Remove 'u' if present - length = float(item['l'].rstrip('u')) - ng = int(item['ng']) - m = int(item['m']) - pcell_type = 'nmos' if 'nmos' in model_name else 'pmos' - if 'HV' in model_name: - pcell_type += 'HV' - params = { - 'w': f'{width}u', - 'l': f'{length}u', - 'ng': f'{ng}' - } - elif model_name in ['cap_cmim']: - width = float(item['w']) * 1e6 - length = float(item['l']) * 1e6 - m = int(item['m']) - pcell_type = model_name - params = { - 'w': f'{width}u', - 'l': f'{length}u' - } - elif model_name in ['dantenna', 'dpantenna']: - width = float(item['w'].rstrip('u')) - length = float(item['l'].rstrip('u')) - pcell_type = model_name - params = { - 'w': f'{width}u', - 'l': f'{length}u' - } - else: - print(f"[WARNING] Unhandled model type: '{model_name}'. Skipping.") - continue - - for i in range(1, m + 1): - print(f"[DEBUG] Creating PCell variant for '{pcell_type}' with params: {params}") - try: - pcell_decl = lib.layout().pcell_declaration(pcell_type) - if pcell_decl is None: - print(f"[ERROR] PCell declaration for '{pcell_type}' not found in library '{LIB}'.") - continue - - pcell = layout.add_pcell_variant(lib, pcell_decl.id(), params) - cell = layout.cell(pcell) - bbox = cell.bbox() - print(f"[DEBUG] Bounding box for PCell '{pcell_type}': {bbox}") - - # Positioning logic - xstep += 100 + bbox.width() - print(f"[DEBUG] Updated xstep: {xstep}") - - # Insert the PCell into the top cell - top_cell.insert(klayout.db.CellInstArray( - pcell, - klayout.db.Trans(klayout.db.Point(xstep, y_offset)) - )) - print(f"[DEBUG] Inserted '{pcell_type}' PCell at position ({xstep}, {y_offset})") - - # Update the maximum height for the current row - cell_h = bbox.height() - if cell_h > hmax: - hmax = cell_h - print(f"[DEBUG] Updated hmax: {hmax}") - - except Exception as e: - print(f"[ERROR] Exception while creating PCell for '{pcell_type}': {e}") - - # After processing each model, update y_offset for the next row - y_offset += hmax - hmax = 0 # Reset for the next row - xstep = 0 # Reset xstep for the next row - print(f"[DEBUG] Updated y_offset: {y_offset}") - - # Ensure output directory exists - try: - pathlib.Path(output).parent.mkdir(parents=True, exist_ok=True) - print(f"[DEBUG] Output directory '{pathlib.Path(output).parent}' is ready.") - except Exception as e: - print(f"[ERROR] Failed to create output directory: {e}") - sys.exit(1) - - # Write the layout to the output GDS file - try: - layout.write(output) - print(f"[DEBUG] Layout successfully written to '{output}'.") - except Exception as e: - print(f"[ERROR] Failed to write layout to '{output}': {e}") - sys.exit(1) - -def main(): - # Check for required arguments - try: - netlist - except NameError: - print("Missing netlist argument. Please define '-rd netlist='") - sys.exit(1) - - try: - output - except NameError: - print("Missing output argument. Please define '-rd output='") - sys.exit(1) - - print("[DEBUG] Opening netlist file...") - # Read the netlist content - try: - with open(netlist, 'r') as file: - netlist_content = file.read() - print(f"[DEBUG] Successfully read netlist file: '{netlist}'") - except Exception as e: - print(f"[ERROR] Failed to read netlist file '{netlist}': {e}") - sys.exit(1) - - # Generate devices and create GDS - generate_devices(netlist_content, output) - print(f'[INFO] GDS file created successfully. Open it using: klayout {output}') - -if __name__ == "__main__": - main() -