diff --git a/ISSCC24/submitted_notebooks/GLayout_OpAmp/GLayout_OpAmp.ipynb b/ISSCC24/submitted_notebooks/GLayout_OpAmp/GLayout_OpAmp.ipynb
new file mode 100644
index 00000000..09ca9aec
--- /dev/null
+++ b/ISSCC24/submitted_notebooks/GLayout_OpAmp/GLayout_OpAmp.ipynb
@@ -0,0 +1,671 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "aK2t7aSWNojQ"
+ },
+ "source": [
+ "# GLayout: Creating PDK-Agnostic P-Cell Based Chip Layouts in Python\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "view-in-github"
+ },
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xjDewsT5Y4lP"
+ },
+ "source": [
+ "```\n",
+ "OpenFASOC Team, November 2023\n",
+ "SPDX-License-Identifier: Apache-2.0\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "L6Ck4z5ujN_k"
+ },
+ "source": [
+ "\n",
+ "|Name|Affiliation| IEEE Member | SSCS Member |\n",
+ "|:-----------------:|:----------:|:----------:|:----------:|\n",
+ "| Harsh Khandeparkar| University of Michigan + Indian Institute of Technology Kharagpur | No | No |\n",
+ "| Anhang Li | University of Michigan | Yes | No |\n",
+ "| Ali Hammoud | University of Michigan | Yes | No |\n",
+ "| Ayushman Tripathi | University of Michigan | No | No |\n",
+ "| Mehdi Saligane (Advisor) | University of Michigan | Yes | Yes |\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "-Xp4cEjkeHIx"
+ },
+ "source": [
+ "# Introduction\n",
+ "Welcome!\n",
+ "This notebook serves as an introduction to the GDSFactory-based layout automation tool **GLayout** and an example two-stage Operational Amplifier (Op-Amp) generator, as a part of [OpenFASoC](https://github.com/idea-fasoc/OpenFASOC).\n",
+ "\n",
+ "## GDSFactory\n",
+ "[GDSFactory](https://gdsfactory.github.io/gdsfactory/index.html) is a Python library for designing integrated circuit layouts in Python and save it directly in the [GDSII](https://en.wikipedia.org/wiki/GDSII) format, and run DRC (Design Rule Checking) and LVS (Layout v/s Schematic) verification, or simulation.\n",
+ "\n",
+ "## GLayout\n",
+ "[GLayout](https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/gdsfactory-gen/glayout/) is a layout automation python package which generates _DRC clean_ circuit layouts and SPICE netlists for any PDK (Process Design Kit). It is composed of two main parts: the _generic PDK framework_, and the _circuit generators_.\n",
+ "\n",
+ "The generic PDK framework allows for describing any PDK in a standardized format, defined by the `MappedPDK` class. The generators are Python functions that take as arguments a `MappedPDK` object, and a set of optional layout parameters to produce a DRC (Design Rule Checking) clean layout of a particular circuit design and the pre-PEX (Parasitic Extraction) SPICE netlist, for LVS (Layout v/s Extraction).\n",
+ "\n",
+ "The post-PEX netlist can be used for simulating a circuit. The simulation and performance evaluation of multiple design variations can be parallelized on a cloud platform for fast design space exploration. Fig. 1 describes the GLayout workflow.\n",
+ "\n",
+ "![workflow](https://i.imgur.com/BA7gY81.png)\n",
+ "\n",
+ "(Fig. 1: GLayout Workflow)\n",
+ "\n",
+ "### Generators\n",
+ "Generators in GLayout are Python functions that generate the layout and SPICE netlist for a circuit component. This allows for describing hierarchical and parameterized circuits, or PCells (Parameterized Cells), in Python.\n",
+ "\n",
+ "A generator can be a utility generator such as a Via, a primitive PCell such as a MOSFET, or a complex circuit as an Op-Amp.\n",
+ "\n",
+ "Generators are PDK-agnostic and hierarchical, and may call other generators. This allows complex components to be composed of simpler components hierarchically. Fig. 2 shows the hierarchical usage of generators in an example Op-Amp design, and Fig. 3 shows the creation of a high-level PCell from a primitive PCell.\n",
+ "\n",
+ "The SPICE netlist for a component is also generated hierarchically along with the layout.\n",
+ "\n",
+ "![hierarchy](https://i.imgur.com/YC4CXrp.png)\n",
+ "\n",
+ "(Fig. 2: Hierarchy of PCells in the Example Op-Amp Design)\n",
+ "\n",
+ "![high level pcell construction](https://i.imgur.com/KSgSHla.png)\n",
+ "\n",
+ "(Fig. 3: Creation of High-Level PCells from Primitive PCells)\n",
+ "\n",
+ "#### List of Generators\n",
+ "##### Utility Generators\n",
+ "- Via\n",
+ "- Guardring\n",
+ "- Routing (Straight, L, and C)\n",
+ "\n",
+ "##### PCell Generators\n",
+ "- Primitive Cells\n",
+ " - FET (NMOS, PMOS)\n",
+ " - MIM Capacitor\n",
+ "- Intermediate PCells\n",
+ " - Differential Pair\n",
+ " - Current Mirror\n",
+ " - Differential to Single Ended Converter\n",
+ "\n",
+ "##### Example Designs\n",
+ "- Two Stage Operational Amplifier"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Using GLayout\n",
+ "### 1. Clone the repository and install dependencies\n",
+ "#### Python Dependencies\n",
+ "* [`gdsfactory`](https://github.com/gdsfactory/gdsfactory): Provides the backend for GDS manipulation.\n",
+ "* [`sky130`](https://github.com/gdsfactory/skywater130): The Skywater 130nm PDK Python package for GDSFactory to use in this demo.\n",
+ "* [`gf180`](https://github.com/gdsfactory/gf180): The GF 180nm PDK Python package for GDSFactory to use in this demo.\n",
+ "* [`gdstk`](https://heitzmann.github.io/gdstk/): (installed as a part of gdsfactory) Used for converting GDS files into SVG images for viewing.\n",
+ "* [`svgutils`](https://svgutils.readthedocs.io/en/latest/): To scale the SVG image.\n",
+ "\n",
+ "#### System Dependencies\n",
+ "* [`klayout`](https://klayout.de/): For DRC (Design Rule Checking).\n"
+ ],
+ "metadata": {
+ "id": "j4dNshkgMM4I"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "# Clone OpenFASoC\n",
+ "!git clone https://github.com/idea-fasoc/OpenFASOC\n",
+ "# Install python dependencies\n",
+ "!pip install sky130\n",
+ "!pip install gf180 prettyprinttree svgutils\n",
+ "!pip install gdsfactory==7.7.0\n",
+ "\n",
+ "import pathlib\n",
+ "import os\n",
+ "# Install KLayout (via conda)\n",
+ "!curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba\n",
+ "conda_prefix_path = pathlib.Path('conda-env')\n",
+ "CONDA_PREFIX = str(conda_prefix_path.resolve())\n",
+ "%env CONDA_PREFIX={CONDA_PREFIX}\n",
+ "\n",
+ "!bin/micromamba create --yes --prefix $CONDA_PREFIX\n",
+ "# Install from the litex-hub channel\n",
+ "!bin/micromamba install --yes --prefix $CONDA_PREFIX \\\n",
+ " --channel litex-hub \\\n",
+ " --channel main \\\n",
+ " klayout\n",
+ "\n",
+ "# Add conda packages to the PATH\n",
+ "PATH = os.environ['PATH']\n",
+ "%env PATH={PATH}:{CONDA_PREFIX}/bin\n",
+ "\n",
+ "%cd OpenFASOC/openfasoc/generators/gdsfactory-gen"
+ ],
+ "metadata": {
+ "id": "JzDjayJIMSHe"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### 2. Basic Usage of the GLayout Framework\n",
+ "Each generator is a Python function that takes a `MappedPDK` object as a parameter and generates a DRC clean layout for the given PDK. The generator may also accept a set of optional layout parameters such as the width or length of a MOSFET. All parameters are normal Python function arguments.\n",
+ "\n",
+ "The generator returns a `GDSFactory.Component` object that can be written to a `.gds` file and viewed using a tool such as Klayout. In this example, the `gdstk` library is used to convert the `.gds` file to an SVG image for viewing.\n",
+ "\n",
+ "The pre-PEX SPICE netlist for the component can be viewed using `component.info['netlist'].generate_netlist()`.\n",
+ "\n",
+ "In the following example the FET generator `glayout.primitives.fet` is imported and run with both the [Skywater 130](https://skywater-pdk.readthedocs.io/en/main/) and [GF180](https://gf180mcu-pdk.readthedocs.io/en/latest/) PDKs."
+ ],
+ "metadata": {
+ "id": "ozTFXBekORtd"
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "#### Demonstration of Basic Layout / Netlist Generation in SKY130 & GF180"
+ ],
+ "metadata": {
+ "id": "MRjvYFZl6o8z"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "from glayout.primitives.fet import nmos\n",
+ "from glayout.pdk.sky130_mapped import sky130_mapped_pdk as sky130\n",
+ "from glayout.pdk.gf180_mapped import gf180_mapped_pdk as gf180\n",
+ "import gdstk\n",
+ "import svgutils.transform as sg\n",
+ "import IPython.display\n",
+ "from IPython.display import clear_output\n",
+ "import ipywidgets as widgets\n",
+ "\n",
+ "# Used to display the results in a grid (notebook only)\n",
+ "left = widgets.Output()\n",
+ "leftSPICE = widgets.Output()\n",
+ "right = widgets.Output()\n",
+ "rightSPICE = widgets.Output()\n",
+ "hide = widgets.Output()\n",
+ "\n",
+ "grid = widgets.GridspecLayout(1, 4)\n",
+ "grid[0, 0] = left\n",
+ "grid[0, 1] = leftSPICE\n",
+ "grid[0, 2] = right\n",
+ "grid[0, 3] = rightSPICE\n",
+ "display(grid)\n",
+ "\n",
+ "def display_gds(gds_file, scale = 3):\n",
+ " # Generate an SVG image\n",
+ " top_level_cell = gdstk.read_gds(gds_file).top_level()[0]\n",
+ " top_level_cell.write_svg('out.svg')\n",
+ "\n",
+ " # Scale the image for displaying\n",
+ " fig = sg.fromfile('out.svg')\n",
+ " fig.set_size((str(float(fig.width) * scale), str(float(fig.height) * scale)))\n",
+ " fig.save('out.svg')\n",
+ "\n",
+ " # Display the image\n",
+ " IPython.display.display(IPython.display.SVG('out.svg'))\n",
+ "\n",
+ "def display_component(component, scale = 3):\n",
+ " # Save to a GDS file\n",
+ " with hide:\n",
+ " component.write_gds(\"out.gds\")\n",
+ "\n",
+ " display_gds('out.gds', scale)\n",
+ "\n",
+ "with hide:\n",
+ " # Generate the sky130 component\n",
+ " component_sky130 = nmos(pdk = sky130, fingers=5)\n",
+ " # Generate the gf180 component\n",
+ " component_gf180 = nmos(pdk = gf180, fingers=5)\n",
+ "\n",
+ "# Display the components' GDS and SPICE netlists\n",
+ "with left:\n",
+ " print('Skywater 130nm N-MOSFET (fingers = 5)')\n",
+ " display_component(component_sky130, scale=2.5)\n",
+ "with leftSPICE:\n",
+ " print('Skywater 130nm SPICE Netlist')\n",
+ " print(component_sky130.info['netlist'].generate_netlist())\n",
+ "\n",
+ "with right:\n",
+ " print('GF 180nm N-MOSFET (fingers = 5)')\n",
+ " display_component(component_gf180, scale=2)\n",
+ "with rightSPICE:\n",
+ " print('GF 180nm SPICE Netlist')\n",
+ " print(component_gf180.info['netlist'].generate_netlist())"
+ ],
+ "metadata": {
+ "id": "H0xylxwHOeKy"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "#### Interactive Primitive Generation in SKY130\n",
+ "The following cell demonstrates the different PCell and Utility generators on the Sky130 PDK."
+ ],
+ "metadata": {
+ "id": "YEaPdbyc-rh2"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "from glayout.primitives import fet, mimcap, guardring\n",
+ "from glayout.components import diff_pair\n",
+ "import ipywidgets as widgets\n",
+ "\n",
+ "selection_button = widgets.RadioButtons(\n",
+ " options=['NMOS', 'PMOS', 'MIM Capacitor', 'Differential Pair', 'Guardring'],\n",
+ " orientation='horizontal',\n",
+ " description='Generator:',\n",
+ " layout=widgets.Layout(position='right')\n",
+ ")\n",
+ "generate_button = widgets.Button(description='Generate', disabled=False)\n",
+ "output = widgets.Output(layout = widgets.Layout(position='left', overflow='visible'))\n",
+ "hide = widgets.Output()\n",
+ "\n",
+ "grid = widgets.GridspecLayout(1, 2)\n",
+ "grid[0, 0] = widgets.VBox([selection_button, generate_button])\n",
+ "grid[0, 1] = output\n",
+ "\n",
+ "display(grid)\n",
+ "\n",
+ "with hide:\n",
+ " component = fet.nmos(pdk = sky130)\n",
+ "with output:\n",
+ " print('NMOS')\n",
+ " display_component(component)\n",
+ "\n",
+ "def generate_component(_):\n",
+ " selected_comp = selection_button.value\n",
+ "\n",
+ " with output:\n",
+ " clear_output()\n",
+ " print(f\"Generating {selected_comp}...\")\n",
+ " with hide:\n",
+ " match selected_comp:\n",
+ " case 'NMOS':\n",
+ " component = fet.nmos(pdk = sky130)\n",
+ " case 'PMOS':\n",
+ " component = fet.pmos(pdk = sky130)\n",
+ " case 'MIM Capacitor':\n",
+ " component = mimcap.mimcap(pdk = sky130)\n",
+ " case 'Differential Pair':\n",
+ " component = diff_pair.diff_pair(pdk = sky130)\n",
+ " case 'Guardring':\n",
+ " component = guardring.tapring(pdk = sky130)\n",
+ " with output:\n",
+ " clear_output()\n",
+ " print(selected_comp)\n",
+ " display_component(component, 3)\n",
+ "\n",
+ "generate_button.on_click(generate_component)"
+ ],
+ "metadata": {
+ "id": "DO_cMVFz-mHo"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### 3. Tweak the Parameters\n",
+ "These are some of the parameters the NMOS FET generator accepts:\n",
+ "* `width`: The gate width of the FET.\n",
+ "* `length`: The gate length of the FET.\n",
+ "* `fingers`: The number of fingers. Each finger shares the same source/drain.\n",
+ "* `multipliers`: Number of multipliers (a multiplier is a row of fingers).\n",
+ "\n",
+ "Run the below cell and use the sliders to adjust the parameters."
+ ],
+ "metadata": {
+ "id": "gI9JNwN_fdL1"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "# Default Values\n",
+ "width=3\n",
+ "length=0.2\n",
+ "fingers=4\n",
+ "multipliers=1\n",
+ "\n",
+ "# Create sliders\n",
+ "width_slider = widgets.FloatSlider(description = 'Width:', min = 1, max = 5, step = 0.5, value = width)\n",
+ "length_slider = widgets.FloatSlider(description = 'Length:', min = 0.2, max = 1, step = 0.1, value = length)\n",
+ "fingers_slider = widgets.IntSlider(description = 'Fingers:', min = 1, max = 10, value = fingers)\n",
+ "multipliers_slider = widgets.IntSlider(description = 'Multipliers:', min = 1, max = 5, value = multipliers)\n",
+ "generate_button = widgets.Button(description='Generate', disabled=False)\n",
+ "\n",
+ "inputs_box = widgets.VBox([width_slider, length_slider, fingers_slider, multipliers_slider, generate_button])\n",
+ "\n",
+ "output = widgets.Output(layout = widgets.Layout(position='left', overflow='visible'))\n",
+ "hide = widgets.Output()\n",
+ "\n",
+ "grid = widgets.GridspecLayout(1, 2)\n",
+ "grid[0, 0] = inputs_box\n",
+ "grid[0, 1] = output\n",
+ "\n",
+ "display(grid)\n",
+ "\n",
+ "def generate_component(_):\n",
+ " width = width_slider.value\n",
+ " length = length_slider.value\n",
+ " fingers = fingers_slider.value\n",
+ " multipliers = multipliers_slider.value\n",
+ "\n",
+ " with output:\n",
+ " clear_output()\n",
+ " print(f\"Generating with width={width}, length={length}, fingers={fingers}, multipliers={multipliers}...\")\n",
+ " with hide:\n",
+ " component = component = fet.nmos(pdk = sky130, width = width, length=length, fingers = fingers, multipliers = multipliers)\n",
+ " with output:\n",
+ " clear_output()\n",
+ " print(f\"N-MOSFET with width={width}, length={length}, fingers={fingers}, multipliers={multipliers}:\")\n",
+ " display_component(component)\n",
+ "\n",
+ "generate_component(None)\n",
+ "\n",
+ "# Regenerate upon change in value\n",
+ "generate_button.on_click(generate_component)"
+ ],
+ "metadata": {
+ "id": "-HIs4q1fC-vY"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### 4. DRC Checking Using External Tools (KLayout)\n",
+ "Design Rule Check (DRC) is the process of ensuring that a particular layout does not violate the constraints or _design rules_ imposed by the PDK.\n",
+ "\n",
+ "[Klayout](https://klayout.de/) is a GDSII viewer and editor that also has a DRC feature. The design rules for the PDK, in this case the Skywater 130 PDK, are described in a `.lydrc` file.\n",
+ "\n",
+ "The following cell runs DRC on the component generated in the previous cell. The number of DRC errors reported will be displayed at the end of the output."
+ ],
+ "metadata": {
+ "id": "g45S96g7BOhr"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "!klayout out.gds -zz -r glayout/pdk/sky130_mapped/sky130.lydrc\n",
+ "!echo -e \"\\n$(grep -c \"\" sky130_drc.txt) DRC Errors Found\""
+ ],
+ "metadata": {
+ "id": "FSi37vB3GyvR"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Complex Circuit Example: Op-Amp\n",
+ "Using the above generators, complex circuit designs can be created by connecting the components. The function for creating such a design would itself be a generator. For example, differential pair generator uses the FET, Via, and routing generators.\n",
+ "\n",
+ "### Design\n",
+ "One such example circuit is the [Operational Amplifier](https://en.wikipedia.org/wiki/Operational_amplifier) (Op-Amp) defined in the `opamp.py` file. This design consists of a differential pair (input stage), a differential to single-ended converter (load), a common source (CS) gain stage, and an output buffer (for testing, it's not a part of the feedback loop), with an improved split-stage feedback created using a capacitor. The differential pair and the gain and output stages are biased using current mirrors.\n",
+ "\n",
+ "Each of the stages, the feedback capacitor, and the biasing circuitry were generated using the exported generators. See the schematic in Fig. 4 for an overview of the circuit. The PCells used (Differential Pair, Current Mirror, etc.) are highlighted with the dotted border.\n",
+ "\n",
+ "In Fig. 5(a), a Skywater 130nm layout for the Op-Amp is shown with the different components annotated. The annotated components are marked in the circuit schematic in Fig. 5(b) for the first two stages of the Op-Amp.\n",
+ "\n",
+ "![schematic](https://i.imgur.com/PUEPdXE.png)\n",
+ "\n",
+ "(Fig. 4: Example Op-Amp Circuit Schematic)\n",
+ "\n",
+ "![schemlayout](https://i.imgur.com/W2askiz.png)\n",
+ "\n",
+ "(Fig. 5: (a) Sky130 Op-Amp Layout and (b) the Corresponding Circuit Schematic for the First Two Stages of the Op-Amp)\n",
+ "\n",
+ "### Parameters\n",
+ "The Op-Amp generator accepts the following optional parameters:\n",
+ "- `half_diffpair_params`: A tuple of (width, length, fingers) for the differential pair.\n",
+ "- `diffpair_bias`: A tuple of (width, length, fingers) for the differential pair bias transistors.\n",
+ "- `half_common_source_params`: A tuple of (width, length, fingers, multipliers) for the common source PMOS transistor.\n",
+ "- `half_common_source_bias`: A tuple of (width, length, fingers, multipliers) for the common source bias transistors. The `multipliers` only apply to the mirror transistor, reference transistor has a multiplier of 1.\n",
+ "- `output_stage_params`: A tuple of (width, length, fingers) for the output stage NMOS transistor.\n",
+ "- `output_stage_bias`: A tuple of (width, length, fingers) for the output stage bias transistors.\n",
+ "- `half_pload`: A tuple of (width, length, fingers) for the load (differential to single-ended converter). The `fingers` only apply to the bottom two transistors.\n",
+ "- `mim_cap_size`: A tuple of (width, length) for individual MIM capacitors.\n",
+ "- `mim_cap_rows`: The number of rows in the MIM capacitor array.\n",
+ "- `rmult`: The multiplier for the width of the routes.\n",
+ "\n",
+ "These parameters can be changed to generate a very wide range of Op-Amp designs."
+ ],
+ "metadata": {
+ "id": "Qw7Hsyft_NHm"
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### 1. Generating the Op-Amp\n",
+ "The cell below generates the Op-Amp with a particular set of parameters and a PDK (Sky130 by default). Change any of the parameters or the PDK set at the beginning of the cell to generate different variations of the Op-Amp."
+ ],
+ "metadata": {
+ "id": "e896CP65iqEY"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "from glayout.components.opamp import opamp\n",
+ "\n",
+ "# Select which PDK to use\n",
+ "pdk = sky130\n",
+ "# pdk = gf180\n",
+ "\n",
+ "# Op-Amp Parameters\n",
+ "half_diffpair_params = (6, 1, 4)\n",
+ "diffpair_bias = (6, 2, 4)\n",
+ "half_common_source_params = (7, 1, 10, 3)\n",
+ "half_common_source_bias = (6, 2, 8, 2)\n",
+ "output_stage_params = (5, 1, 16)\n",
+ "output_stage_bias = (6, 2, 4)\n",
+ "half_pload = (6,1,6)\n",
+ "mim_cap_size = (12, 12)\n",
+ "mim_cap_rows = 3\n",
+ "rmult = 2\n",
+ "\n",
+ "hide = widgets.Output()\n",
+ "\n",
+ "# Generate the Op-Amp\n",
+ "print('Generating Op-Amp...')\n",
+ "with hide:\n",
+ " component = opamp(pdk, half_diffpair_params, diffpair_bias, half_common_source_params, half_common_source_bias, output_stage_params, output_stage_bias, half_pload, mim_cap_size, mim_cap_rows, rmult)\n",
+ "\n",
+ "# Display the Op-Amp\n",
+ "clear_output()\n",
+ "display_component(component, 0.5)"
+ ],
+ "metadata": {
+ "id": "qcbCRjQh_c9g"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### 2. Sweeping Variations\n",
+ "The [`sky130_nist_tapeout.py`](https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130_nist_tapeout.py) file contains utilities for generating a matrix of Op-Amps with different parameters in the Sky130 PDK, running simulations, and generating statistics. This Python file is documented [here](https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/README.md).\n",
+ "\n",
+ "In the cell below, an array of different Op-Amp parameters will be generated and a matrix of all the different variations will be created and displayed. Probe pads and \"Nanofab\" micropads are added for layout (See Fig. 6).\n",
+ "\n",
+ "The `get_small_parameter_list()` function is used to generate a small list of parameters. In test mode (default), only 2 Op-Amp varations will be generated. Set the `TEST_MODE` variable to `False` to generate 1700+ variations. NOTE: This may take a very long time to run.\n",
+ "\n",
+ "The `create_opamp_matrix()` function generates Op-Amps from a given list of parameter values and appends them to a single GDS file for display. The function also adds \"pads\" to the Op-Amp that are used for probing on the physical layout. (See Fig. 6)\n",
+ "\n",
+ "![pads](https://i.imgur.com/5YIsYSY.png)\n",
+ "\n",
+ "(Fig 6. Pads Added to the Op-Amps in the Matrix)"
+ ],
+ "metadata": {
+ "id": "ayxczg2_lZdH"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "%cd tapeout_and_RL\n",
+ "from sky130_nist_tapeout import *\n",
+ "\n",
+ "# Test mode. Set to False to generate 1700+ variations.\n",
+ "TEST_MODE = True\n",
+ "TEST_NUM_VARIANTS = 2 # These many variants will be generated if TEST_MODE = True\n",
+ "\n",
+ "# Generate parameter list\n",
+ "parameter_list = get_small_parameter_list()\n",
+ "\n",
+ "# Generate the Op-Amp matrix\n",
+ "create_opamp_matrix(save_dir_name = '.', params = parameter_list, indices = [i for i in range(TEST_NUM_VARIANTS)] if TEST_MODE else None)\n",
+ "\n",
+ "# Display the Op-Amp matrix\n",
+ "display_gds('opamp_matrix.gds', 0.35)"
+ ],
+ "metadata": {
+ "id": "1RlGEAkmyRkL"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### 3. PEX and Spice Simulation\n",
+ "The `brute_force_full_layout_and_PEXsim()` function generates the full layouts for a given list of parameters, runs the post-PEX simulations, and returns the results. [This](https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/opamp_perf_eval.sp) SPICE testbench is used for the simulations.\n",
+ "\n",
+ "In test mode, only 8 variants of the Op-Amp are simulated. Set `TEST_MODE` to `False` to run simulations for all of the 1700+ variations. NOTE: This may take a very long time to run."
+ ],
+ "metadata": {
+ "id": "AMuWdisV86Qc"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "# Test mode. Set to False to simulate the whole 1700+ variations.\n",
+ "TEST_MODE = True\n",
+ "\n",
+ "# Define a set of parameters to test\n",
+ "params_array = parameter_list[:8] if TEST_MODE else parameter_list\n",
+ "\n",
+ "# Run the simulations and get the results\n",
+ "!rm -r save_gds_by_index # cleans up any previous simulations\n",
+ "results_array = brute_force_full_layout_and_PEXsim(sky130, params_array)"
+ ],
+ "metadata": {
+ "id": "wKPMRJ4i9qoY"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### 4. Generate Statistics and Plot\n",
+ "The below cell generates a Gain v/s Unity Gain Bandwidth (UGB) plot from the results generated above.\n",
+ "\n",
+ "Based on these results, the best variation of the Op-Amp can be chosen for a given user specification. Specifications such as the highest gain or the least power consumption can be targeted.\n",
+ "\n",
+ "These results can also be used along with various optimization algorithms to efficiently converge towards the best possible parameters for the given specifications, in a fully automated loop."
+ ],
+ "metadata": {
+ "id": "LxmqcEdFr7_M"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import matplotlib as mpl\n",
+ "from matplotlib import colormaps as cm\n",
+ "cmap = cm.get_cmap('jet')\n",
+ "\n",
+ "try:\n",
+ " params_list_of_dict = [{**opamp_parameters_de_serializer(opparam),**{\"index\":i}} for i,opparam in enumerate(params_array)]\n",
+ " results_list_of_dict = [{**opamp_results_de_serializer(opresult),**{\"index\":i}} for i,opresult in enumerate(results_array)]\n",
+ "except:\n",
+ " params_list_of_dict = [{**opamp_parameters_de_serializer_old(opparam),**{\"index\":i}} for i,opparam in enumerate(params_array)]\n",
+ " results_list_of_dict = [{**opamp_results_de_serializer_old(opresult),**{\"index\":i}} for i,opresult in enumerate(results_array)]\n",
+ "\n",
+ "# ilist is the list of output stage current bias (all of them are the same=93.5uA)\n",
+ "ugblist = np.array([opresult[\"ugb\"] for opresult in results_list_of_dict])\n",
+ "gainlist = np.array([opresult[\"dcGain\"] for opresult in results_list_of_dict])\n",
+ "powerlist = np.array([opresult[\"power\"] for opresult in results_list_of_dict])\n",
+ "freqlist = ugblist/10**(gainlist/20)\n",
+ "\n",
+ "fig, ax = plt.subplots(figsize = (10, 8))\n",
+ "colorlist = []\n",
+ "cnorm = mpl.colors.LogNorm(vmin=10e-5,vmax=1e-3)\n",
+ "sm = mpl.cm.ScalarMappable(cmap=cmap, norm=cnorm)\n",
+ "\n",
+ "for i in powerlist:\n",
+ " colorlist.append(cmap(cnorm(i)))\n",
+ "ax.scatter(freqlist,gainlist,s=1,alpha=1,c=colorlist)\n",
+ "plt.xlabel('Frequency ugb / Hz')\n",
+ "plt.ylabel('DC Gain / dB20')\n",
+ "ticks=[0.1e-6,1e-6,10e-6,100e-6]\n",
+ "aspect=60\n",
+ "cbar = fig.colorbar(sm, orientation='vertical', ax=ax, label='Power')\n",
+ "ax.set_xscale('log')"
+ ],
+ "metadata": {
+ "id": "Pgv94uUgsGI4"
+ },
+ "execution_count": null,
+ "outputs": []
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "provenance": []
+ },
+ "kernelspec": {
+ "display_name": "Python 3.9.13 64-bit",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python",
+ "version": "3.9.13"
+ },
+ "vscode": {
+ "interpreter": {
+ "hash": "397704579725e15f5c7cb49fe5f0341eb7531c82d19f2c29d197e8b64ab5776b"
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
\ No newline at end of file