diff --git a/openfasoc/generators/gdsfactory-gen/GLayout_Cells.ipynb b/openfasoc/generators/gdsfactory-gen/GLayout_Cells.ipynb new file mode 100644 index 000000000..f82ea0b2c --- /dev/null +++ b/openfasoc/generators/gdsfactory-gen/GLayout_Cells.ipynb @@ -0,0 +1,557 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "aK2t7aSWNojQ" + }, + "source": [ + "# GLayout: List of Built-in Cells" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xjDewsT5Y4lP" + }, + "source": [ + "```\n", + "OpenFASOC Team, Feb 2024\n", + "SPDX-License-Identifier: Apache-2.0\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", + "### List of Generators\n", + "\n", + "**Utility Generators**\n", + "- Via\n", + "- Guardring\n", + "- Routing (Straight, L, and C)\n", + "\n", + "**PCell Generators**\n", + "1. Primitive Cells / `glayout.primitives` \n", + " - FET (NMOS, PMOS)\n", + " - MIM Capacitor\n", + " - Guard Rings with Metalization\n", + " - Via\n", + " \n", + "\n", + "2. Intermediate PCells / `glayout.components`\n", + " - Differential Pair\n", + " - Differential to Single-Ended Converter\n", + "\n", + "\n", + "3. WIP PCells (The cells can be used inside other PCells, but are not available as standalone PCells yet)\n", + " - Current Mirror\n", + " \n", + "**High-Level Design Examples**\n", + "- OPAMP / `glayout.components.opamp`\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j4dNshkgMM4I" + }, + "source": [ + "## Preparation\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" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 1.1. Installing the binary dependency `klayout` using micromamba\n", + "**You only need to run this once**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JzDjayJIMSHe", + "scrolled": true + }, + "outputs": [], + "source": [ + "# Setup the environment for the OpenFASOC GDSFactory generator\n", + "# You only need to run this block once!\n", + "\n", + "# 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" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 1.2. Adding the `klayout` binary to the system path, then goto the GLayout directory\n", + "**You need to run this each time you restart the kernel**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Setup the environment for the OpenFASOC GDSFactory generator\n", + "\n", + "# Adding micro-mamba binary directory to the PATH\n", + "# This directory contains Klayout\n", + "import pathlib\n", + "import os\n", + "conda_prefix_path = pathlib.Path('conda-env')\n", + "CONDA_PREFIX = str(conda_prefix_path.resolve())\n", + "%env CONDA_PREFIX={CONDA_PREFIX}\n", + "# Add conda packages to the PATH\n", + "PATH = os.environ['PATH']\n", + "%env PATH={PATH}:{CONDA_PREFIX}/bin" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 1.3. Importing Libraries and Utility Functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "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", + "# Redirect all outputs here\n", + "hide = widgets.Output()\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", + " # 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", + " display_gds('out.gds', scale)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## List of Built-in Cells" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Primitives\n", + "#### MOSFET (nmos/pmos)\n", + "\n", + "**Parameters:**\n", + "- **pdk:** Which PDK to use\n", + "- **width:** Width of one finger (um)\n", + "- **length:** Length of one finger (um). The default value is the minimum channel length available\n", + "- **fingers:** Number of fingers per transistor\n", + "- **multipliers:** Number of transistors in this cell\n", + "- **with_tie:** bool\n", + "- **with_dummy:** tuple of 2 bools\n", + "- **with_dnwell:** bool\n", + "- **with_substrate_tap:** bool\n", + "- **sd_route_topmet:** Expose the S/D connection on which metal layer\n", + "- **gate_route_topmet:** Expose the Gate connection on which metal layer\n", + "- **sd_route_left:** Choose which direction the S/D connection goes to\n", + "- **rmult:** Integer multipler of routing width\n", + "- **sd_rmult:** Same as above\n", + "- **gate_rmult:** Same as above\n", + "- **interfinger_rmult:** Same as above\n", + "- **tie_layers:** Run the body tie metal on which layer. This entry is a tuple with 2 elements\n", + " - X metal\n", + " - Y metal\n", + "- **substrate_tap_layers:** Run the substrate tie metal on which layer. This entry is a tuple with 2 elements\n", + " - X metal\n", + " - Y metal\n", + "- **dummy_routes:** Enable routing to the dummy transistors\n", + "\n", + "Note that most of the parameters have a default value. The user usually doesn't need to populate all of them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.primitives.fet import nmos\n", + "# Used to display the results in a grid (notebook only)\n", + "left = widgets.Output()\n", + "leftSPICE = widgets.Output()\n", + "grid = widgets.GridspecLayout(1, 2)\n", + "grid[0, 0] = left\n", + "grid[0, 1] = leftSPICE\n", + "display(grid)\n", + "\n", + "comp = nmos(pdk = sky130, fingers=5)\n", + "# Display the components' GDS and SPICE netlists\n", + "with left:\n", + " print('Skywater 130nm N-MOSFET (fingers = 5)')\n", + " display_component(comp, scale=2.5)\n", + "with leftSPICE:\n", + " print('Skywater 130nm SPICE Netlist')\n", + " print(comp.info['netlist'].generate_netlist())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### MIM Capacitor\n", + "**Parameters:**\n", + "- **pdk:** Which PDK to use\n", + "- **Size:** A tuple of 2 values\n", + " - X Size: um\n", + " - Y Size: um" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.primitives.mimcap import mimcap\n", + "# Used to display the results in a grid (notebook only)\n", + "left = widgets.Output()\n", + "leftSPICE = widgets.Output()\n", + "grid = widgets.GridspecLayout(1, 2)\n", + "grid[0, 0] = left\n", + "grid[0, 1] = leftSPICE\n", + "display(grid)\n", + "\n", + "comp = mimcap(pdk=sky130, size=[20.0,5.0])\n", + "# Display the components' GDS and SPICE netlists\n", + "with left:\n", + " print('Skywater 130nm MIM Capacitor (10.0 x 5.0)')\n", + " display_component(comp, scale=2.5)\n", + "with leftSPICE:\n", + " print('Skywater 130nm SPICE Netlist')\n", + " print(comp.info['netlist'].generate_netlist())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Guard Ring\n", + "\n", + "**Parameters:**\n", + "- **pdk:** Which PDK to use\n", + "- **enclosed_rectangle:** A tuple of 2 values\n", + " - X Size (um)\n", + " - Y Size (um)\n", + "- **sdlayer:** Which diffusion layer?\n", + "- **horizontal_glayer:** Which metal layer to use for the X routing\n", + "- **vertical_glayer:** Which metal layer to use for the Y routing\n", + "- **sides:** A tuple of 4 bools" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.primitives.guardring import tapring\n", + "# Used to display the results in a grid (notebook only)\n", + "left = widgets.Output()\n", + "leftSPICE = widgets.Output()\n", + "grid = widgets.GridspecLayout(1, 2)\n", + "grid[0, 0] = left\n", + "grid[0, 1] = leftSPICE\n", + "display(grid)\n", + "\n", + "comp = tapring(pdk=sky130, enclosed_rectangle=[10.0, 5.0])\n", + "# Display the components' GDS and SPICE netlists\n", + "with left:\n", + " print('Skywater 130nm MIM Capacitor (10.0 x 5.0)')\n", + " display_component(comp, scale=2.5)\n", + "\n", + "# This cell does not have LVS netlist" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Intermediate PClls" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Diff Pair\n", + "create a diffpair with 2 transistors placed in two rows with common centroid place. Sources are shorted\n", + "\n", + "**Parameters:**\n", + "- **pdk:** Which PDK to use\n", + "- **width:** Width of the transistors (um)\n", + "- **length:** Length of the transistors, None or 0 means use min length (um)\n", + "- **fingers:** Number of fingers in the transistors (must be 2 or more)\n", + "- **short_source:** If true connects source of both transistors. Otherwise they will be exposed as routing terminals.\n", + "- **n_or_p_fet:** If true the diffpair is made of nfets else it is made of pfets\n", + "- **substrate_tap:** if true place a tapring around the diffpair (connects on met1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.components.diff_pair import diff_pair\n", + "# Used to display the results in a grid (notebook only)\n", + "left = widgets.Output()\n", + "leftSPICE = widgets.Output()\n", + "grid = widgets.GridspecLayout(1, 2)\n", + "grid[0, 0] = left\n", + "grid[0, 1] = leftSPICE\n", + "display(grid)\n", + "\n", + "comp = diff_pair(pdk=sky130)\n", + "# Display the components' GDS and SPICE netlists\n", + "with left:\n", + " print('Skywater 130nm Differential Pair')\n", + " display_component(comp, scale=2.5)\n", + "with leftSPICE:\n", + " print('Skywater 130nm SPICE Netlist')\n", + " print(comp.info['netlist'].generate_netlist())\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Cascode load of the OPAMP \n", + "(aka. Differential to Single Ended Converter)\n", + "\n", + "**Parameters:**\n", + "- **pdk:** Which PDK to use\n", + "- **rmult:** Routing Width Multiplier\n", + "- **half_pload:** a 3-element tuple describing the PMOS inside\n", + " - Transistor Width (um)\n", + " - Transistor Length (um)\n", + " - Transistor Multiplier\n", + "- **via_xlocation:** X position delta of the two staggered vias (um) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.components.differential_to_single_ended_converter import differential_to_single_ended_converter\n", + "# Used to display the results in a grid (notebook only)\n", + "left = widgets.Output()\n", + "leftSPICE = widgets.Output()\n", + "grid = widgets.GridspecLayout(1, 2)\n", + "grid[0, 0] = left\n", + "grid[0, 1] = leftSPICE\n", + "display(grid)\n", + "\n", + "comp = differential_to_single_ended_converter(pdk=sky130, rmult=1, half_pload=[2,0.5,1], via_xlocation=0)\n", + "# Display the components' GDS and SPICE netlists\n", + "with left:\n", + " print('Skywater 130nm Cascode Active Load')\n", + " display_component(comp, scale=2.5)\n", + "with leftSPICE:\n", + " print('Skywater 130nm SPICE Netlist')\n", + " print(comp.info['netlist'].generate_netlist())\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Qw7Hsyft_NHm" + }, + "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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e896CP65iqEY" + }, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "qcbCRjQh_c9g" + }, + "outputs": [], + "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": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + }, + "vscode": { + "interpreter": { + "hash": "397704579725e15f5c7cb49fe5f0341eb7531c82d19f2c29d197e8b64ab5776b" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}