From 8b8ce9ef7cca220209207a1f0b129cd3d52cfdd2 Mon Sep 17 00:00:00 2001 From: Dharma Anargya Jowandy <16523104@std.stei.itb.ac.id> Date: Tue, 28 Apr 2026 18:21:15 +0700 Subject: [PATCH 1/6] feat: add 5T OTA Tutorial --- tutorial/glayout_tutorial_5T_OTA_part1.ipynb | 1010 ++++++++++++++++++ 1 file changed, 1010 insertions(+) create mode 100644 tutorial/glayout_tutorial_5T_OTA_part1.ipynb diff --git a/tutorial/glayout_tutorial_5T_OTA_part1.ipynb b/tutorial/glayout_tutorial_5T_OTA_part1.ipynb new file mode 100644 index 00000000..a2b08e07 --- /dev/null +++ b/tutorial/glayout_tutorial_5T_OTA_part1.ipynb @@ -0,0 +1,1010 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dd691cf9", + "metadata": {}, + "source": [ + "# Tutorial: 5-Transistor OTA Layout Generation using gLayout\n", + "\n", + "**By gLayout Team**\n", + "\n", + "**Content creators:** Adrian Sami Pratama, Dharma Anargya Jowandy" + ] + }, + { + "attachments": { + "3ca2bb99-af6d-417c-9680-1247489846e0.png": { + "image/png": "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AJAglgAAABLEEgAAQIJYAgAASBBLAAAACWIJAAAgQSwBABSpSZMmxZvf/ObsGBgiYgkAACBBLAEAACSIJQAAgASxBAAAkCCWAAAAEsQSAABAglgCAABIEEsAAAAJYgkAACBBLAEAACSIJQAAgASxBAAAkCCWAAAAEsQSAABAglgCAABIEEsAAAAJYgkAACBBLAEAACSIJQAAgASxBAAAkCCWAAAAEsQSAABAglgCAABIEEsAAAAJYgkAACBBLAEAACSIJQAAgASxBAAAkCCWAAAAEsQSwBF68cUXsyOAIbFr167sCDiKxBLAEbruuuviqquuiq985SvCCThijz32WNx8883x1re+NbsEHGViCWAI5PP5+OAHPxgnnXSScAIOW99AKi8vjzVr1sSjjz6avQw4ysQSwBB5/vnn47777osTTzwxPvjBD8arX/1q4QQMKBVIV1xxRWzZsiXWrVuXvRwYAWIJYAhNnTo1Vq5cGc8//3x89atf7RdO9957r3CCUexAgbRp06b41Kc+FeXl5dmnASNELAEMk1Q4XXPNNcIJRplsIDU3NwskKBK5fD6fzw4BiOjs7IzS0tK48cYbs0v7eeCBB+LMM8+Mr33ta9mlpG984xvR3NwcX//61+OPf/xjTJgwId70pjfFu9/97pgzZ072cqDIdHZ2xpIlS2Lbtm2xc+fOePLJJ+P888+PK6+8MqZNm3ZIYfT000/HG9/4xvjIRz4Sr3nNa7LLvTZu3BgRERs2bMguAUNALAEMoLOzM2bNmpUd97N9+/a48MIL4+tf/3p2KekPf/hDrF27Nr72ta/FunXr4rjjjouXvexlceGFF8b/+l//KyIimpub47rrrosnnngi+3SgwKxevTpuuumm3v9ev//978e73vWuOOmkk+L555+P0tLSuOqqq2Lq1Klx4YUXZp+e1BNLF154YZx44onZ5f1MnDgxFi5cmB0DQ0AsARyhj370o/Hss8/GV7/61exSr55Aam5ujrVr18YrX/nKmDp1akybNi2mTJnSG2UrVqyI6I6lOXPmxJ49ezJ/ElBoVq9eHQsWLIiOjo6IPrvSHR0dkc/ne//b//73vx/nnHNOTJs27aDh1BNLO3fujLFjx2aXgaPEe5YAhskf/vCHaGpqiiuvvDJe/epXx4c+9KF41ateFV//+tfjueeeixUrVsSUKVOyTwOO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kCCWAAAAEsQSAABAglgCAABIEEsAAAAJYgkAACBBLAEAACSIJQAAgASxBAAAkCCWAAAAEsQSAABAglgCAABIEEsAAAAJYgkAACBBLAEAACSIJQAAgASxBAAAkCCWAAAAEsQSAABAglgaIj/72c+yIwAAoIiJpUF64IEHoqqqKt72trdFRMQ73vGOKC0tjfe///3x61//Ons5AABQZMTSICxYsCCuuOKKaG1tjeeee6533tnZGY2NjfG2t70tmpub93sOAABQXMTSYVqxYkXcdNNNsXfv3uxSr507d8a8efPsMAEAQBETS4fpS1/6UnaUtHPnzvjIRz6SHQMAAEVCLB2GjRs3xiOPPJIdD+gHP/hBdgQAABQJsXQYfvSjH2VHB9TZ2Rm5XM7jKD8efPDB7P8UAABw2HL5fD6fHZJ28803x6c+9ans+IC+/e1vx/jx47NjhklpaWls2LAhJk2alF0CGHErV66MRYsWRUdHR3YJgAIklg7D17/+9XjPe96THQ/o1FNPjV/+8pfZMcMol8uJJaBgiSWA4uIY3mH4u7/7u3jTm96UHQ+o5zOYAACA4iOWDtOh7iwdf/zxce2112bHAABAkRBLh+mWW26J973vfdlxP9ddd1387d/+bXYMAAAUCbE0CE1NTfGJT3wieSTvbW97W9x9993x6U9/OrsEAAAUEbE0SLfccks8/vjjvVH0oQ99KB588MF4+OGHY9asWdnLAQCAIiOWjtC73vWuiIiYMWNGTJw4MbsMAAAUKbEEAACQIJYAAAASxBIAAECCWAIAAEgQSwAAAAliCQAAIEEsAQAAJIglAACABLEEAACQIJYAAAASxBIAAECCWAIAAEgQSwAAAAliCQAAIEEsAQAAJIglAACABLE0BM4555zsCAD6OeGEE2LixInZMQAFSiwdobPOOisef/zxOO2007JLALCfF198MTZu3JgdA1CgxBIAAECCWAIAAEgQSwAAAAliCQAAIEEsAQAAJIglAACABLEEAACQIJYAAAASxBIAAECCWAIAAEgQSwAAAAliCQAAIEEsAQAAJIglAACABLEEAACQIJYAAAASxBIAAECCWAIAAEgQSwAAAAliCQAAIEEsAQAAJIglAACABLEEAACQIJYAAAASxBIAAECCWAIAAEgQSwAAAAliCQAAIEEsAQAAJIglAACABLEEAACQIJYA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WAIAAEgQSwAAAAliCQAAIEEsAQAAJIglAACABLEEAACQIJYKyNNPP50dAVAgfvrTn2ZHABzjxNII++53vxvXXnttvPGNb8wuAVBAtm7dGm9+85tj4cKFwglglBBLI6AnkM4888y47LLLYufOnXaVAIrAr371q2htbY03v/nNwglgFBBLR0kqkG688cb4xS9+Ebfffnv2cgAK1COPPBLt7e1x1VVXxTe/+c1485vfHP/pP/2nWLhwYfzkJz/JXg5AERNLw+hAgdTa2hpz5syJ173uddmnAVDgzj333Lj++uvj4Ycfjvb29njve98b3/zmN6OiokI4ARxDxNIQ++53vxsf/vCH+wXSL3/5S4EEcAwSTgDHrlw+n89nhxy6zs7OuOaaa+JnP/tZPPvss/H888/H5ZdfHldeeWVMmzYtTj311OxT+tm1a1ecffbZ8fd///fxile8Irvc68EHH4xJkybFihUrIiKiubk55syZE3v27MleCsAAtm7dGuedd17s2bMnTjnllIiIKC0tjZKSkigpKcle3qujoyO2bdsWu3fvzi4lbdu2LdauXRv33Xdf/OQnP4nTTjstzjjjjLjwwgvjS1/6UvZyAAqQnaUj9MILL8S3vvWt+M1vfhPPP/98nHzyyXHWWWfFG97whkMKpcMxadKkmDhxYu/f//Vf/3Xs2rVrv2sAOLAzzzwznn/++TjuuON6Z5MmTTpgKA3GG97whhgzZkzv3U5feOGF+MUvfhFr167NXgpAgbKzdIQ6OzujtLQ0Ojo64pRTTom1a9dGc3NzNDc3x6mnnhpTp06NqVOnRlVVVfapvXp2lp566qk488wzs8sAFIB777035s+fH0899VR2qdfvfve73teAtWvX9r4OTJs2LSorK2PlypWxaNGi6OjoyD4VgAJkZ2kIvepVr4oZM2bEmjVr4re//W0sW7YsfvWrX8V/+S//JV73utfFnDlzoqWlJfs0AIrYb3/721i1alVMmzYtXv3qV0dtbW2ceuqp0dLSEr/85S/jn//5n6OysjL7NACKgFgaJgOF0+TJk4UTQJHrG0gnnXSSQAI4Romlo6BvOD3//PP9wukzn/lM9ikAFKi+gfS6171OIAEcw8TSUTZQOOVyueylABSYF198cb9AuuuuuwQSwDFMLI2gbDgBULhOOukkgQQwyoilAvGqV73KnfAACphA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" + } + }, + "cell_type": "markdown", + "id": "2a3ec579", + "metadata": {}, + "source": [ + "___\n", + "## 1. Introduction\n", + "\n", + "### Tutorial Objectives\n", + "\n", + "This notebook is a tutorial on:\n", + "\n", + "- **Importing** and **Placement** of FETs and other macros/Pcells with relative coordinates, placing and connecting `via_stack` on the ports of the FETs, and encircling them with taprings.\n", + "- **Routing** between the placed vias using `straight_route`, `c_route`, and `L_route`, and understanding when to use each strategy based on port orientations.\n", + "- **Placing and connecting PINs** for future LVS runs.\n", + "\n", + "### What is a 5T OTA?\n", + "\n", + "A 5-Transistor Operational Transconductance Amplifier (5T OTA) is one of the most fundamental analog circuit building blocks. It converts a differential input voltage into an output current. The circuit consists of:\n", + "\n", + "- **M1, M2** — Differential input pair (NMOS)\n", + "- **M3, M4** — Current mirror load (PMOS)\n", + "- **M5** — Tail current source (NMOS)\n", + "\n", + "### Schematic Reference\n", + "\n", + "![image.png](attachment:3ca2bb99-af6d-417c-9680-1247489846e0.png)\n" + ] + }, + { + "cell_type": "markdown", + "id": "f4f72230", + "metadata": {}, + "source": [ + "## 2. Environment Setup\n", + "\n", + "### Setting Up the Live GDSII Viewer\n", + "\n", + "We first source the shell environment to ensure all PDK-related environment variables are properly loaded into the Python session." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "417e721f", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import subprocess\n", + "\n", + "# Source .bashrc and load environment variables into the current Python session\n", + "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", + "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", + "env_vars = {}\n", + "for line in result.stdout.splitlines():\n", + " if '=' in line:\n", + " key, value = line.split('=', 1)\n", + " env_vars[key] = value\n", + "\n", + "os.environ.update(env_vars)" + ] + }, + { + "cell_type": "markdown", + "id": "50eae849", + "metadata": {}, + "source": [ + "We define two helper functions for displaying GDS output inline in the notebook. display_gds reads a GDS file and renders it as an SVG, while display_component wraps it to accept a gdsfactory Component object directly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "617616e3", + "metadata": {}, + "outputs": [], + "source": [ + "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", + "hide = widgets.Output()\n", + "\n", + "def display_gds(gds_file, path, scale=3):\n", + " top_level_cell = gdstk.read_gds(gds_file).top_level()[0]\n", + " top_level_cell.write_svg(os.path.join(path, 'out.svg'))\n", + "\n", + " fig = sg.fromfile(os.path.join(path, 'out.svg'))\n", + " fig.set_size((str(float(fig.width) * scale), str(float(fig.height) * scale)))\n", + " fig.save(os.path.join(path, 'out.svg'))\n", + "\n", + " IPython.display.display(IPython.display.SVG(os.path.join(path, 'out.svg')))\n", + " os.remove(os.path.join(path, 'out.gds'))\n", + "\n", + "def display_component(component, path, scale=3):\n", + " with hide:\n", + " component.write_gds(os.path.join(path, 'out.gds'))\n", + " display_gds(os.path.join(path, 'out.gds'), path, scale)" + ] + }, + { + "cell_type": "markdown", + "id": "917d84d5", + "metadata": {}, + "source": [ + "### Importing gLayout Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10253f85", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout import MappedPDK, sky130, gf180\n", + "from gdsfactory import Component\n", + "from gdsfactory.components import text_freetype, rectangle\n", + "\n", + "from glayout import nmos, pmos\n", + "from glayout import via_stack\n", + "from glayout import rename_ports_by_orientation\n", + "from glayout import tapring\n", + "\n", + "from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port\n", + "from glayout.util.port_utils import add_ports_perimeter, print_ports\n", + "from glayout.util.snap_to_grid import component_snap_to_grid\n", + "from glayout.spice.netlist import Netlist\n", + "\n", + "from glayout.routing.straight_route import straight_route\n", + "from glayout.routing.c_route import c_route\n", + "from glayout.routing.L_route import L_route" + ] + }, + { + "cell_type": "markdown", + "id": "56187bb7", + "metadata": {}, + "source": [ + "## 3. Design Parameters & Configuration\n", + "\n", + "### Transistor Kwargs\n", + "\n", + "nmos_kwargs and pmos_kwargs are Python dictionaries that store a shared set of physical layout parameters for the transistors. Instead of repeating the same parameters in every nmos() or pmos() call, we define them once here and pass them using the kwargs syntax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9bebb109", + "metadata": {}, + "outputs": [], + "source": [ + "nmos_kwargs = {\n", + " \"with_tie\": False, # add tie layer for bulk connection\n", + " \"with_dnwell\": False, # deep n-well for substrate isolation\n", + " \"sd_route_topmet\": \"met2\", # top metal layer for source/drain routing\n", + " \"gate_route_topmet\": \"met2\", # top metal layer for gate routing\n", + " \"sd_route_left\": True, # route source/drain to the left\n", + " \"rmult\": None, # metal width multiplier for source/drain (None = default)\n", + " \"gate_rmult\": 1, # metal width multiplier for gate\n", + " \"interfinger_rmult\": 1, # metal width multiplier between fingers\n", + " \"substrate_tap_layers\": (\"met2\", \"met1\"), # layers for substrate tap\n", + " \"dummy_routes\": True # add routing to dummy fets\n", + "}\n", + "\n", + "pmos_kwargs = {\n", + " \"with_tie\": False,\n", + " \"dnwell\": False,\n", + " \"sd_route_topmet\": \"met2\",\n", + " \"gate_route_topmet\": \"met2\",\n", + " \"sd_route_left\": True,\n", + " \"rmult\": None,\n", + " \"gate_rmult\": 1,\n", + " \"interfinger_rmult\": 1,\n", + " \"substrate_tap_layers\": (\"met2\", \"met1\"),\n", + " \"dummy_routes\": True\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "c79f1ae6", + "metadata": {}, + "source": [ + "### Top-Level Configuration Dictionary\n", + "\n", + "All sizing and layout rule parameters are centralized in a single configuration dictionary fivet_ota_config. This makes it easy to adjust the design without modifying function internals. The dictionary is organized into nested sub-dictionaries, one per sub-circuit." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f6aceb8", + "metadata": {}, + "outputs": [], + "source": [ + "fivet_ota_config = {\n", + " \"pdk\": gf180,\n", + "\n", + " # Differential Input Pair (M1, M2) — NMOS\n", + " \"input_pair\": {\n", + " \"width\": 5.75,\n", + " \"length\": 0.4,\n", + " \"fingers\": 2,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"nmos\",\n", + " },\n", + "\n", + " # Current Mirror Load (M3, M4) — PMOS\n", + " \"current_mirror\": {\n", + " \"width\": 8.45,\n", + " \"length\": 0.4,\n", + " \"fingers\": 5,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"pmos\",\n", + " },\n", + "\n", + " # Tail Current Source (M5) — NMOS\n", + " \"tail_source\": {\n", + " \"width\": 9.55,\n", + " \"length\": 0.7,\n", + " \"fingers\": 5,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"nmos\",\n", + " },\n", + "\n", + " # Layout and routing rules\n", + " \"layout_rules\": {\n", + " \"spacing\": 2.0,\n", + " \"routing_metal\": \"met2\",\n", + " \"dummy_devices\": True,\n", + " \"tie_layers\": (\"met2\", \"met1\"), # tapring metal layers\n", + " \"sd_rmult\": 1, # source/drain metal width multiplier\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "ffc31ec0", + "metadata": {}, + "source": [ + "### Creating the Top-Level Component" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd8913d2", + "metadata": {}, + "outputs": [], + "source": [ + "top_level = Component(name=\"fivet_ota\")\n", + "pdk = fivet_ota_config[\"pdk\"]" + ] + }, + { + "cell_type": "markdown", + "id": "119616e8", + "metadata": {}, + "source": [ + "#### 4. Sub-circuit Layout: Current Mirror Load (M3 & M4)\n", + "\n", + "The current mirror load consists of two PMOS transistors: a reference device (M4) with a diode connection, and a mirror device (M3) whose drain provides the output current.\n", + "\n", + "### nwell Padding\n", + "\n", + "- Each PMOS transistor has its own nwell region. A gap between them violates PDK spacing rules.\n", + "- `cm_comp.add_padding(layers=(pdk.get_glayer(\"nwell\"),), default=1)` merges both regions into one continuous nwell.\n", + "\n", + "### Tapring Layer Selection\n", + "\n", + "- PMOS bulk is the nwell, so the tapring uses `sdlayer=\"n+s/d\"` to tie it to VDD.\n", + "- NMOS sub-cells use `sdlayer=\"p+s/d\"` instead, as their bulk is the p-substrate.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4358daa1", + "metadata": {}, + "outputs": [], + "source": [ + "def current_mirror(pdk, config):\n", + " cm_comp = Component(name=\"current_mirror\")\n", + " \n", + " # Extract parameters from config\n", + " width = config[\"current_mirror\"][\"width\"]\n", + " length = config[\"current_mirror\"][\"length\"]\n", + " fingers = config[\"current_mirror\"][\"fingers\"]\n", + " multipliers = config[\"current_mirror\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # Instantiate two PMOS transistors\n", + " pfet_m4 = pmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False, True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **pmos_kwargs)\n", + " \n", + " pfet_m3 = pmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True, False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **pmos_kwargs)\n", + " \n", + " cm4_ref = cm_comp << pfet_m4\n", + " cm3_ref = cm_comp << pfet_m3\n", + " cm4_ref.name = \"pfet_m4\"\n", + " cm3_ref.name = \"pfet_m3\"\n", + " \n", + " # Placement: move pfet_m4 to the right of pfet_m3\n", + " cm4_ref.movex(evaluate_bbox(pfet_m3)[0] + pdk.util_max_metal_seperation())\n", + " \n", + " # Tapring enclosure\n", + " tap_ring = tapring(pdk,\n", + " enclosed_rectangle=evaluate_bbox(\n", + " cm_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]),\n", + " sdlayer=\"n+s/d\",\n", + " horizontal_glayer=tie_layers[0],\n", + " vertical_glayer=tie_layers[1])\n", + " shift_amount = -prec_center(cm_comp.flatten())[0]\n", + " tring_ref = cm_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + " \n", + " # nwell padding to merge the two separate nwell regions\n", + " cm_comp.add_padding(layers=(pdk.get_glayer(\"nwell\"),), default=1)\n", + " \n", + " # Internal routing\n", + " cm_comp << straight_route(pdk, cm4_ref.ports[\"multiplier_0_source_E\"],\n", + " cm3_ref.ports[\"multiplier_0_source_E\"])\n", + " cm_comp << straight_route(pdk, cm4_ref.ports[\"multiplier_0_gate_E\"],\n", + " cm3_ref.ports[\"multiplier_0_gate_E\"])\n", + " cm_comp << c_route(pdk, cm4_ref.ports[\"multiplier_0_gate_E\"],\n", + " cm4_ref.ports[\"multiplier_0_drain_E\"])\n", + " \n", + " # Expose ports\n", + " cm_comp.add_ports(cm4_ref.get_ports_list(), prefix=\"M4_\")\n", + " cm_comp.add_ports(cm3_ref.get_ports_list(), prefix=\"M3_\")\n", + " cm_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " \n", + " return cm_comp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4705c5fd", + "metadata": {}, + "outputs": [], + "source": [ + "cm = current_mirror(pdk, fivet_ota_config)\n", + "cm_ref = top_level << cm\n", + "cm_ref.name = \"current_mirror\"\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "7bbc61a8", + "metadata": {}, + "source": [ + "## 5. Sub-circuit Layout: Differential Input Pair (M1 & M2)\n", + "\n", + "The differential pair consists of two matched NMOS transistors. M1 is the negative input and M2 is the positive input. Their sources are shorted together and connected to the tail current source.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f31a01e6", + "metadata": {}, + "outputs": [], + "source": [ + "def diff_pair(pdk, config):\n", + " dp_comp = Component(name=\"diff_pair\")\n", + "\n", + " # Extract parameters from config\n", + " width = config[\"input_pair\"][\"width\"]\n", + " length = config[\"input_pair\"][\"length\"]\n", + " fingers = config[\"input_pair\"][\"fingers\"]\n", + " multipliers = config[\"input_pair\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + "\n", + " # Instantiate two NMOS transistors\n", + " m1 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True, False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " m2 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False, True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + "\n", + " m1_ref = dp_comp << m1\n", + " m2_ref = dp_comp << m2\n", + " m1_ref.name = \"M1\" # negative input\n", + " m2_ref.name = \"M2\" # positive input\n", + "\n", + " # Placement\n", + " ref_dimensions = evaluate_bbox(m1)\n", + " m2_ref.movex(m1_ref.xmax)\n", + " m2_ref.movex(ref_dimensions[0] / 2)\n", + " m2_ref.movex(pdk.util_max_metal_seperation())\n", + "\n", + " # Connect sources together\n", + " dp_comp << straight_route(pdk,\n", + " m1_ref.ports[\"multiplier_0_source_E\"],\n", + " m2_ref.ports[\"multiplier_0_source_W\"])\n", + "\n", + " # Tapring enclosure\n", + " tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(\n", + " dp_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]))\n", + " shift_amount = -prec_center(dp_comp.flatten())[0]\n", + " tring_ref = dp_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + "\n", + " # Expose ports\n", + " dp_comp.add_ports(m1_ref.get_ports_list(), prefix=\"M1_\")\n", + " dp_comp.add_ports(m2_ref.get_ports_list(), prefix=\"M2_\")\n", + " dp_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + "\n", + " return dp_comp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38499af7", + "metadata": {}, + "outputs": [], + "source": [ + "dp = diff_pair(pdk, fivet_ota_config)\n", + "dp_ref = top_level << dp\n", + "dp_ref.name = \"diff_pair\"\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "c04e3097", + "metadata": {}, + "source": [ + "## 6. Sub-circuit Layout: Tail Current Source (M5)\n", + "\n", + "The tail current source consists of two NMOS transistors: a reference device with a diode connection (M6), and a mirror device that sinks the tail current for the differential pair (M5). The layout follows the same structure as the current mirror load — both devices share their source and gate connections, with a c_route shorting the gate to drain on the reference side." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f458c7eb", + "metadata": {}, + "outputs": [], + "source": [ + "def tail_current(pdk, config):\n", + " tail_comp = Component(name=\"tail_current\")\n", + " \n", + " # Extract parameters from config\n", + " width = config[\"tail_source\"][\"width\"]\n", + " length = config[\"tail_source\"][\"length\"]\n", + " fingers = config[\"tail_source\"][\"fingers\"]\n", + " multipliers = config[\"tail_source\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # Instantiate two NMOS transistors\n", + " nfet_m6 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False, True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " \n", + " nfet_m5 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True, False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " \n", + " m6 = tail_comp << nfet_m6\n", + " m5 = tail_comp << nfet_m5\n", + " m6.name = \"nfet_m6\"\n", + " m5.name = \"nfet_m5\"\n", + " \n", + " # Placement\n", + " m6.movex(evaluate_bbox(nfet_m5)[0] + pdk.util_max_metal_seperation())\n", + " \n", + " # Tapring enclosure\n", + " tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(\n", + " tail_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]))\n", + " shift_amount = -prec_center(tail_comp.flatten())[0]\n", + " tring_ref = tail_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + " \n", + " # Internal routing\n", + " tail_comp << straight_route(pdk, m6.ports[\"multiplier_0_source_E\"],\n", + " m5.ports[\"multiplier_0_source_E\"])\n", + " tail_comp << straight_route(pdk, m6.ports[\"multiplier_0_gate_E\"],\n", + " m5.ports[\"multiplier_0_gate_E\"])\n", + " tail_comp << c_route(pdk, m6.ports[\"multiplier_0_gate_E\"],\n", + " m6.ports[\"multiplier_0_drain_E\"])\n", + " \n", + " # Expose ports\n", + " tail_comp.add_ports(m6.get_ports_list(), prefix=\"M6_\")\n", + " tail_comp.add_ports(m5.get_ports_list(), prefix=\"M5_\")\n", + " tail_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " return tail_comp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9dfc2260", + "metadata": {}, + "outputs": [], + "source": [ + "tail = tail_current(pdk, fivet_ota_config)\n", + "tail_ref = top_level << tail\n", + "tail_ref.name = \"tail_current\"\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "0a1767a1", + "metadata": {}, + "source": [ + "## 7. Placement\n", + "\n", + "### Vertical Stacking and Center Alignment\n", + "\n", + "The three sub-circuits are stacked vertically: current mirror on top, differential pair in the middle, and tail current source at the bottom. Each block is moved downward relative to the one above it using `ymin` of the upper block as the reference. After vertical placement, all three blocks are horizontally aligned to the center of the current mirror using their absolute center coordinates." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "246e0b09", + "metadata": {}, + "outputs": [], + "source": [ + "dp_dimensions = evaluate_bbox(dp)\n", + "tail_dimensions = evaluate_bbox(tail)\n", + "\n", + "# Place diff_pair below current_mirror\n", + "dp_ref.movey(cm_ref.ymin - dp_dimensions[1] / 2 - pdk.util_max_metal_seperation())\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")\n", + "\n", + "# Place tail_current below diff_pair\n", + "tail_ref.movey(dp_ref.ymin - tail_dimensions[1] / 2 - pdk.util_max_metal_seperation())\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")\n", + "\n", + "# Align all blocks to the horizontal center of the current mirror\n", + "dp_ref.movex(cm_ref.center[0] - dp_ref.center[0])\n", + "tail_ref.movex(cm_ref.center[0] - tail_ref.center[0])\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "32cb0c63", + "metadata": {}, + "source": [ + "## 8. Routing\n", + "\n", + "### Via Stack Placement\n", + "\n", + "Before routing, all required via stacks are placed first. The center coordinate of a port can be accessed using cell_name.ports[\"port_name\"].center, which is used alongside other placement techniques to position each via accurately." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d3158cd", + "metadata": {}, + "outputs": [], + "source": [ + "viam2m3 = via_stack(pdk, \"met2\", \"met3\", centered=True)\n", + "viam1m2 = via_stack(pdk, \"met1\", \"met2\", centered=True)\n", + "\n", + "# Via for left drain of current mirror (MIR side)\n", + "drain_m3_via = top_level << viam2m3\n", + "drain_m3_via.move(cm_ref.ports[\"M3_multiplier_0_drain_W\"].center).movex(-5)\n", + "\n", + "# Via for right drain of current mirror (REF side)\n", + "drain_m4_via = top_level << viam2m3\n", + "drain_m4_via.move(cm_ref.ports[\"M4_multiplier_0_drain_E\"].center).movex(5)\n", + "\n", + "# Via for left drain of diff pair (M1)\n", + "drain_m1_via = top_level << viam2m3\n", + "drain_m1_via.move(dp_ref.ports[\"M1_multiplier_0_drain_W\"].center).movex(-5)\n", + "drain_m1_via.movex(drain_mir_via.x - drain_m1_via.x)\n", + "\n", + "# Via for right drain of diff pair (M2)\n", + "drain_m2_via = top_level << viam2m3\n", + "drain_m2_via.move(dp_ref.ports[\"M2_multiplier_0_drain_E\"].center).movex(5)\n", + "drain_m2_via.movex(drain_ref_via.x - drain_m2_via.x)\n", + "\n", + "# Via for common source of diff pair\n", + "source_m1_via = top_level << viam2m3\n", + "source_m1_via.move(dp_ref.ports[\"M1_multiplier_0_source_W\"].center)\n", + "\n", + "# Via for drain of tail current sink\n", + "drain_m5_via = top_level << viam2m3\n", + "drain_m5_via.move(tail_ref.ports[\"M5_multiplier_0_drain_W\"].center)\n", + "source_m1_via.movex(drain_sink_via.x - source_m1_via.x)\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")" + ] + }, + { + "attachments": { + "296208d4-4bce-4dae-b0d2-93e2d5ba431c.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "id": "3412f3d3-c63f-4cc8-84d7-f7b38866f600", + "metadata": {}, + "source": [ + "![image.png](attachment:296208d4-4bce-4dae-b0d2-93e2d5ba431c.png)\n", + "\n", + "Now that the vias are placed, time to make metal routing.\n", + "There are three basic types of routing available:\n", + "\n", + "- `straight_route` - the ports must be straight to each other, i.e, opposite direction and same x or y coordinate.\n", + "- `L_route` - has a 90° bend, ports must be perpendicular to each other.\n", + "- `c_route` - has two 90° bends(c shaped), ports must have same direction.\n", + "\n", + "Each routing function has multiple parameters. See [here](https://github.com/idea-fasoc/OpenFASOC/tree/main/openfasoc/generators/glayout/glayout/flow/routing)\n", + "\n" + ] + }, + { + "attachments": { + "8b1ced05-5107-4390-ae04-49a3f1634573.png": { + "image/png": "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dcAjEKoR50TCzvhQsDEMD00JFqk6CplYiRGDaDiH4LVtTJ2cF1ICRAo9MSkLMZFhwIA47nvkRF6jmpnZe4+KSZIg3ohiHcKJFtQmKfCTOBuKHC0sx7pBMEBCNkXijX/7xr333BXKD2X+xWuuHfDBpYnCyohHXV+KrTqLs0JtigmMPgSJmENIttGFcw4ZxsQ6YLZj2mZWCMX8/Oc//9a3vnXHHXd87Wtfu/322/+1Cl/96tcjfe0bX/3qv379q1//l7u+/rWvxnAnwh13PPzQo8ywFfzLX/389tvvvOvOO796xx3f/cH377oL7J3//p3vffWO23eaNq3WqBvihx9+uH9ggAxrKL/zzW987evfuOPO2NWz/TBCmzAl0L/8y798/etf/8lP/1somMQIh1IlGBWjYOCdg+IGg/1k8qTriIPv0IshARIPYml/sIDjDC86sKhVJcHneCFdtPipBx544N5774X2H3rooXvuuee+++5Dzv0bhQfuv//h+x98/NH5y1f0tYrS+2LZsqeeeGLRgicXPPDgA48//viDDzzy6P0PPfnEwofuf+DNf/8W50xZ5k889mizf6A12HTWPPboI488/Njjjzx696/vvv/+BysaPUzMwQQwGczhkUcewZRQjFk99thj27ENai/NtB9bf4xj4Qc/+MHChQvXrl27fPnyvr6+Z5555umnn14xFJatWLl8xfKVK1ashIosmQAAEABJREFUamf09/evXr162rQZf/5nr3v9G17X29uN5Nq+gVVr+p5atGT16rWotmTJktVr+soQ/vRP//SvXveXf/umN//561/35294/V/8+evzgcGVK1eiyfJnCWuGw6pVq8BiVoiXPP3MT37680EYHkpKsq1gcAorm0A24BWSrGfrqyJPMcfHTJS6oC5QJ34hJAAxriPjo/AhYVOJGsLHGWAlCt8J15TdF6++5rrrrvviF7/4uc997tprr73++uu//OUvf+lLX7p6OCA5Qld/+ZovX3vj4/Ofmrzj1MmTJvzmVz+/5kuf+fxVn/7K1V/6wue+ePNNt15z/S3X33DzddfdcsNNN02fsdP0aTtOnrLDpIkTps+Y1tvb+41vfOPLX/7ynXfejkPrllHh1lEBxyuON9wV3HAD+rgJZxsKL7jggq1/h/6eM9xmLBH8VRiXV7ziFQcccMCBBx54yCGHHHTQQYce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DwIzQqgDjcOgoDngByg2BwdVFfloBQZFiAEeVMvzMsvqABuaww9itsgEHto10cPGhJroE01QE4TR0SFyNq65neVsM5YIcodWrr766pNOOunYY489/fTT3/3ud59yyimnrhdOPvXUk0879VTQUUcdceKJxx/3jqPPPP20k084/vRTTznj1FNOOv6dZ5xxxkknnYCAfkAnnHDSqaeefua73/v+sz4w7/AjTz3jXaeecfqpp592/IknnHzqSSedgg5P3WRAPxj9xBNPRIx+UOc973nPpZdezBKafWsdh+5GljlDvqSQp1YdTk7WxARQysGxJBWlLB16oSSQGEiVhnvTlBXeaYzBQNqqKaRNmpCkJIkGDiUsyF133XX22Weff/75l19++RVXXHHppZeed955lwwHJIfZ+Lz8kosvvuD8T1/5iSsuu/TSiy685JKLPvnJT1x26cUf/vCHr7jiio9f+YlPXPmpj33sExdffOkFF138iU9+6qJLLr34sss//slPfezKT6DGpz9z1ZVXXvnRj370yk987BMf/2ibwIM+UYVPf/rTV1111cc//vGPfOQjH/vYx8D/4Ac/APi3e9qWLNE555xz3XXX3XzzzbfeeusNN9xw/fXXg0FyA7rp5ptBX/3a12655ZabEW6/9brrr/3iF7945Sc+/rrX/3mjUYs+S5pYk6T1xh677/mOY479whe+eO2119x1523XX3fDLTffdP11N6LdNddcc8st6Ak8CBnr0U033XTjjTG/GgWpm2644StfvfOmM045Yc6MKVbzjMt6KpN60x3G1evO99a4t049NVMR9daoB8l6ZMB36AWRwLiMxtWokjDkzD01jkJGXOPuLPK9kH9G3Zn21m13TZ0JJ7zzuKOPPhonysknn/yOd7zjqKOOOu6443BQvXM4IDmaUIQ6Bx98MFrhRPy7v/nb8ePH9/f3L1269OGHH37i8fmLFy+G+/Pyl7/8yCocc9yxh807/JCDDz3mmOPQ6qCDDjrssMPmzZt32Khw6Kjwtre9bf/990c1jHL44Yf/0z/90yGHHHLhhRfS9h62GUsErxUuK8PBtUMuLnKQxKUjaH01wfEWEpXSs7OhlS9e9NT5557z3ne/679//BNcWgMo8JDhQsO7Bnq+8pXrTjnltFtv+So6gXPuAyUJxrDOOdRB5iapPTomUBEx3vIFlwY0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" + }, + "9515e368-7045-46bd-a307-5e2a901ce228.png": { + "image/png": "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dcAjEKoR50TCzvhQsDEMD00JFqk6CplYiRGDaDiH4LVtTJ2cF1ICRAo9MSkLMZFhwIA47nvkRF6jmpnZe4+KSZIg3ohiHcKJFtQmKfCTOBuKHC0sx7pBMEBCNkXijX/7xr333BXKD2X+xWuuHfDBpYnCyohHXV+KrTqLs0JtigmMPgSJmENIttGFcw4ZxsQ6YLZj2mZWCMX8/Oc//9a3vnXHHXd87Wtfu/322/+1Cl/96tcjfe0bX/3qv379q1//l7u+/rWvxnAnwh13PPzQo8ywFfzLX/389tvvvOvOO796xx3f/cH377oL7J3//p3vffWO23eaNq3WqBvihx9+uH9ggAxrKL/zzW987evfuOPO2NWz/TBCmzAl0L/8y798/etf/8lP/1somMQIh1IlGBWjYOCdg+IGg/1k8qTriIPv0IshARIPYml/sIDjDC86sKhVJcHneCFdtPipBx544N5774X2H3rooXvuuee+++5Dzv0bhQfuv//h+x98/NH5y1f0tYrS+2LZsqeeeGLRgicXPPDgA48//viDDzzy6P0PPfnEwofuf+DNf/8W50xZ5k889mizf6A12HTWPPboI488/Njjjzx696/vvv/+BysaPUzMwQQwGczhkUcewZRQjFk99thj27ENai/NtB9bf4xj4Qc/+MHChQvXrl27fPnyvr6+Z5555umnn14xFJatWLl8xfKVK1ashIosmQAAEABJREFUamf09/evXr162rQZf/5nr3v9G17X29uN5Nq+gVVr+p5atGT16rWotmTJktVr+soQ/vRP//SvXveXf/umN//561/35294/V/8+evzgcGVK1eiyfJnCWuGw6pVq8BiVoiXPP3MT37680EYHkpKsq1gcAorm0A24BWSrGfrqyJPMcfHTJS6oC5QJ34hJAAxriPjo/AhYVOJGsLHGWAlCt8J15TdF6++5rrrrvviF7/4uc997tprr73++uu//OUvf+lLX7p6OCA5Qld/+ZovX3vj4/Ofmrzj1MmTJvzmVz+/5kuf+fxVn/7K1V/6wue+ePNNt15z/S3X33DzddfdcsNNN02fsdP0aTtOnrLDpIkTps+Y1tvb+41vfOPLX/7ynXfejkPrllHh1lEBxyuON9wV3HAD+rgJZxsKL7jggq1/h/6eM9xmLBH8VRiXV7ziFQcccMCBBx54yCGHHHTQQYce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DwIzQqgDjcOgoDngByg2BwdVFfloBQZFiAEeVMvzMsvqABuaww9itsgEHto10cPGhJroE01QE4TR0SFyNq65neVsM5YIcodWrr766pNOOunYY489/fTT3/3ud59yyimnrhdOPvXUk0879VTQUUcdceKJxx/3jqPPPP20k084/vRTTznj1FNOOv6dZ5xxxkknnYCAfkAnnHDSqaeefua73/v+sz4w7/AjTz3jXaeecfqpp592/IknnHzqSSedgg5P3WRAPxj9xBNPRIx+UOc973nPpZdezBKafWsdh+5GljlDvqSQp1YdTk7WxARQysGxJBWlLB16oSSQGEiVhnvTlBXeaYzBQNqqKaRNmpCkJIkGDiUsyF133XX22Weff/75l19++RVXXHHppZeed955lwwHJIfZ+Lz8kosvvuD8T1/5iSsuu/TSiy685JKLPvnJT1x26cUf/vCHr7jiio9f+YlPXPmpj33sExdffOkFF138iU9+6qJLLr34sss//slPfezKT6DGpz9z1ZVXXvnRj370yk987BMf/2ibwIM+UYVPf/rTV1111cc//vGPfOQjH/vYx8D/4Ac/APi3e9qWLNE555xz3XXX3XzzzbfeeusNN9xw/fXXg0FyA7rp5ptBX/3a12655ZabEW6/9brrr/3iF7945Sc+/rrX/3mjUYs+S5pYk6T1xh677/mOY479whe+eO2119x1523XX3fDLTffdP11N6LdNddcc8st6Ak8CBnr0U033XTjjTG/GgWpm2644StfvfOmM045Yc6MKVbzjMt6KpN60x3G1evO99a4t049NVMR9daoB8l6ZMB36AWRwLiMxtWokjDkzD01jkJGXOPuLPK9kH9G3Zn21m13TZ0JJ7zzuKOPPhonysknn/yOd7zjqKOOOu6443BQvXM4IDmaUIQ6Bx98MFrhRPy7v/nb8ePH9/f3L1269OGHH37i8fmLFy+G+/Pyl7/8yCocc9yxh807/JCDDz3mmOPQ6qCDDjrssMPmzZt32Khw6Kjwtre9bf/990c1jHL44Yf/0z/90yGHHHLhhRfS9h62GUsErxUuK8PBtUMuLnKQxKUjaH01wfEWEpXSs7OhlS9e9NT5557z3ne/679//BNcWgMo8JDhQsO7Bnq+8pXrTjnltFtv+So6gXPuAyUJxrDOOdRB5iapPTomUBEx3vIFlwY0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" + } + }, + "cell_type": "markdown", + "id": "6736c99f-6ca5-43d8-b671-32ce4854bc3b", + "metadata": {}, + "source": [ + "### Port Routing Convention\n", + "\n", + "![image.png](attachment:9515e368-7045-46bd-a307-5e2a901ce228.png)![image.png](attachment:8b1ced05-5107-4390-ae04-49a3f1634573.png)\n", + " \n", + "- The top diagram shows a connection from A's East port to B's West port — this routes metal through the narrow gap between the two components, which risks DRC violations due to spacing constraints.\n", + "- The bottom diagram shows the preferred approach: connecting A's West port to B's East port instead, routing the metal around the outside and keeping the space between components clear." + ] + }, + { + "cell_type": "markdown", + "id": "e7456ec3", + "metadata": {}, + "source": [ + "### Vertical Routes: Connecting Drain Nodes Through Vias" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b0129e4", + "metadata": {}, + "outputs": [], + "source": [ + "# CM MIR drain → M1 diff pair drain\n", + "top_level << straight_route(pdk,\n", + " drain_m3_via.ports[\"top_met_N\"],\n", + " drain_m1_via.ports[\"top_met_S\"])\n", + "\n", + "# CM REF drain → M2 diff pair drain\n", + "top_level << straight_route(pdk,\n", + " drain_m4_via.ports[\"top_met_N\"],\n", + " drain_m2_via.ports[\"top_met_S\"])\n", + "\n", + "# Diff pair common source → tail drain\n", + "top_level << straight_route(pdk,\n", + " source_m1_via.ports[\"top_met_N\"],\n", + " drain_m5_via.ports[\"top_met_S\"])\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "48b7d96d", + "metadata": {}, + "source": [ + "### Horizontal Routes: Connecting Sub-circuit Ports to Vias" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94ca5358", + "metadata": {}, + "outputs": [], + "source": [ + "# Current mirror MIR drain → left via\n", + "top_level << straight_route(pdk,\n", + " cm_ref.ports[\"M3_multiplier_0_drain_E\"],\n", + " drain_mir_via.ports[\"bottom_met_W\"])\n", + "\n", + "# Current mirror REF drain → right via\n", + "top_level << straight_route(pdk,\n", + " cm_ref.ports[\"M4_multiplier_0_drain_W\"],\n", + " drain_ref_via.ports[\"bottom_met_E\"])\n", + "\n", + "# M1 drain → left via\n", + "top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M1_multiplier_0_drain_E\"],\n", + " drain_m1_via.ports[\"bottom_met_W\"])\n", + "\n", + "# M2 drain → right via\n", + "top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M2_multiplier_0_drain_W\"],\n", + " drain_m2_via.ports[\"bottom_met_E\"])\n", + "\n", + "# M2 source → tail drain via\n", + "top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M2_multiplier_0_source_E\"],\n", + " drain_sink_via.ports[\"bottom_met_W\"])\n", + "\n", + "# VDD: CM source to N+ tapring\n", + "top_level << straight_route(pdk,\n", + " cm_ref.ports[\"M3_multiplier_0_source_E\"],\n", + " cm_ref.ports[\"TRING_W_top_met_W\"])\n", + "\n", + "# VSS: Tail source to P+ tapring\n", + "top_level << straight_route(pdk,\n", + " tail_ref.ports[\"M6_multiplier_0_source_E\"],\n", + " tail_ref.ports[\"TRING_W_top_met_W\"])\n", + "\n", + "# Connect tail tapring to diff pair tapring\n", + "top_level << straight_route(pdk,\n", + " dp_ref.ports[\"TRING_S_top_met_N\"],\n", + " tail_ref.ports[\"TRING_N_top_met_S\"])\n", + "\n", + "display_component(top_level, scale=1, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "959ad708", + "metadata": {}, + "source": [ + "## 9. Add Outermost Tapring\n", + "\n", + "We add a final tapring that encloses the entire layout. This connects to the inner taprings of the sub-cells to form a continuous VSS guard ring.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dba02378", + "metadata": {}, + "outputs": [], + "source": [ + "flat = top_level.flatten()\n", + "cx, cy = prec_center(flat)\n", + "actual_cx = -cx\n", + "actual_cy = -cy\n", + "\n", + "tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(\n", + " flat,\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]))\n", + "\n", + "tring_ref = top_level << tap_ring\n", + "tring_ref.movex(destination=actual_cx)\n", + "tring_ref.movey(destination=actual_cy)\n", + "\n", + "# Connect outer P+ tapring to inner P+ tapring\n", + "top_level << straight_route(pdk,\n", + " tail_ref.ports[\"TRING_W_top_met_E\"],\n", + " tring_ref.ports[\"W_top_met_W\"])\n", + "\n", + "# Snap to grid and rename ports by orientation\n", + "component = component_snap_to_grid(rename_ports_by_orientation(top_level))\n", + "display_component(component, scale=2, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "ea58a6ee", + "metadata": {}, + "source": [ + "## 10. I/O Port Vias & Pin Labeling\n", + "\n", + "### Adding Via Stacks for I/O Signals\n", + "\n", + "We place via stacks at each I/O port location, stepping up from metal 2 to metal 3. These vias serve as accessible landing pads for the top-level signals: VINP, VINN, VOUT, IN_CUR, VDD, and VSS." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "295d72fc", + "metadata": {}, + "outputs": [], + "source": [ + "viam2m3 = via_stack(pdk, \"met2\", \"met3\", centered=True)\n", + "\n", + "vinp_via = top_level << viam2m3\n", + "vinn_via = top_level << viam2m3\n", + "in_cur_via = top_level << viam2m3\n", + "vout_via = top_level << viam2m3\n", + "vdd_via = top_level << viam2m3\n", + "vss_via = top_level << viam2m3\n", + "\n", + "# Position each via at the corresponding port\n", + "vinn_via.move(dp_ref.ports[\"M1_multiplier_0_gate_W\"].center)\n", + "vinp_via.move(dp_ref.ports[\"M2_multiplier_0_gate_E\"].center)\n", + "in_cur_via.move(tail_ref.ports[\"M6_multiplier_0_drain_W\"].center)\n", + "vout_via.move(dp_ref.ports[\"M1_multiplier_0_drain_W\"].center)\n", + "vdd_via.move(cm_ref.ports[\"M3_multiplier_0_source_W\"].center)\n", + "vss_via.move(tail_ref.ports[\"M5_multiplier_0_source_W\"].center)\n", + "\n", + "display_component(top_level, scale=2, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "35f8b71a", + "metadata": {}, + "source": [ + "### Exposing Ports to the Top Level" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01b0bfbc", + "metadata": {}, + "outputs": [], + "source": [ + "# Add ports from sub-circuits\n", + "top_level.add_ports(cm_ref.get_ports_list(), prefix=\"CM_\")\n", + "top_level.add_ports(dp_ref.get_ports_list(), prefix=\"DP_\")\n", + "top_level.add_ports(tail_ref.get_ports_list(), prefix=\"TAIL_\")\n", + "\n", + "# Add ports from I/O vias\n", + "top_level.add_ports(vinp_via.get_ports_list(), prefix=\"VINP_\")\n", + "top_level.add_ports(vinn_via.get_ports_list(), prefix=\"VINN_\")\n", + "top_level.add_ports(in_cur_via.get_ports_list(), prefix=\"INCUR_\")\n", + "top_level.add_ports(vout_via.get_ports_list(), prefix=\"VOUT_\")\n", + "top_level.add_ports(vdd_via.get_ports_list(), prefix=\"VDD_\")\n", + "top_level.add_ports(vss_via.get_ports_list(), prefix=\"VSS_\")\n", + "\n", + "component = component_snap_to_grid(rename_ports_by_orientation(top_level))\n", + "display_component(component, scale=2, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "bd78a24b", + "metadata": {}, + "source": [ + "### Adding Pin Labels\n", + "\n", + "We need to add I/O pins, power supply pins, and bias pins to the layout. These labels are required for LVS (Layout vs. Schematic) verification and parasitic extraction (PEX). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1d6c893", + "metadata": {}, + "outputs": [], + "source": [ + "psize = (0.5, 0.5)\n", + "move_info = list()\n", + "\n", + "# VSS\n", + "vsslabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"), size=psize, centered=True).copy()\n", + "vsslabel.add_label(text=\"VSS\", layer=pdk.get_glayer(\"met2_label\"))\n", + "move_info.append((vsslabel, component.ports[\"VSS_bottom_met_E\"], None))\n", + "\n", + "# VDD\n", + "vddlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"), size=psize, centered=True).copy()\n", + "vddlabel.add_label(text=\"VDD\", layer=pdk.get_glayer(\"met2_label\"))\n", + "move_info.append((vddlabel, component.ports[\"VDD_bottom_met_E\"], None))\n", + "\n", + "# VINP\n", + "vinplabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"), size=psize, centered=True).copy()\n", + "vinplabel.add_label(text=\"VINP\", layer=pdk.get_glayer(\"met2_label\"))\n", + "move_info.append((vinplabel, component.ports[\"VINP_bottom_met_E\"], None))\n", + "\n", + "# VINN\n", + "vinnlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"), size=psize, centered=True).copy()\n", + "vinnlabel.add_label(text=\"VINN\", layer=pdk.get_glayer(\"met2_label\"))\n", + "move_info.append((vinnlabel, component.ports[\"VINN_bottom_met_W\"], None))\n", + "\n", + "# VOUT\n", + "voutlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"), size=psize, centered=True).copy()\n", + "voutlabel.add_label(text=\"VOUT\", layer=pdk.get_glayer(\"met2_label\"))\n", + "move_info.append((voutlabel, component.ports[\"VOUT_bottom_met_W\"], None))\n", + "\n", + "# IN_CUR\n", + "in_curlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"), size=psize, centered=True).copy()\n", + "in_curlabel.add_label(text=\"IN_CUR\", layer=pdk.get_glayer(\"met2_label\"))\n", + "move_info.append((in_curlabel, component.ports[\"INCUR_bottom_met_W\"], None))\n", + "\n", + "for comp, prt, alignment in move_info:\n", + " alignment = ('c', 'b') if alignment is None else alignment\n", + " compref = align_comp_to_port(comp, prt, alignment=alignment)\n", + " top_level.add(compref)\n", + "\n", + "component = top_level.flatten()\n", + "display_component(top_level, scale=2, path=\"../../\")" + ] + }, + { + "cell_type": "markdown", + "id": "3ff5cda3", + "metadata": {}, + "source": [ + "## 11. DRC Verification\n", + "\n", + "The final step is to run a Design Rule Check (DRC) using Magic. The `drc_magic` function runs Magic's DRC engine on the component and returns a report of any violations.\n", + "\n", + "A clean DRC result confirms that all spacing, enclosure, and width rules defined by the GF180 PDK are satisfied." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eff53f5a", + "metadata": {}, + "outputs": [], + "source": [ + "component.name = \"5T_OTA\"\n", + "drc_result = gf180.drc_magic(component, component.name)" + ] + } + ], + "metadata": { + "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.10.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 4f6300cd9b5ff9cd15a5043ab226b27a15799ddf Mon Sep 17 00:00:00 2001 From: Dharma Anargya Jowandy <16523104@std.stei.itb.ac.id> Date: Sat, 2 May 2026 06:14:11 +0700 Subject: [PATCH 2/6] feat: add 5T OTA Tutorial Part 2 --- tutorial/glayout_tutorial_5T_OTA_part2.ipynb | 1119 ++++++++++++++++++ 1 file changed, 1119 insertions(+) create mode 100644 tutorial/glayout_tutorial_5T_OTA_part2.ipynb diff --git a/tutorial/glayout_tutorial_5T_OTA_part2.ipynb b/tutorial/glayout_tutorial_5T_OTA_part2.ipynb new file mode 100644 index 00000000..6d36083e --- /dev/null +++ b/tutorial/glayout_tutorial_5T_OTA_part2.ipynb @@ -0,0 +1,1119 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5f6dedb2-7032-4661-ae60-4f3a7ebe726b", + "metadata": {}, + "source": [ + "# Tutorial 2: 5-Transistor OTA LVS, PEX, and simulation using gLayout\n", + "\n", + "**By gLayout Team**\n", + "\n", + "**Content creators:** Adrian Sami Pratama, Dharma Anargya Jowandy" + ] + }, + { + "cell_type": "markdown", + "id": "2ac43d65-343e-4f91-ace4-9a6a250dc18b", + "metadata": {}, + "source": [ + "___\n", + "# Tutorial Objectives\n", + "\n", + "This notebook is a tutorial on-\n", + "\n", + "- **LVS (Layout Versus Schematic):** \n", + " You will learn how to compare your physical layout with the original schematic to ensure they are functionally identical. This process helps catch connectivity or device mismatches before fabrication.\n", + "\n", + "- **Extraction and Simulation:** \n", + " The tutorial will guide you through extracting parasitic elements from your layout, such as capacitance and resistance, to create a more accurate circuit model. You will then simulate the extracted netlist to analyze and verify the real-world performance of your design." + ] + }, + { + "attachments": { + "c3ac3bfb-ecf2-4168-83c4-f9514dc6e1ce.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "id": "212e6186-57a1-44bc-879d-a49057db104b", + "metadata": {}, + "source": [ + "### What is a 5T OTA?\n", + "\n", + "A 5-Transistor Operational Transconductance Amplifier (5T OTA) is one of the most fundamental analog circuit building blocks. It converts a differential input voltage into an output current. The circuit consists of:\n", + "\n", + "- **M1, M2** — Differential input pair (NMOS)\n", + "- **M3, M4** — Current mirror load (PMOS)\n", + "- **M5** — Tail current source (NMOS)\n", + "\n", + "### Schematic Reference\n", + "\n", + "![image.png](attachment:c3ac3bfb-ecf2-4168-83c4-f9514dc6e1ce.png)" + ] + }, + { + "cell_type": "markdown", + "id": "091484d8-57a2-424f-a752-ab057290f153", + "metadata": {}, + "source": [ + "## **NetList generation and LVS**\n", + "let's go through the step by step procedure to generate LVS and DRC clean layout of a FVF cell." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6654744d-e646-408a-85ab-5d3967dea400", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import subprocess\n", + "\n", + "# Run a shell, source .bashrc, then printenv\n", + "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", + "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", + "env_vars = {}\n", + "for line in result.stdout.splitlines():\n", + " if '=' in line:\n", + " key, value = line.split('=', 1)\n", + " env_vars[key] = value\n", + "\n", + "# Now, update os.environ with these\n", + "os.environ.update(env_vars)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "84912826-fbd5-4772-b0a4-0371872dd155", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout import MappedPDK, sky130 , gf180\n", + "#from gdsfactory.cell import cell\n", + "from gdsfactory import Component\n", + "from gdsfactory.components import text_freetype, rectangle" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "0bc7007a-0211-4d40-860d-3c06706cd120", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout import nmos, pmos\n", + "from glayout import via_stack\n", + "from glayout import rename_ports_by_orientation\n", + "from glayout import tapring" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b30913c0-26b9-44ff-819e-e1a9dc39335f", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port\n", + "from glayout.util.port_utils import add_ports_perimeter,print_ports\n", + "from glayout.util.snap_to_grid import component_snap_to_grid\n", + "from glayout.spice.netlist import Netlist" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "08aab530-2236-4a81-b5c4-e43a8dd19f66", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.routing.straight_route import straight_route\n", + "from glayout.routing.c_route import c_route\n", + "from glayout.routing.L_route import L_route" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f19e0887-350c-44c9-9a34-bf04504c6208", + "metadata": {}, + "outputs": [], + "source": [ + "fivet_ota_code_string = \"\"\"\n", + "from glayout import MappedPDK, sky130 , gf180\n", + "# from gdsfactory.cell import cell\n", + "from gdsfactory import Component\n", + "from gdsfactory.components import text_freetype, rectangle\n", + "\n", + "from glayout import nmos, pmos\n", + "from glayout import via_stack\n", + "from glayout import rename_ports_by_orientation\n", + "from glayout import tapring\n", + "\n", + "from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port\n", + "from glayout.util.port_utils import add_ports_perimeter,print_ports\n", + "from glayout.util.snap_to_grid import component_snap_to_grid\n", + "from glayout.spice.netlist import Netlist\n", + "\n", + "from glayout.routing.straight_route import straight_route\n", + "from glayout.routing.c_route import c_route\n", + "from glayout.routing.L_route import L_route\n", + "\n", + "\n", + "###### Only Required for IIC-OSIC Docker\n", + "import os\n", + "import subprocess\n", + "\n", + "# Run a shell, source .bashrc, then printenv\n", + "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", + "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", + "env_vars = {}\n", + "for line in result.stdout.splitlines():\n", + " if '=' in line:\n", + " key, value = line.split('=', 1)\n", + " env_vars[key] = value\n", + "\n", + "# Now, update os.environ with these\n", + "os.environ.update(env_vars)\n", + "\n", + "\n", + "def add_fivet_ota_labels(\n", + " fivet_ota_in: Component,\n", + " pdk: MappedPDK,\n", + ") -> Component:\n", + " fivet_ota_in.unlock()\n", + "\n", + " psize=(0.5,0.5)\n", + " # list that will contain all port/comp info\n", + " move_info = list()\n", + " # create labels and append to info list\n", + "\n", + " # vss\n", + " vsslabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vsslabel.add_label(text=\"VSS\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vsslabel,fivet_ota_in.ports[\"VSS_bottom_met_E\"],None))\n", + " #vss_ref = top_level << vsslabel;\n", + " \n", + " # vdd\n", + " vddlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vddlabel.add_label(text=\"VDD\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vddlabel,fivet_ota_in.ports[\"VDD_bottom_met_E\"],None))\n", + " #vdd_ref = top_level << vddlabel;\n", + " \n", + " # vinp\n", + " vinplabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vinplabel.add_label(text=\"VINP\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vinplabel,fivet_ota_in.ports[\"VINP_bottom_met_E\"],None))\n", + " #vinp_ref = top_level << vinplabel;\n", + " \n", + " # vinn\n", + " vinnlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vinnlabel.add_label(text=\"VINN\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vinnlabel,fivet_ota_in.ports[\"VINN_bottom_met_W\"],None))\n", + " #vinn_ref = top_level << vinnlabel;\n", + " \n", + " # vout\n", + " voutlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " voutlabel.add_label(text=\"VOUT\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((voutlabel,fivet_ota_in.ports[\"VOUT_bottom_met_W\"],None))\n", + " #out_ref = top_level << outlabel;\n", + " \n", + " # in_cur\n", + " in_curlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " in_curlabel.add_label(text=\"IN_CUR\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((in_curlabel,fivet_ota_in.ports[\"INCUR_bottom_met_W\"],None))\n", + " #in_cur_ref = top_level << in_curlabel;\n", + "\n", + " # move everything to position\n", + " for comp, prt, alignment in move_info:\n", + " alignment = ('c','b') if alignment is None else alignment\n", + " compref = align_comp_to_port(comp, prt, alignment=alignment)\n", + " fivet_ota_in.add(compref)\n", + " \n", + " return fivet_ota_in.flatten()\n", + "\n", + "# @cell\n", + "\n", + "def fivet_ota(\n", + " pdk: MappedPDK,\n", + " input_pair: dict = {\n", + " \"width\": 5.75,\n", + " \"length\": 0.4,\n", + " \"fingers\": 2,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"nmos\",\n", + " },\n", + " current_mirror_cfg: dict = {\n", + " \"width\": 8.45,\n", + " \"length\": 0.4,\n", + " \"fingers\": 5,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"pmos\",\n", + " },\n", + " tail_source: dict = {\n", + " \"width\": 9.55,\n", + " \"length\": 0.7,\n", + " \"fingers\": 5,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"nmos\",\n", + " },\n", + " layout_rules: dict = {\n", + " \"spacing\": 2.0,\n", + " \"routing_metal\": \"met2\",\n", + " \"dummy_devices\": True,\n", + " \"tie_layers\": (\"met2\", \"met1\"),\n", + " \"sd_rmult\": 1,\n", + " },\n", + " **kwargs\n", + ") -> Component:\n", + "\n", + " pdk.activate()\n", + "\n", + " fivet_ota_config = {\n", + " \"input_pair\": input_pair,\n", + " \"current_mirror\": current_mirror_cfg,\n", + " \"tail_source\": tail_source,\n", + " \"layout_rules\": layout_rules,\n", + " }\n", + "\n", + " nmos_kwargs = {\n", + " \"with_tie\": False,\n", + " \"with_dnwell\": False,\n", + " \"sd_route_topmet\": \"met2\",\n", + " \"gate_route_topmet\": \"met2\",\n", + " \"sd_route_left\": True,\n", + " \"rmult\": None,\n", + " \"gate_rmult\": 1,\n", + " \"interfinger_rmult\": 1,\n", + " \"substrate_tap_layers\": layout_rules[\"tie_layers\"],\n", + " \"dummy_routes\": True,\n", + " }\n", + "\n", + " pmos_kwargs = {\n", + " \"with_tie\": False,\n", + " \"dnwell\": False,\n", + " \"sd_route_topmet\": \"met2\",\n", + " \"gate_route_topmet\": \"met2\",\n", + " \"sd_route_left\": True,\n", + " \"rmult\": None,\n", + " \"gate_rmult\": 1,\n", + " \"interfinger_rmult\": 1,\n", + " \"substrate_tap_layers\": layout_rules[\"tie_layers\"],\n", + " \"dummy_routes\": True,\n", + " }\n", + " \n", + " #top level component\n", + " top_level = Component(name=\"fivet_ota\")\n", + "\n", + " def current_mirror(pdk, config):\n", + " cm_comp = Component(name=\"current_mirror\")\n", + " \n", + " # ambil parameter dari config\n", + " width = config[\"current_mirror\"][\"width\"]\n", + " length = config[\"current_mirror\"][\"length\"]\n", + " fingers = config[\"current_mirror\"][\"fingers\"]\n", + " multipliers = config[\"current_mirror\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # ============ Instantiate 2 PMOS ============\n", + " pfet_ref = pmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False, True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **pmos_kwargs)\n", + " pfet_mir = pmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True, False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **pmos_kwargs)\n", + " \n", + " cref_ref = cm_comp << pfet_ref\n", + " cmir_ref = cm_comp << pfet_mir\n", + " cref_ref.name = \"pfet_ref\"\n", + " cmir_ref.name = \"pfet_mir\"\n", + " \n", + " # ============ Placement ============\n", + " cref_ref.movex(evaluate_bbox(pfet_mir)[0] + pdk.util_max_metal_seperation())\n", + " \n", + " # ============ Tapring ============\n", + " tap_ring = tapring(pdk, \n", + " enclosed_rectangle=evaluate_bbox(\n", + " cm_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]),\n", + " sdlayer=\"n+s/d\", \n", + " horizontal_glayer=tie_layers[0], \n", + " vertical_glayer=tie_layers[1]) \n", + " shift_amount = -prec_center(cm_comp.flatten())[0]\n", + " tring_ref = cm_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + " \n", + " # Add nwell padding to close gap between nwell\n", + " cm_comp.add_padding(layers=(pdk.get_glayer(\"nwell\"),), default=1)\n", + " \n", + " # ============ Internal routing ============\n", + " cm_comp << straight_route(pdk, cref_ref.ports[\"multiplier_0_source_E\"],\n", + " cmir_ref.ports[\"multiplier_0_source_E\"])\n", + " cm_comp << straight_route(pdk, cref_ref.ports[\"multiplier_0_gate_E\"],\n", + " cmir_ref.ports[\"multiplier_0_gate_E\"])\n", + " cm_comp << c_route(pdk, cref_ref.ports[\"multiplier_0_gate_E\"],\n", + " cref_ref.ports[\"multiplier_0_drain_E\"])\n", + " \n", + " # ============ Expose ports ============\n", + " cm_comp.add_ports(cref_ref.get_ports_list(), prefix=\"REF_\")\n", + " cm_comp.add_ports(cmir_ref.get_ports_list(), prefix=\"MIR_\")\n", + " cm_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " cm_comp.info.update({\"pfet_ref\": pfet_ref, \"pfet_mir\": pfet_mir})\n", + " \n", + " return cm_comp\n", + " \n", + " # ============ Add to top level ============\n", + " cm = current_mirror(pdk, fivet_ota_config)\n", + " cm_ref = top_level << cm\n", + " cm_ref.name = \"current_mirror\"\n", + "\n", + " def diff_pair(pdk, config):\n", + " dp_comp = Component(name=\"diff_pair\")\n", + " \n", + " # ambil parameter dari config\n", + " width = config[\"input_pair\"][\"width\"]\n", + " length = config[\"input_pair\"][\"length\"]\n", + " fingers = config[\"input_pair\"][\"fingers\"]\n", + " multipliers = config[\"input_pair\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # ============ Instantiate 2 NMOS ============\n", + " m1 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True , False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " m2 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False,True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " \n", + " m1_ref = dp_comp << m1\n", + " m2_ref = dp_comp << m2\n", + " m1_ref.name = \"M1\" # M1 is the negative input diffpair\n", + " m2_ref.name = \"M2\" # M2 is the positive input diffpair\n", + " \n", + " # ============ Placement ============\n", + " ref_dimensions = evaluate_bbox(m1)\n", + " m2_ref.movex(m1_ref.xmax)\n", + " m2_ref.movex(ref_dimensions[0]/2)\n", + " m2_ref.movex(pdk.util_max_metal_seperation())\n", + " \n", + " # ============ Internal routing ============\n", + " dp_comp << straight_route(pdk,\n", + " m1_ref.ports[\"multiplier_0_source_E\"],\n", + " m2_ref.ports[\"multiplier_0_source_W\"])\n", + " \n", + " # ============ Tapring ============\n", + " tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(\n", + " dp_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]))\n", + " shift_amount = -prec_center(dp_comp.flatten())[0]\n", + " tring_ref = dp_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + " \n", + " # ============ Expose ports ============\n", + " dp_comp.add_ports(m1_ref.get_ports_list(), prefix=\"M1_\")\n", + " dp_comp.add_ports(m2_ref.get_ports_list(), prefix=\"M2_\")\n", + " dp_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " dp_comp.info.update({\"M1\": m1, \"M2\": m2})\n", + " \n", + " return dp_comp\n", + " \n", + " # ============ Test ============\n", + " dp = diff_pair(pdk, fivet_ota_config)\n", + " dp_ref = top_level << dp\n", + " dp_ref.name = \"diff_pair\"\n", + " \n", + " def tail_current(pdk, config):\n", + " tail_comp = Component(name=\"tail_current\")\n", + " \n", + " # ambil parameter dari config\n", + " width = config[\"tail_source\"][\"width\"]\n", + " length = config[\"tail_source\"][\"length\"]\n", + " fingers = config[\"tail_source\"][\"fingers\"]\n", + " multipliers = config[\"tail_source\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # ============ Instantiate 2 NMOS ============\n", + " nfet_ref = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False, True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " nfet_mir = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True, False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " \n", + " tref_ref = tail_comp << nfet_ref\n", + " tmir_ref = tail_comp << nfet_mir\n", + " tref_ref.name = \"nfet_ref\"\n", + " tmir_ref.name = \"nfet_mir\"\n", + " \n", + " # ============ Placement ============\n", + " tref_ref.movex(evaluate_bbox(nfet_mir)[0] + pdk.util_max_metal_seperation())\n", + " \n", + " # ============ Tapring ============\n", + " tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(\n", + " tail_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]))\n", + " shift_amount = -prec_center(tail_comp.flatten())[0]\n", + " tring_ref = tail_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + " \n", + " # ============ Internal routing ============\n", + " tail_comp << straight_route(pdk, tref_ref.ports[\"multiplier_0_source_E\"],\n", + " tmir_ref.ports[\"multiplier_0_source_E\"])\n", + " tail_comp << straight_route(pdk, tref_ref.ports[\"multiplier_0_gate_E\"],\n", + " tmir_ref.ports[\"multiplier_0_gate_E\"])\n", + " tail_comp << c_route(pdk, tref_ref.ports[\"multiplier_0_gate_E\"],\n", + " tref_ref.ports[\"multiplier_0_drain_E\"])\n", + " \n", + " # ============ Expose ports ============\n", + " tail_comp.add_ports(tref_ref.get_ports_list(), prefix=\"REF_\")\n", + " tail_comp.add_ports(tmir_ref.get_ports_list(), prefix=\"MIR_\")\n", + " tail_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " tail_comp.info.update({\"nfet_ref\": nfet_ref, \"nfet_mir\": nfet_mir})\n", + " \n", + " return tail_comp\n", + " \n", + " # ============ Test ============\n", + " tail = tail_current(pdk, fivet_ota_config)\n", + " tail_ref = top_level << tail\n", + " tail_ref.name = \"tail_current\"\n", + " \n", + " # Hitung bbox masing-masing di top-level\n", + " dp_dimensions = evaluate_bbox(dp)\n", + " tail_dimensions = evaluate_bbox(tail)\n", + " \n", + " # diff_pair → letakkan di bawah current_mirror\n", + " dp_ref.movey(cm_ref.ymin - dp_dimensions[1]/2 - pdk.util_max_metal_seperation())\n", + " \n", + " # tail_current → letakkan di bawah diff_pair\n", + " tail_ref.movey(dp_ref.ymin - tail_dimensions[1]/2 - pdk.util_max_metal_seperation())\n", + " \n", + " # Pakai center absolut langsung\n", + " dp_ref.movex(cm_ref.center[0] - dp_ref.center[0])\n", + " tail_ref.movex(cm_ref.center[0] - tail_ref.center[0])\n", + "\n", + " viam2m3 = via_stack(pdk, \"met2\", \"met3\", centered=True)\n", + " viam1m2 = via_stack(pdk, \"met1\", \"met2\", centered=True)\n", + " \n", + " # Via drain kiri current mirror\n", + " drain_mir_via = top_level << viam2m3\n", + " drain_mir_via.move(cm_ref.ports[\"MIR_multiplier_0_drain_W\"].center).movex(-5)\n", + " \n", + " # Via drain kanan current mirror\n", + " drain_ref_via = top_level << viam2m3\n", + " drain_ref_via.move(cm_ref.ports[\"REF_multiplier_0_drain_E\"].center).movex(5)\n", + " \n", + " # Via drain kiri diffpair\n", + " drain_m1_via = top_level << viam2m3\n", + " drain_m1_via.move(dp_ref.ports[\"M1_multiplier_0_drain_W\"].center).movex(-5)\n", + " drain_m1_via.movex(drain_mir_via.x - drain_m1_via.x)\n", + " \n", + " # Via drain kanan diffpair\n", + " drain_m2_via = top_level << viam2m3\n", + " drain_m2_via.move(dp_ref.ports[\"M2_multiplier_0_drain_E\"].center).movex(5)\n", + " drain_m2_via.movex(drain_ref_via.x - drain_m2_via.x)\n", + " \n", + " # Via source diffpair\n", + " source_m1_via = top_level << viam2m3\n", + " source_m1_via.move(dp_ref.ports[\"M1_multiplier_0_source_W\"].center)\n", + " \n", + " # Via drain current tail\n", + " drain_sink_via = top_level << viam2m3\n", + " drain_sink_via.move(tail_ref.ports[\"MIR_multiplier_0_drain_W\"].center)\n", + " source_m1_via.movex(drain_sink_via.x - source_m1_via.x)\n", + "\n", + " # 1. CM kanan (drain_mir_via) → drain M1 diff pair\n", + " top_level << straight_route(pdk,\n", + " drain_mir_via.ports[\"top_met_N\"],\n", + " drain_m1_via.ports[\"top_met_S\"])\n", + " \n", + " # 2. CM kiri (drain_ref_via) → drain M2 diff pair\n", + " top_level << straight_route(pdk,\n", + " drain_ref_via.ports[\"top_met_N\"],\n", + " drain_m2_via.ports[\"top_met_S\"])\n", + " \n", + " # 3. Source common diff pair → drain tail\n", + " top_level << straight_route(pdk,\n", + " source_m1_via.ports[\"top_met_N\"],\n", + " drain_sink_via.ports[\"top_met_S\"])\n", + "\n", + " # MIR drain → drain_ref_via\n", + " top_level << straight_route(pdk,\n", + " cm_ref.ports[\"MIR_multiplier_0_drain_E\"],\n", + " drain_mir_via.ports[\"bottom_met_W\"])\n", + " \n", + " # REF drain → drain_mir_via\n", + " top_level << straight_route(pdk,\n", + " cm_ref.ports[\"REF_multiplier_0_drain_W\"],\n", + " drain_ref_via.ports[\"bottom_met_E\"])\n", + " \n", + " # M1 drain → drain_l_pair_via\n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M1_multiplier_0_drain_E\"],\n", + " drain_m1_via.ports[\"bottom_met_W\"])\n", + " \n", + " # M2 drain → drain_r_pair_via\n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M2_multiplier_0_drain_W\"],\n", + " drain_m2_via.ports[\"bottom_met_E\"])\n", + " \n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M2_multiplier_0_source_E\"],\n", + " drain_sink_via.ports[\"bottom_met_W\"])\n", + " \n", + " # Route VDD to N+ tapring\n", + " top_level << straight_route(pdk,\n", + " cm_ref.ports[\"MIR_multiplier_0_source_E\"],\n", + " cm_ref.ports[\"TRING_W_top_met_W\"])\n", + " \n", + " # Route VSS to P+ tapring\n", + " top_level << straight_route(pdk,\n", + " tail_ref.ports[\"REF_multiplier_0_source_E\"],\n", + " tail_ref.ports[\"TRING_W_top_met_W\"])\n", + " \n", + " # Route tail tapring to diffpair tapring\n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"TRING_S_top_met_N\"],\n", + " tail_ref.ports[\"TRING_N_top_met_S\"])\n", + "\n", + " # Expose signal ports \n", + " top_level.add_port(name=\"VSS_bottom_met_E\", port=tail_ref.ports[\"REF_multiplier_0_source_E\"])\n", + " top_level.add_port(name=\"VDD_bottom_met_E\", port=cm_ref.ports[\"MIR_multiplier_0_source_E\"])\n", + " top_level.add_port(name=\"VINP_bottom_met_E\", port=dp_ref.ports[\"M2_multiplier_0_gate_E\"])\n", + " top_level.add_port(name=\"VINN_bottom_met_W\", port=dp_ref.ports[\"M1_multiplier_0_gate_W\"])\n", + " top_level.add_port(name=\"VOUT_bottom_met_W\", port=drain_mir_via.ports[\"top_met_W\"])\n", + " top_level.add_port(name=\"INCUR_bottom_met_W\", port=tail_ref.ports[\"MIR_multiplier_0_gate_W\"])\n", + "\n", + " top_level.info.update({\n", + " \"m1\": dp.info[\"M1\"],\n", + " \"m2\": dp.info[\"M2\"],\n", + " \"pfet_ref\": cm.info[\"pfet_ref\"],\n", + " \"pfet_mir\": cm.info[\"pfet_mir\"],\n", + " \"nfet_tail_ref\": tail.info[\"nfet_ref\"],\n", + " \"nfet_tail_mir\": tail.info[\"nfet_mir\"],\n", + " })\n", + " return component_snap_to_grid(rename_ports_by_orientation(top_level))\n", + "\n", + "if __name__ == \"__main__\":\n", + " comp = fivet_ota(gf180)\n", + " comp = add_fivet_ota_labels(comp, gf180)\n", + " comp.name = \"FIVET_OTA\"\n", + " comp.write_gds('out_fivet_ota.gds')\n", + " comp.show()\n", + " print(\"...Running DRC...\")\n", + " drc_result = gf180.drc_magic(comp, \"FIVET_OTA\")\n", + "\n", + "\"\"\"\n", + "\n", + "fivet_ota_init_string = \"\"\"\n", + "### Glayout 5T OTA Cell.\n", + "\n", + "from .my_fivet_ota import fivet_ota, add_fivet_ota_labels\n", + "\n", + "__all__ = [\n", + " 'fivet_ota',\n", + " 'add_fivet_ota_labels',\n", + "]\n", + "\"\"\"\n", + "\n", + "directory = \"../../FIVET_OTA/\"\n", + "os.makedirs(directory, exist_ok=True)\n", + "\n", + "# Save to a .py file\n", + "with open(directory + \"my_fivet_ota.py\", \"w\") as file:\n", + " file.write(fivet_ota_code_string)\n", + "\n", + "with open(directory + \"__init__.py\", \"w\") as file:\n", + " file.write(fivet_ota_init_string)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "07910ef2-f3bc-4886-828b-8f38f40d8c40", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "from pathlib import Path\n", + "sys.path.append(os.path.abspath(\"../../FIVET_OTA\"))\n", + "from my_fivet_ota import fivet_ota, add_fivet_ota_labels" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1a07b0e6-e02b-48f7-8b03-86c888c3371e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2026-05-01 16:15:58.849\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mgdsfactory.pdk\u001b[0m:\u001b[36mactivate\u001b[0m:\u001b[36m337\u001b[0m - \u001b[1m'gf180' PDK is now active\u001b[0m\n", + "\u001b[32m2026-05-01 16:16:31.274\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mgdsfactory.klive\u001b[0m:\u001b[36mshow\u001b[0m:\u001b[36m55\u001b[0m - \u001b[1mMessage from klive: {\"version\": \"0.4.1\", \"klayout_version\": \"0.30.2\", \"type\": \"reload\", \"file\": \"/tmp/gdsfactory/FIVET_OTA.gds\"}\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + ".subckt NMOS D G S B l=0.4 w=5.75 m=2 dm=1 \n", + "XMAIN D G S B nfet_03v3 l={l} w={w} m={m}\n", + "XDUMMY1 B B B B nfet_03v3 l={l} w={w} m={dm}\n", + ".ends NMOS\n", + "\n", + ".subckt PMOS D G S B l=0.4 w=8.45 m=5 dm=1 \n", + "XMAIN D G S B pfet_03v3 l={l} w={w} m={m}\n", + "XDUMMY1 B B B B pfet_03v3 l={l} w={w} m={dm}\n", + ".ends PMOS\n", + "\n", + ".subckt NMOS_1 D G S B l=0.7 w=9.55 m=5 dm=1 \n", + "XMAIN D G S B nfet_03v3 l={l} w={w} m={m}\n", + "XDUMMY1 B B B B nfet_03v3 l={l} w={w} m={dm}\n", + ".ends NMOS_1\n", + "\n", + ".subckt FIVET_OTA VINP VINN VOUT VDD VSS IN_CUR\n", + "X0 VOUT VINP VTAIL VSS NMOS l=0.4 w=5.75 m=1.0 dm=1\n", + "X1 VMID VINN VTAIL VSS NMOS l=0.4 w=5.75 m=1.0 dm=1\n", + "X2 VMID VMID VDD VDD PMOS l=0.4 w=8.45 m=2.5 dm=1\n", + "X3 VOUT VMID VDD VDD PMOS l=0.4 w=8.45 m=2.5 dm=1\n", + "X4 VTAIL IN_CUR VSS VSS NMOS_1 l=0.7 w=9.55 m=2.5 dm=1\n", + "X5 VTAIL IN_CUR VSS VSS NMOS_1 l=0.7 w=9.55 m=2.5 dm=1\n", + ".ends FIVET_OTA\n" + ] + } + ], + "source": [ + "def fivet_ota_netlist(ota_in: Component) -> Component:\n", + " m1 = ota_in.info[\"m1\"]\n", + " m2 = ota_in.info[\"m2\"]\n", + " pfet_ref = ota_in.info[\"pfet_ref\"]\n", + " pfet_mir = ota_in.info[\"pfet_mir\"]\n", + " nfet_tail_ref = ota_in.info[\"nfet_tail_ref\"]\n", + " nfet_tail_mir = ota_in.info[\"nfet_tail_mir\"]\n", + "\n", + " netlist = Netlist(\n", + " circuit_name='FIVET_OTA',\n", + " nodes=['VINP', 'VINN', 'VOUT', 'VDD', 'VSS', 'IN_CUR']\n", + " )\n", + "\n", + " netlist.connect_netlist(m1.info['netlist'], [('D', 'VOUT'), ('G', 'VINP'), ('S', 'VTAIL'), ('B', 'VSS')])\n", + " netlist.connect_netlist(m2.info['netlist'], [('D', 'VMID'), ('G', 'VINN'), ('S', 'VTAIL'), ('B', 'VSS')])\n", + " netlist.connect_netlist(pfet_ref.info['netlist'], [('D', 'VMID'), ('G', 'VMID'), ('S', 'VDD'), ('B', 'VDD')])\n", + " netlist.connect_netlist(pfet_mir.info['netlist'], [('D', 'VOUT'), ('G', 'VMID'), ('S', 'VDD'), ('B', 'VDD')])\n", + " netlist.connect_netlist(nfet_tail_ref.info['netlist'], [('D','VTAIL'), ('G','IN_CUR'), ('S','VSS'), ('B','VSS')])\n", + " netlist.connect_netlist(nfet_tail_mir.info['netlist'], [('D','VTAIL'), ('G','IN_CUR'), ('S','VSS'), ('B','VSS')])\n", + "\n", + " ota_in.info['netlist'] = netlist\n", + " return ota_in\n", + "\n", + "my_ota = fivet_ota_netlist(fivet_ota(gf180))\n", + "my_ota = add_fivet_ota_labels(my_ota, gf180)\n", + "my_ota.name = \"FIVET_OTA\"\n", + "my_ota.show()\n", + "print(my_ota.info['netlist'].generate_netlist())" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "78bce88a-b817-442b-a47f-f65cf9ee8d5e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "VSS_bottom_met_E\n", + "VDD_bottom_met_E\n", + "VINP_bottom_met_E\n", + "VINN_bottom_met_W\n", + "VOUT_bottom_met_W\n", + "INCUR_bottom_met_W\n" + ] + } + ], + "source": [ + "comp = fivet_ota(gf180)\n", + "print_ports(comp)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "2311f1ca-3c76-447f-9603-7349ec87f3f5", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2026-05-01 16:17:32.614\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mgdsfactory.component\u001b[0m:\u001b[36m_write_library\u001b[0m:\u001b[36m1851\u001b[0m - \u001b[1mWrote to 'fivet_ota.gds'\u001b[0m\n", + "\u001b[32m2026-05-01 16:17:32.674\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mgdsfactory.klive\u001b[0m:\u001b[36mshow\u001b[0m:\u001b[36m55\u001b[0m - \u001b[1mMessage from klive: {\"version\": \"0.4.1\", \"klayout_version\": \"0.30.2\", \"type\": \"reload\", \"file\": \"/tmp/gdsfactory/fivet_OTA.gds\"}\u001b[0m\n", + "\u001b[32m2026-05-01 16:17:32.702\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mgdsfactory.component\u001b[0m:\u001b[36m_write_library\u001b[0m:\u001b[36m1851\u001b[0m - \u001b[1mWrote to '/tmp/tmpqbwclfe_/fivet_OTA.gds'\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "using user specified pdk_root, will search for required files in the specified directory\n", + "\n", + "Magic 8.3 revision 528 - Compiled on Wed Jun 18 09:45:25 PM CEST 2025.\n", + "Starting magic under Tcl interpreter\n", + "Using the terminal as the console.\n", + "WARNING: RLIMIT_NOFILE is above 1024 and Tcl_Version<9 this may cause runtime issues [rlim_cur=1048576]\n", + "Using NULL graphics device.\n", + "Processing system .magicrc file\n", + "Sourcing design .magicrc for technology gf180mcuD ...\n", + "10 Magic internal units = 1 Lambda\n", + "Input style import: scaleFactor=10, multiplier=2\n", + "The following types are not handled by extraction and will be treated as non-electrical types:\n", + " obsactive mvobsactive filldiff fillpoly m1hole obsm1 fillm1 obsv1 m2hole obsm2 fillm2 obsv2 m3hole obsm3 fillm3 m4hole obsm4 fillm4 m5hole obsm5 fillm5 glass fillblock lvstext obscomment \n", + "Scaled tech values by 10 / 1 to match internal grid scaling\n", + "Loading gf180mcuD Device Generator Menu ...\n", + "Using technology \"gf180mcuD\", version 1.0.525-0-gf2e289d\n", + "Warning: Calma reading is not undoable! I hope that's OK.\n", + "Library written using GDS-II Release 6.0\n", + "Library name: library\n", + "Reading \"fivet_OTA\".\n", + "Extracting fivet_OTA into fivet_OTA.ext:\n", + "fivet_OTA: 6 warnings\n", + "exttospice finished.\n", + "Extracting fivet_OTA into fivet_OTA.ext:\n", + "fivet_OTA: 6 warnings\n", + "exttosim finished.\n", + "Extracting fivet_OTA into fivet_OTA.ext:\n", + "fivet_OTA: 6 warnings\n", + "exttospice finished.\n", + "\n", + "Running netgen command: netgen -batch lvs \"/tmp/tmpqbwclfe_/fivet_OTA_lvsmag.spice fivet_OTA\" \"/tmp/tmpqbwclfe_/fivet_OTA.spice fivet_OTA\" /foss/pdks/gf180mcuD/libs.tech/netgen/gf180mcuD_setup.tcl /tmp/tmpqbwclfe_/fivet_OTA_lvs.rpt\n", + "Netgen 1.5.295 compiled on Wed Jun 18 09:36:23 PM CEST 2025\n", + "Warning: netgen command 'format' use fully-qualified name '::netgen::format'\n", + "Warning: netgen command 'global' use fully-qualified name '::netgen::global'\n", + "Reading netlist file /tmp/tmpqbwclfe_/fivet_OTA_lvsmag.spice\n", + "Call to undefined subcircuit nfet_03v3\n", + "Creating placeholder cell definition.\n", + "Call to undefined subcircuit pfet_03v3\n", + "Creating placeholder cell definition.\n", + "Reading netlist file /tmp/tmpqbwclfe_/fivet_OTA.spice\n", + "Call to undefined subcircuit pmos_3p3\n", + "Creating placeholder cell definition.\n", + "Call to undefined subcircuit nmos_3p3\n", + "Creating placeholder cell definition.\n", + "Call to undefined subcircuit nfet_03v3\n", + "Creating placeholder cell definition.\n", + "Call to undefined subcircuit pfet_03v3\n", + "Creating placeholder cell definition.\n", + "\n", + "Reading setup file /foss/pdks/gf180mcuD/libs.tech/netgen/gf180mcuD_setup.tcl\n", + "\n", + "No property area found for device c\n", + "Model nfet_03v3 pin 1 == 3\n", + "No property par1 found for device nfet_03v3\n", + "No property NRD found for device nfet_03v3\n", + "No property NRS found for device nfet_03v3\n", + "No property par found for device nfet_03v3\n", + "No property sa found for device nfet_03v3\n", + "No property sb found for device nfet_03v3\n", + "No property sd found for device nfet_03v3\n", + "No property par found for device nfet_03v3\n", + "No property dtemp found for device nfet_03v3\n", + "No property nf found for device nfet_03v3\n", + "Model nfet_03v3 pin 1 == 3\n", + "No property par1 found for device nfet_03v3\n", + "No property NRD found for device nfet_03v3\n", + "No property NRS found for device nfet_03v3\n", + "No property par found for device nfet_03v3\n", + "No property sa found for device nfet_03v3\n", + "No property sb found for device nfet_03v3\n", + "No property sd found for device nfet_03v3\n", + "No property par found for device nfet_03v3\n", + "No property dtemp found for device nfet_03v3\n", + "No property nf found for device nfet_03v3\n", + "No property as found for device nfet_03v3\n", + "No property ad found for device nfet_03v3\n", + "No property ps found for device nfet_03v3\n", + "No property pd found for device nfet_03v3\n", + "Model pfet_03v3 pin 1 == 3\n", + "No property par1 found for device pfet_03v3\n", + "No property NRD found for device pfet_03v3\n", + "No property NRS found for device pfet_03v3\n", + "No property par found for device pfet_03v3\n", + "No property sa found for device pfet_03v3\n", + "No property sb found for device pfet_03v3\n", + "No property sd found for device pfet_03v3\n", + "No property par found for device pfet_03v3\n", + "No property dtemp found for device pfet_03v3\n", + "No property nf found for device pfet_03v3\n", + "Model pfet_03v3 pin 1 == 3\n", + "No property par1 found for device pfet_03v3\n", + "No property NRD found for device pfet_03v3\n", + "No property NRS found for device pfet_03v3\n", + "No property par found for device pfet_03v3\n", + "No property sa found for device pfet_03v3\n", + "No property sb found for device pfet_03v3\n", + "No property sd found for device pfet_03v3\n", + "No property par found for device pfet_03v3\n", + "No property dtemp found for device pfet_03v3\n", + "No property nf found for device pfet_03v3\n", + "No property as found for device pfet_03v3\n", + "No property ad found for device pfet_03v3\n", + "No property ps found for device pfet_03v3\n", + "No property pd found for device pfet_03v3\n", + "Comparison output logged to file /tmp/tmpqbwclfe_/fivet_OTA_lvs.rpt\n", + "Logging to file \"/tmp/tmpqbwclfe_/fivet_OTA_lvs.rpt\" enabled\n", + "Circuit nfet_03v3 contains no devices.\n", + "Circuit pfet_03v3 contains no devices.\n", + "\n", + "Contents of circuit 1: Circuit: 'fivet_OTA'\n", + "Circuit fivet_OTA contains 30 device instances.\n", + " Class: nfet_03v3 instances: 18\n", + " Class: pfet_03v3 instances: 12\n", + "Circuit contains 14 nets.\n", + "Contents of circuit 2: Circuit: 'fivet_OTA'\n", + "Circuit fivet_OTA contains 20 device instances.\n", + " Class: nfet_03v3 instances: 12\n", + " Class: pfet_03v3 instances: 8\n", + "Circuit contains 8 nets.\n", + "\n", + "Circuit was modified by parallel/series device merging.\n", + "New circuit summary:\n", + "\n", + "Contents of circuit 1: Circuit: 'fivet_OTA'\n", + "Circuit fivet_OTA contains 12 device instances.\n", + " Class: nfet_03v3 instances: 8\n", + " Class: pfet_03v3 instances: 4\n", + "Circuit contains 14 nets.\n", + "Contents of circuit 2: Circuit: 'fivet_OTA'\n", + "Circuit fivet_OTA contains 7 device instances.\n", + " Class: nfet_03v3 instances: 4\n", + " Class: pfet_03v3 instances: 3\n", + "Circuit contains 8 nets.\n", + "\n", + "Circuit 1 contains 12 devices, Circuit 2 contains 7 devices. *** MISMATCH ***\n", + "Circuit 1 contains 14 nets, Circuit 2 contains 8 nets. *** MISMATCH ***\n", + "\n", + "\n", + "Final result: \n", + "Netlists do not match.\n", + "Port matching may fail to disambiguate symmetries.\n", + "Logging to file \"/tmp/tmpqbwclfe_/fivet_OTA_lvs.rpt\" disabled\n", + "LVS Done.\n", + "\n", + "\n", + "Circuit 1 cell nfet_03v3 and Circuit 2 cell nfet_03v3 are black boxes.\n", + "Warning: Equate pins: cell nfet_03v3 is a placeholder, treated as a black box.\n", + "Warning: Equate pins: cell nfet_03v3 is a placeholder, treated as a black box.\n", + "\n", + "Subcircuit pins:\n", + "Circuit 1: nfet_03v3 |Circuit 2: nfet_03v3 \n", + "-------------------------------------------|-------------------------------------------\n", + "1 |1 \n", + "2 |2 \n", + "3 |3 \n", + "4 |4 \n", + "---------------------------------------------------------------------------------------\n", + "Cell pin lists are equivalent.\n", + "Device classes nfet_03v3 and nfet_03v3 are equivalent.\n", + "\n", + "Circuit 1 cell pfet_03v3 and Circuit 2 cell pfet_03v3 are black boxes.\n", + "Warning: Equate pins: cell pfet_03v3 is a placeholder, treated as a black box.\n", + "Warning: Equate pins: cell pfet_03v3 is a placeholder, treated as a black box.\n", + "\n", + "Subcircuit pins:\n", + "Circuit 1: pfet_03v3 |Circuit 2: pfet_03v3 \n", + "-------------------------------------------|-------------------------------------------\n", + "1 |1 \n", + "2 |2 \n", + "3 |3 \n", + "4 |4 \n", + "---------------------------------------------------------------------------------------\n", + "Cell pin lists are equivalent.\n", + "Device classes pfet_03v3 and pfet_03v3 are equivalent.\n", + "Flattening unmatched subcell NMOS in circuit fivet_OTA (1)(2 instances)\n", + "Flattening unmatched subcell PMOS in circuit fivet_OTA (1)(4 instances)\n", + "Flattening unmatched subcell NMOS_1 in circuit fivet_OTA (1)(4 instances)\n", + "\n", + "Class fivet_OTA (0): Merged 18 parallel devices.\n", + "Class fivet_OTA (1): Merged 13 parallel devices.\n", + "Subcircuit summary:\n", + "Circuit 1: fivet_OTA |Circuit 2: fivet_OTA \n", + "-------------------------------------------|-------------------------------------------\n", + "nfet_03v3 (18->8) |nfet_03v3 (30->4) **Mismatch** \n", + "pfet_03v3 (12->4) |pfet_03v3 (24->3) **Mismatch** \n", + "Number of devices: 12 **Mismatch** |Number of devices: 7 **Mismatch** \n", + "Number of nets: 14 **Mismatch** |Number of nets: 8 **Mismatch** \n", + "---------------------------------------------------------------------------------------\n", + "NET mismatches: Class fragments follow (with fanout counts):\n", + "Circuit 1: fivet_OTA |Circuit 2: fivet_OTA \n", + "\n", + "---------------------------------------------------------------------------------------\n", + "Net: IN_CUR |Net: IN_CUR \n", + " nfet_03v3/2 = 2 | nfet_03v3/2 = 1 \n", + " nfet_03v3/(1|3) = 1 | \n", + " | \n", + "Net: VSS |Net: VSS \n", + " nfet_03v3/4 = 8 | nfet_03v3/4 = 4 \n", + " nfet_03v3/(1|3) = 2 | nfet_03v3/(1|3) = 3 \n", + " | nfet_03v3/2 = 1 \n", + " | \n", + "Net: VDD |Net: VDD \n", + " pfet_03v3/(1|3) = 2 | pfet_03v3/(1|3) = 4 \n", + " pfet_03v3/4 = 4 | pfet_03v3/4 = 3 \n", + " | pfet_03v3/2 = 1 \n", + " | \n", + "Net: a_n210_n3343# |(no matching net) \n", + " nfet_03v3/(1|3) = 2 | \n", + " nfet_03v3/2 = 1 | \n", + " | \n", + "Net: a_1670_n3343# |(no matching net) \n", + " nfet_03v3/(1|3) = 2 | \n", + " nfet_03v3/2 = 1 | \n", + " | \n", + "Net: a_n1146_n6321# |(no matching net) \n", + " nfet_03v3/(1|3) = 2 | \n", + " nfet_03v3/2 = 1 | \n", + " | \n", + "Net: a_2546_n6321# |(no matching net) \n", + " nfet_03v3/(1|3) = 2 | \n", + " nfet_03v3/2 = 1 | \n", + " | \n", + "Net: a_n786_n925# |(no matching net) \n", + " pfet_03v3/(1|3) = 2 | \n", + " pfet_03v3/2 = 1 | \n", + " | \n", + "Net: a_2246_n925# |(no matching net) \n", + " pfet_03v3/(1|3) = 2 | \n", + " pfet_03v3/2 = 1 | \n", + "---------------------------------------------------------------------------------------\n", + "DEVICE mismatches: Class fragments follow (with node fanout counts):\n", + "Circuit 1: fivet_OTA |Circuit 2: fivet_OTA \n", + "\n", + "---------------------------------------------------------------------------------------\n", + "Instance: nfet_03v3:2 |Instance: NMOS:0/nfet_03v3:DUMMY1 \n", + " (1,3) = (10,3) | (1,3) = (8,8) \n", + " 2 = 3 | 2 = 8 \n", + " 4 = 10 | 4 = 8 \n", + " | \n", + "Instance: nfet_03v3:5 |Instance: PMOS:2/pfet_03v3:DUMMY1 \n", + " (1,3) = (3,3) | (1,3) = (8,8) \n", + " 2 = 3 | 2 = 8 \n", + " 4 = 10 | 4 = 8 \n", + " | \n", + "Instance: nfet_03v3:12 |(no matching instance) \n", + " (1,3) = (3,3) | \n", + " 2 = 3 | \n", + " 4 = 10 | \n", + " | \n", + " | \n", + "Instance: nfet_03v3:15 |(no matching instance) \n", + " (1,3) = (3,3) | \n", + " 2 = 3 | \n", + " 4 = 10 | \n", + " | \n", + " | \n", + "Instance: nfet_03v3:29 |(no matching instance) \n", + " (1,3) = (3,3) | \n", + " 2 = 3 | \n", + " 4 = 10 | \n", + " | \n", + " | \n", + "Instance: pfet_03v3:3 |(no matching instance) \n", + " (1,3) = (3,3) | \n", + " 2 = 3 | \n", + " 4 = 6 | \n", + " | \n", + " | \n", + "Instance: pfet_03v3:7 |(no matching instance) \n", + " (1,3) = (3,3) | \n", + " 2 = 3 | \n", + " 4 = 6 | \n", + " | \n", + "---------------------------------------------------------------------------------------\n", + "Netlists do not match.\n", + "Port matching may fail to disambiguate symmetries.\n", + "\n", + "Subcircuit pins:\n", + "Circuit 1: fivet_OTA |Circuit 2: fivet_OTA \n", + "-------------------------------------------|-------------------------------------------\n", + "IN_CUR |IN_CUR \n", + "Cell pin lists are equivalent.\n", + "Device classes fivet_OTA and fivet_OTA are equivalent.\n", + "\n", + "Final result: Netlists do not match.\n", + "Port matching may fail to disambiguate symmetries.\n", + "\n" + ] + } + ], + "source": [ + "my_ota = fivet_ota_netlist(add_fivet_ota_labels(fivet_ota(gf180), gf180))\n", + "my_ota.name = \"fivet_OTA\"\n", + "ota_gds = my_ota.write_gds(\"fivet_ota.gds\")\n", + "my_ota.show()\n", + "netgen_lvs_result = gf180.lvs_netgen(my_ota, my_ota.name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "710d8e22-8f9a-44ee-9f25-0d252b674063", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "GLdev", + "language": "python", + "name": "gldev" + }, + "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.10.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From c334af54d9a7cdddbd9c471588c1dc59e14958d3 Mon Sep 17 00:00:00 2001 From: AdrianSPratama <16523037@std.stei.itb.ac.id> Date: Sun, 3 May 2026 16:23:41 +0700 Subject: [PATCH 3/6] feat part 2 of 5T OTA tutorial notebook In this part 2 5T OTA tutorial notebook, we did LVS, PEX, and post layout simulation. Only LVS is finished, the others are still not finished. This cell is already DRC and LVS clean --- tutorial/glayout_tutorial_5T_OTA_part2.ipynb | 971 +++++++++++++++++++ 1 file changed, 971 insertions(+) create mode 100644 tutorial/glayout_tutorial_5T_OTA_part2.ipynb diff --git a/tutorial/glayout_tutorial_5T_OTA_part2.ipynb b/tutorial/glayout_tutorial_5T_OTA_part2.ipynb new file mode 100644 index 00000000..1d14a4f5 --- /dev/null +++ b/tutorial/glayout_tutorial_5T_OTA_part2.ipynb @@ -0,0 +1,971 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5f6dedb2-7032-4661-ae60-4f3a7ebe726b", + "metadata": {}, + "source": [ + "# Tutorial 2: 5-Transistor OTA LVS, PEX, and simulation using gLayout\n", + "\n", + "**By gLayout Team**\n", + "\n", + "**Content creators:** Adrian Sami Pratama, Dharma Anargya Jowandy" + ] + }, + { + "cell_type": "markdown", + "id": "2ac43d65-343e-4f91-ace4-9a6a250dc18b", + "metadata": {}, + "source": [ + "___\n", + "# Tutorial Objectives\n", + "\n", + "This notebook is a tutorial on-\n", + "\n", + "- **LVS (Layout Versus Schematic):** \n", + " You will learn how to compare your physical layout with the original schematic to ensure they are functionally identical. This process helps catch connectivity or device mismatches before fabrication.\n", + "\n", + "- **Extraction and Simulation:** \n", + " The tutorial will guide you through extracting parasitic elements from your layout, such as capacitance and resistance, to create a more accurate circuit model. You will then simulate the extracted netlist to analyze and verify the real-world performance of your design." + ] + }, + { + "attachments": { + "c3ac3bfb-ecf2-4168-83c4-f9514dc6e1ce.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAApAAAAJnCAYAAAAgImPxAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAFTHSURBVHhe7d11fFX1H8fx94IVORglEqNDGlGURhAUfhKKKLUBinQjISCTTumQDgmVbpEGaWkEhI0czVj3+f0hIDuEHNnG4vV8PO7j8eP7+Z6rP5W7F+eee66NYRiGAAAAgBdka14AAAAAnoeABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLbAzDMMyLAICE4fTp07pz5455OV689tpryp07t3kZAAhIAEjIJk+erHHjxqlYsWLmUZzau3evVq1apZIlS5pHAEBAAkBCNnXqVJ06dUpDhw41j+JMaGiomjVrpkGDBhGQAJ6KayABIIGzt7dXypQp4+3h4OBg/lsAgBgISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQBJzdXd6tiwvNzd3R89Phm2RgHh5o0A8N8QkACQhFw//rM+qdZNWZuO1xlvb3l7e8vbe5dqX52jgqW66WhglPkQALCMgASAJCNI2yYMVc5eM9W1Tin9830y2dR8wixNLbNSHUYfVliMYwDAOgISAJKMU9q5O1iFSxWRo3mkNKpa+01d3vCrrppHAGARAQkASYa//PzNa/9ImcZV0f5+CjEPAMAiAhIAAACWEJAAkIREBN3X7lVzNGfOUx6//mneDgD/CQEJAElGYbUeM0QVXjevP1CkmUYNbqhs5nUAsMjGMAzDvAgASBimTp2qs2fPasyYMeZRnAkICFCjRo00aNAglSxZ0jwGAM5AAkBSEh0Zqpt7l6rph28q16MbiWfX+91m6IS3n7gLJIDYQEACQBJyfssEdeizRh/0X6y/Ht1I/Ih65L2stg2+0vJzweZDAMAyAhIAkow72jprgYp1HaZP3soj+0frGfRem94aVu2Uho3fpcAYxwCAdQQkACQZJ7RnX7iy5njtsXh8yEllK5fV7R3bdcU8AgCLCEgASDIiFRFpXvuHfQoHGZERXAcJ4KURkAAAALCEgASAJCTkrq9m9fOUp+dTHqO2mLcDwH/CfSABIAGzdh/IUN30ua7nfc7aPkVaZc7mqhTmwWO4DySAf8MZSABIalI4K2PW7MqVK9cTj9f/JR4B4EUQkACQZFzQvK+7qlbNGuoxeILmzFmmv4LMewDg5RGQAJBkFFbn2bM1Z1gXlc3tr3md2snDw1OevcZq/wU/82YA+M8ISABIQuxd0uqtDz3k4dFLP53YpwVDOqmUsVONyheTu3sV/XzNfAQAWEdAAkCS5KAM2XMoV95iatF7qrbt2aGtXq+r/6dDdda8FQAsIiABIEkK1ZkNizVn1hR1/8pDzb/sqm+32OvbH3orv3krAFhEQAJAEhLm56sF33nK09NDHp4eWnoupd76qL0mz56t2bNnq2FB8xEAYB0BCQAJXGBgoIKDn3d3x4d2q02xN9V20jHlrPylFv7+pxZ+6yGPzz5QoWxpZWPe/gxBQUEKCQkxLwPAIwQkACRgxYsX1/nz59WuXTv5+PgoKur532TdeskV+V8/pG+bV1XuXLnk6mTe8WxhYWE6fvy4WrZsKTc3N2XJksW8BQAkvokGABK+y5cva/z48Tpz5ozq1Kmj//3vf8qcObN520vx9vbW/PnzdeDAAVWvXl1NmjRR+vTpzdsAQCIgASBxCAoK0oEDBzR16lQ5ODioU6dOKlmypGxtX+6NpPDwcG3evFlz5syRq6urWrdurWLFisne3t68FQAeISABIBG5ceOG5s+frx9//FGtW7dW69atzVss6du3rzZt2qRevXqpevXqSpMmjXkLADyBgASARGjr1q1q06aN3nnnHfXs2VN58+Z94bOGYWFh2rdvnwYNGqQUKVJo+PDheuONN8zbAOCZCEgASKR8fX01bdo0HThwQA0aNFC9evXk6upq3hbDxYsXtWzZMm3cuFF16tRRy5Yt5eRk4ZM2AEBAAkDiFhYWps2bN2vp0qWKiIhQ586dVbZsWfM2SdK6des0adIkFShQQI0aNVLp0qVlZ2dn3gYA/4qABIBELjo6Wvfv39e0adM0bdo09e7dW02aNJGLi4sk6ebNm/r++++1atUq9ezZU/Xr11fKlCllY/Oid4YEgJgISABIQvbs2aN+/fopZ86cGjBggAICAtSrVy85Oztr/Pjxypo1q/kQALCMgASAJOby5csaN26cDh8+LEmqW7euGjdurAwZMpi3AsB/QkACQBIUFBSk+vXr6+2331a/fv1e+BPaAPAiXu4OtACABCllypRKmTKlMmbMSDwCiHUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEhvDMAzzIoDkITg4WEuXLjUvI4mYOHGiChUqpGrVqplHSORSpEihxo0bm5eBeENAAsnYtWvXlDt3brVs2VKpU6c2j5HIXb16VVFRUeZlJHLe3t46e/asbt++LRsbG/MYiBcEJJCMXbt2TYULF9axY8eUKVMm8xiJXHh4uHkJScCPP/6oQYMG6fLlywQkXhkCEkjGrl27piJFiujMmTMEJJBIzJ07V998840uXbpEQOKV4UM0AAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEASETs7e3l4OBgXgbiFQEJAEAiki1bNhUrVsy8DMQrAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISADPZBiGeQlAPImMjDQvAQkGAQngmY4cOaKdO3fqxo0bxCQQDyIjI3Xx4kX9+uuvunTpknkMJBgEJIBn2rhxo8aPH6+vvvpKAwcO1LZt2xQREWHeBuAl3b59W6tXr1bv3r3VuXNntWnTRtHR0eZtQIJBQAJ4rlSpUql3794KDQ1Vhw4dVLx4cQ0aNEh//fUXZyWBl7Rv3z516dJFFSpU0PDhw5UhQwZ17dpV6dOnN28FEhQCEsBz2draqmzZsho2bJiOHz+uESNG6M8//9T777+vjz76SNOnT5e3t7fu37+vqKgo8+EAHoiIiNDdu3d19OhRjRw5UpUqVVKzZs0UFRWlWbNmadeuXerVq5eKFy8uBwcH8+FAgmJjcAoBSLauXbumggULas2aNUqTJo15rNmzZyswMFAzZ840j3T58mVt375dO3bskK+vr7JkyaISJUooe/bsypQpkwoWLKh06dKZDwOSlZs3b+rUqVO6d++eTp8+rTNnzsjf31+5c+dWxYoV9c477yhjxowxjvH399cHH3ygr7/+WtmzZ48xk6RDhw5p2bJlWrNmjWxsbMxjIF4QkEAydu3aNdWoUUNFixY1jyRJp06dUqlSpTR79mzz6JGoqCjt2rVL06dP16pVq5QyZUrlzZtXgwYNUuXKlbVnzx75+Pjo888/Nx8KJDkBAQFatmyZ3nnnHeXLl0+LFy9W//79dfv2bdnZ2alx48Zq1qyZihUrJnt7e/Ph0mMBmS5dOqVOndo8VmBgoFxdXTV37lwCEq8MAQkkY1FRUbp165Z5+ZGJEyfK19f3qWcgIyIitGPHDi1atEhbt25Vnjx5VLt2bVWrVk2DBg1Sq1atVK1aNc2dO1e7d+/W9OnTzU8BJDm3bt1Snz591Lx5c5UvX15Lly7VunXr1KpVK23ZskUbN25UQECAatWqpUaNGqlkyZLmp3gUkKNGjVKuXLnMY0mSg4MD10nileIaSCAZs7OzU5YsWZ75SJUq1aO9YWFhun37to4dO6YBAwbo7bffVseOHZU5c2YtX75cmzZtUseOHZU9e3Y5OTnFODPCp0mRXNnZ2SlVqlQqXbq0+vfvry1btmjChAny9/fXRx99pGrVqun777/X6dOnde/evRh3OXBzc3vi9+TDB/GIV42ABPBcERERWrNmjYYNG6YuXbqoZ8+eioyM1NChQ3Xo0CENHjxYxYoVMx8G4CkcHR1VqVIlTZkyRYcPH1bbtm114cIF9e7dW926ddPYsWO1c+dOBQUFmQ8FEhQCEsBzHT16VCtWrFDatGnVsmVLzZw5U4MHD1aNGjXk5ORk3g7gBbm5ualBgwYaNWqURo8erYYNGyo8PFxLlizR1atXzduBBIWABPBMpUqV0po1azRy5Ei1b99elStXVrZs2czbALwEBwcH5cmTRzVr1lTPnj01duxYrV+/Xra2/IhGwsV/nQCeqUaNGsqePbtcXV2f+YlRALHHwcFBGTJkUOnSpZU7d27zGEgwCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAkMdevX9fq1au1aNEi7du3zzwGgJdGQAJAEjJkyBDVrFlTzZo101dffaV69eqpUaNGOnXqlHkrAPxnBCSQaJ1W31J59eaQg/IPe3zd0L2Tq9X47cLqvvpPGUa0zv02W5+Uy6Zc7u7KU2mQTvmHP34AkoDQ0FC1adNGffv21dGjR+Xn5yd/f3/5+vpqyZIlatq0qS5evGg+DInc5QOL9EHVBvrlwDXTJEw3532s14tV1bKzjy0bkbqyaYRKVu4u78eWAasISCCRczo8SmM333n0ayMyRAf2/KLTt9PJQVJ44J/6YcyPqjlsj/7y9ta8WhvUb+hmBcZ4FiR2y5Yt0/z5883Ljxw+fFjdu3c3LyMJcI06qZFTtsT8PX1zrZovi1DBx9ckBfke0fQflurstQjTBLDGxjAMw7wIIDE4rb6l6uhi+6l64/wNtRrcWG6SwgL2aXKHSdoXEKLcHt/J631nHdx2Xfkqv6UMDpL/oWFq0t9fQ38aoiIu5ud8ef7+/urUqZOaNm2qqlWrau7cuVq3bp2GDBli3vpKREVFmZcSvejoaHl5eWnJkiWKjo42jx9JkyaNfvvtN+XKlcs8kiTdvXv3uccnZKGhoUqVKpVsbGzMo3h19+5djRo1Su3bt1eFChX0yy+/aOvWrRo5cqScnZ3N21/a5QOL1GvcJuUwXFR6zCR9nFmSgrV/aBfNKltJAV1mqMHPW1Q/vyTD0JKhTbXtxH39drGgNu4eKXfzEwIviIAEEq2/A9K3/0FVO/217GuP0qdFU+v+unZq9XtZFbuyRiH1v9OQOo+fg7iuce+V0bb3FmpJz0pyiIP3IMwBOWfOHHXs2FGpUqUyb4136dOn1507/5ytTUr8/PwUGhpqXn5CxowZ5ejoaF6WJEVGRpqXEo38+fPr/PnzrzyAo6OjFRUVpV9++UUVK1aMp4Dcp6+qO+qjQzV1d3wVye+Avmrzq9rPK66hpUc/CsjoXzupwshUGtzDTV8Pv6bFmwlI/HcEJJBo/R2QNwedUtOAyToU8rbaNC+hZZ/nkV+H7fL/obf8HwvIcyu81PLb2boR4q6+Py3WJ4UzyTkOvt7aHJBz587Vjh07NG3aNPPWVyIpfqf37du31axZM61fv948esKqVatUp04d83KiFxUVpYTw4+zWrVv65ptv5OnpqfLly8dbQHYa+qlmFB6uz28vV+E9E9TtaF7N62SoSfG/A/LDzOv1afYWqr7NV58HT1DNby8RkHgpcXD+AUC8skmhvO65FHLnuG6dX6aZZ9rqg+LmTVK+uv2144i3Dq9tq5U9mmvNyfi7CtLGxkb29vYJ4pEUubm5qXjx4kqRIoV5FEPmzJlVoUIF83KSYGdn98S/61f1sLV9FT9aS6p9l+P6adVf2nckSBWL5Hhs5q+1fQZqVbHPlPLYHC1av0+3fU/ol7kr5BP82DbAglfxXzmAWGWjTO7uigoM0KZpk2X3lacev8ItxGeNxoxaoIsPflA4ZK2mwpnvy/fG7cd2IbFr2LCh3N2ffT7J0dFRHTp0ULp06cwjJBHFvuiiu9OnaEuYjQrkej3GLE+d3prVqliMNeBlEJBAEmDvlknZ/ffLa7KT2jZ9LcbMJnUGHVkwTos3nFCEpODN32jTraJ6s0TMHzBI3IoXL67BgwcrR47Hzzz9zdnZWZ999pnatGljHiEpyV5Drufn6cytVMr5WtrHBmlUvOZH8vDwkIeHhz6r9Zbcsr6hBs3rKlccfJAOyQMBCSRaKZTutWzK4CzJJqvKVyyiXK2bqq6zJBt7pXXLJFeXFHLK8Ja8JnXRoUF1lN/dXW/3u6ZBc6epXKak+XZucmVra6uPP/5Y+/btU8uWLVWgQAE5ODioZs2amjx5siZPnqz06dObD0MiZ++YShnd0svRzkZSftVqWUPlSr6jLCltJLko42uZ5WK6ssHWKY2yZnYVrwB4GXyIBkCsetqHaHbu3KkZM2aYtyIObdu2Te3bt9eJEyfMI8ShW7duqU+fPmrevHm8fIgGeFU4AwkASdCrvh8igKSNgAQAAIAlBCQAAAAsISABAABgCQEJAAAASwhIAAAAWEJAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlfJUhkIxdu3ZNr7/+utzc3GRnZ2ce/2fZs2fX2LFj9e6772rVqlW6e/euPDw8zNsQh7Zv36527drxVYbx7M6dO5o/f75q1aqlAgUKaO3atWrVqpV520sJDw+Xu7u7Dhw4wDcO4ZUhIIFk7Nq1aypYsKDWrFmjTJkymcf/maOjo7JmzSonJyfzCPGEgEwYQkJCdPHiRfPySzlw4ICWLFmi1atXE5B4ZQhIIBm7du2aihQpojNnzsRqQOLVIyCTrm3btmncuHFatmwZAYlXhmsgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISCAZCw4OlpOTk2xteSkAEouIiAgZhmFeBuIVPzWAZMzPz0+GYSg4ONg8ApBA3bx5Uz4+PuZlIF4RkEAyFxISorCwMPMygAQqPDxcd+/eNS8D8YqABAAAgCUEJAAAACyxMbgSFy9gxIgR5qVXxtnZWR06dDAv4z84ePCgqlWrpoMHDypfvnzmMRKx7du3q127djpx4oR5hERu9uzZGjBggC5evCgbGxvzGIgXnIHEv5o0aZIWLVqke/fuvfKHr6+vtmzZYv5bBAAA8YgzkHiugwcP6r333tPKlStVqVIl8zjeTZkyRZs2bdLy5cvNI/wHnIFMujgDmXRxBhIJAWcg8Uw3btzQmDFj9O233yaIeAQAAAkDAYmnCg0N1bx582Rvb68vvvjCPAYAAMkYAYmn8vb21u7du9W2bVulTJnSPAYAAMkYAYknhIWFacSIESpbtqxKly5tHgMAgGSOgMQTZs2apevXr6tJkyZKkSKFeQwAAJI5AhIx/P777xo3bpz69eunHDlymMcAAAAEJP5x48YNDRgwQF9++aXefvtt8xgAAEAiIPFQZGSkVqxYoaxZs6pTp06yteU/jeQgPDxchmEoKChIUVFR5jGABCQoKEjnzp3T0aNHFRgYqLVr18rX11dhYWHmrUCcoxIgSTp06JDWrVunL774QnZ2dpKkgMvHtHrpRl2OfvJe8zeOb9LK7X/IL/TvX9/x3qvFc+ZozoPHTu+Qx3Zf0Y4VG3Tx/mNLitKtM/u18YC35H9SPz927OOPTcevP34QYomfn582b96smTNnKjQ0VIMHD9asWbN08uRJ81YACcCZM2fUvXt3lStXTuPGjZOfn5/q1KmjChUqaNKkSfLz8zMfAsQpAhKSpBkzZqhcuXIqVarUP4tB5zVrcC9NPfzkmakNE3toweZzCpd0e8M36th3qA48ar3bmv9NDy387ZwiJEnHNe2boToYowUj9NeWRRqxeO/ji7qwZrha/HAsxhpiV1hYmIYOHSpPT0/NnTtXERER+vnnn9W1a1d5eHho7dq15kMAvEJ//fWXvv76a02dOlV37tyRJD38Ernz589ryJAhGj9+vOkoIG4RkNCkSZPk4+Ojli1bysXF5dF66vzlVb+UsyZ2maGbMY7YpnXb7PV2lcrKeH+dPm26XO92myavbh7y8PCQh0dndWr2hhaPH6X95+7FOPKp0hTRxx5/H/tBqSxS3vcePI+HahTNYt6NlzRixAiNHz9eV65cifG2dWBgoA4ePKiOHTtyJhJIICIiIrRw4UKtW7fOPHrkzp07Gj58uPbv328eAXGGgEzmDh48qL59++qbb75RxowZYw5tM6ppx4ay3TtGa/56GBpROjnwK53O+anqV3XTnh9Ga/f/xqht6SxK+eiOP/Yq8lY1vesaqN/+uqIn3wB/OYZhKDIyMsk+4lJISMijt62f5fLly/r555/NywBeAV9fX+3du/dfr1EODw/XpEmTzMtAnLExHp4HR7Jz69YtderUSaVLl1a3bt3M4wcuaGy5stpU/Wct8aqsNMFH1bZwVUUM268fGmXQT60+1Pg3l2ln68ym4+5quVcn/WznqQV9wtSk6DDV/2W7GhR4OA/V71N665u/yuq30Z89OmrvkCp650wPRc/94NHa46ZMmaLp06ercePG5lGS4ebmZl56afb29nJyctLRo0c1aNAg8zgGGxsbffLJJ5o3b54cHR3NYyQS27dvV7t27fTtt9+aR0hErly5olGjRunq1avm0ROyZ8+uS5cumZeBOEFAJmO3bt1Sly5dVKxYMfXs2dM8fiRwaztV6HpLo9YtVZkz/ZWn+Q39enqaSrr4EZBxJDw8XP7+/ubl/+xhQO7evVsbNmwwj59Qo0YNLVmyROnSpTOPkEicO3fuX882I+G7e/eu1q5dq7t375pHT8iRI4cuXrxoXgbiBAGZzB05ckSVK1fWsmXLVLVqVfP4b5H+Gtj4Xdk0Hi/X2b3020fr9YtHetkpWrsHVVc1754Knfl+zGP8zmlY528V/mkv9avlqH61vlDJ758MyBkhtTWza7VHh71IQG7cuDHJv8VqGMa/vmX1X+zdu1dVqlQxL8dga2urFi1aaNq0adzOKRGLiopSaGjoow9bIHG6evWqOnXqpF9//VXR0dHm8SP29vZq0qSJZs+ebR4BccNAsjdt2jSjcuXKhq+vr3n0yN7xrYyG//vIKFyqufFrRNQ/g+trjapuhY1JB32NwPCHixHGiQ1TjNr/+9LYdfauYRiGMa3928aQ9T5G+INDI+6fM0a2aWz0W3r40VMZhmH8PriyYdNsbYy1x02ePNmoW7eueRkWVKpUyZD0zEeqVKmMGTNmmA8D8ApEREQY3333nZEiRYonfq8+/nBxcTEOHTpkPhyIM5xegL788ksVLFhQM2fOVHBwsHksScr73seyPbdNUW9WVBVbm38GmT/Qkvn1tG9KNw0b/fD+jd9r3LwTatSxu8rmc5UkVapeU4eXDtfYyX/vmTB5lo7aFdfn5fP981yIF3369JG7u7tsbB779/hAypQp9emnnyb5SwSAxMLe3l6fffaZateubR494ubmpr59+8a8DRsQx+y+5QprSMqcObN+/PFH5ciRQ7ly5TKP5ZjSVbmKlNL/ar+n7BlS6vH0cMlbVVXfKqCI69f09+3DXVS5RRfVLpVZf9+SXHLLW1bFsqVUwP0ARUtyzphX9Rt+rDdeT/3YM0mOabOoSJGiKpEzTYz1hw4ePKjz58+rUaNG5hFeUK5cuZQ3b14FBATo1q1bCg0NlYODg/Lnz68ePXqobdu2Sp8+vfkwAK9I+vTpH8XhpUuXFBgY+OgPgAULFlS3bt30xRdfyMnJyXQkEHe4BhLSg+ulZs2ape3bt2vOnDmyt7c3b0kQpkyZok2bNmn58uXmESyIioqSv7+/zp07p2rVqmn16tUqWLCgMmTIoBQpHt2PCUACEhwcrOvXr2vp0qVasWKFBg8erCJFiih9+vRycHAwbwfiFG9hQ5JkZ2enevXq6fbt2xo9enScfIADCYednZ1cXV2VJUsW2dvbq1ixYsqSJQvxCCRgLi4uyp07t8qUKaNcuXKpatWqypIlC/GIV4KAxCNubm7y8vLSzJkztXv3bvMYAABAIiBhVrZsWX399dcaNGiQfHx8zGMAAAACEk9q2rSpcuTIoblz5yo8PNw8BgAAyRwBiSc4ODioe/fu+uOPP3To0CHzGAAAJHMEJJ7K3d1dFStW1MSJExUQEGAeAwCAZIyAxFM5OjqqSZMm0oNb5wAAADxEQOKZMmXKpB49emjw4MHasmWLeQwAAJIpbiSOfzV9+nRNmDBB1atXN4/inWEY8vHx4UbiseTSpUsqXry4zp8/z7fPAInE5s2bNWPGDC1atOipX0kKxAcCEi9k3Lhx5qVXxtbWVh06dDAv4z8gIIHEh4BEQkBAAskYAQkkPgQkEgKugQQAAIAlBCQAAAAsISABAABgCQEJAAAASwhIAAAAWEJAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlBCQAAAAsISABAABgCQEJAAAASwhIAAAAWEJAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlBCSQzNna8jIAALCGnxxAMmZraysHBwcZhmEeAUigbG1tFR0dbV4G4hUBCSRjtra2Cg0NVVRUlHkEIIGytbVVRESEeRmIVwQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEtsDMMwzIsAEh5vb2+dPHlSkZGR5tF/FhwcrHbt2unMmTPKlCmTeQwgAdq2bZu8vLzUoUMH2djYmMf/mY2NjSpXrqy0adOaR8ATCEggAWvdurWaNWumd999VzNnztTkyZNjPfSio6O1bt062dnZmUcAEqDDhw9r6NChCgwMNI9eyv79+7V9+3a98cYbGjJkiBwdHdWtWzfzNkAiIJO3iIgI3bt3L1bPaL0MZ2dnpU2bVra2XFnx0LvvvisvLy9Vq1ZNkydP1oULFzRq1CjzNgB4aXZ2djp8+LCKFy+uHj16yMnJSd999515Gx6T0H6OOjk5ydXVNVbPTD8LAZmMnT59Wt27d5ezs3O8/Mf2PJGRkcqYMaP69u2rnDlzmsfJFgEJIL4QkNYYhqHffvtN06ZNM49eifDwcGXMmFETJkyQs7OzeRzrCMhk7PTp02rXrp3GjRsne3t78zheRUZGav78+UqfPr169eplHidbBCSA+EJAWvPwGvJSpUrpvffeM4/j3alTp7Rw4UItXLiQgETcehiQGzduVIoUKczjeHfkyBG1atVK33//vcqXL28eJ0sEJID4QkBaM2rUKO3bt0/Tpk1T+vTpzeN4t3v3bo0ePTreApKLzZBglChRQm3btlWbNm0UEBBgHgMAkCDs3btXI0eOVNeuXRNEPL4KBCQSlObNm6to0aLq378/EQkASHCuXbumwYMHa8CAASpXrpx5nGwQkEhQ7OzsNGzYMJ07d05bt241jwEAeGWCg4M1depUubm5ydPT0zxOVghIJDg5cuRQ27ZttWDBAl26dMk8BgDglTh27JiOHj366A4myRkBiQSpXLlySps2rebOnWseAQAQ7+7fv6+JEyeqevXqKlCggHmc7BCQSJBcXV3Vrl077dq1SwcPHjSPAQCIV7Nnz1ZYWJjq16//ym99lxAQkEiwihcvroYNG8rT01M+Pj7mMQAA8WLHjh2aOXOmevbsqddee808TpYISCRYNjY2atKkiUqWLKlx48aZx4hFId671P2Tppp/xtc8UtC91WpY/hPN23U1xvrZX/rrswk7YqwBeL6w6yc0uPlnmrT7rJ68CfM2NS3zvsZu8P77lxdnqLy7u9zd3eVe4RtdevIAxANfX19169ZNbdq0UenSpc3jZIuAxFPdv3FZt+4FKco8CLuviz4+8guO0P3bt3Q/NEKSFHTniq7d8lPE4wdEBOv6tVsKkRQVHqQbl6892v9Q2P3r8rkdEmPtcY6Ojvrhhx907NgxrVixQtHR0eYtiAXRkSG6rRMa8dlo/RnjK11D9fOXA/RHCl/5h/49iI4K0rFV09S+92AduBT6+GYA/8KIDNfd6NOa0H6Cjoc+/noWpp8/761DzlflFxyhqAtzVaDA12rws7e8vb21vOEJ5Sv0rbyfeFFGXIqIiNC8efNUtGhRtW3bVra2j2eToZCA27p6008RUea6j5LftYu6cTdIwfdvy/deiCRDYUH3dPXyTYVEPv7vPlqBN6/KNyBSigrTnRtX5ePjE/NxM+Hd1o6AxFOtGdRAXwxYoGumPri9vp/ylmmo9ccvaEKXzpr84E/Ra/uVV9mPe2nvpaB/NntvVONanbUl2pDv8TVqXuYtdV607/Gn0x+TPpN71+ffrsfR0VEtWrTQ4sWLdeXKFfMYsaakPCue1g97Ah+tBP81R8si2+u97P/s8t03TN2HbFS67EWUyuGfdQAvqrAavnVVU3b/EwUhPks0O7Sr6rj//evA25F6t8dCeT444VWifmPlPbtU+68/OgRxzDAM7dixQ7t27VKnTp3MYyk6UifXj1MDz/46dj045izosLq9W0x9Fx7R1knd9PGoHZIidHT9KFUt9oGm7bn22OZ7+vnLKvpk3jXpxh/q3+ITfd6lpwYOHPjg0Vl1Pu2l9X8+9vM1ASAg8VRvf9xMIX9s07Hzdx5bvak1y/Yrc4Oh+ijfY8uS5Ogg/z+3adIvv+tZX46ZKs19rfp+ktbfeMaG56hdu7ayZ8+uRYsWmUeIRYUb1JSP11xdlCQFaveENcr5RTU9/j0LTqnfUp/Z36t11RJK+dg6gBeX/8PKutRnhs5LkoJ1YNavytG2htwezNOWbalZ39VUuge/vn/umK4pp9xcHz0F4lhoaKgWLFigmjVrqmDBguaxZJtCRUpXU5HoffpxX8zLf+7tmq/198upwWclZL7ZT1r9pXF95+iUaf0f7mo2eKxmz5794DFHY0oeUYdhW+Rv3voK8V3Yydjzvgs76uYJDezQWfc/GqKxn5WVrY3kf3iuGjRZoqa7lquZfDSok5fsPPqoV7Ui+qlHJW24V1inj/0lzyUb9aW7rXR2uap98rO6/rFAxf9Yql7Df1U+p+Maf7WVTv/2hTJJ2jukisr92UPGvA9i/PWf5o8//lCrVq3Ur18/lSlTxjyOISwsTLdu3TIvvzBHR0fz0ivh6emp0aNHx/l3YQed+1Xt+ixSoyFeujPgHQX0uqSvXt+lz5tvU695X2lJp7rK2myh2r+X8+ER+m1wG/UPbqLdg2uYng3As4ReOay+vUepZLuBchz9pnx6+alH3kNq126jvprZWxu+KKrAuss0sH7+fw7y3qhPm3+lsGb7tKRVJsXVq9Pj34XduXNnBQUFqW3btuZt8S4wMFC2trays7Mzj15Y5syZY/ycu3HjhiIiYl5SZbZhwwYdP35c06dPV4YMGczjv0X5ac13bdT+/Hvymd/yweI5DS5fW8tr/qQ93+TRriFt1S/oc+0eXEX7fx6o0avClOPCBq2rOlUnvcpLuqM5dctpRvXN2lXvmtp9MUFFR47QV4WzPfrLXJlVWyXnfaiT29oo06PVmOL7u7AJyGTseQEp3dfGMT004Egp/Tq7tVLbhmjH5L7quCe7Ds7vInu/s6aArKqdOb/T53Zj9f64Atp7yEuFr66KGZBj96jjkEb6sWwN+Xrt07xWhfXHsBcPyLCwMPXs2VNr165VVFTcXgjk4uKioKBX/3bBzZs3tXr16ngMyBEqe3aA2p76XCOLbFGnc5W0uEVhDWxHQAKx4Z+AHKrGwcNUfcsnmlf9tPoczqFZnetodPM3YgRk6MU96teri7bl6K39w+vKxvyEscgckHPmzFG6dA/Pgb46OXPm1N27dxUY+M/lNfHBxsZGffv2lYeHx3Pj9d6e7/Vew83qdWqFPkljr4jDP6jk5wv09a/b1TR7kLaYAnLc5qwa3iWLmlXvoOpzzqhH1QgtiBGQo/Ral+5qnDfL33+BO7vV2nOo0vbYpKVNn/0J8PgOSBlItk6dOmVUqVLFCA8PN48MwzCMoJNLjQ+KVjS+vxBhRPr9ZQxv09gYtPyIEW0YhnHnT+O7Jp8bQzafMKINw1jSvYrRfsIuI9g4b0x7L4/x/jerjRvHlhlVi31urImKNi4fXGw0btzR2HcxxDD+mGrUqvy+seDgDWPX4MqGmq41/6WfEBkZaSxevNioUqWKcfXqVfM41kVGRiaIR7ly5YzNmzcbhmEYkyZNMrp162b+W40VgWc3Gc0/9jTWn71lBN3bbXzXpJXRp1t/Y/bvPkaY/y2jT9N3jQm/+jx+hLF5UFPjnT4bH1sD8G9CLh8yujb5zJj/u48RbRw1BtZpZPQbOMyYtv6oEW0YxoimRYz+v5z5e/Ol7cbAVlWNOn0XGOdvP/11OjbZ2toaR44cMQzDMLp162b06dPnidekV/WIjo42/+3GuU2bNhk1atQwjh49ah6ZXDUmfpDPKNRllxFiBBvbx3gaZbv8/GAWaPw2uJnxTp8NhmGEGft+6mN83nqCcdnfMM4tbmPUqt3F2HXNx5j9UT7j3YkXDePq70bbD8oZ5ep+Ynh4eDx69Fh8wvTXfNKuXbuMevXqGcHBweZRnOAaSDyTS57KalQpQhNnHdN1n7066+emCsXy/sufgHPry1mzlO3AYC387ZzCzWNJKtFanRtm07LJP8r77vPfQnjo8uXLWrlypdq3bx8v9+Cys7NLEA8bm+f/044LDg6F5Oa4RT9fcFCJ7A+vyAIQ+4op7+t7tfign/Llyh7ztfXeAfVt0UJ/Fuih8T0aK3cG87tEccvGxubR28YJ4fEqXgsrV66sypUra/78+fL3f97Vh6/p4y71FLF8ivaeOal1f4Sp2f/eNG96Qq6P+qlOfh/NnrtF92J8YNV8DeRsjfi0yOMbEgQCEs/mmFENG1TXxQlTtXLbZgXleFMFcr7AxyayV5Tnx4W0YPwUnXj8U9mPeaemh7Jcnq8R60+aR081Y8YMpUuXTu+99555hFhm7+ikHIUrq3SBbHotg4t5DCAW5Sn7gd7ImlG5Xo/5VvGRBYM1+rfzWj+mjaqUeHAvSHd3TTgUYxviUIoUKfTxxx/r+PHj2rx5s3kcQ+bKX6phmvWaPGu9LtjlVpVCz7pS8R/2Tln0Qd26ur54qOZcfPxT2YkDAYnncqzcXhOKLlCn4Uf0RoV3lPnZl4HE8OaHnVWruBR0/+nXrKRyL6uWTWop6trT5487cOCANm3apC+//FJp0qQxjxELUuarrjk/zVLNfG6SnbM+6DpTPw71UCYnGzmkdtPgebseu/5RklKqWt95XP8IWOT0eimNnv+jmrydUzaS3vKYpGXTuso91d9n2HrMO6GB9fOrRIcVCo02dO/a3/eBfPjowH2s41W+fPn0xRdfaOLEibp//755/A/7POr+bW2tHjNO9vmKK4urk3nHU9goZ4Wa8qhVUlf/evb9kBOqRBWQ4eHhun///it5xPfFuwlHRjXo202dv/xKDSs9uEGZJDmmVfEKFVU0699/as5V5n29VSiTHn47qGPWQvLsOERdvqyh122klBncVaFCWbmlfFigjirVwENdunaRR4XXHz2t2aVLl/TVV1+pbdu2KlGihHkMAECc+vDDD1WgQAG1b9/+uW9lu9Rop/4du6jFR5WV/tE9cu2VpVh5fVDqNUm2cstVShXLFVLKR1ckZNLH7TqodcfWqlUwpeSSUW+/V1EF0iX8d38S1aew9+/fr8mTJz/3X2Bcef/999W6dWvzcqL2/E9hv3pBQUHq1q2bAgMDNW3aNKVM+QJvnycx7777rry8vOL8U9gAYPfYp7B79OghJycnfffdd+ZtydLly5fVuHFjeXp6ytPT0zxOEOL7U9iJKiBXr16t5cuXq2XLlnJyepHTw7Fj3rx5kpTkvo85oQfkihUrNH36dC1evDjZvnVNQAKILwTk8x04cEA9evTQuHHjVLx4cfP4lYvvgExUb2Hrwf35SpQoodKlS7/go4TyZ8uo3EXM6y/+cHXl1v/x7fz585oxY4Y8PT2TbTwCABKOEiVK6N1339XMmTMVEJDwvps6viW6gLTuksbXKK5he83rSMiWLFmi3Llz86lrAECCkCJFCrVo0UK3bt3Stm3bzONkJxkEZBoVL5tVh3/dpeT6MZjE5rffftOvv/6qli1bcvYXAJBg5MqVS3Xr1tWIESN08eJF8zhZSQYB6aud229p+/f1VPjBfbT+fhRXq29/0X//tmTEhRs3bqhZs2Zq2rSp3njjDfMYAIBXxs7OTnXr1lWpUqXUp08f8zhZSQYBWUjf7DykG3du6dJj99Ly9j6qGd82UEbzdrwyYWFhGjZsmKpUqaIWLVrI7jnfPYqYAgMDtX79ep08eVJ37txRWFiYeQuAZMgwDIWEhOjmzZvasWOHFi5caN4CixwdHTVw4EAdPXpUs2bNUlRUlHlLspAMApJrIBODqKgorVy5UkePHtWIESPMY7yApUuX6ssvv1SXLl00bNgwrV+/XufPnzdvA5AMBAcH6+TJk/rll1/k5eWlTp06qX379jp58sW+/QvPly5dOo0ZM0ZLly5Ntv9ME91tfDZu3Kjhw4dbuCfgHa1pVUHjMk/X8sHllco8fgHffvut7t27l2Rv49OoUaNXfrYvPDxcu3btUv369dWgQQPzONl60dv4BAYGqkOHDqpZs6YyZsyoI0eO6OTJk/L19VWePHlUqVIlVapUSRkzcs4dSMouXLig7du3a/v27bp3756yZcumokWLqnjx4lq9erVsbGw0ZMgQ82ESt/GxLCIiQiNHjtTly5dVpkwZ8zjeXblyRUePHo232/gkg4A8oa/zVdHYa1KWTKn0TyalUbXm/TX0Bd7GTsoBOXXq1ARxOwLDMFSyZEk1b95cadOmNY+TLasB2aRJE1WrVk2RkZEKCAjQ3bt3tXr1aq1du1Y+Pj6qWrWqGjVqpCpVqpifAkAidf/+fW3evFmLFy/W4cOHVapUKdWpU0fVqlVT6tSplTJlStnZ2al3794EZCw7e/asZsyYodu3b5tH8c4wDBUsWFAdO3YkIM3+W0C+vKQakEj4qlWrpj59+jwKyKNHj6p3797mbQoODtaAAQP01VdfqVq1auaxoqOjdfz4cS1evFg//fSTXF1dVbt2bTVo0ECpU6dWpkyZ4uUFB8DLMQxDAQEB8vPzk4+Pj5YvX64tW7YoZcqUqlevnurXr688efKYD5Mk9e7dWwEBAerevbt5JEnKkyfPo4Ds2bOnHB0dCUg8UzIIyDv6ffFGnQkNN607KHuhsnrnrbz6tx+bBCRelZ9//lmFCxdW4cKFtWLFCs2ZM+epH5CJjIzU6dOnNXfu3KcGpB5cZ3r16lUdO3ZMP/74o44ePSo9+IquLVu2JIi3YAA8X0REhJYsWaJvvvlGLi4ucnR0VPny5fXRRx+pWLFiypQpk/mQR3r16qVly5Y9MzBtbGw0depU5ciRQytWrFB0dLTq169v3gZIyTcgT2l27x36ZP4CtXyPgETiEBAQoKCgIPOy9OB7w/v27asvvvjiiYAMDAzU9u3btXXrVp05c0bp06dX3rx5VaJECdnb2+vzzz/XmTNnnvuDB0DCsW3bNg0dOlReXl66ePGijh49qgsXLsjf31/lypVTpUqVVKZMmSfeVejdu7eCg4Of+i7GQ1myZDEvAU+VDALyaUK0e3Rb/ejwhUZ1eIeARKL3tGsgjx49+ugt66xZs6p27dqqV6+eMmfOrNSpU8vBwUHXrl1TkSJFCEggEdm2bZvGjRunZcuWSZJCQ0MVGBiov/76S8uWLdPmzZuVIkUK1atXTw0aNFD+/PmlBwH5vGsgASuSwW18nsZZDoa/rl7w1qv/+AgQe65fv67Ro0erevXq+vzzzxUcHKz58+fr999/V9++fVW4cGFlyJBBDg4O5kMBJEI2NjZydnZWxowZVa5cOY0cOVI7duxQ3759dezYMdWsWVN169bVrFmznvkOBvBfJIOAvKPfF/+oOXPmxHjM2HBOpd8tJT7vi6QiY8aMmj59ui5fvqyOHTtq9+7dmjRpkipUqGDeCiAJS506tT766CMtWrRIW7duVf369bVr1y55e3ubtwL/WTIIyKer1u57tapdSI7mAZAIOTk5qWXLlpozZ46+//571atXT25ubuZtAJKZnDlzqlmzZpo8ebJGjBihjz/+2LwF+E+SQUBmULlGlXR77TwF5vtIHh61Zbd2rDZHFVZWJ/NeIHGyt7dXgQIF5O7ubh4BgJycnFSoUCGVKlXKPAL+k2QQkOFa6VFcC13aqfG7rpLc1PSnSYrs/76+XeerRPMJIgAAgAQiGQTkCa1fG6Eang3k+mitvLp0Kqkzvx/U/Rh7AQAA8G+SQUC6KntWW/neuhdj9d4t3xi/BgAAwItJBgGZQx371taOkV215+6DpcvzNXDxDVWuU51PYQMAAFiUDALSTqk/na9N7W3UuLS73N3d5V6xv2rMOKLWZZ1kY94OAACA50oGAfm3/M1mydvb+9Hj67cfTu5oz6JduhFzOwAAAJ4h2QTks13U3PYzdda8DAAAgKciIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAliSPgIwM0BUfH/nEeFzSzbuBilIpTbszWxXMxwAAAOCpkkFAhurPH7uq2v/aauDAgY89hmnRuqMKNG8HAADAcyWDgLyouV4/qUyv2Zo9+/HHZHVq8i5fZQgAAGBRMgjI1MrsZqvXX8tsHgAAAOA/SAYB6aam7T/UwtZt9OtTr4EEAACAFYkqINOkSSM/Pz/9+uuv8vPzM4+fwVdr9qVS9XdC9aPFayBDQkL0+++/6+zZs3rttdfMYwAAgGTJxjAMw7yYUN2/f1+bN2/W4sWL5eLioh49euiNN94wb4sV9+/f18SJE7V//37VrFlTderU0euvv27eBiRq165dU5EiRXTmzBllypTJPAaQAG3btk3jxo3TsmXLZGNjYx4D8SJRnYFMmzat6tWrpylTpihbtmxq1aqVfvrpJ0VGRpq3vpRjx47p888/1+HDh/Xtt9/qiy++IB4BAAAeSFQBKUm2trZyc3OTl5eX+vfvr2HDhql79+66detWjJC8fm6hOrccoKM3T6hvqaJyd3c3PYqr1be/6NaD/YZh6P79+5o2bZo++OADVaxYUfPnz1fJkiVlb2//6HkBAACSu0T1FvbT/Pnnnxo/frxu3LihunXr6n//+5/SprV+c57t27frxx9/VEhIiFq0aKHKlSubtwBJDm9hA4kPb2EjIUh0ZyDNChYsqNGjR6t58+ZauXKlBgwYoDNnzpi3PdP169c1YsQIDRkyRAULFtTw4cNVqVIl8zYgyUqbNq3SpEljXgaQQEVGRsrR0dG8DMSrRB+QkuTs7KzatWtr7NixSp06tWrUqKFFixaZtz3h6NGjatSokQ4ePKhx48apbdu2ypo1K3+iQ7Jx+/Zt+fv768qVK+YRgATK3t5e4eHh5mUgXiWJgNSDayOzZ8+u7777ThMnTtSIESPUrVs3nT9/XuZ36e/fv68ffvhBX331lWrWrKkFCxaoYMGC/IkOyU5UVJSioqIUERFhHgFIwMw/14D4lmQC8nF16tTRihUrZBiGunfvrpUrV8rPz+/RfR07d+6sNWvWPPoAjoODg/kpAAAA8AxJMiAlKWfOnBowYICaNm2qhQsXqmPHjhozZoyGDx+ut956S5MmTVKlSpX4hDUAAIBFSTYg9eDDAXXr1tXUqVPl4uKirVu3qk+fPmrVqhX3dQQAAPiPknRA6sG1kRkyZND//vc/5ciRQ8WKFeOsIwAAwEtI8gEJAACA2EVAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlBCQAAAAsISABAABgCQEJAAAASwhIAAAAWEJAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlBCQAAAAsISABAABgiY1hGIZ5MSlat26dfv75Z02ePFlOTk7mMZAs/fHHH6pUqZI2bNig/Pnzm8dIAni9S3p27typqVOnasWKFbKxsTGPgXhBQALJ2B9//KGyZcvqjTfeUNq0ac1jJGKGYcjGxkYlSpQwj5DIXb58WYGBgdqwYQMBiVeGgASSsTt37mjt2rXmZSQBp0+f1oIFCzR27FjzCElA+vTpVbVqVfMyEG8ISABIgrZv36527drpxIkT5hEAvDQ+RAMAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACxJVgEZGBiokJAQ8zIAAAAsSNIBGRYWpl9//VW1atXSp59+qp9++kn58uVTnTp1dO7cOYWHh5sPAQAAwL9IsgEZHh6uRYsWqUWLFtqwYYMCAwMlSXfu3NGaNWtUp04drV+/XtHR0eZDAQAA8BxJNiBv3bqlKVOm6MqVK+aRJOncuXOaPHmyvL29zSMAAAA8R5INyJ07d+rw4cPm5Ueio6N14MABnT9/3jwCAADAc9gYhmGYF5OCrl27auzYseblJ6RLl06pUqUyL8MkXbp0WrVqldzd3c0jAAnQ9u3b1a5dO504ccI8AoCXlmQDsm/fvhoyZIh5OQY7OzuNHTtWn376qWxtk+zJ2FhRpUoVAhJIRAhIAHEpyQbk1q1bVbduXfn7+5tHj2TOnFmLFi1SlSpVzCOYFC1alIAEEhECEkBcSrKn3QoVKqRSpUqZlx+xsbFRmTJllC9fPvMIAAAAz5FkAzJLlizy8vJSsWLF5ODgEGPm4OCgggULqlu3bnr99ddjzAAAAPB8STYgJalChQqaNm2aevTooYIFC8rV1VUVK1bU119/rQULFvDWNQAAwH+QpANSkt5++20NGjRIrVq10ptvvqmJEyfq22+/fe7b2wAAAHi2JB+QD2XIkEFp0qRRtmzZ+MQ1AADAS6CkAAAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMCSZBOQzs7Oypgxo2xsbMwjAEhy7O3tVbp0afMyAMSKZBOQrq6uCgoKkqOjo3kEAElOZGSkDh06ZF4GgFiRbAISAAAAsYOABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCXJLiCjoqIUGRkZqw8AeFm8NgFITGwMwzDMi0nRpk2b5OXlpbp165pHLyVr1qxq2LChDMPQpUuXlDlzZqVOndq8LdErWrSoVq1aJXd3d/MIgEXh4eG6cuWKMmfOrJQpU8rb21sbN25UYGCgeet/dubMGW3btk3nzp0zjwDgpSWbM5AFCxaM9Xi8cOGCVq5cqcjISAUHB2vevHk6duyYeRsAxBAcHKxx48bpypUrkqT9+/dr8eLFCg8PN2/9zwoUKKCuXbualwEgViSbM5CGYSgqKsq8/FI2b96sJUuWaPLkyQoLC1Pfvn1Vu3Zt1apVy7w10eMMJBB7/Pz81KxZMw0ePFhFixbVkiVL9Pvvv8vLy0suLi7m7S/F3t7evAQALy3ZnIG0sbGRvb19rD5sbZPNPz4AcczOzu6J15jYeABAXKCAAAAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEg8VyXLl3SmjVrdO/ePS1fvlybNm2Sr6+veRsAAEhGCEg8VXh4uFavXq127drJ09NTV69eVc+ePdWiRQt5enpq8+bN5kMAAEAykfgC8vgclSlWUz9fMw8idGr1eH3wfjcdvx8qSbq6a54+Ke8ud3d3lX9/mM6ZD8Ez7dmzR+3atdP69et1+/ZtSVJUVJSuXr2qjRs3qkuXLtqyZYv5MAAJym61KVNZ43bdMw90Z/MglSpS/cFraaB+G+ypInn/fr10d3dXuS7zdT802nwYAEiJMiDD/XX50m3N7NpFh8P/WQ676a3fls/QvpthCo82dPPEL2rbab4+GLJF57291TLbTHkNXCd/4/Enw9NERkZqwIAB8vX1VVRUlHksSTpx4oTGjx+v69evm0cAEoxQ3Tq1X8vmTdaeS//8Xo6+c0DDVhxQ2LlrCoyUFH1LR/+4oDcH75e3t7e8vb31+9imSuuU+H5EAIgfifTVoYBq/C9Ev+/xe7Ry68IBXY96QwWy/f3ri+tnKvydT1T9LXfZSvqg8zC9lTVKIY9FJ55u//792rVrlyIjI82jGPbs2SMfHx/zMoAExDFtemV2y67bFy/o74SMkM+hE8qSO48cnB3/3nT7hs7cT69SRVPEPBgAnsHGMIzEdU7u0HhlrrZP43d/Ip+V99W8a3NlcZI2DKym39M20MHNp+Q1f6RO9ymtX9O1VRH7kzp9JVQZsn2gLoM+0YO+jBWbNm3SokWLNHnyZIWFhenrr79WQECA8uTJY94a74KCguTv7//o1zY2NrKzs4ux51n+/PNP7dy5U9HR//721WeffZYg/v8CLyIqKuqFfx/EpdDQUK1cuVK//PKLihYtqiVLlmj//v367rvv5OLiYt7+En5T4+ztVOjrbxXp5KQuTf+ntMZtLZg+Q4abs6Z0nK8vDx/WpwHL1KjLGDllzC0XBzu55H1HbVo20xtZHgQmAJgk2oBccK6//pw5S4U+HqD38h5W1zJL9L9J5TTyuz3ymj9SBzzTaJB/U33v1VNlXnfS4rYVdOrNsRo98GNlND/nf/S0gAwPD1f+/PnNW+Odr6+vrly5EmPNxsbm0f+OjIxUQEBAjPlDly5dkre39wsF5Oeff6433njDvAwkSP7+/kqTJo15Od6FhoZq6dKlWrp0aTwEZAdVnT9PJ37ZKc+BXylP2EXNmDFJZWpW09e1vNTq8GFVOz9VTb6cr0ZLpqtWemnP0olacPltLRrJ29gAnsFIbA6OMzKl/dzYHn7X+GnSaGP22hNGwLKmRpmB+4z7hxcaH3zYzjh4N9iY0jSH0X7uiUeHXTkx3ahVoZmx6UxgjKd7GRs3bjQ8PDyM4OBg4969e0bbtm2NdevWmbclOps3bzbs7OwMSc99pE2b1ti1a5f5cAD/4t69e0adOnWMY8eOGYZhGIsXLza6du1qBAUFmbe+pM3G568XMmYdCzA29f3K8Np53bj0xyyjX+eFxg3f1ca7GUoasy+ajzGMgCOLjQ+L1zYW3fI3jwDAMAzDSLx/tEyRRiXdU8v78h7NmHJczRqWjTHOnPk1hQeH6OFVfPb2rsqUIYVSpLCPsQ9PqlatmooVK2ZejsHW1lZVqlSRu7u7eQQgwUmltz7MrQ1LD+nkttVStUrK9GgWrbveR7V5758Ki/z7DSlHZxelSO+mNDaJ90cEgLiViF8d7JQjT1Zd2D5X44Iaq2HBmNM3P/JQ+B/rdOJ6mCTpwKIp8k9bSNm5pueFeHl5KXfu3DHe9n5c1qxZ5enpqcyZM5tHABKgNCXeUY4NIzR6tZOqvRvzavCQi3s1tv9Y7QsIkSIDtXfXVoXnK63izk4x9gHAQ4k4IKUUuQupWFi0ins0lDljXnv7M3mWjVDnKgXk7u6uvisLqOvo1srlbNqIp6pZs6bGjx+v3Llzy8HBQXZ2drKzs5Otra0KFiyoOXPmqFatWgniAwkAXoDzm/qs/GldeaO53nF9fGCrrO800cjGKdSq3Btyz1tY3dcEq2NXT2V15vc3gKdLfB+iSUDMH6Lp27evateurVq1apm3JlrBwcFat26djh07Jjs7O5UpU0YffPDBM89MAvh3fn5+atasmQYPHhzHH6IBgLiRqM9AIu65uLjo448/lpeXlwYMGKAPP/yQeAQAIJkjIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgLyBfj5+ZmXACBeREZG6u7du+ZlAHilCMhnCAoK0qJFi1S3bl3duHHDPAaAeBEaGqopU6bo66+/1pEjR8xjAHglCMgHgoODdePGDW3cuFEdOnRQmTJlNH78eB0+fFiRkQ+/EBEA4pdhGLp165Z+//131atXT++9957GjRunU6dO6d69e4qIiDAfAgBxLlkHZFBQkI4fP65ffvlF33zzjTp27KgffvhB2bNn19y5c7Vy5UoVKlTIfBgAxLuuXbtq7969atOmjS5cuKC+ffuqe/fuGjNmjLZs2aIrV64oOjrafBgAxIlkGZBnzpzRDz/8oHbt2mnAgAH67bffVKhQIXXt2lXTp09Xjx49VLZsWTk68r3ZABKOzJkzq0GDBho5cqRGjRqlTz75RGFhYZozZ47at2+vvn37asOGDVwzCSDOJZuvMjx06JCGDh2qixcv6urVqypfvrzq16+vt99+WxkyZFDKlCllaxuzp+/fv6+GDRsqW7ZsypAhQ4yZJHl7eysiIkKLFy9Osl9lCCD2Pe2rDCdOnKgyZcrI3t4+xt7w8HDt27dPX3/9terVqxdjJklhYWEKDAzUhQsXtGHDBq1evVr37t1Tjhw5VKZMGQ0fPtx8CAC8tGRzBvLKlSvasWOH/Pz85OzsrMyZM8vNzU3Ozs6ys7N7Ih5fhLu7u2rXri17e3ulSZNGw4cPV/Xq1c3bACCGlClT6scff5S7u7v04LXk7bfffiIeX4S9vb3s7Ozk6uoqNzc3pU+fXuHh4bp+/boWLFhg3g4AsSLZnIHctGmTFi5cqHHjxunMmTPatm2bDhw4IDs7O2XNmlUVKlRQ/vz5lTdvXjk7O0uPnYEcM2aMihQpYn5KAIhzAQEB6tevnypXrqy6detKkqKjo3Xjxg2dPXtWR48e1fHjx+Xr66vs2bOrYsWKqlChgs6fP6927drpxIkT5qcEgJeW7AJyypQpcnFxkWEYunfvns6dO6djx47pjz/+kK+vr9KmTavy5curYsWKypw5MwEJ4JV6PCBr1qypPXv2aMuWLTpx4oRSpUqlHDly6J133lH+/PmVJ08e2dnZSZK2b99OQAKIM8k2IB8XHR2toKAg3b17V3v27NGqVau0a9culSpVSqdOndKKFSsISACvxMOAPH/+vM6cOSMXFxd9+OGHqlu3rtzd3ZUqVSo5OTmZDyMgAcQp6xf+JUG2trZKnTq1cubMqc8++0yLFi3SgQMHVKVKFaVPn/4/XR8JALHFxcVFGTJk0Pjx43XkyBENHjxYb775ptzc3J4ajwAQ1yijZ8iSJYs6d+6snTt3KkuWLOYxAMQLBwcHtW3bVnPmzFHNmjXNYwB4JQjIf+Hg4CBXV1fzMgDEC0dHR73++uvmZQB4pQhIAAAAWEJAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlBCQAAAAsISABAABgCQEJAAAASwhIAAAAWEJAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlBCQAAAAsISABAABgCQEJAAAASwhIAAAAWEJAAgAAwBICEgAAAJYQkAAAALCEgAQAAIAlBCQAAAAsSTYB6eLioujoaEVGRppHAJDk2NraysHBwbwMALEi2QSkv7+/wsLCFB4ebh4BAADAgmQTkLdv31ZUVJR5GQCSpOjoaP7ADCDOJJuABAAAQOwgIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYYmMYhmFeTIoWLlyojh07mpeBOBUREaGQkBC5ubmZR0Ccy549u/bv329eBoCXlmwC8uTJk7p69ap5GYhTBw8e1MyZM7V27VrzCIhz6dOnV6ZMmczLAPDSkk1AAq/CmjVr1LNnT506dco8AgAg0eIaSAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkAAAALCEgAQAAYAkBCQAAAEsISAAAAFhCQAIAAMASAhIAAACWEJAAAACwhIAEAACAJQQkEMcMwzAvAQCQqBGQQBzx8/PTsWPHdOfOHZ06dUrR0dHmLQAAJEo2BqdHgFh36dIljRkzRhcvXlS+fPn0119/qW7dumrQoIFSpkxp3g4AQKJCQAKxbP369Ro8eLDefPNNdezYUVmyZNHBgwfVt29fFShQQOPHj5ezs7P5MAAAEg3ewgZiQVRUlG7cuKH+/fvryy+/VIcOHTR27Fi5u7vL2dlZFSpU0Pz58yVJ77zzjlauXKmQkBDz0wAAkChwBhJ4SREREVqzZo2WLFmiLFmyqHXr1ipUqJB52yOLFi3SggUL9Oabb+qTTz5RkSJFzFsAAEjQCEjgJZw9e1bTpk2Tt7e36tWrp/r16//rNY6GYejMmTNasmSJdu/erebNm6thw4ZKkSKFeSsAAAkSAQn8B9HR0dq0aZN69OihKlWqqF+/fnJ1dZW9vb156zOFhIRo69at8vLyUqFChTRgwADlypXLvA0AgASHgAQsMAxDt2/f1oQJE7RlyxZ16NBBn376qXmbJeHh4erYsaP+/PNPeXl5qUyZMnJxcTFvAwAgwSAggRd0//597d69W7NmzVKaNGnUo0eP517raEVgYKBWrVqlxYsXK2/evPriiy9UoEAB2dryOTcAQMJDQAIv4PLly4/u6+jh4aF3331XGTJkMG97aadPn9asWbN0/vx51a9fX/Xr1+dsJAAgwSEggX8RERGhKlWqqHTp0urUqZNy585t3hKrQkJCdODAAX3zzTeqUKGCBg8ebN4CAMArxftjwL94+BWELxyPhqHA21d1416gogzJMIJ13cdHPj4+unI32Lz7Cc7OzqpYsaIaNWqkiIgI8xgAgFeOgARiWYT3bxrQtokmrD2swMhgHfm5j5o27KCBA7vIo91QrT4VaD4EAIBEhYAEYpWf1g9sp/PFvlbnhhWVNmCvBrSfrmL9Zmv27Dnq6/67Wnv9qvvmwwAASEQISCBWndXyZVdUtmp1uTlIOrNbv/nn0Vsl3CSlVcXqZXRv40ZdMB8GAEAiQkACcSCFnZ2kaF04/oeMUm1UNfvf65ERkeatAAAkOgQkEKvyq0b11Lry10WFh/lo6YrTcq9ZUW6S5HdBkxesVe5PGugFPooDAECCRUACsSqd3u83QeG/eMrTo52WpaqnaZ5FdOvMTg3v2EkHsrfU9P7VldZ8GAAAiQj3gQT+RVhYmKpVq6Z58+a98G18/G5clF+oZJ8mk15P76LI0EDdvnVPDhkyK72Lg/mIp5o8ebJ8fHw0YsQI8wgAgFeKM5BAHLC3TyHntG7K6uoiwwjW7eu3FRoVpeBQroEEACR+BCQQy7gPJAAgqSMggVjFfSABAEkfAQnEKu4DCQBI+ghIIA5wH0gAQFJGQAKxivtAAgCSPgISiFXcBxIAkPRxH0jgX3AfSAAAYuIMJBCrwnTr4kX5RTjKNfPrej29iyTJ3imVsmTP/sLxCABAQkZAArHqsn7q30/1ar6rL/uO1Zw5P+t0gHkPAACJGwEJxKq8ajNntuaM6a9axSK1/Lve8vD0lGdXL60/dsu8GQCARImABGKZja29ir/vIQ+P7pq7a5uWjOyrmq/9pfY1SsndvYLmXDIfAQBA4kJAAnEmhdJlzaZc7nn0Uesx2rp3p7aOKKnulXrqmHkrAACJCAEJxJkwXdi+SnNmz9bA7i3UqEkH9V8XoOErR6iYeSsAAIkIAQnEsujIcK0c4SlPz78fM4/bqkDFFpo4Z7Zmz56tltQjACCRIyCBWHVIHfPml8eInXIu3Eg/bDmk5YM95NG4rkrldZOdeTsAAIkQAQnEsoazfXTv9l+a3ON/yp8rl9yczTsAAEjcCEggVpVWxSrmNQAAkhYCEgAAAJYQkAAAALCEgAQAAIAlBCQAAAAsISABAABgCQEJAAAASwhIAAAAWGJjGIZhXgTwj7CwMFWoUEHfffedMmXKZB7HmZUrVyo4OFgjRowwjwAAeKUISOBfhIeHq1q1asqZM6d5FKcCAgJUsGBBDR8+3DwCAOCVIiCBf2EYhm7evGlejhcODg5ydXU1LwMA8EoRkAAAALCED9EAAADAEgISAAAAlhCQAAAAsISABAAAgCUEJAAAACwhIAEAAGAJAQkAAABLCEgAAABYQkACAADAEgISAAAAlhCQAAAAsISABAAAgCX/B/3NbMJ8hbVMAAAAAElFTkSuQmCC" + } + }, + "cell_type": "markdown", + "id": "212e6186-57a1-44bc-879d-a49057db104b", + "metadata": {}, + "source": [ + "## What is a 5T OTA?\n", + "\n", + "A 5-Transistor Operational Transconductance Amplifier (5T OTA) is one of the most fundamental analog circuit building blocks. It converts a differential input voltage into an output current. The circuit consists of:\n", + "\n", + "- **M1, M2** — Differential input pair (NMOS)\n", + "- **M3, M4** — Current mirror load (PMOS)\n", + "- **M5** — Tail current source (NMOS)\n", + "\n", + "Note that the sixth transistor (M6) is used for reference current input\n", + "\n", + "### Schematic Reference\n", + "\n", + "![image.png](attachment:c3ac3bfb-ecf2-4168-83c4-f9514dc6e1ce.png)" + ] + }, + { + "cell_type": "markdown", + "id": "091484d8-57a2-424f-a752-ab057290f153", + "metadata": {}, + "source": [ + "# **NetList generation and LVS**\n", + "let's go through the step by step procedure to generate LVS and DRC clean layout of a 5T OTA cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6654744d-e646-408a-85ab-5d3967dea400", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import subprocess\n", + "\n", + "# Run a shell, source .bashrc, then printenv\n", + "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", + "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", + "env_vars = {}\n", + "for line in result.stdout.splitlines():\n", + " if '=' in line:\n", + " key, value = line.split('=', 1)\n", + " env_vars[key] = value\n", + "\n", + "# Now, update os.environ with these\n", + "os.environ.update(env_vars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84912826-fbd5-4772-b0a4-0371872dd155", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout import MappedPDK, sky130 , gf180\n", + "#from gdsfactory.cell import cell\n", + "from gdsfactory import Component\n", + "from gdsfactory.components import text_freetype, rectangle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0bc7007a-0211-4d40-860d-3c06706cd120", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout import nmos, pmos\n", + "from glayout import via_stack\n", + "from glayout import rename_ports_by_orientation\n", + "from glayout import tapring" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b30913c0-26b9-44ff-819e-e1a9dc39335f", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port\n", + "from glayout.util.port_utils import add_ports_perimeter,print_ports\n", + "from glayout.util.snap_to_grid import component_snap_to_grid\n", + "from glayout.spice.netlist import Netlist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08aab530-2236-4a81-b5c4-e43a8dd19f66", + "metadata": {}, + "outputs": [], + "source": [ + "from glayout.routing.straight_route import straight_route\n", + "from glayout.routing.c_route import c_route\n", + "from glayout.routing.L_route import L_route" + ] + }, + { + "cell_type": "markdown", + "id": "8ededba7-acb7-4eb5-9559-520b98fba95e", + "metadata": {}, + "source": [ + "## Demonstration of Basic Layout / Netlist Generation in SKY130 & GF180\n", + "\n", + "The code below is an example of basic layout / netlist generation for primitive cells. We also define some of the function below that we will use for viewing our netlist-generated layout (for comparison purposes)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c66926e1-c832-4e70-83cd-1d945a8f806e", + "metadata": {}, + "outputs": [], + "source": [ + "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,with_dnwell=False)\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)\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())" + ] + }, + { + "cell_type": "markdown", + "id": "5c7a2bcc-090f-4ada-9424-c74a5787bb92", + "metadata": {}, + "source": [ + "## Python Code for 5T OTA\n", + "\n", + "Below is the python code for generating 5T OTA. We will need this to generate the spice netlist. This code is similar to the part 1 notebook of this tutorial, where we generated the layout of this cell. It only has some minor syntax changes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f19e0887-350c-44c9-9a34-bf04504c6208", + "metadata": {}, + "outputs": [], + "source": [ + "fivet_ota_code_string = \"\"\"\n", + "from glayout import MappedPDK, sky130 , gf180\n", + "# from gdsfactory.cell import cell\n", + "from gdsfactory import Component\n", + "from gdsfactory.components import text_freetype, rectangle\n", + "\n", + "from glayout import nmos, pmos\n", + "from glayout import via_stack\n", + "from glayout import rename_ports_by_orientation\n", + "from glayout import tapring\n", + "\n", + "from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port\n", + "from glayout.util.port_utils import add_ports_perimeter,print_ports\n", + "from glayout.util.snap_to_grid import component_snap_to_grid\n", + "from glayout.spice.netlist import Netlist\n", + "\n", + "from glayout.routing.straight_route import straight_route\n", + "from glayout.routing.c_route import c_route\n", + "from glayout.routing.L_route import L_route\n", + "\n", + "\n", + "###### Only Required for IIC-OSIC Docker\n", + "import os\n", + "import subprocess\n", + "\n", + "# Run a shell, source .bashrc, then printenv\n", + "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", + "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", + "env_vars = {}\n", + "for line in result.stdout.splitlines():\n", + " if '=' in line:\n", + " key, value = line.split('=', 1)\n", + " env_vars[key] = value\n", + "\n", + "# Now, update os.environ with these\n", + "os.environ.update(env_vars)\n", + "\n", + "\n", + "def add_fivet_ota_labels(\n", + " fivet_ota_in: Component,\n", + " pdk: MappedPDK,\n", + ") -> Component:\n", + " fivet_ota_in.unlock()\n", + "\n", + " psize=(0.5,0.5)\n", + " # list that will contain all port/comp info\n", + " move_info = list()\n", + " # create labels and append to info list\n", + "\n", + " # vss\n", + " vsslabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vsslabel.add_label(text=\"VSS\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vsslabel,fivet_ota_in.ports[\"VSS_bottom_met_E\"],None))\n", + " #vss_ref = top_level << vsslabel;\n", + " \n", + " # vdd\n", + " vddlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vddlabel.add_label(text=\"VDD\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vddlabel,fivet_ota_in.ports[\"VDD_bottom_met_E\"],None))\n", + " #vdd_ref = top_level << vddlabel;\n", + " \n", + " # vinp\n", + " vinplabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vinplabel.add_label(text=\"VINP\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vinplabel,fivet_ota_in.ports[\"VINP_bottom_met_E\"],None))\n", + " #vinp_ref = top_level << vinplabel;\n", + " \n", + " # vinn\n", + " vinnlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " vinnlabel.add_label(text=\"VINN\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((vinnlabel,fivet_ota_in.ports[\"VINN_bottom_met_W\"],None))\n", + " #vinn_ref = top_level << vinnlabel;\n", + " \n", + " # vout\n", + " voutlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " voutlabel.add_label(text=\"VOUT\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((voutlabel,fivet_ota_in.ports[\"VOUT_bottom_met_W\"],None))\n", + " #out_ref = top_level << outlabel;\n", + " \n", + " # in_cur\n", + " in_curlabel = rectangle(layer=pdk.get_glayer(\"met2_pin\"),size=psize,centered=True).copy()\n", + " in_curlabel.add_label(text=\"IN_CUR\",layer=pdk.get_glayer(\"met2_label\"))\n", + " move_info.append((in_curlabel,fivet_ota_in.ports[\"INCUR_bottom_met_W\"],None))\n", + " #in_cur_ref = top_level << in_curlabel;\n", + "\n", + " # move everything to position\n", + " for comp, prt, alignment in move_info:\n", + " alignment = ('c','b') if alignment is None else alignment\n", + " compref = align_comp_to_port(comp, prt, alignment=alignment)\n", + " fivet_ota_in.add(compref)\n", + " \n", + " return fivet_ota_in.flatten()\n", + "\n", + "# @cell\n", + "\n", + "def fivet_ota(\n", + " pdk: MappedPDK,\n", + " input_pair: dict = {\n", + " \"width\": 5.75,\n", + " \"length\": 0.4,\n", + " \"fingers\": 2,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"nmos\",\n", + " },\n", + " current_mirror: dict = {\n", + " \"width\": 8.45,\n", + " \"length\": 0.4,\n", + " \"fingers\": 5,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"pmos\",\n", + " },\n", + " tail_source: dict = {\n", + " \"width\": 9.55,\n", + " \"length\": 0.7,\n", + " \"fingers\": 5,\n", + " \"multipliers\": 1,\n", + " \"device_type\": \"nmos\",\n", + " },\n", + " layout_rules: dict = {\n", + " \"spacing\": 2.0,\n", + " \"routing_metal\": \"met2\",\n", + " \"dummy_devices\": True,\n", + " \"tie_layers\": (\"met2\", \"met1\"),\n", + " \"sd_rmult\": 1,\n", + " },\n", + " **kwargs\n", + ") -> Component:\n", + "\n", + " pdk.activate()\n", + "\n", + " fivet_ota_config = {\n", + " \"input_pair\": input_pair,\n", + " \"current_mirror\": current_mirror,\n", + " \"tail_source\": tail_source,\n", + " \"layout_rules\": layout_rules,\n", + " }\n", + "\n", + " nmos_kwargs = {\n", + " \"with_tie\": False,\n", + " \"with_dnwell\": False,\n", + " \"sd_route_topmet\": \"met2\",\n", + " \"gate_route_topmet\": \"met2\",\n", + " \"sd_route_left\": True,\n", + " \"rmult\": None,\n", + " \"gate_rmult\": 1,\n", + " \"interfinger_rmult\": 1,\n", + " \"substrate_tap_layers\": layout_rules[\"tie_layers\"],\n", + " \"dummy_routes\": True,\n", + " }\n", + "\n", + " pmos_kwargs = {\n", + " \"with_tie\": False,\n", + " \"dnwell\": False,\n", + " \"sd_route_topmet\": \"met2\",\n", + " \"gate_route_topmet\": \"met2\",\n", + " \"sd_route_left\": True,\n", + " \"rmult\": None,\n", + " \"gate_rmult\": 1,\n", + " \"interfinger_rmult\": 1,\n", + " \"substrate_tap_layers\": layout_rules[\"tie_layers\"],\n", + " \"dummy_routes\": True,\n", + " }\n", + " \n", + " #top level component\n", + " top_level = Component(name=\"fivet_ota\")\n", + "\n", + " def current_mirror(pdk, config):\n", + " cm_comp = Component(name=\"current_mirror\")\n", + " \n", + " # take config parameters\n", + " width = config[\"current_mirror\"][\"width\"]\n", + " length = config[\"current_mirror\"][\"length\"]\n", + " fingers = config[\"current_mirror\"][\"fingers\"]\n", + " multipliers = config[\"current_mirror\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # ============ Instantiate 2 PMOS ============\n", + " pfet_ref = pmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False, True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **pmos_kwargs)\n", + " pfet_mir = pmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True, False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **pmos_kwargs)\n", + " \n", + " cref_ref = cm_comp << pfet_ref\n", + " cmir_ref = cm_comp << pfet_mir\n", + " cref_ref.name = \"pfet_ref\"\n", + " cmir_ref.name = \"pfet_mir\"\n", + " \n", + " # ============ Placement ============\n", + " cref_ref.movex(evaluate_bbox(pfet_mir)[0] + pdk.util_max_metal_seperation())\n", + " \n", + " # ============ Tapring ============\n", + " tap_ring = tapring(pdk, \n", + " enclosed_rectangle=evaluate_bbox(\n", + " cm_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]),\n", + " sdlayer=\"n+s/d\", \n", + " horizontal_glayer=tie_layers[0], \n", + " vertical_glayer=tie_layers[1]) \n", + " shift_amount = -prec_center(cm_comp.flatten())[0]\n", + " tring_ref = cm_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + " \n", + " # Add nwell padding to close gap between nwell\n", + " cm_comp.add_padding(layers=(pdk.get_glayer(\"nwell\"),), default=1)\n", + " \n", + " # ============ Internal routing ============\n", + " cm_comp << straight_route(pdk, cref_ref.ports[\"multiplier_0_source_E\"],\n", + " cmir_ref.ports[\"multiplier_0_source_E\"])\n", + " cm_comp << straight_route(pdk, cref_ref.ports[\"multiplier_0_gate_E\"],\n", + " cmir_ref.ports[\"multiplier_0_gate_E\"])\n", + " cm_comp << c_route(pdk, cref_ref.ports[\"multiplier_0_gate_E\"],\n", + " cref_ref.ports[\"multiplier_0_drain_E\"])\n", + "\n", + " # Route dummy to tapring (We have to route it manually since we generated the tapring manually)\n", + " cm_comp << straight_route(pdk, cmir_ref.ports[\"multiplier_0_dummy_L_gsdcon_top_met_E\"],\n", + " tring_ref.ports[\"W_top_met_W\"])\n", + " cm_comp << straight_route(pdk, cref_ref.ports[\"multiplier_0_dummy_R_gsdcon_top_met_W\"],\n", + " tring_ref.ports[\"E_top_met_E\"])\n", + " \n", + " # ============ Expose ports ============\n", + " cm_comp.add_ports(cref_ref.get_ports_list(), prefix=\"REF_\")\n", + " cm_comp.add_ports(cmir_ref.get_ports_list(), prefix=\"MIR_\")\n", + " cm_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " cm_comp.info.update({\"pfet_ref\": pfet_ref, \"pfet_mir\": pfet_mir})\n", + " \n", + " return cm_comp\n", + " \n", + " # ============ Add to top level ============\n", + " cm = current_mirror(pdk, fivet_ota_config)\n", + " cm_ref = top_level << cm\n", + " cm_ref.name = \"current_mirror\"\n", + "\n", + " def diff_pair(pdk, config):\n", + " dp_comp = Component(name=\"diff_pair\")\n", + " \n", + " # take config parameters\n", + " width = config[\"input_pair\"][\"width\"]\n", + " length = config[\"input_pair\"][\"length\"]\n", + " fingers = config[\"input_pair\"][\"fingers\"]\n", + " multipliers = config[\"input_pair\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # ============ Instantiate 2 NMOS ============\n", + " m1 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True , False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " m2 = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False,True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " \n", + " m1_ref = dp_comp << m1\n", + " m2_ref = dp_comp << m2\n", + " m1_ref.name = \"M1\" # M1 is the negative input diffpair\n", + " m2_ref.name = \"M2\" # M2 is the positive input diffpair\n", + " \n", + " # ============ Placement ============\n", + " ref_dimensions = evaluate_bbox(m1)\n", + " m2_ref.movex(m1_ref.xmax)\n", + " m2_ref.movex(ref_dimensions[0]/2)\n", + " m2_ref.movex(pdk.util_max_metal_seperation())\n", + " \n", + " # ============ Internal routing ============\n", + " dp_comp << straight_route(pdk,\n", + " m1_ref.ports[\"multiplier_0_source_E\"],\n", + " m2_ref.ports[\"multiplier_0_source_W\"])\n", + " \n", + " # ============ Tapring ============\n", + " tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(\n", + " dp_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]))\n", + " shift_amount = -prec_center(dp_comp.flatten())[0]\n", + " tring_ref = dp_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + "\n", + " # Route dummy to tapring (We have to route it manually since we generated the tapring manually)\n", + " dp_comp << straight_route(pdk, m1_ref.ports[\"multiplier_0_dummy_L_gsdcon_top_met_E\"],\n", + " tring_ref.ports[\"W_top_met_W\"])\n", + " dp_comp << straight_route(pdk, m2_ref.ports[\"multiplier_0_dummy_R_gsdcon_top_met_W\"],\n", + " tring_ref.ports[\"E_top_met_E\"])\n", + " \n", + " # ============ Expose ports ============\n", + " dp_comp.add_ports(m1_ref.get_ports_list(), prefix=\"M1_\")\n", + " dp_comp.add_ports(m2_ref.get_ports_list(), prefix=\"M2_\")\n", + " dp_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " dp_comp.info.update({\"M1\": m1, \"M2\": m2})\n", + " \n", + " return dp_comp\n", + " \n", + " # ============ Test ============\n", + " dp = diff_pair(pdk, fivet_ota_config)\n", + " dp_ref = top_level << dp\n", + " dp_ref.name = \"diff_pair\"\n", + " \n", + " def tail_current(pdk, config):\n", + " tail_comp = Component(name=\"tail_current\")\n", + " \n", + " # take config parameters\n", + " width = config[\"tail_source\"][\"width\"]\n", + " length = config[\"tail_source\"][\"length\"]\n", + " fingers = config[\"tail_source\"][\"fingers\"]\n", + " multipliers = config[\"tail_source\"][\"multipliers\"]\n", + " tie_layers = config[\"layout_rules\"][\"tie_layers\"]\n", + " sd_rmult = config[\"layout_rules\"][\"sd_rmult\"]\n", + " \n", + " # ============ Instantiate 2 NMOS ============\n", + " nfet_ref = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(False, True),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " nfet_mir = nmos(pdk, width=width, length=length, fingers=fingers,\n", + " multipliers=multipliers,\n", + " with_substrate_tap=False,\n", + " with_dummy=(True, False),\n", + " tie_layers=tie_layers,\n", + " sd_rmult=sd_rmult,\n", + " **nmos_kwargs)\n", + " \n", + " tref_ref = tail_comp << nfet_ref\n", + " tmir_ref = tail_comp << nfet_mir\n", + " tref_ref.name = \"nfet_ref\"\n", + " tmir_ref.name = \"nfet_mir\"\n", + " \n", + " # ============ Placement ============\n", + " tref_ref.movex(evaluate_bbox(nfet_mir)[0] + pdk.util_max_metal_seperation())\n", + " \n", + " # ============ Tapring ============\n", + " tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(\n", + " tail_comp.flatten(),\n", + " padding=pdk.get_grule(\"nwell\", \"active_diff\")[\"min_enclosure\"]))\n", + " shift_amount = -prec_center(tail_comp.flatten())[0]\n", + " tring_ref = tail_comp << tap_ring\n", + " tring_ref.movex(destination=shift_amount)\n", + " \n", + " # ============ Internal routing ============\n", + " tail_comp << straight_route(pdk, tref_ref.ports[\"multiplier_0_source_E\"],\n", + " tmir_ref.ports[\"multiplier_0_source_E\"])\n", + " tail_comp << straight_route(pdk, tref_ref.ports[\"multiplier_0_gate_E\"],\n", + " tmir_ref.ports[\"multiplier_0_gate_E\"])\n", + " tail_comp << c_route(pdk, tref_ref.ports[\"multiplier_0_gate_E\"],\n", + " tref_ref.ports[\"multiplier_0_drain_E\"])\n", + "\n", + " # Route dummy to tapring (We have to route it manually since we generated the tapring manually)\n", + " tail_comp << straight_route(pdk, tmir_ref.ports[\"multiplier_0_dummy_L_gsdcon_top_met_E\"],\n", + " tring_ref.ports[\"W_top_met_W\"])\n", + " tail_comp << straight_route(pdk, tref_ref.ports[\"multiplier_0_dummy_R_gsdcon_top_met_W\"],\n", + " tring_ref.ports[\"E_top_met_E\"])\n", + " \n", + " # ============ Expose ports ============\n", + " tail_comp.add_ports(tref_ref.get_ports_list(), prefix=\"REF_\")\n", + " tail_comp.add_ports(tmir_ref.get_ports_list(), prefix=\"MIR_\")\n", + " tail_comp.add_ports(tring_ref.get_ports_list(), prefix=\"TRING_\")\n", + " tail_comp.info.update({\"nfet_ref\": nfet_ref, \"nfet_mir\": nfet_mir})\n", + " \n", + " return tail_comp\n", + " \n", + " # ============ Test ============\n", + " tail = tail_current(pdk, fivet_ota_config)\n", + " tail_ref = top_level << tail\n", + " tail_ref.name = \"tail_current\"\n", + " \n", + " # Evaluate bbox for every sub circuit\n", + " dp_dimensions = evaluate_bbox(dp)\n", + " tail_dimensions = evaluate_bbox(tail)\n", + " \n", + " # diff_pair → place it below current_mirror\n", + " dp_ref.movey(cm_ref.ymin - dp_dimensions[1]/2 - pdk.util_max_metal_seperation())\n", + " \n", + " # tail_current → place it below diff_pair\n", + " tail_ref.movey(dp_ref.ymin - tail_dimensions[1]/2 - pdk.util_max_metal_seperation())\n", + " \n", + " # Use center coordinates\n", + " dp_ref.movex(cm_ref.center[0] - dp_ref.center[0])\n", + " tail_ref.movex(cm_ref.center[0] - tail_ref.center[0])\n", + "\n", + " viam2m3 = via_stack(pdk, \"met2\", \"met3\", centered=True)\n", + " viam1m2 = via_stack(pdk, \"met1\", \"met2\", centered=True)\n", + " \n", + " # Left current mirror drain via\n", + " drain_mir_via = top_level << viam2m3\n", + " drain_mir_via.move(cm_ref.ports[\"MIR_multiplier_0_drain_W\"].center).movex(-5)\n", + " \n", + " # Right current mirror drain via\n", + " drain_ref_via = top_level << viam2m3\n", + " drain_ref_via.move(cm_ref.ports[\"REF_multiplier_0_drain_E\"].center).movex(5)\n", + " \n", + " # Left diffpair drain via\n", + " drain_m1_via = top_level << viam2m3\n", + " drain_m1_via.move(dp_ref.ports[\"M1_multiplier_0_drain_W\"].center).movex(-5)\n", + " drain_m1_via.movex(drain_mir_via.x - drain_m1_via.x)\n", + " \n", + " # Right diffpair drain via\n", + " drain_m2_via = top_level << viam2m3\n", + " drain_m2_via.move(dp_ref.ports[\"M2_multiplier_0_drain_E\"].center).movex(5)\n", + " drain_m2_via.movex(drain_ref_via.x - drain_m2_via.x)\n", + " \n", + " # Diffpair source via\n", + " source_m1_via = top_level << viam2m3\n", + " source_m1_via.move(dp_ref.ports[\"M1_multiplier_0_source_W\"].center)\n", + " \n", + " # Current tail drain via\n", + " drain_sink_via = top_level << viam2m3\n", + " drain_sink_via.move(tail_ref.ports[\"MIR_multiplier_0_drain_W\"].center)\n", + " source_m1_via.movex(drain_sink_via.x - source_m1_via.x)\n", + "\n", + " # 1. Right current mirror (drain_mir_via) → drain M1 diff pair\n", + " top_level << straight_route(pdk,\n", + " drain_mir_via.ports[\"top_met_N\"],\n", + " drain_m1_via.ports[\"top_met_S\"])\n", + " \n", + " # 2. Left current mirror (drain_ref_via) → drain M2 diff pair\n", + " top_level << straight_route(pdk,\n", + " drain_ref_via.ports[\"top_met_N\"],\n", + " drain_m2_via.ports[\"top_met_S\"])\n", + " \n", + " # 3. Source common diff pair → drain tail\n", + " top_level << straight_route(pdk,\n", + " source_m1_via.ports[\"top_met_N\"],\n", + " drain_sink_via.ports[\"top_met_S\"])\n", + "\n", + " # MIR drain → drain_ref_via\n", + " top_level << straight_route(pdk,\n", + " cm_ref.ports[\"MIR_multiplier_0_drain_E\"],\n", + " drain_mir_via.ports[\"bottom_met_W\"])\n", + " \n", + " # REF drain → drain_mir_via\n", + " top_level << straight_route(pdk,\n", + " cm_ref.ports[\"REF_multiplier_0_drain_W\"],\n", + " drain_ref_via.ports[\"bottom_met_E\"])\n", + " \n", + " # M1 drain → drain_l_pair_via\n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M1_multiplier_0_drain_E\"],\n", + " drain_m1_via.ports[\"bottom_met_W\"])\n", + " \n", + " # M2 drain → drain_r_pair_via\n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M2_multiplier_0_drain_W\"],\n", + " drain_m2_via.ports[\"bottom_met_E\"])\n", + " \n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"M2_multiplier_0_source_E\"],\n", + " drain_sink_via.ports[\"bottom_met_W\"])\n", + " \n", + " # Route VDD to N+ tapring\n", + " top_level << straight_route(pdk,\n", + " cm_ref.ports[\"MIR_multiplier_0_source_E\"],\n", + " cm_ref.ports[\"TRING_W_top_met_W\"])\n", + " \n", + " # Route VSS to P+ tapring\n", + " top_level << straight_route(pdk,\n", + " tail_ref.ports[\"REF_multiplier_0_source_E\"],\n", + " tail_ref.ports[\"TRING_W_top_met_W\"])\n", + " \n", + " # Route tail tapring to diffpair tapring\n", + " top_level << straight_route(pdk,\n", + " dp_ref.ports[\"TRING_S_top_met_N\"],\n", + " tail_ref.ports[\"TRING_N_top_met_S\"])\n", + "\n", + " # Expose signal ports \n", + " top_level.add_port(name=\"VSS_bottom_met_E\", port=tail_ref.ports[\"REF_multiplier_0_source_E\"])\n", + " top_level.add_port(name=\"VDD_bottom_met_E\", port=cm_ref.ports[\"MIR_multiplier_0_source_E\"])\n", + " top_level.add_port(name=\"VINP_bottom_met_E\", port=dp_ref.ports[\"M2_multiplier_0_gate_E\"])\n", + " top_level.add_port(name=\"VINN_bottom_met_W\", port=dp_ref.ports[\"M1_multiplier_0_gate_W\"])\n", + " top_level.add_port(name=\"VOUT_bottom_met_W\", port=drain_mir_via.ports[\"top_met_W\"])\n", + " top_level.add_port(name=\"INCUR_bottom_met_W\", port=tail_ref.ports[\"MIR_multiplier_0_gate_W\"])\n", + "\n", + " top_level.info.update({\n", + " \"m1\": dp.info[\"M1\"],\n", + " \"m2\": dp.info[\"M2\"],\n", + " \"pfet_ref\": cm.info[\"pfet_ref\"],\n", + " \"pfet_mir\": cm.info[\"pfet_mir\"],\n", + " \"nfet_tail_ref\": tail.info[\"nfet_ref\"],\n", + " \"nfet_tail_mir\": tail.info[\"nfet_mir\"],\n", + " })\n", + " return component_snap_to_grid(rename_ports_by_orientation(top_level))\n", + "\n", + "if __name__ == \"__main__\":\n", + " comp = fivet_ota(gf180)\n", + " comp = add_fivet_ota_labels(comp, gf180)\n", + " comp.name = \"FIVET_OTA\"\n", + " comp.write_gds('out_fivet_ota.gds')\n", + " comp.show()\n", + " print(\"...Running DRC...\")\n", + " drc_result = gf180.drc_magic(comp, \"FIVET_OTA\")\n", + "\n", + "\"\"\"\n", + "\n", + "fivet_ota_init_string = \"\"\"\n", + "### Glayout 5T OTA Cell.\n", + "\n", + "from .my_fivet_ota import fivet_ota, add_fivet_ota_labels\n", + "\n", + "__all__ = [\n", + " 'fivet_ota',\n", + " 'add_fivet_ota_labels',\n", + "]\n", + "\"\"\"\n", + "\n", + "directory = \"../../FIVET_OTA/\"\n", + "os.makedirs(directory, exist_ok=True)\n", + "\n", + "# Save to a .py file\n", + "with open(directory + \"my_fivet_ota.py\", \"w\") as file:\n", + " file.write(fivet_ota_code_string)\n", + "\n", + "with open(directory + \"__init__.py\", \"w\") as file:\n", + " file.write(fivet_ota_init_string)" + ] + }, + { + "cell_type": "markdown", + "id": "52a013c8-c989-4918-a973-f74bc300784a", + "metadata": {}, + "source": [ + "Import the python code to this notebook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07910ef2-f3bc-4886-828b-8f38f40d8c40", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "from pathlib import Path\n", + "sys.path.append(os.path.abspath(\"../../FIVET_OTA\"))\n", + "from my_fivet_ota import fivet_ota, add_fivet_ota_labels" + ] + }, + { + "cell_type": "markdown", + "id": "3e9ad5c7-6fa8-485f-9350-1ff266278146", + "metadata": {}, + "source": [ + "## Spice Netlist Generation\n", + "\n", + "We generate spice netlist (schematic) with the code below. First, we have to access the sub cells (the nfets and pfets) with `.info` method. After that, we define the input and output pins with `Netlist()` function. We connect the fets with `.connect_netlist()` method. Note that what we mean by spice netlist is the schematic netlist (we will use these words interchangeably)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a07b0e6-e02b-48f7-8b03-86c888c3371e", + "metadata": {}, + "outputs": [], + "source": [ + "def fivet_ota_netlist(ota_in: Component) -> Component:\n", + " m1 = ota_in.info[\"m1\"]\n", + " m2 = ota_in.info[\"m2\"]\n", + " pfet_ref = ota_in.info[\"pfet_ref\"]\n", + " pfet_mir = ota_in.info[\"pfet_mir\"]\n", + " nfet_tail_ref = ota_in.info[\"nfet_tail_ref\"]\n", + " nfet_tail_mir = ota_in.info[\"nfet_tail_mir\"]\n", + "\n", + " # Define the top netlist, also the input and output ports\n", + " netlist = Netlist(\n", + " circuit_name='FIVET_OTA_1',\n", + " nodes=['VINP', 'VINN', 'VOUT', 'VDD', 'VSS', 'IN_CUR']\n", + " )\n", + "\n", + " # Connect the netlist\n", + " netlist.connect_netlist(m1.info['netlist'], [('D', 'VOUT'), ('G', 'VINN'), ('S', 'VTAIL'), ('B', 'VSS')])\n", + " netlist.connect_netlist(m2.info['netlist'], [('D', 'VMID'), ('G', 'VINP'), ('S', 'VTAIL'), ('B', 'VSS')])\n", + " netlist.connect_netlist(pfet_ref.info['netlist'], [('D', 'VMID'), ('G', 'VMID'), ('S', 'VDD'), ('B', 'VDD')])\n", + " netlist.connect_netlist(pfet_mir.info['netlist'], [('D', 'VOUT'), ('G', 'VMID'), ('S', 'VDD'), ('B', 'VDD')])\n", + " netlist.connect_netlist(nfet_tail_ref.info['netlist'], [('D','IN_CUR'), ('G','IN_CUR'), ('S','VSS'), ('B','VSS')])\n", + " netlist.connect_netlist(nfet_tail_mir.info['netlist'], [('D','VTAIL'), ('G','IN_CUR'), ('S','VSS'), ('B','VSS')])\n", + "\n", + " ota_in.info['netlist'] = netlist\n", + " return ota_in\n", + "\n", + "# This is an example of using the function to generate our spice netlist with specified properties\n", + "# Notice that we flattened the multi finger into one finger. Use the total width of the finger for the schematic netlist\n", + "my_ota = fivet_ota_netlist(fivet_ota((gf180),\n", + " input_pair = {\n", + " \"width\": 11.5,\n", + " \"length\": 0.4,\n", + " \"fingers\": 1,\n", + " \"multipliers\": 1\n", + " },\n", + " current_mirror = {\n", + " \"width\": 42.25,\n", + " \"length\": 0.4,\n", + " \"fingers\": 1,\n", + " \"multipliers\": 1\n", + " },\n", + " tail_source = {\n", + " \"width\": 47.75,\n", + " \"length\": 0.7,\n", + " \"fingers\": 1,\n", + " \"multipliers\": 1\n", + " })\n", + " )\n", + "my_ota = add_fivet_ota_labels(my_ota, gf180)\n", + "my_ota.name = \"FIVET_OTA_1\"\n", + "my_ota.write_gds('out_OTA.gds') # For writing the generated schematic netlist into gds layout\n", + "my_ota.show()\n", + "print(my_ota.info['netlist'].generate_netlist())" + ] + }, + { + "cell_type": "markdown", + "id": "d83c9cef-4332-49d4-af9f-3b4c4bea2023", + "metadata": {}, + "source": [ + "Print port list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78bce88a-b817-442b-a47f-f65cf9ee8d5e", + "metadata": {}, + "outputs": [], + "source": [ + "# Check port list\n", + "comp = fivet_ota(gf180)\n", + "print_ports(comp)" + ] + }, + { + "cell_type": "markdown", + "id": "8dc53c6e-e39b-4fd4-ae21-4aa66c03e47b", + "metadata": {}, + "source": [ + "## Run LVS\n", + "\n", + "Below is the example of how to run the LVS. There were property errors when using multi fingers for schematic netlist, so better just make it into one finger and use the total width for your schematic netlist." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2311f1ca-3c76-447f-9603-7349ec87f3f5", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate the schematic netlist, just as the previous step\n", + "ota = fivet_ota_netlist(add_fivet_ota_labels(fivet_ota((gf180),\n", + " input_pair = {\n", + " \"width\": 11.5,\n", + " \"length\": 0.4,\n", + " \"fingers\": 1,\n", + " \"multipliers\": 1\n", + " },\n", + " current_mirror = {\n", + " \"width\": 42.25,\n", + " \"length\": 0.4,\n", + " \"fingers\": 1,\n", + " \"multipliers\": 1\n", + " },\n", + " tail_source = {\n", + " \"width\": 47.75,\n", + " \"length\": 0.7,\n", + " \"fingers\": 1,\n", + " \"multipliers\": 1\n", + " }), gf180))\n", + "ota.name = \"fivet_OTA\"\n", + "ota_gds = ota.write_gds(\"fivet_OTA.gds\") # For writing the generated schematic netlist into gds layout\n", + "display_gds(ota_gds)\n", + "ota.show()\n", + "netgen_lvs_result = gf180.lvs_netgen(ota, ota.name)" + ] + }, + { + "cell_type": "markdown", + "id": "710d8e22-8f9a-44ee-9f25-0d252b674063", + "metadata": {}, + "source": [ + "# PEX and Post Layout Simulation" + ] + }, + { + "cell_type": "markdown", + "id": "a8e174d3-18dd-4a56-9ddc-8d82ed41ab9c", + "metadata": {}, + "source": [ + "> 🚧 **Under Construction**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88911f0e-f522-4930-8fd9-fc9d19219176", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "GLdev", + "language": "python", + "name": "gldev" + }, + "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.10.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From ba50d36bf57d263f7066fa344dbbf378d68a6ac6 Mon Sep 17 00:00:00 2001 From: Dharma Anargya Jowandy <16523104@std.stei.itb.ac.id> Date: Mon, 4 May 2026 11:30:07 +0700 Subject: [PATCH 4/6] fix: Connect Dummy to tapring --- tutorial/glayout_tutorial_5T_OTA_part1.ipynb | 44 ++++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/tutorial/glayout_tutorial_5T_OTA_part1.ipynb b/tutorial/glayout_tutorial_5T_OTA_part1.ipynb index a2b08e07..d66f7bd7 100644 --- a/tutorial/glayout_tutorial_5T_OTA_part1.ipynb +++ b/tutorial/glayout_tutorial_5T_OTA_part1.ipynb @@ -282,7 +282,7 @@ "id": "119616e8", "metadata": {}, "source": [ - "#### 4. Sub-circuit Layout: Current Mirror Load (M3 & M4)\n", + "## 4. Sub-circuit Layout: Current Mirror Load (M3 & M4)\n", "\n", "The current mirror load consists of two PMOS transistors: a reference device (M4) with a diode connection, and a mirror device (M3) whose drain provides the output current.\n", "\n", @@ -361,8 +361,12 @@ " cm3_ref.ports[\"multiplier_0_source_E\"])\n", " cm_comp << straight_route(pdk, cm4_ref.ports[\"multiplier_0_gate_E\"],\n", " cm3_ref.ports[\"multiplier_0_gate_E\"])\n", - " cm_comp << c_route(pdk, cm4_ref.ports[\"multiplier_0_gate_E\"],\n", - " cm4_ref.ports[\"multiplier_0_drain_E\"])\n", + "\n", + " # Route dummy to tapring (We have to route it manually since we generated the tapring manually)\n", + " cm_comp << straight_route(pdk, cm3_ref.ports[\"multiplier_0_dummy_L_gsdcon_top_met_E\"],\n", + " tring_ref.ports[\"W_top_met_W\"])\n", + " cm_comp << straight_route(pdk, cm4_ref.ports[\"multiplier_0_dummy_R_gsdcon_top_met_W\"],\n", + " tring_ref.ports[\"E_top_met_E\"])\n", " \n", " # Expose ports\n", " cm_comp.add_ports(cm4_ref.get_ports_list(), prefix=\"M4_\")\n", @@ -383,7 +387,8 @@ "cm_ref = top_level << cm\n", "cm_ref.name = \"current_mirror\"\n", "\n", - "display_component(top_level, scale=1, path=\"../../\")" + "display_component(top_level, scale=1, path=\"../../\")\n", + "# top_level.show()" ] }, { @@ -454,6 +459,12 @@ " tring_ref = dp_comp << tap_ring\n", " tring_ref.movex(destination=shift_amount)\n", "\n", + " # Route dummy to tapring (We have to route it manually since we generated the tapring manually)\n", + " dp_comp << straight_route(pdk, m1_ref.ports[\"multiplier_0_dummy_L_gsdcon_top_met_E\"],\n", + " tring_ref.ports[\"W_top_met_W\"])\n", + " dp_comp << straight_route(pdk, m2_ref.ports[\"multiplier_0_dummy_R_gsdcon_top_met_W\"],\n", + " tring_ref.ports[\"E_top_met_E\"])\n", + "\n", " # Expose ports\n", " dp_comp.add_ports(m1_ref.get_ports_list(), prefix=\"M1_\")\n", " dp_comp.add_ports(m2_ref.get_ports_list(), prefix=\"M2_\")\n", @@ -544,6 +555,12 @@ " m5.ports[\"multiplier_0_gate_E\"])\n", " tail_comp << c_route(pdk, m6.ports[\"multiplier_0_gate_E\"],\n", " m6.ports[\"multiplier_0_drain_E\"])\n", + "\n", + " # Route dummy to tapring (We have to route it manually since we generated the tapring manually)\n", + " tail_comp << straight_route(pdk, m5.ports[\"multiplier_0_dummy_L_gsdcon_top_met_E\"],\n", + " tring_ref.ports[\"W_top_met_W\"])\n", + " tail_comp << straight_route(pdk, m6.ports[\"multiplier_0_dummy_R_gsdcon_top_met_W\"],\n", + " tring_ref.ports[\"E_top_met_E\"])\n", " \n", " # Expose ports\n", " tail_comp.add_ports(m6.get_ports_list(), prefix=\"M6_\")\n", @@ -638,12 +655,12 @@ "# Via for left drain of diff pair (M1)\n", "drain_m1_via = top_level << viam2m3\n", "drain_m1_via.move(dp_ref.ports[\"M1_multiplier_0_drain_W\"].center).movex(-5)\n", - "drain_m1_via.movex(drain_mir_via.x - drain_m1_via.x)\n", + "drain_m1_via.movex(drain_m4_via.x - drain_m1_via.x)\n", "\n", "# Via for right drain of diff pair (M2)\n", "drain_m2_via = top_level << viam2m3\n", "drain_m2_via.move(dp_ref.ports[\"M2_multiplier_0_drain_E\"].center).movex(5)\n", - "drain_m2_via.movex(drain_ref_via.x - drain_m2_via.x)\n", + "drain_m2_via.movex(drain_m3_via.x - drain_m2_via.x)\n", "\n", "# Via for common source of diff pair\n", "source_m1_via = top_level << viam2m3\n", @@ -652,7 +669,7 @@ "# Via for drain of tail current sink\n", "drain_m5_via = top_level << viam2m3\n", "drain_m5_via.move(tail_ref.ports[\"M5_multiplier_0_drain_W\"].center)\n", - "source_m1_via.movex(drain_sink_via.x - source_m1_via.x)\n", + "source_m1_via.movex(drain_m5_via.x - source_m1_via.x)\n", "\n", "display_component(top_level, scale=1, path=\"../../\")" ] @@ -752,12 +769,12 @@ "# Current mirror MIR drain → left via\n", "top_level << straight_route(pdk,\n", " cm_ref.ports[\"M3_multiplier_0_drain_E\"],\n", - " drain_mir_via.ports[\"bottom_met_W\"])\n", + " drain_m3_via.ports[\"bottom_met_W\"])\n", "\n", "# Current mirror REF drain → right via\n", "top_level << straight_route(pdk,\n", " cm_ref.ports[\"M4_multiplier_0_drain_W\"],\n", - " drain_ref_via.ports[\"bottom_met_E\"])\n", + " drain_m4_via.ports[\"bottom_met_E\"])\n", "\n", "# M1 drain → left via\n", "top_level << straight_route(pdk,\n", @@ -772,7 +789,7 @@ "# M2 source → tail drain via\n", "top_level << straight_route(pdk,\n", " dp_ref.ports[\"M2_multiplier_0_source_E\"],\n", - " drain_sink_via.ports[\"bottom_met_W\"])\n", + " drain_m5_via.ports[\"bottom_met_W\"])\n", "\n", "# VDD: CM source to N+ tapring\n", "top_level << straight_route(pdk,\n", @@ -959,7 +976,8 @@ " top_level.add(compref)\n", "\n", "component = top_level.flatten()\n", - "display_component(top_level, scale=2, path=\"../../\")" + "display_component(top_level, scale=2, path=\"../../\")\n", + "# top_level.show()" ] }, { @@ -988,9 +1006,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "GLdev", "language": "python", - "name": "python3" + "name": "gldev" }, "language_info": { "codemirror_mode": { From eee4464f27a51f9fc7aa6f3fd751a8580c3add10 Mon Sep 17 00:00:00 2001 From: AL-255 Date: Thu, 28 May 2026 05:13:19 -0400 Subject: [PATCH 5/6] Make all 12 tutorial notebooks runnable end-to-end + add notebook CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tutorial state was a mix of broken imports (glayout.elementary, glayout.flow), missing PDK env-var handling, hardcoded /foss/designs paths, and a shipping defect in the gf180 DRC deck. After the fixes the same 12 notebooks pass under both a bare conda env and inside hpretl/iic-osic-tools. Source fixes (CI-neutral — DRC/LVS results unchanged): * gf180_mapped: route gf180.drc() through a tiny wrapper deck that bridges klayout>0.29 -rd var names and loads the bundled standalone gf180mcu.drc, replacing the broken gf180mcu_drc.lydrc that referenced unbundled drc/rule_decks/*.drc fragments * mappedpdk: invoke netgen via `bash $(which netgen)` so the wrapper's `#!/bin/sh` + bashisms don't blow up under dash Notebook fixes: * import paths updated to current glayout layout (cells/elementary, cells/composite/opamp) in GLayout_Cells + glayout_opamp * glayout_opamp: drop external-OpenFASOC RL/tapeout cells (sky130_nist_tapeout, gen_spec, model, eval), replace with a pointer to that repo, keep the layout-generation flow * INV_part2: add missing `from gdsfactory import Component`, replace broken inv_netlist cell, populate empty LVS/PEX placeholder cells, replace the `comp` HTML-repr cell that needed layer_views the PDK doesn't provide * FVF/INV part1: writer cells now drop helpers in tutorial/{FVF,INV}/ instead of ../../{FVF,INV}/; INV writer stashes fet_P/fet_N on top_level.info so part2's netlist generator can find them * BJT tutorials: drop /foss/designs hardcoding, use Path.cwd() instead * every PDK-touching notebook now starts with a unified env bootstrap that sources ~/.bashrc (for iic-osic-tools), uses setdefault so caller-exported vars always win, and derives PDKPATH=$PDK_ROOT/$PDK when missing * HOW_TO_RUN.md documents both the iic-osic-tools venv workaround (gdsfactory 7.x venv shadowing the image's 9.x) and the bare-conda flow CI: * new notebooks.yml workflow runs in parallel with drc.yml on push/PR, exercises every tutorial/**/*.ipynb via jupyter nbconvert, emits summary.json + junit.xml in the same shape as run_cell_drc.py * tests/notebooks/run_tutorial_notebooks.py is the runner; sorts so part1 notebooks execute before part2 (helper-module dependency) * .gitignore re-includes .github/ since the pre-existing `.*/` rule was swallowing new workflow files; also ignores tutorial-time scratch (helpers, GDS, SVGs, lyrdb, magic ext dirs) --- .github/workflows/notebooks.yml | 140 ++++++++ .gitignore | 25 +- src/glayout/pdk/gf180_mapped/gf180_mapped.py | 2 +- .../pdk/gf180_mapped/gf180mcu_drc_wrapper.drc | 14 + src/glayout/pdk/mappedpdk.py | 13 +- tests/notebooks/run_tutorial_notebooks.py | 311 ++++++++++++++++++ .../test_bjt_custom_pattern.ipynb | 33 +- .../BJT_tutorials/test_bjt_gdsfactory.ipynb | 33 +- tutorial/BJT_tutorials/test_bjt_glayout.ipynb | 36 +- tutorial/GLayout_Cells.ipynb | 11 +- tutorial/HOW_TO_RUN.md | 229 +++++++++++++ tutorial/glayout_opamp.ipynb | 145 +------- tutorial/glayout_tutorial_FVF_part1.ipynb | 64 ++-- tutorial/glayout_tutorial_FVF_part2.ipynb | 57 ++-- tutorial/glayout_tutorial_INV_part1.ipynb | 61 ++-- tutorial/glayout_tutorial_INV_part2.ipynb | 175 +++++++--- 16 files changed, 1084 insertions(+), 265 deletions(-) create mode 100644 .github/workflows/notebooks.yml create mode 100644 src/glayout/pdk/gf180_mapped/gf180mcu_drc_wrapper.drc create mode 100644 tests/notebooks/run_tutorial_notebooks.py create mode 100644 tutorial/HOW_TO_RUN.md diff --git a/.github/workflows/notebooks.yml b/.github/workflows/notebooks.yml new file mode 100644 index 00000000..d5d2390f --- /dev/null +++ b/.github/workflows/notebooks.yml @@ -0,0 +1,140 @@ +name: Tutorial Notebooks + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +# Cancel superseded runs on the same branch — pushing N commits in a row +# shouldn't keep N CI runs going. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + notebooks: + name: Run tutorial notebooks + runs-on: ubuntu-22.04 + timeout-minutes: 30 + + # Same container + Python setup as drc.yml, so the two workflows can + # share a venv cache. iic-osic-tools ships klayout, magic, netgen, both + # PDKs (under /foss/pdks), but Python 3.12 + gdsfactory 9.x — we install + # CPython 3.10 via uv and let glayout's pinned gdsfactory 7.7 / numpy 1.x + # come along. + container: + image: hpretl/iic-osic-tools:latest + options: --user root + env: + # Both sky130A and gf180mcuD live here; glayout's gf180 module reads + # this at import time even when a notebook only touches sky130, so + # it has to be set globally. + PDK_ROOT: /foss/pdks + # FVF / INV / BJT tutorials default to gf180; the bootstrap cell in + # each notebook derives PDKPATH from PDK_ROOT/PDK if unset. + PDK: gf180mcuD + PDKPATH: /foss/pdks/gf180mcuD + DEBIAN_FRONTEND: noninteractive + PYTHONUNBUFFERED: "1" + # The image sets PYTHONPATH to its 3.12 site-packages, which would + # leak gdsfactory 9.x into the 3.10 kernel. + PYTHONPATH: "" + # GitHub Actions overrides the image's ENTRYPOINT with `tail -f`, + # so the iic-osic-tools entrypoint that normally enriches PATH with + # /foss/tools/{bin,klayout,...} never runs. + PATH: /foss/tools/bin:/foss/tools/sak:/foss/tools/kactus2:/foss/tools/klayout:/foss/tools/osic-multitool:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + + # iic-osic-tools' default container shell is dash, which doesn't grok + # the bash features uv's installer and our notebooks rely on. + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v4 + + - name: Cache uv + CPython 3.10 + id: cache-uv + uses: actions/cache@v4 + with: + # HOME=/headless in the image even when running as root. + path: | + /headless/.local/bin/uv + /headless/.local/bin/uvx + /headless/.local/share/uv + key: uv-py310-${{ runner.os }}-v1 + + - name: Install Python 3.10 (uv) + run: | + set -euxo pipefail + if [ ! -x "$HOME/.local/bin/uv" ]; then + curl -LsSf https://astral.sh/uv/install.sh | sh + fi + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + export PATH="$HOME/.local/bin:$PATH" + uv python install 3.10 + echo "PYTHON310=$(uv python find 3.10)" >> "$GITHUB_ENV" + + - name: Show tool versions + run: | + set -euxo pipefail + klayout -v + magic -d null -noconsole -T minimum &1 | head -5 || true + netgen -batch lvs -version 2>&1 | head -3 || true + ngspice -v 2>&1 | head -3 || true + "$PYTHON310" --version + ls "$PDK_ROOT" + + - name: Cache python venv + id: cache-venv + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.venv + # Bust the cache on dependency-relevant changes. notebooks adds + # jupyter/nbconvert/ipykernel/ipywidgets/gdstk/svgutils on top of + # the DRC venv, so a separate cache key avoids touching the + # drc-venv cache. + key: notebooks-venv-py310-${{ runner.os }}-${{ hashFiles('setup.py', 'src/glayout/**/*.py') }}-v1 + restore-keys: | + notebooks-venv-py310-${{ runner.os }}- + + - name: Create venv and install glayout + jupyter (cache miss) + if: steps.cache-venv.outputs.cache-hit != 'true' + run: | + set -euxo pipefail + rm -rf "$GITHUB_WORKSPACE/.venv" + "$PYTHON310" -m venv "$GITHUB_WORKSPACE/.venv" + . "$GITHUB_WORKSPACE/.venv/bin/activate" + # uv pip install is ~3-5x faster than pip and auto-detects + # $VIRTUAL_ENV after `activate`. glayout's install_requires already + # pulls in gdstk / svgutils / ipywidgets; jupyter+nbconvert+ + # ipykernel are the only extras the notebook harness needs. + uv pip install -e . + uv pip install jupyter nbconvert ipykernel + + - name: Run all tutorial notebooks + run: | + set -euxo pipefail + . "$GITHUB_WORKSPACE/.venv/bin/activate" + python tests/notebooks/run_tutorial_notebooks.py \ + --out-dir notebook_results + + - name: Upload notebook artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: tutorial-notebooks + # Includes executed/*.ipynb, logs/*.log, summary.json, junit.xml — + # everything needed to triage a regression without rerunning CI. + path: notebook_results + retention-days: 14 + + - name: Publish JUnit summary + # Skip when junit.xml wasn't produced (the runner died before write). + if: ${{ always() && hashFiles('notebook_results/junit.xml') != '' }} + uses: mikepenz/action-junit-report@v4 + with: + report_paths: notebook_results/junit.xml + check_name: Tutorial notebooks + require_tests: true diff --git a/.gitignore b/.gitignore index 3a77cd7d..cdd55cd8 100644 --- a/.gitignore +++ b/.gitignore @@ -256,6 +256,29 @@ cython_debug/ *.sim .*.* .*/ +# Re-include project directories that start with a dot but aren't user state +# (workflow defs, etc.). Without these negations the `.*/` rule above hides +# anything new under .github/ from `git status`. +!.github/ +!.github/** _*.json out/ -drc_results/ \ No newline at end of file +drc_results/ +notebook_results/ + +# tutorial-time generated artifacts: helper modules written by part 1 of +# FVF/INV, scratch GDS/SVGs used for inline display, klayout DRC reports, +# magic extraction directories. None of these should be committed. +tutorial/FVF/ +tutorial/INV/ +tutorial/ext/ +tutorial/klayout_drc/ +tutorial/out/ +tutorial/BJT_tutorials/ext/ +tutorial/BJT_tutorials/out/ +tutorial/*.gds +tutorial/*.svg +tutorial/*.lyrdb +tutorial/fvf.gds +tutorial/out.gds +tutorial/out.svg \ No newline at end of file diff --git a/src/glayout/pdk/gf180_mapped/gf180_mapped.py b/src/glayout/pdk/gf180_mapped/gf180_mapped.py index e9091cf1..0b170bb9 100644 --- a/src/glayout/pdk/gf180_mapped/gf180_mapped.py +++ b/src/glayout/pdk/gf180_mapped/gf180_mapped.py @@ -105,7 +105,7 @@ # note for DRC, there is mim_option 'A'. This is the one configured for use -gf180_lydrc_file_path = Path(__file__).resolve().parent / "gf180mcu_drc.lydrc" +gf180_lydrc_file_path = Path(__file__).resolve().parent / "gf180mcu_drc_wrapper.drc" # openfasoc_dir = Path(__file__).resolve().parent.parent.parent.parent.parent.parent.parent # pdk_root = Path('/usr/bin/miniconda3/share/pdk/') pdk_root = Path(os.getenv('PDK_ROOT')) diff --git a/src/glayout/pdk/gf180_mapped/gf180mcu_drc_wrapper.drc b/src/glayout/pdk/gf180_mapped/gf180mcu_drc_wrapper.drc new file mode 100644 index 00000000..f5928f68 --- /dev/null +++ b/src/glayout/pdk/gf180_mapped/gf180mcu_drc_wrapper.drc @@ -0,0 +1,14 @@ +# Bridge MappedPDK.drc()'s '-rd in_gds=... -rd report_file=...' (klayout > 0.29 +# convention) into the variables the standalone gf180mcu.drc deck reads +# ($input / $report). Also presets variant-A metal stack so the deck doesn't +# bail out. We use eval(File.read(...)) instead of Ruby's load() because the +# bundled deck uses klayout DRC DSL methods (source, polygons, report, ...) +# that only exist in the enclosing DRC interpreter scope. +$input = $in_gds if defined?($in_gds) && $in_gds && $input.nil? +$report = $report_file if defined?($report_file) && $report_file && $report.nil? +$run_mode ||= "flat" +$metal_top ||= "30K" +$metal_level||= "3LM" +$mim_option ||= "A" +$thr ||= 4 +eval(File.read(File.join(File.dirname(File.expand_path(__FILE__)), "gf180mcu.drc"))) diff --git a/src/glayout/pdk/mappedpdk.py b/src/glayout/pdk/mappedpdk.py index 382b189e..05b487f9 100644 --- a/src/glayout/pdk/mappedpdk.py +++ b/src/glayout/pdk/mappedpdk.py @@ -874,14 +874,19 @@ def write_spice(input_cdl, output_spice, lvs_schematic_ref_file): print(content) print("==== SPICE MAG END ====") - lvssetup_file = self.pdk_files['lvs_setup_tcl_file'] if lvs_setup_tcl_file is None else lvs_setup_tcl_file - netgen_command = f'netgen -batch lvs "{str(lvsmag_path)} {design_name}" "{str(spice_path)} {design_name}" {lvssetup_file} {str(report_path)}' + lvssetup_file = self.pdk_files['lvs_setup_tcl_file'] if lvs_setup_tcl_file is None else lvs_setup_tcl_file + # The netgen wrapper ships with `#!/bin/sh` but uses bashisms + # (e.g. `${i//\"/\\\"}`) that explode under dash. Force bash by + # passing the script to it directly. + _netgen_bin = shutil.which('netgen') or 'netgen' + netgen_command = f'bash {_netgen_bin} -batch lvs "{str(lvsmag_path)} {design_name}" "{str(spice_path)} {design_name}" {lvssetup_file} {str(report_path)}' print(f"Running netgen command: {netgen_command.strip()}") netgen_subproc = subprocess.run( netgen_command, shell=True, - check=True, - capture_output=True + check=True, + capture_output=True, + executable='/bin/bash', ) netgen_subproc_code = netgen_subproc.returncode netgen_subproc_out = netgen_subproc.stdout.decode('utf-8') diff --git a/tests/notebooks/run_tutorial_notebooks.py b/tests/notebooks/run_tutorial_notebooks.py new file mode 100644 index 00000000..a2cc3d93 --- /dev/null +++ b/tests/notebooks/run_tutorial_notebooks.py @@ -0,0 +1,311 @@ +"""Execute every tutorial notebook end-to-end and emit a pass/fail report. + +Used by the GitHub Actions CI workflow at ``.github/workflows/notebooks.yml``. + +Discovery & ordering +-------------------- +Picks up every ``.ipynb`` under ``tutorial/`` and runs each one via +``jupyter nbconvert --to notebook --execute``. Within a single run, notebooks +that *write* helper modules (FVF/INV part 1) must execute before the notebooks +that *import* those helpers (FVF/INV part 2). We sort everything so that +``…_part1.ipynb`` deterministically lands before ``…_part2.ipynb``, then fall +back to alphabetical order. + +Output layout +------------- +:: + + / + executed/.ipynb # post-execution copy, kept on PASS too for diffing + logs/.log # full nbconvert stdout+stderr + summary.json # { total, pass, fail, error, results: [...] } + junit.xml # JUnit shaped like tests/drc/run_cell_drc.py + +Exit code +--------- +Non-zero if any notebook failed (cell error / kernel crash / timeout). Mirrors +the DRC runner so the workflow surfaces a red check + JUnit annotations. +""" +from __future__ import annotations + +import argparse +import json +import os +import re +import shutil +import subprocess +import sys +import time +import xml.etree.ElementTree as ET +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Dict, List, Optional + + +REPO_ROOT = Path(__file__).resolve().parents[2] +TUTORIAL_DIR = REPO_ROOT / "tutorial" + + +def _order_key(nb: Path) -> tuple: + """Sort notebooks so helper-writers (part 1) precede helper-consumers + (part 2), then alphabetical. We also pull the env-probing + ``GLayout_Introduction`` to the front so any import-time failure shows up + against the simplest notebook (easier triage in the JUnit view). + """ + name = nb.name.lower() + rel = str(nb.relative_to(TUTORIAL_DIR)).lower() + # Tier 0: GLayout_Introduction — runs first as a smoke-test. + if "glayout_introduction" in name: + return (0, rel) + # Tier 1: other layout-only walkthroughs. + if any(s in name for s in ("glayout_via", "glayout_cmirror", "glayout_cells")): + return (1, rel) + # Tier 2: part-1 notebooks that emit helpers for part-2. + if "part1" in name: + return (2, rel) + # Tier 3: part-2 notebooks that consume helpers from part-1. + if "part2" in name: + return (3, rel) + # Tier 4: everything else (BJT, opamp, …). + return (4, rel) + + +def _discover() -> List[Path]: + nbs = sorted(TUTORIAL_DIR.rglob("*.ipynb"), key=_order_key) + # Defensively skip checkpoint files in case .ipynb_checkpoints survives. + return [p for p in nbs if ".ipynb_checkpoints" not in p.parts] + + +@dataclass +class NotebookResult: + notebook: str # path relative to repo root + status: str # pass / fail / error + duration_s: float + errored_cell: Optional[int] = None + error_name: Optional[str] = None + error_message: Optional[str] = None + nbconvert_returncode: Optional[int] = None + log_tail: Optional[str] = None + executed_path: Optional[str] = None # path relative to out-dir + + def to_dict(self) -> Dict[str, Any]: + return {k: v for k, v in self.__dict__.items() if v is not None} + + +_ANSI = re.compile(r"\x1b\[[0-9;]*[A-Za-z]") + + +def _scan_executed(path: Path) -> Optional[Dict[str, Any]]: + """Return the first errored code cell from an executed notebook, or None + if every code cell ran to completion. Strips ANSI so the JUnit body is + readable in GitHub's UI. + """ + try: + nb = json.loads(path.read_text()) + except Exception as exc: # nbconvert crashed before writing a parseable file + return {"cell": -1, "ename": "NotebookParseError", "evalue": str(exc), "tb": ""} + for i, c in enumerate(nb.get("cells", [])): + if c.get("cell_type") != "code": + continue + for out in c.get("outputs", []): + if out.get("output_type") == "error": + tb = _ANSI.sub("", "\n".join(out.get("traceback", []))) + return { + "cell": i, + "ename": out.get("ename", "?"), + "evalue": out.get("evalue", ""), + "tb": tb, + } + return None + + +def _run_one( + nb: Path, + executed_dir: Path, + log_dir: Path, + timeout_per_cell: int, + timeout_per_notebook: int, + kernel_name: str, +) -> NotebookResult: + rel = str(nb.relative_to(REPO_ROOT)) + stem = nb.stem + workdir = nb.parent + executed = executed_dir / f"{stem}.ipynb" + log = log_dir / f"{stem}.log" + executed.parent.mkdir(parents=True, exist_ok=True) + log.parent.mkdir(parents=True, exist_ok=True) + + cmd = [ + "jupyter", "nbconvert", "--to", "notebook", "--execute", + f"--ExecutePreprocessor.timeout={timeout_per_cell}", + "--ExecutePreprocessor.allow_errors=True", + f"--ExecutePreprocessor.kernel_name={kernel_name}", + "--output", str(executed), + nb.name, + ] + print(f"[RUN] {rel}", flush=True) + start = time.monotonic() + try: + proc = subprocess.run( + cmd, cwd=workdir, capture_output=True, text=True, + timeout=timeout_per_notebook, + ) + rc = proc.returncode + log.write_text((proc.stdout or "") + "\n" + (proc.stderr or "")) + except subprocess.TimeoutExpired as exc: + # nbconvert blew through the per-notebook timeout — treat as error, + # not failure, so the matrix view differentiates "test broke" from + # "test ran and a cell complained". + elapsed = time.monotonic() - start + log.write_text((exc.stdout or "") + "\n" + (exc.stderr or "") + f"\n[timeout after {timeout_per_notebook}s]\n") + return NotebookResult( + notebook=rel, status="error", duration_s=elapsed, + error_name="NotebookTimeout", + error_message=f"nbconvert exceeded {timeout_per_notebook}s", + log_tail=(exc.stderr or "")[-800:], + executed_path=str(executed.relative_to(executed_dir.parent)) if executed.exists() else None, + ) + + elapsed = time.monotonic() - start + rel_executed = str(executed.relative_to(executed_dir.parent)) if executed.exists() else None + + if rc != 0 and not executed.exists(): + # nbconvert crashed before writing the executed file (e.g. kernel + # spec missing). Surface stderr so triage is one click away. + return NotebookResult( + notebook=rel, status="error", duration_s=elapsed, + nbconvert_returncode=rc, + error_name="NbconvertCrash", + error_message=f"nbconvert exit {rc} with no output notebook", + log_tail=(proc.stderr or proc.stdout or "")[-800:], + executed_path=rel_executed, + ) + + err = _scan_executed(executed) if executed.exists() else None + if err is None: + return NotebookResult( + notebook=rel, status="pass", duration_s=elapsed, + nbconvert_returncode=rc, + executed_path=rel_executed, + ) + # The executed notebook has at least one error cell. Build a one-line + # human summary plus the (trimmed) traceback so JUnit can render it. + tb_lines = [l for l in err["tb"].splitlines() if l.strip()] + tb_tail = "\n".join(tb_lines[-12:]) + return NotebookResult( + notebook=rel, status="fail", duration_s=elapsed, + nbconvert_returncode=rc, + errored_cell=err["cell"], + error_name=err["ename"], + error_message=err.get("evalue", "")[:200], + log_tail=tb_tail, + executed_path=rel_executed, + ) + + +def _write_summary(results: List[NotebookResult], out: Path) -> None: + body = { + "total": len(results), + "pass": sum(1 for r in results if r.status == "pass"), + "fail": sum(1 for r in results if r.status == "fail"), + "error": sum(1 for r in results if r.status == "error"), + "results": [r.to_dict() for r in results], + } + out.write_text(json.dumps(body, indent=2)) + print(json.dumps({k: body[k] for k in ("total", "pass", "fail", "error")}, indent=2), flush=True) + + +def _write_junit(results: List[NotebookResult], out: Path) -> None: + suite = ET.Element( + "testsuite", + attrib={ + "name": "glayout-tutorial-notebooks", + "tests": str(len(results)), + "failures": str(sum(1 for r in results if r.status == "fail")), + "errors": str(sum(1 for r in results if r.status == "error")), + "skipped": "0", + }, + ) + for r in results: + case = ET.SubElement( + suite, "testcase", + attrib={ + "classname": "tutorial.notebooks", + "name": r.notebook, + "time": f"{r.duration_s:.2f}", + }, + ) + if r.status == "fail": + msg = f"{r.error_name or 'cell error'}: {r.error_message or ''}".strip() + ET.SubElement(case, "failure", attrib={"message": msg}).text = json.dumps(r.to_dict(), indent=2) + elif r.status == "error": + msg = f"{r.error_name or 'error'}: {r.error_message or ''}".strip() + ET.SubElement(case, "error", attrib={"message": msg}).text = json.dumps(r.to_dict(), indent=2) + ET.ElementTree(suite).write(out, encoding="utf-8", xml_declaration=True) + + +def main() -> int: + p = argparse.ArgumentParser() + p.add_argument("--out-dir", default="notebook_results") + p.add_argument( + "--cell-timeout", type=int, default=180, + help="Per-cell timeout passed to nbconvert (default: 180s).", + ) + p.add_argument( + "--notebook-timeout", type=int, default=900, + help="Hard wall-clock cap per notebook (default: 900s).", + ) + p.add_argument( + "--kernel-name", default="python3", + help="Jupyter kernel name to launch (default: python3).", + ) + p.add_argument( + "--only", default=None, + help="Comma-separated notebook basenames (with or without .ipynb) to limit the run.", + ) + args = p.parse_args() + + out_dir = Path(args.out_dir).resolve() + executed_dir = out_dir / "executed" + log_dir = out_dir / "logs" + out_dir.mkdir(parents=True, exist_ok=True) + executed_dir.mkdir(parents=True, exist_ok=True) + log_dir.mkdir(parents=True, exist_ok=True) + + nbs = _discover() + if args.only: + wanted = {s.strip().removesuffix(".ipynb") for s in args.only.split(",") if s.strip()} + nbs = [nb for nb in nbs if nb.stem in wanted] + + if not nbs: + print("no tutorial notebooks found", file=sys.stderr) + return 2 + + print(f"running {len(nbs)} notebook(s):", flush=True) + for nb in nbs: + print(f" - {nb.relative_to(REPO_ROOT)}", flush=True) + print(flush=True) + + results: List[NotebookResult] = [] + for nb in nbs: + r = _run_one( + nb, executed_dir, log_dir, + timeout_per_cell=args.cell_timeout, + timeout_per_notebook=args.notebook_timeout, + kernel_name=args.kernel_name, + ) + print(f"[{r.status.upper()}] {r.notebook} ({r.duration_s:.1f}s)" + + (f" — cell {r.errored_cell} {r.error_name}: {r.error_message}" + if r.status != "pass" else ""), + flush=True) + results.append(r) + + _write_summary(results, out_dir / "summary.json") + _write_junit(results, out_dir / "junit.xml") + + # Exit non-zero so the workflow fails on the first regression. + return 0 if all(r.status == "pass" for r in results) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tutorial/BJT_tutorials/test_bjt_custom_pattern.ipynb b/tutorial/BJT_tutorials/test_bjt_custom_pattern.ipynb index 806dd21a..4b6fe1be 100644 --- a/tutorial/BJT_tutorials/test_bjt_custom_pattern.ipynb +++ b/tutorial/BJT_tutorials/test_bjt_custom_pattern.ipynb @@ -8,6 +8,36 @@ "# Custom patter for routing in BJTs" ] }, + { + "cell_type": "code", + "id": "bootstrap-env", + "metadata": {}, + "source": [ + "# Environment bootstrap — works in both iic-osic-tools and bare-venv setups.\n", + "# iic-osic-tools defines PDK_ROOT / PDK / PDKPATH in ~/.bashrc but jupyter\n", + "# kernels launched from its menu don't inherit them, so we re-source bashrc.\n", + "# setdefault() means we never overwrite variables already exported by the\n", + "# caller. In bare-venv setups, PDKPATH is derived from PDK_ROOT/PDK if not\n", + "# already set. See tutorial/HOW_TO_RUN.md for the env-var contract.\n", + "import os\n", + "import subprocess\n", + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n" + ], + "outputs": [], + "execution_count": null + }, { "cell_type": "code", "execution_count": null, @@ -322,7 +352,8 @@ "metadata": {}, "outputs": [], "source": [ - "designs_path=Path(\"/foss/designs\")\n", + "import pathlib\n", + "designs_path=pathlib.Path.cwd() / \"out\"\n", "library = designs_path / \"libs\" / \"core_bandgap\"\n", "cell_name = \"bandgap_bjt\"\n", "cell_path = library / cell_name\n", diff --git a/tutorial/BJT_tutorials/test_bjt_gdsfactory.ipynb b/tutorial/BJT_tutorials/test_bjt_gdsfactory.ipynb index 9d590565..c878bdc0 100644 --- a/tutorial/BJT_tutorials/test_bjt_gdsfactory.ipynb +++ b/tutorial/BJT_tutorials/test_bjt_gdsfactory.ipynb @@ -8,6 +8,36 @@ "## Generating BJT using pure GDSfactory" ] }, + { + "cell_type": "code", + "id": "bootstrap-env", + "metadata": {}, + "source": [ + "# Environment bootstrap — works in both iic-osic-tools and bare-venv setups.\n", + "# iic-osic-tools defines PDK_ROOT / PDK / PDKPATH in ~/.bashrc but jupyter\n", + "# kernels launched from its menu don't inherit them, so we re-source bashrc.\n", + "# setdefault() means we never overwrite variables already exported by the\n", + "# caller. In bare-venv setups, PDKPATH is derived from PDK_ROOT/PDK if not\n", + "# already set. See tutorial/HOW_TO_RUN.md for the env-var contract.\n", + "import os\n", + "import subprocess\n", + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n" + ], + "outputs": [], + "execution_count": null + }, { "cell_type": "code", "execution_count": null, @@ -606,7 +636,8 @@ "metadata": {}, "outputs": [], "source": [ - "path_to_dir = Path(\"/foss/designs/gLayout/tutorial\").resolve() / \"ext\" / bjt.name\n", + "import pathlib\n", + "path_to_dir = pathlib.Path.cwd().resolve() / \"ext\" / bjt.name\n", "extract_pex(bjt, path_to_dir)" ] } diff --git a/tutorial/BJT_tutorials/test_bjt_glayout.ipynb b/tutorial/BJT_tutorials/test_bjt_glayout.ipynb index f2eb49cd..4a7c181d 100644 --- a/tutorial/BJT_tutorials/test_bjt_glayout.ipynb +++ b/tutorial/BJT_tutorials/test_bjt_glayout.ipynb @@ -8,6 +8,36 @@ "## Using the BJT models from glayout" ] }, + { + "cell_type": "code", + "id": "bootstrap-env", + "metadata": {}, + "source": [ + "# Environment bootstrap — works in both iic-osic-tools and bare-venv setups.\n", + "# iic-osic-tools defines PDK_ROOT / PDK / PDKPATH in ~/.bashrc but jupyter\n", + "# kernels launched from its menu don't inherit them, so we re-source bashrc.\n", + "# setdefault() means we never overwrite variables already exported by the\n", + "# caller. In bare-venv setups, PDKPATH is derived from PDK_ROOT/PDK if not\n", + "# already set. See tutorial/HOW_TO_RUN.md for the env-var contract.\n", + "import os\n", + "import subprocess\n", + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n" + ], + "outputs": [], + "execution_count": null + }, { "cell_type": "code", "execution_count": null, @@ -256,7 +286,8 @@ }, "outputs": [], "source": [ - "path_to_dir = Path(\"/foss/designs/gLayout/tutorial\").resolve() / \"ext\" / bjt_pnp.name\n", + "import pathlib\n", + "path_to_dir = pathlib.Path.cwd().resolve() / \"ext\" / bjt_pnp.name\n", "extract_pex(bjt_pnp, path_to_dir)" ] }, @@ -267,7 +298,8 @@ "metadata": {}, "outputs": [], "source": [ - "path_to_dir = Path(\"/foss/designs/gLayout/tutorial\").resolve() / \"ext\" / bjt_npn.name\n", + "import pathlib\n", + "path_to_dir = pathlib.Path.cwd().resolve() / \"ext\" / bjt_npn.name\n", "extract_pex(bjt_npn, path_to_dir)" ] }, diff --git a/tutorial/GLayout_Cells.ipynb b/tutorial/GLayout_Cells.ipynb index a9ce3db2..fdd466f1 100644 --- a/tutorial/GLayout_Cells.ipynb +++ b/tutorial/GLayout_Cells.ipynb @@ -196,7 +196,7 @@ "- **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\uff1a** A tuple of 4 bools" + "- **sides:** A tuple of 4 bools" ] }, { @@ -253,8 +253,7 @@ "metadata": {}, "outputs": [], "source": [ - "# WIP glayout.elementary not found ()\n", - "from glayout.elementary.diff_pair import diff_pair\n", + "from glayout.cells.elementary.diff_pair.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", @@ -298,8 +297,7 @@ "metadata": {}, "outputs": [], "source": [ - "# WIP No module named 'glayout.composite'\n", - "from glayout.composite.differential_to_single_ended_converter import differential_to_single_ended_converter\n", + "from glayout.cells.composite.differential_to_single_ended_converter.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", @@ -375,8 +373,7 @@ }, "outputs": [], "source": [ - "# WIP No module named 'glayout.composite'\n", - "from glayout.composite.opamp import opamp\n", + "from glayout.cells.composite.opamp.opamp import opamp\n", "\n", "# Select which PDK to use\n", "pdk = gf180\n", diff --git a/tutorial/HOW_TO_RUN.md b/tutorial/HOW_TO_RUN.md new file mode 100644 index 00000000..80ae8e57 --- /dev/null +++ b/tutorial/HOW_TO_RUN.md @@ -0,0 +1,229 @@ +# How to Run the gLayout Tutorials + +This document covers two supported environments: + +- **iic-osic-tools docker image** (`hpretl/iic-osic-tools`) — the primary, batteries-included setup. All EDA tools, both PDKs (sky130 and gf180mcu), and the right env vars are pre-baked. +- **Bare conda/venv** on a Linux host — for users who already have the EDA toolchain installed locally and want to iterate without docker. + +Both flows resolve to the same notebooks. The bootstrap cell at the top of every EDA-touching notebook detects whichever environment it's running in and fills in any missing variables, so the notebook bodies are the same in both places. + +--- + +## What each tutorial needs + +| Tutorial | Tools required | PDK needed | Notes | +|---|---|---|---| +| `GLayout_Introduction.ipynb` | klayout (for `.show()` only) | sky130 + gf180 import time | Pure layout. | +| `GLayout_Via.ipynb` | klayout | sky130 + gf180 | Pure layout. | +| `GLayout_Cmirror.ipynb` | klayout | sky130 + gf180 | Pure layout. | +| `GLayout_Cells.ipynb` | klayout | gf180 | Layout-only walkthrough of every built-in cell, including a full opamp. | +| `glayout_tutorial_FVF_part1.ipynb` | klayout | gf180 | Generates `tutorial/FVF/my_FVF.py` helper used by part 2. Runs klayout DRC at the end. | +| `glayout_tutorial_FVF_part2.ipynb` | klayout, **magic**, **netgen**, **ngspice** | gf180 | LVS + Magic-based PEX + ngspice AC sweep. Run part 1 first. | +| `glayout_tutorial_INV_part1.ipynb` | klayout | gf180 | Generates `tutorial/INV/my_INV.py` helper used by part 2. | +| `glayout_tutorial_INV_part2.ipynb` | klayout, magic, netgen | gf180 | LVS + Magic-based PEX. Run part 1 first. | +| `glayout_opamp.ipynb` | klayout | sky130 | Generates and shows a two-stage opamp. The RL-optimization section is documented but lives in the OpenFASOC repo — see the markdown cell in the notebook. | +| `BJT_tutorials/test_bjt_glayout.ipynb` | klayout, magic | gf180 | BJT layout + magic extraction. | +| `BJT_tutorials/test_bjt_custom_pattern.ipynb` | klayout | gf180 | BJT custom-pattern routing demo. Writes a GDS into `tutorial/out/`. | +| `BJT_tutorials/test_bjt_gdsfactory.ipynb` | klayout, magic | gf180 | BJT built via raw gdsfactory + extraction. | + +PDK column lists what's needed *at runtime*. `glayout`'s gf180 module reads `PDK_ROOT` at **import time**, so even notebooks that don't run gf180 DRC need `PDK_ROOT` set to *something*. + +--- + +## Required environment variables + +| Variable | Purpose | Example (iic-osic-tools) | Example (bare local) | +|---|---|---|---| +| `PDK_ROOT` | Root directory holding the PDK install. Used at glayout import time to compute magic/netgen paths. | `/foss/pdks` | `/home/you/openmpw/pdk/volare/gf180mcu/versions/` | +| `PDK` | Which PDK variant to use. Used by the magic-DRC/LVS helpers to find `${PDKPATH}/libs.tech/magic/${PDK}.magicrc` etc. | `gf180mcuD` (or `sky130A`) | `gf180mcuD` | +| `PDKPATH` | Full path to the specific PDK variant directory. The bootstrap cell auto-derives this as `$PDK_ROOT/$PDK` if you don't set it. | `/foss/pdks/gf180mcuD` | `$PDK_ROOT/$PDK` | + +The bootstrap cell at the top of every PDK-touching notebook does: + +1. `source ~/.bashrc 2>/dev/null && printenv` so iic-osic-tools' shell-set vars are inherited by the kernel even when the kernel was started from a menu launcher. +2. `os.environ.setdefault(...)` so anything you exported in the calling shell wins. +3. Computes `PDKPATH=$PDK_ROOT/$PDK` if `PDKPATH` isn't already set. + +So in practice: in iic-osic-tools you don't need to do anything. In a bare local setup you need to export `PDK_ROOT` and `PDK` (and optionally `PDKPATH`). + +--- + +## Running inside iic-osic-tools (recommended) + +The image already provides klayout, magic, netgen, ngspice, and both PDKs under `/foss/pdks`. `PDK_ROOT`, `PDK`, `PDKPATH` are set in the image's `~/.bashrc`. + +**One important gotcha**: recent `hpretl/iic-osic-tools` builds ship Python **3.12** with **gdsfactory 9.x** and **numpy 2.x**, while `glayout` is written against **gdsfactory 7.x** and **numpy 1.x**. We work around that by installing `glayout` into a Python venv that pins those two deps, and registering the venv as a dedicated Jupyter kernel. Verified against tag `hpretl/iic-osic-tools:chipathon` — all 12 tutorials pass end-to-end. + +### One-time setup inside the container + +```bash +# 1. Start the image with this repo mounted at /foss/designs/gLayout: +# docker run -it --rm \ +# -v "$PWD":/foss/designs/gLayout \ +# -p 8888:8888 hpretl/iic-osic-tools:latest bash +# (Or use the existing chipathon container and copy the repo into /foss/designs/.) + +# 2. Source bashrc so PDK_ROOT / PDK / PDKPATH and the EDA-tool PATH are set: +source ~/.bashrc + +# 3. Create a venv that inherits the container's site-packages (klayout, +# matplotlib, pandas, etc.) but lets us pin gdsfactory + numpy: +python3.12 -m venv --system-site-packages /tmp/glayout-venv +source /tmp/glayout-venv/bin/activate + +# 4. Make sure the venv's gdsfactory 7.x and numpy 1.x shadow the system +# versions. The bashrc-set PYTHONPATH would otherwise put the system +# site-packages first, so we drop it for the rest of this shell: +unset PYTHONPATH + +# 5. Install pinned deps and glayout itself: +pip install "gdsfactory>6.0.0,<=7.7.0" "numpy<2" gdstk svgutils ipykernel +cd /foss/designs/gLayout +pip install --no-deps -e . + +# 6. Register the venv as a dedicated Jupyter kernel and force PYTHONPATH="" +# inside it (so the kernel doesn't inherit the system gdsfactory): +python -m ipykernel install --user --name glayout-venv --display-name "glayout-venv" +cat > ~/.local/share/jupyter/kernels/glayout-venv/kernel.json <<'EOF' +{ + "argv": ["/tmp/glayout-venv/bin/python", "-Xfrozen_modules=off", + "-m", "ipykernel_launcher", "-f", "{connection_file}"], + "display_name": "glayout-venv", + "language": "python", + "metadata": {"debugger": true}, + "env": {"PYTHONPATH": ""} +} +EOF +``` + +### Per-session + +```bash +source ~/.bashrc +source /tmp/glayout-venv/bin/activate +unset PYTHONPATH +cd /foss/designs/gLayout/tutorial +jupyter notebook --ip=0.0.0.0 --no-browser --allow-root +``` + +When you open a notebook, **switch the kernel to `glayout-venv`** via *Kernel → Change kernel*. The first cell of the notebooks is the env bootstrap; if you didn't `source ~/.bashrc` before starting Jupyter, the bootstrap will re-source it for you. + +Then run notebooks in this order if you plan the full FVF/INV walk-through: + +- `…_FVF_part1.ipynb` → `…_FVF_part2.ipynb` +- `…_INV_part1.ipynb` → `…_INV_part2.ipynb` + +(Part 1 writes the `my_FVF.py` / `my_INV.py` helper module that part 2 imports.) + +### Headless / CI alternative + +If you just want to verify everything runs without opening Jupyter: + +```bash +source ~/.bashrc && source /tmp/glayout-venv/bin/activate && unset PYTHONPATH +cd /foss/designs/gLayout/tutorial +for nb in GLayout_*.ipynb glayout_*part1.ipynb glayout_*part2.ipynb \ + glayout_opamp.ipynb BJT_tutorials/*.ipynb; do + jupyter nbconvert --to notebook --execute \ + --ExecutePreprocessor.timeout=180 \ + --ExecutePreprocessor.kernel_name=glayout-venv \ + --output "/tmp/$(basename $nb .ipynb)__executed.ipynb" "$nb" +done +``` + +--- + +## Running in a bare conda/venv on Linux + +### 1. Install EDA tools + +You need klayout (≥ 0.29 OK; the deck supports both `<0.29` and `>0.29` argument conventions), magic, netgen, ngspice on `PATH`. + +Quick check: +```bash +for t in klayout magic netgen ngspice; do + command -v "$t" >/dev/null && echo "OK $t -> $(command -v "$t")" || echo "MISSING $t" +done +``` + +**Known wart:** the netgen wrapper (`bin/netgen`) ships with `#!/bin/sh` but uses bash-only parameter substitution. On Ubuntu where `/bin/sh` is `dash`, you'll see `Bad substitution` errors. `glayout` works around this by invoking netgen via `bash $(which netgen)` explicitly, so you don't need to patch the wrapper. + +### 2. Install the PDKs (volare) + +[Volare](https://github.com/efabless/volare) is the easiest way: + +```bash +pip install volare +mkdir -p ~/pdk +volare enable --pdk sky130 --pdk-root ~/pdk +volare enable --pdk gf180mcu --pdk-root ~/pdk +``` + +After this: +- sky130 lives at `~/pdk/sky130/versions//{sky130A,sky130B}` +- gf180mcu lives at `~/pdk/gf180mcu/versions//{gf180mcuA,…,gf180mcuD}` + +### 3. Create a conda env and install glayout editable + +```bash +conda create -n gLayout python=3.10 -y +conda activate gLayout +cd /path/to/gLayout +pip install -e . +pip install jupyter ipykernel nbconvert +``` + +### 4. Export the env vars + +For the FVF / INV / BJT tutorials (gf180-based): +```bash +export PDK_ROOT=~/pdk/gf180mcu/versions/ +export PDK=gf180mcuD +export PDKPATH=$PDK_ROOT/$PDK +``` + +For the opamp notebook (sky130-based, but layout-only — `PDKPATH` isn't actually used): +```bash +export PDK_ROOT=~/pdk/sky130/versions/ +export PDK=sky130A +export PDKPATH=$PDK_ROOT/$PDK +``` + +The bootstrap cell will fill in `PDKPATH` for you if you don't set it, so you can get away with just `PDK_ROOT` and `PDK`. + +### 5. Launch Jupyter and run + +```bash +cd tutorial +jupyter notebook +``` + +Same ordering rule as iic-osic-tools: run FVF/INV part 1 before part 2. + +--- + +## Troubleshooting + +### "ModuleNotFoundError: No module named 'glayout'" +You installed gLayout into a different Python than the kernel is using. Confirm with `import sys; print(sys.executable)` from inside the notebook, then `pip install -e .` into that same Python. + +### "RuntimeError: error running klayout DRC" / "Unable to open file: …/drc/rule_decks/antenna.drc" +You're on an older `glayout` install that points the gf180 DRC at `gf180mcu_drc.lydrc` (which includes rule_deck fragments that aren't bundled). Reinstall from this repo (`pip install -e .`) — the fix routes through a wrapper deck that calls the bundled standalone `gf180mcu.drc`. + +### "KeyError: 'PDKPATH'" +The bootstrap cell didn't run, or you don't have `PDK_ROOT` + `PDK` set. Run the first code cell of the notebook explicitly, or `export PDK_ROOT=… PDK=…` and restart the kernel. + +### "DRC report file not found" from `gf180.drc_magic(...)` +Magic ran but didn't produce a `.rpt`. Usually means `$PDKPATH/libs.tech/magic/$PDK.magicrc` doesn't exist for the variant you set. Double-check `$PDKPATH` actually exists: `ls $PDKPATH/libs.tech/magic/`. + +### "netgen: Bad substitution" or `lvs_netgen` raising `CalledProcessError` immediately +Your `/bin/sh` is dash but the netgen wrapper uses bash syntax. The current `glayout` mainline already forces bash via `bash $(which netgen) …`, so this should be resolved — if you still see it, you're on an older glayout install. + +### "Netlists do not match" in the LVS report +This is the LVS report's *content*, not a tool failure. The netgen call still succeeded (exit code 0). The FVF part 2 schematic intentionally doesn't model dummy devices, so a mismatch against the real layout is expected for that tutorial; treat it as illustrative. + +### "layer_views for Pdk 'gf180' is None" +You did `comp` as the last line of a cell, which triggers gdsfactory's HTML repr. `glayout`'s gf180 PDK doesn't ship `layer_views`, so this raises. Use `comp.show()` (writes a GDS and opens klayout) or just `print(comp.name)` instead. + +### Notebooks 5–8 (FVF/INV) — "ModuleNotFoundError: No module named 'my_FVF'" / "'my_INV'" +You ran part 2 before part 1. Part 1 writes the helper module to `tutorial/FVF/` or `tutorial/INV/`. Run part 1 first, or vendor the helper manually before retrying. diff --git a/tutorial/glayout_opamp.ipynb b/tutorial/glayout_opamp.ipynb index cecb4387..8905b4fb 100644 --- a/tutorial/glayout_opamp.ipynb +++ b/tutorial/glayout_opamp.ipynb @@ -387,7 +387,7 @@ "source": [ "\n", "\n", - "from glayout.flow.blocks.composite.opamp import opamp\n", + "from glayout.cells.composite.opamp.opamp import opamp\n", "\n", "# Select which PDK to use\n", "pdk = sky130\n", @@ -439,28 +439,23 @@ ] }, { - "cell_type": "code", - "execution_count": null, - "id": "dae84129", + "cell_type": "markdown", + "id": "opamp-rl-removed", "metadata": {}, - "outputs": [], "source": [ - "# change the directory of tapeout_and_RL, from OpenFASOC repo\n", - "get_ipython().run_line_magic('cd', '/content/OpenFASOC/openfasoc/generators/glayout/tapeout/tapeout_and_RL')\n", - "from sky130_nist_tapeout import *\n", + "## RL-Based Parameter Optimization (external)\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", + "The original tutorial continued with a reinforcement-learning loop that\n", + "generated thousands of opamp variants, simulated them with `sky130_nist_tapeout`,\n", + "and trained a policy with `gen_spec` / `model` / `eval`. Those scripts live in\n", + "the OpenFASOC repo, not in `gLayout`:\n", "\n", - "# Generate parameter list\n", - "parameter_list = get_small_parameter_list()\n", + "- Simulation harness: `OpenFASOC/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py`\n", + "- ML driver: `OpenFASOC/openfasoc/MLoptimization/{gen_spec,model,eval}.py`\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)\n" + "To run those cells, clone OpenFASOC and follow its README — the dependencies\n", + "(`ray`, `torch`, `gymnasium`, …) are heavy and out of scope for this tutorial,\n", + "which focuses on the layout-generation half of the flow.\n" ] }, { @@ -478,24 +473,6 @@ "In test mode, only the first 8 sets of parameters are simulated. Set `TEST_MODE` to `False` to run simulations on the whole range. NOTE: This may take a very long time to run." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0668311f", - "metadata": {}, - "outputs": [], - "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", - "get_ipython().system('rm -r save_gds_by_index # cleans up any previous simulations')\n", - "results_array = brute_force_full_layout_and_PEXsim(sky130, params_array)" - ] - }, { "cell_type": "markdown", "id": "de31bed3-992a-44d7-abc7-8518961e760b", @@ -507,47 +484,6 @@ "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." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "7877cb45", - "metadata": {}, - "outputs": [], - "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')" - ] - }, { "cell_type": "markdown", "id": "df6f8890-d74a-40f8-b272-0bf69049b939", @@ -584,16 +520,6 @@ "The following cell install all of these libraries and their dependencies." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "a098676d", - "metadata": {}, - "outputs": [], - "source": [ - "get_ipython().system('pip install gym gymnasium ray[tune] torch dm_tree lz4')" - ] - }, { "cell_type": "markdown", "id": "c01bf495-45c1-4e68-8415-7711a3dac6a2", @@ -607,36 +533,6 @@ "NOTE: This will take a very long time." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "b55f45ad", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "get_ipython().run_line_magic('cd', '/content/OpenFASOC/openfasoc/MLoptimization')\n", - "\n", - "from gen_spec import generate_random_specs\n", - "\n", - "# Generate 100 specifications in train.yaml\n", - "generate_random_specs(\"train.yaml\", int(100))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "491317d2", - "metadata": {}, - "outputs": [], - "source": [ - "from model import train_model\n", - "\n", - "# Train the model and save the checkpoint to ./checkpoint_save\n", - "train_model('./checkpoint_save')" - ] - }, { "cell_type": "markdown", "id": "f3183b39-aa07-4fa7-ae0e-b299ade44b06", @@ -649,21 +545,6 @@ "\n", "NOTE: This will take a very long time. This time can be reduced if it is run in parallel." ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0aa3833a", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "from eval import evaluate_model\n", - "\n", - "generate_random_specs(\"train.yaml\", int(100))\n", - "evaluate_model('./checkpoint_save')" - ] } ], "metadata": { diff --git a/tutorial/glayout_tutorial_FVF_part1.ipynb b/tutorial/glayout_tutorial_FVF_part1.ipynb index 736a5069..3808aed2 100644 --- a/tutorial/glayout_tutorial_FVF_part1.ipynb +++ b/tutorial/glayout_tutorial_FVF_part1.ipynb @@ -39,7 +39,7 @@ "source": [ "## **Target** **Block** : **Flipped Voltage Follwer Cell**\n", "\n", - "A **voltage follower**\u2014also known as a unity-gain buffer or buffer amplifier\u2014is an electronic circuit in which the output voltage precisely follows the input voltage, providing a voltage gain of one. Typically implemented using an operational amplifier (op-amp) with negative feedback, the voltage follower features extremely high input impedance and very low output impedance. This configuration allows it to isolate circuit stages, preventing the loading of the input source and enabling the circuit to drive low-impedance loads without signal degradation.\n", + "A **voltage follower**—also known as a unity-gain buffer or buffer amplifier—is an electronic circuit in which the output voltage precisely follows the input voltage, providing a voltage gain of one. Typically implemented using an operational amplifier (op-amp) with negative feedback, the voltage follower features extremely high input impedance and very low output impedance. This configuration allows it to isolate circuit stages, preventing the loading of the input source and enabling the circuit to drive low-impedance loads without signal degradation.\n", "\n", "The voltage follower is fundamental in analog circuit design, ensuring signal fidelity and stability across a wide range of electronic applications. The **Flipped Voltage Follower (FVF)** is an advanced analog circuit topology derived from the conventional source follower, optimized for low-voltage, low-power applications. Unlike the standard voltage follower, the FVF employs a feedback structure that forces the input transistor to operate at a constant drain current, independent of variations in input voltage or load current. This is achieved using shunt negative feedback and ancillary biasing circuitry, resulting in improved linearity and significantly reduced output impedance compared to traditional designs.\n", "\n", @@ -57,7 +57,7 @@ "![](_images/FVF.png)\n", "\n", "```bibtex\n", - "Domala, N., Sasikala, G. Low power flipped voltage follower current mirror with improved input output impedances. S\u0101dhan\u0101 46, 142 (2021). https://doi.org/10.1007/s12046-021-01665-6\n", + "Domala, N., Sasikala, G. Low power flipped voltage follower current mirror with improved input output impedances. Sādhanā 46, 142 (2021). https://doi.org/10.1007/s12046-021-01665-6\n", "```" ] }, @@ -100,20 +100,27 @@ "metadata": {}, "outputs": [], "source": [ + "# Environment bootstrap — works in both iic-osic-tools and bare-venv setups.\n", + "# iic-osic-tools defines PDK_ROOT / PDK / PDKPATH in ~/.bashrc but jupyter\n", + "# kernels launched from its menu don't inherit them, so we re-source bashrc.\n", + "# setdefault() means we never overwrite variables already exported by the\n", + "# caller. In bare-venv setups, PDKPATH is derived from PDK_ROOT/PDK if not\n", + "# already set. See tutorial/HOW_TO_RUN.md for the env-var contract.\n", "import os\n", "import subprocess\n", - "\n", - "# Run a shell, source .bashrc, then printenv\n", - "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", - "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", - "env_vars = {}\n", - "for line in result.stdout.splitlines():\n", - " if '=' in line:\n", - " key, value = line.split('=', 1)\n", - " env_vars[key] = value\n", - "\n", - "# Now, update os.environ with these\n", - "os.environ.update(env_vars)" + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n" ] }, { @@ -874,21 +881,22 @@ "from glayout.routing.L_route import L_route\n", "\n", "\n", - "###### Only Required for IIC-OSIC Docker\n", + "# Environment bootstrap — both iic-osic-tools and bare-venv. See HOW_TO_RUN.md.\n", "import os\n", "import subprocess\n", - "\n", - "# Run a shell, source .bashrc, then printenv\n", - "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", - "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", - "env_vars = {}\n", - "for line in result.stdout.splitlines():\n", - " if '=' in line:\n", - " key, value = line.split('=', 1)\n", - " env_vars[key] = value\n", - "\n", - "# Now, update os.environ with these\n", - "os.environ.update(env_vars)\n", + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n", "\n", "\n", "def add_fvf_labels(\n", @@ -1065,7 +1073,7 @@ "] \n", "\"\"\"\n", "\n", - "directory = \"../../FVF/\"\n", + "directory = \"./FVF/\"\n", "os.makedirs(directory, exist_ok=True)\n", "\n", "# Save to a .py file\n", diff --git a/tutorial/glayout_tutorial_FVF_part2.ipynb b/tutorial/glayout_tutorial_FVF_part2.ipynb index 2cb77131..1ba93304 100644 --- a/tutorial/glayout_tutorial_FVF_part2.ipynb +++ b/tutorial/glayout_tutorial_FVF_part2.ipynb @@ -8,7 +8,7 @@ "source": [ "# Tutorial 2: **Notebook** - *FVF*\n", "\n", - "> \ud83d\udea7 **Under Construction**\n", + "> 🚧 **Under Construction**\n", "\n", "**By \n", "gLayout Team**\n", @@ -44,7 +44,7 @@ "source": [ "## **Target** **Block** : **Flipped Voltage Follwer Cell**\n", "\n", - "A **voltage follower**\u2014also known as a unity-gain buffer or buffer amplifier\u2014is an electronic circuit in which the output voltage precisely follows the input voltage, providing a voltage gain of one. Typically implemented using an operational amplifier (op-amp) with negative feedback, the voltage follower features extremely high input impedance and very low output impedance. This configuration allows it to isolate circuit stages, preventing the loading of the input source and enabling the circuit to drive low-impedance loads without signal degradation.\n", + "A **voltage follower**—also known as a unity-gain buffer or buffer amplifier—is an electronic circuit in which the output voltage precisely follows the input voltage, providing a voltage gain of one. Typically implemented using an operational amplifier (op-amp) with negative feedback, the voltage follower features extremely high input impedance and very low output impedance. This configuration allows it to isolate circuit stages, preventing the loading of the input source and enabling the circuit to drive low-impedance loads without signal degradation.\n", "\n", "The voltage follower is fundamental in analog circuit design, ensuring signal fidelity and stability across a wide range of electronic applications. The **Flipped Voltage Follower (FVF)** is an advanced analog circuit topology derived from the conventional source follower, optimized for low-voltage, low-power applications. Unlike the standard voltage follower, the FVF employs a feedback structure that forces the input transistor to operate at a constant drain current, independent of variations in input voltage or load current. This is achieved using shunt negative feedback and ancillary biasing circuitry, resulting in improved linearity and significantly reduced output impedance compared to traditional designs.\n", "\n", @@ -62,7 +62,7 @@ "![](_images/FVF.png)\n", "\n", "```bibtex\n", - "Domala, N., Sasikala, G. Low power flipped voltage follower current mirror with improved input output impedances. S\u0101dhan\u0101 46, 142 (2021). https://doi.org/10.1007/s12046-021-01665-6\n", + "Domala, N., Sasikala, G. Low power flipped voltage follower current mirror with improved input output impedances. Sādhanā 46, 142 (2021). https://doi.org/10.1007/s12046-021-01665-6\n", "```" ] }, @@ -82,20 +82,27 @@ "metadata": {}, "outputs": [], "source": [ + "# Environment bootstrap — works in both iic-osic-tools and bare-venv setups.\n", + "# iic-osic-tools defines PDK_ROOT / PDK / PDKPATH in ~/.bashrc but jupyter\n", + "# kernels launched from its menu don't inherit them, so we re-source bashrc.\n", + "# setdefault() means we never overwrite variables already exported by the\n", + "# caller. In bare-venv setups, PDKPATH is derived from PDK_ROOT/PDK if not\n", + "# already set. See tutorial/HOW_TO_RUN.md for the env-var contract.\n", "import os\n", "import subprocess\n", - "\n", - "# Run a shell, source .bashrc, then printenv\n", - "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", - "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", - "env_vars = {}\n", - "for line in result.stdout.splitlines():\n", - " if '=' in line:\n", - " key, value = line.split('=', 1)\n", - " env_vars[key] = value\n", - "\n", - "# Now, update os.environ with these\n", - "os.environ.update(env_vars)" + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n" ] }, { @@ -204,22 +211,22 @@ "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", + " top_level_cell.write_svg('out.svg')\n", "\n", " # Scale the image for displaying\n", - " fig = sg.fromfile('../../out.svg')\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", + " fig.save('out.svg')\n", "\n", " # Display the image\n", - " IPython.display.display(IPython.display.SVG('../../out.svg'))\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", + " component.write_gds(\"out.gds\")\n", "\n", - " display_gds('../../out.gds', scale)\n", + " display_gds('out.gds', scale)\n", "\n", "with hide:\n", " # Generate the sky130 component\n", @@ -259,7 +266,7 @@ "import sys\n", "import os\n", "from pathlib import Path\n", - "sys.path.append(os.path.abspath(\"../../FVF\"))\n", + "sys.path.append(os.path.abspath(\"./FVF\"))\n", "\n", "from my_FVF import flipped_voltage_follower,add_fvf_labels" ] @@ -364,7 +371,7 @@ "outputs": [], "source": [ "design_name=fvf.name\n", - "path_to_dir = Path(\"../../FVF\").resolve() / \"ext\" / design_name\n", + "path_to_dir = Path(\"./FVF\").resolve() / \"ext\" / design_name\n", "if not path_to_dir.exists():\n", " path_to_dir.mkdir(parents=True, exist_ok=False)\n", "\n", @@ -523,7 +530,7 @@ }, "outputs": [], "source": [ - "tb1_file = \"../../FVF/ext/fvf/\"+str(tb1_path).split(\"/\")[-1]\n", + "tb1_file = \"./FVF/ext/fvf/\"+str(tb1_path).split(\"/\")[-1]\n", "\n", "try:\n", " ngspice_subproc = subprocess.run(\n", @@ -608,7 +615,7 @@ "metadata": {}, "outputs": [], "source": [ - "tb2_file = \"../../FVF/ext/fvf/\"+str(tb2_path).split(\"/\")[-1]\n", + "tb2_file = \"./FVF/ext/fvf/\"+str(tb2_path).split(\"/\")[-1]\n", "\n", "try:\n", " ngspice_subproc = subprocess.run(\n", diff --git a/tutorial/glayout_tutorial_INV_part1.ipynb b/tutorial/glayout_tutorial_INV_part1.ipynb index c8c3cdcb..eb3160d5 100644 --- a/tutorial/glayout_tutorial_INV_part1.ipynb +++ b/tutorial/glayout_tutorial_INV_part1.ipynb @@ -93,20 +93,27 @@ "metadata": {}, "outputs": [], "source": [ + "# Environment bootstrap — works in both iic-osic-tools and bare-venv setups.\n", + "# iic-osic-tools defines PDK_ROOT / PDK / PDKPATH in ~/.bashrc but jupyter\n", + "# kernels launched from its menu don't inherit them, so we re-source bashrc.\n", + "# setdefault() means we never overwrite variables already exported by the\n", + "# caller. In bare-venv setups, PDKPATH is derived from PDK_ROOT/PDK if not\n", + "# already set. See tutorial/HOW_TO_RUN.md for the env-var contract.\n", "import os\n", "import subprocess\n", - "\n", - "# Run a shell, source .bashrc, then printenv\n", - "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", - "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", - "env_vars = {}\n", - "for line in result.stdout.splitlines():\n", - " if '=' in line:\n", - " key, value = line.split('=', 1)\n", - " env_vars[key] = value\n", - "\n", - "# Now, update os.environ with these\n", - "os.environ.update(env_vars)" + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n" ] }, { @@ -814,20 +821,22 @@ "from glayout.routing.c_route import c_route\n", "from glayout.routing.L_route import L_route\n", "\n", + "# Environment bootstrap — both iic-osic-tools and bare-venv. See HOW_TO_RUN.md.\n", "import os\n", "import subprocess\n", - "\n", - "# Run a shell, source .bashrc, then printenv\n", - "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", - "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", - "env_vars = {}\n", - "for line in result.stdout.splitlines():\n", - " if '=' in line:\n", - " key, value = line.split('=', 1)\n", - " env_vars[key] = value\n", - "\n", - "# Now, update os.environ with these\n", - "os.environ.update(env_vars)\n", + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n", "\n", "def add_inv_labels(\n", " inv_in: Component,\n", @@ -901,6 +910,8 @@ " fet_P = pmos(pdk, width=width[0], fingers=fingers[0], multipliers=multipliers[0], with_dummy=dummy_1, with_substrate_tap=False, length=length[0], tie_layers=tie_layers1, sd_rmult=sd_rmult, **kwargs )\n", " fet_N = nmos(pdk, width=width[1], fingers=fingers[1], multipliers=multipliers[1], with_dummy=dummy_2, with_substrate_tap=False, length=length[1], tie_layers=tie_layers2, sd_rmult=sd_rmult, with_dnwell=False, **kwargs)\n", " \n", + " top_level.info.update({\"fet_P\": fet_P, \"fet_N\": fet_N})\n", + " top_level.info.update({\"fet_P\": fet_P, \"fet_N\": fet_N})\n", " fet_P_ref = top_level << fet_P\n", " fet_N_ref = top_level << fet_N \n", " fet_P_ref.name = \"fet_P\"\n", @@ -990,7 +1001,7 @@ "] \n", "\"\"\"\n", "\n", - "directory = \"../../INV/\"\n", + "directory = \"./INV/\"\n", "os.makedirs(directory, exist_ok=True)\n", "\n", "# Save to a .py file\n", diff --git a/tutorial/glayout_tutorial_INV_part2.ipynb b/tutorial/glayout_tutorial_INV_part2.ipynb index 494b6077..bec0ebc9 100644 --- a/tutorial/glayout_tutorial_INV_part2.ipynb +++ b/tutorial/glayout_tutorial_INV_part2.ipynb @@ -8,7 +8,7 @@ "source": [ "# Tutorial 2: **Notebook** - *FVF*\n", "\n", - "> \ud83d\udea7 **Under Construction**\n", + "> 🚧 **Under Construction**\n", "\n", "**By \n", "gLayout Team**\n", @@ -75,20 +75,27 @@ "metadata": {}, "outputs": [], "source": [ + "# Environment bootstrap — works in both iic-osic-tools and bare-venv setups.\n", + "# iic-osic-tools defines PDK_ROOT / PDK / PDKPATH in ~/.bashrc but jupyter\n", + "# kernels launched from its menu don't inherit them, so we re-source bashrc.\n", + "# setdefault() means we never overwrite variables already exported by the\n", + "# caller. In bare-venv setups, PDKPATH is derived from PDK_ROOT/PDK if not\n", + "# already set. See tutorial/HOW_TO_RUN.md for the env-var contract.\n", "import os\n", "import subprocess\n", - "\n", - "# Run a shell, source .bashrc, then printenv\n", - "cmd = 'bash -c \"source ~/.bashrc && printenv\"'\n", - "result = subprocess.run(cmd, shell=True, text=True, capture_output=True)\n", - "env_vars = {}\n", - "for line in result.stdout.splitlines():\n", - " if '=' in line:\n", - " key, value = line.split('=', 1)\n", - " env_vars[key] = value\n", - "\n", - "# Now, update os.environ with these\n", - "os.environ.update(env_vars)" + "try:\n", + " _printenv = subprocess.run(\n", + " ['bash', '-c', 'source ~/.bashrc 2>/dev/null && printenv'],\n", + " text=True, capture_output=True, timeout=10,\n", + " ).stdout\n", + " for _line in _printenv.splitlines():\n", + " if '=' in _line:\n", + " _k, _v = _line.split('=', 1)\n", + " os.environ.setdefault(_k, _v)\n", + "except Exception:\n", + " pass\n", + "if 'PDK_ROOT' in os.environ and 'PDK' in os.environ:\n", + " os.environ.setdefault('PDKPATH', os.path.join(os.environ['PDK_ROOT'], os.environ['PDK']))\n" ] }, { @@ -98,9 +105,8 @@ "outputs": [], "source": [ "from glayout import MappedPDK, sky130 , gf180\n", - "#from gdsfactory.cell import cell\n", - "# from gdsfactory import Component\n", - "# from gdsfactory.components import text_freetype, rectangle\n", + "from gdsfactory import Component\n", + "from gdsfactory.components import text_freetype, rectangle\n", "\n", "import gdsfactory as gf\n", "gf.clear_cache()" @@ -202,22 +208,22 @@ "# 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", + "# top_level_cell.write_svg('out.svg')\n", "\n", "# # Scale the image for displaying\n", - "# fig = sg.fromfile('../../out.svg')\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", + "# fig.save('out.svg')\n", "\n", "# # Display the image\n", - "# IPython.display.display(IPython.display.SVG('../../out.svg'))\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", + "# component.write_gds(\"out.gds\")\n", "\n", - "# display_gds('../../out.gds', scale)\n", + "# display_gds('out.gds', scale)\n", "\n", "# with hide:\n", "# # Generate the sky130 component\n", @@ -249,7 +255,7 @@ "source": [ "import sys\n", "import os\n", - "sys.path.append(os.path.abspath(\"../../INV\"))\n", + "sys.path.append(os.path.abspath(\"./INV\"))\n", "\n", "from my_INV import inverter,add_inv_labels" ] @@ -270,7 +276,50 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "magicrc_file = Path(os.environ['PDKPATH']) / \"libs.tech\" / \"magic\" / f\"{os.environ['PDK']}.magicrc\"\n", + "\n", + "design_name = comp.name\n", + "path_to_dir = Path(\"./INV\").resolve() / \"ext\" / design_name\n", + "path_to_dir.mkdir(parents=True, exist_ok=True)\n", + "\n", + "pex_path = path_to_dir / f\"{design_name}_pex.spice\"\n", + "gds_path = path_to_dir / f\"{design_name}.gds\"\n", + "comp.write_gds(str(gds_path))\n", + "\n", + "magic_script = f\"\"\"\n", + "drc off\n", + "gds flatglob *\\\\$\\\\$*\n", + "gds read {gds_path}\n", + "flatten {design_name}\n", + "load {design_name}\n", + "select top cell\n", + "extract do local\n", + "extract all\n", + "ext2sim labels on\n", + "ext2sim\n", + "extresist tolerance 10\n", + "extresist\n", + "ext2spice lvs\n", + "ext2spice cthresh 0\n", + "ext2spice extresist on\n", + "ext2spice -o {pex_path}\n", + "exit\n", + "\"\"\"\n", + "with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.tcl') as f:\n", + " f.write(magic_script)\n", + " magic_script_path = f.name\n", + "\n", + "magic_cmd = f\"magic -rcfile {magicrc_file} -noconsole -dnull < {magic_script_path}\"\n", + "res = subprocess.run(magic_cmd, shell=True, capture_output=True, text=True)\n", + "print(res.stdout[-800:])\n", + "if res.returncode != 0:\n", + " print(\"STDERR:\", res.stderr[-800:])\n", + "print(\"Wrote:\", pex_path if pex_path.exists() else '(extraction did not produce a .spice file)')\n" + ] }, { "cell_type": "code", @@ -292,18 +341,21 @@ "metadata": {}, "outputs": [], "source": [ - "def inv_netlist(fet_1: Component, fet_2: Component) -> Netlist:\n", - "\n", + "# Build a SPICE netlist for the inverter using the fet references that\n", + "# my_INV.inverter() stashed on top_level.info (the same pattern FVF uses).\n", + "def inv_netlist(inv_in: Component) -> Component:\n", + " fet_p = inv_in.info[\"fet_P\"]\n", + " fet_n = inv_in.info[\"fet_N\"]\n", " netlist = Netlist(circuit_name='INVERTER', nodes=['VIN', 'VBULK', 'VOUT', 'VDD'])\n", - " \n", - " netlist.connect_netlist(fet_1.info['netlist'], [('D', 'VOUT'), ('G', 'VIN'), ('S', 'VDD'), ('B', 'VBULK')])\n", - " netlist.connect_netlist(fet_2.info['netlist'], [('D', 'VOUT'), ('G', 'VIN'), ('S', 'VBULK'), ('B', 'VBULK')])\n", - " \n", - " return netlist\n", - "\n", - "\n", - "\n", - "comp.info['netlist'] = fvf_netlist(fet_1, fet_2)" + " netlist.connect_netlist(fet_p.info['netlist'],\n", + " [('D', 'VOUT'), ('G', 'VIN'), ('S', 'VDD'), ('B', 'VBULK')])\n", + " netlist.connect_netlist(fet_n.info['netlist'],\n", + " [('D', 'VOUT'), ('G', 'VIN'), ('S', 'VBULK'), ('B', 'VBULK')])\n", + " inv_in.info['netlist'] = netlist\n", + " return inv_in\n", + "\n", + "comp = inv_netlist(comp)\n", + "print(comp.info['netlist'].generate_netlist())\n" ] }, { @@ -312,7 +364,8 @@ "metadata": {}, "outputs": [], "source": [ - "comp\n" + "print('Inverter component:', comp.name)\n", + "print('ports:', list(comp.ports)[:8], '...')\n" ] }, { @@ -330,7 +383,10 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# Run netgen LVS against the labeled inverter layout.\n", + "netgen_lvs_result = gf180.lvs_netgen(comp, comp.name)\n" + ] }, { "cell_type": "markdown", @@ -344,7 +400,50 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "magicrc_file = Path(os.environ['PDKPATH']) / \"libs.tech\" / \"magic\" / f\"{os.environ['PDK']}.magicrc\"\n", + "\n", + "design_name = comp.name\n", + "path_to_dir = Path(\"./INV\").resolve() / \"ext\" / design_name\n", + "path_to_dir.mkdir(parents=True, exist_ok=True)\n", + "\n", + "pex_path = path_to_dir / f\"{design_name}_pex.spice\"\n", + "gds_path = path_to_dir / f\"{design_name}.gds\"\n", + "comp.write_gds(str(gds_path))\n", + "\n", + "magic_script = f\"\"\"\n", + "drc off\n", + "gds flatglob *\\\\$\\\\$*\n", + "gds read {gds_path}\n", + "flatten {design_name}\n", + "load {design_name}\n", + "select top cell\n", + "extract do local\n", + "extract all\n", + "ext2sim labels on\n", + "ext2sim\n", + "extresist tolerance 10\n", + "extresist\n", + "ext2spice lvs\n", + "ext2spice cthresh 0\n", + "ext2spice extresist on\n", + "ext2spice -o {pex_path}\n", + "exit\n", + "\"\"\"\n", + "with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.tcl') as f:\n", + " f.write(magic_script)\n", + " magic_script_path = f.name\n", + "\n", + "magic_cmd = f\"magic -rcfile {magicrc_file} -noconsole -dnull < {magic_script_path}\"\n", + "res = subprocess.run(magic_cmd, shell=True, capture_output=True, text=True)\n", + "print(res.stdout[-800:])\n", + "if res.returncode != 0:\n", + " print(\"STDERR:\", res.stderr[-800:])\n", + "print(\"Wrote:\", pex_path if pex_path.exists() else '(extraction did not produce a .spice file)')\n" + ] } ], "metadata": { From 261154ec6c85c89cacac3b25aae68adee4c5f4d2 Mon Sep 17 00:00:00 2001 From: AL-255 Date: Thu, 28 May 2026 05:29:34 -0400 Subject: [PATCH 6/6] notebooks CI: fail fast if any tutorial notebook still has cell outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds tests/notebooks/check_outputs_cleared.py — a stdlib-only precheck that flags any code cell shipping with execution_count != None or non-empty outputs. notebooks.yml now runs this right after checkout, before the nbconvert step, so a contributor who forgets to clear outputs fails CI in under a second instead of waiting through the ~3 min execution pass. The script also has a --fix mode that strips outputs in-place, mirroring `jupyter nbconvert --clear-output --inplace`, for use as a local pre-push hook. --- .github/workflows/notebooks.yml | 7 ++ tests/notebooks/check_outputs_cleared.py | 119 +++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 tests/notebooks/check_outputs_cleared.py diff --git a/.github/workflows/notebooks.yml b/.github/workflows/notebooks.yml index d5d2390f..c718264a 100644 --- a/.github/workflows/notebooks.yml +++ b/.github/workflows/notebooks.yml @@ -54,6 +54,13 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Check that notebook outputs are cleared + # Fail fast (sub-second, stdlib-only) before the multi-minute + # nbconvert pass. Catches PRs that ship notebooks with stale outputs + # — those bloat the diff and defeat the cleared-outputs convention + # the repo settled on in PR #89. + run: python3 tests/notebooks/check_outputs_cleared.py + - name: Cache uv + CPython 3.10 id: cache-uv uses: actions/cache@v4 diff --git a/tests/notebooks/check_outputs_cleared.py b/tests/notebooks/check_outputs_cleared.py new file mode 100644 index 00000000..a8eb4c75 --- /dev/null +++ b/tests/notebooks/check_outputs_cleared.py @@ -0,0 +1,119 @@ +"""Fail if any tutorial notebook ships with cell outputs or execution counts. + +Run as a CI precheck before ``run_tutorial_notebooks.py``. Stdlib-only so it +can run before any venv is set up — fails in a fraction of a second when a +contributor forgets to strip outputs, instead of after the multi-minute +nbconvert pass. + +Usage: + python tests/notebooks/check_outputs_cleared.py + python tests/notebooks/check_outputs_cleared.py --fix # strip in-place + +A code cell counts as dirty if either: + * ``execution_count`` is not None (i.e. the cell has been run), or + * ``outputs`` is non-empty. + +Markdown / raw cells are ignored. Cell metadata (e.g. ``metadata.execution``, +``metadata.widgets``) is left alone — we only care about renderable outputs. +""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import List, Tuple + + +REPO_ROOT = Path(__file__).resolve().parents[2] +TUTORIAL_DIR = REPO_ROOT / "tutorial" + + +def _scan(nb_path: Path) -> List[Tuple[int, str]]: + """Return [(cell_index, reason), ...] for every dirty code cell.""" + try: + nb = json.loads(nb_path.read_text()) + except (OSError, json.JSONDecodeError) as e: + return [(-1, f"unreadable notebook: {e}")] + dirty: List[Tuple[int, str]] = [] + for i, c in enumerate(nb.get("cells", [])): + if c.get("cell_type") != "code": + continue + if c.get("execution_count") is not None: + dirty.append((i, f"execution_count={c['execution_count']!r}")) + if c.get("outputs"): + dirty.append((i, f"{len(c['outputs'])} output(s)")) + return dirty + + +def _clear(nb_path: Path) -> bool: + """Strip outputs in-place. Returns True if the file changed.""" + nb = json.loads(nb_path.read_text()) + changed = False + for c in nb.get("cells", []): + if c.get("cell_type") != "code": + continue + if c.get("execution_count") is not None: + c["execution_count"] = None + changed = True + if c.get("outputs"): + c["outputs"] = [] + changed = True + if changed: + nb_path.write_text(json.dumps(nb, indent=1, ensure_ascii=False) + "\n") + return changed + + +def main() -> int: + p = argparse.ArgumentParser() + p.add_argument("--fix", action="store_true", + help="Strip outputs in-place instead of just reporting.") + args = p.parse_args() + + nbs = sorted( + nb for nb in TUTORIAL_DIR.rglob("*.ipynb") + if ".ipynb_checkpoints" not in nb.parts + ) + if not nbs: + print("no tutorial notebooks found", file=sys.stderr) + return 2 + + bad: List[Tuple[Path, List[Tuple[int, str]]]] = [] + for nb in nbs: + if args.fix: + if _clear(nb): + print(f"[FIXED] {nb.relative_to(REPO_ROOT)}") + continue + dirty = _scan(nb) + if dirty: + bad.append((nb, dirty)) + + if args.fix: + return 0 + + if not bad: + print(f"OK: all {len(nbs)} tutorial notebook(s) have cleared outputs.") + return 0 + + print("FAIL: the following notebooks ship with non-cleared outputs:\n", file=sys.stderr) + for nb, dirty in bad: + rel = nb.relative_to(REPO_ROOT) + # Cap the noise — one line per offending cell, first 10 cells per nb. + shown = dirty[:10] + more = len(dirty) - len(shown) + for i, why in shown: + print(f" {rel}: cell {i}: {why}", file=sys.stderr) + if more > 0: + print(f" {rel}: ... and {more} more", file=sys.stderr) + print( + "\nFix:\n" + " jupyter nbconvert --clear-output --inplace tutorial/**/*.ipynb tutorial/**/**/*.ipynb\n" + "or:\n" + " python tests/notebooks/check_outputs_cleared.py --fix", + file=sys.stderr, + ) + return 1 + + +if __name__ == "__main__": + sys.exit(main())