diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Divide_by_2.ipynb b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Divide_by_2.ipynb new file mode 100644 index 000000000..2a15d652c --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Divide_by_2.ipynb @@ -0,0 +1,219 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Divide by two circuit \n", + "=========AUTHORS===========\n", + "Dr. Kalenteridis Vasileios \n", + "Batzolis Eleftherios" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A5eacEPxidt-" + }, + "source": [ + "Python Code Overview\n", + "The Python code in this Jupyter notebook builds and places components in the divide-by-2 circuit using the sky130 PDK and gdsfactory layout library.\n", + "\n", + "create_nmos_latch Function: Defines a single NMOS-based latch with eight NMOS transistors. Transistors are arranged in the current mirror, transmission gate, and cross-coupled inverter configurations. The function:\n", + "\n", + "Creates NMOS transistors with specific widths and lengths.\n", + "Positions and connects these transistors using both straight and C-routes for proper wiring.\n", + "Adds ports for input (D, Dp), clock (CLK, CLKN), output (Q, Qp), and power (VDD, VSS).\n", + "create_divide_by_two_circuit Function: Builds the entire divide-by-2 circuit by instantiating two NMOS latches.\n", + "\n", + "Places the second latch below the first latch with adequate separation.\n", + "Connects the output of the first latch to the input of the second and vice versa (feedback).\n", + "Adds ports for the clock (CLK, CLKN), output (OUT, OUT_B), and power (VDD, VSS).\n", + "Connects power ports of both latches to ensure consistent power distribution.\n", + "Circuit Display and GDS Export:\n", + "\n", + "After creating the divide-by-two component, display_component shows a scaled view of the component.\n", + "The component is written to a GDS file, divide_two.gds, for further physical verification and layout." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "z1O_2qcOtc4u" + }, + "outputs": [], + "source": [ + "# Setup the environment for the OpenFASOC GDSFactory generator\n", + "# You only need to run this block once!\n", + "\n", + "# Clone OpenFASoC\n", + "\n", + "!git clone https://github.com/idea-fasoc/OpenFASOC\n", + "#used for testing \n", + "#!git clone https://github.com/Lefteris-B/OpenFASOC\n", + "\n", + "# Install python dependencies\n", + "!pip install sky130\n", + "!pip install gf180 prettyprinttree svgutils\n", + "!pip install gdsfactory==7.7.0\n", + "\n", + "import pathlib\n", + "import os\n", + "\n", + "# Install KLayout (via conda)\n", + "!curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba\n", + "conda_prefix_path = pathlib.Path('conda-env')\n", + "CONDA_PREFIX = str(conda_prefix_path.resolve())\n", + "%env CONDA_PREFIX={CONDA_PREFIX}\n", + "\n", + "!bin/micromamba create --yes --prefix $CONDA_PREFIX\n", + "\n", + "# Install from the litex-hub channel\n", + "!bin/micromamba install --yes --prefix $CONDA_PREFIX \\\n", + " --channel litex-hub \\\n", + " --channel main \\\n", + " klayout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Q42D2FwTtnep" + }, + "outputs": [], + "source": [ + "# Setup the environment for the OpenFASOC GDSFactory generator\n", + "\n", + "# Adding micro-mamba binary directory to the PATH\n", + "# This directory contains Klayout\n", + "import pathlib\n", + "import os\n", + "conda_prefix_path = pathlib.Path('conda-env')\n", + "CONDA_PREFIX = str(conda_prefix_path.resolve())\n", + "%env CONDA_PREFIX={CONDA_PREFIX}\n", + "# Add conda packages to the PATH\n", + "PATH = os.environ['PATH']\n", + "%env PATH={PATH}:{CONDA_PREFIX}/bin\n", + "\n", + "%cd /content/OpenFASOC/openfasoc/generators/glayout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QBLvF3RmWLDs" + }, + "outputs": [], + "source": [ + "# Intall glayout\n", + "%cd /content/OpenFASOC/openfasoc/generators/glayout\n", + "!pip install -e ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7PzrVo7iW1Jf" + }, + "outputs": [], + "source": [ + "# Confirm Instalation\n", + "!pip show glayout" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "QoIgABmKWS6h" + }, + "outputs": [], + "source": [ + "import glayout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "WyTs66GIwOdh", + "outputId": "60be3b67-fa27-45b6-b8c7-64aced026d9a" + }, + "outputs": [], + "source": [ + "%cd /content/OpenFASOC/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys\n", + "from latch_design import create_nmos_latch_layout\n", + "from divide_by_two import create_divide_by_two_circuit\n", + "from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as sky130\n", + "import gdstk\n", + "import svgutils.transform as sg\n", + "import IPython.display\n", + "from IPython.display import clear_output\n", + "import ipywidgets as widgets\n", + "\n", + "# Redirect all outputs here\n", + "hide = widgets.Output()\n", + "\n", + "def display_gds(gds_file, scale = 3):\n", + " # Generate an SVG image\n", + " top_level_cell = gdstk.read_gds(gds_file).top_level()[0]\n", + " top_level_cell.write_svg('out.svg')\n", + " # Scale the image for displaying\n", + " fig = sg.fromfile('out.svg')\n", + " fig.set_size((str(float(fig.width) * scale), str(float(fig.height) * scale)))\n", + " fig.save('out.svg')\n", + "\n", + " # Display the image\n", + " IPython.display.display(IPython.display.SVG('out.svg'))\n", + "\n", + "def display_component(component, scale = 3):\n", + " # Save to a GDS file\n", + " with hide:\n", + " component.write_gds(\"out.gds\")\n", + " display_gds('out.gds', scale)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4f71z53aiduD" + }, + "outputs": [], + "source": [ + "# Create and display the designs\n", + "latch = create_nmos_latch_layout(sky130)\n", + "display_component(latch, scale=3)\n", + "\n", + "divider = create_divide_by_two_circuit(sky130)\n", + "display_component(divider, scale=3)\n", + "\n", + "# Save GDS files\n", + "latch.write_gds(\"latch.gds\")\n", + "divider.write_gds(\"divide_two.gds\")" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Licence.md b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Licence.md new file mode 100644 index 000000000..b616c02c6 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Licence.md @@ -0,0 +1,13 @@ +Copyright [2024] [Kalantaridis Vasileios , Batzolis Eleftherios] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Readme.md b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Readme.md new file mode 100644 index 000000000..c0f63d428 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/Readme.md @@ -0,0 +1,29 @@ +# Divide-by-2 Frequency Divider Circuit + +This project implements a digital frequency divider circuit using NMOS-only transistors, developed with the `sky130` PDK (Process Design Kit) for the OpenFASOC platform. The circuit design and layout are generated programmatically using Python in a Jupyter notebook and `gdsfactory` library. + +## Circuit Overview + +The Divide-by-2 frequency divider circuit is composed of two cross-coupled NMOS latches arranged to halve the input clock frequency. It has applications in digital clock management, signal processing, and low-power frequency division circuits. + +### Key Components + +- **NMOS Latches**: Two NMOS-based latches (flip-flops) form the core of the circuit. +- **Cross-Coupled Inverters**: Cross-coupled inverters within each latch store binary states (Q and Q') for feedback stability. +- **Current Mirror and Transmission Gates**: These NMOS structures facilitate controlled switching and stable latch operation. +- **Feedback Routing**: Feedback from the output of the second latch to the input of the first latch ensures accurate toggling and frequency division. + +## Repository Contents + +- **`Divide_by_2.ipynb`**: Jupyter notebook containing the Python code for building and simulating the divide-by-2 circuit layout. +- **`divide_two.gds`**: Generated GDS file of the layout, ready for physical verification and integration with other IC designs. +- **`README.md`**: Documentation file describing the project and usage. + +## Prerequisites + +- **Python** (3.7 or later) +- **Jupyter Notebook** +- **gdsfactory**: For creating and visualizing IC layouts +- **sky130 PDK**: Process Design Kit from SkyWater, available [here](https://github.com/google/skywater-pdk) +- **glayout**: Layout tools compatible with `gdsfactory` and SkyWater PDK + diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/divide_by_two.py b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/divide_by_two.py new file mode 100644 index 000000000..6925d58ae --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/divide_by_two.py @@ -0,0 +1,30 @@ +from gdsfactory import Component +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.routing.c_route import c_route +from latch_design import create_nmos_latch_layout + +def create_divide_by_two_circuit(pdk): + divider = Component("divide_by_two") + + # Create two latch instances + latch1 = divider << create_nmos_latch_layout(pdk) + latch2 = divider << create_nmos_latch_layout(pdk) + + # Position and connect the latches + latch2.movey(-latch1.size[1] - pdk.util_max_metal_seperation()) + divider << straight_route(pdk, latch1.ports["Q"], latch2.ports["D"]) + divider << straight_route(pdk, latch1.ports["Qp"], latch2.ports["Dp"]) + divider << c_route(pdk, latch2.ports["Q"], latch1.ports["D"]) + divider << c_route(pdk, latch2.ports["Qp"], latch1.ports["Dp"]) + + # Add external ports and connect power + divider.add_port("CLK", port=latch1.ports["CLK"]) + divider.add_port("CLKN", port=latch1.ports["CLKN"]) + divider.add_port("OUT", port=latch2.ports["Q"]) + divider.add_port("OUT_B", port=latch2.ports["Qp"]) + divider.add_port("VDD", port=latch1.ports["VDD"]) + divider.add_port("VSS", port=latch1.ports["VSS"]) + divider << straight_route(pdk, latch1.ports["VDD"], latch2.ports["VDD"]) + divider << straight_route(pdk, latch1.ports["VSS"], latch2.ports["VSS"]) + + return divider diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/latch_design.py b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/latch_design.py new file mode 100644 index 000000000..79c5a48b1 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/Divide_by_two_SystemsGenesys/latch_design.py @@ -0,0 +1,49 @@ +from gdsfactory import Component +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as sky130 +from glayout.flow.primitives.fet import nmos +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.routing.c_route import c_route + +def create_nmos_latch_layout(pdk): + latch = Component("nmos_latch") + # Create and configure NMOS transistors + m2 = latch << nmos(pdk, width=1, length=0.15) + m7 = latch << nmos(pdk, width=1, length=0.15) + m3 = latch << nmos(pdk, width=2, length=0.15) + m4 = latch << nmos(pdk, width=2, length=0.15) + m5 = latch << nmos(pdk, width=2, length=0.15) + m6 = latch << nmos(pdk, width=2, length=0.15) + m8 = latch << nmos(pdk, width=2, length=0.15) + + # Positioning and routing logic + spacing = pdk.util_max_metal_seperation() + m2.size[0] + m7.movex(6 * spacing) + m3.movex(2 * spacing) + m4.movex(3 * spacing) + m5.movex(4 * spacing) + m6.movex(5 * spacing) + m8.movex(spacing) + + # Routing and connections + latch << straight_route(pdk, m2.ports["multiplier_0_drain_E"], m8.ports["multiplier_0_source_E"]) + latch << straight_route(pdk, m7.ports["multiplier_0_drain_E"], m6.ports["multiplier_0_source_E"]) + latch << straight_route(pdk, m3.ports["multiplier_0_drain_E"], m4.ports["multiplier_0_drain_E"]) + latch << straight_route(pdk, m4.ports["multiplier_0_drain_E"], m5.ports["multiplier_0_drain_E"]) + latch << straight_route(pdk, m3.ports["multiplier_0_gate_E"], m4.ports["multiplier_0_gate_E"]) + latch << straight_route(pdk, m4.ports["multiplier_0_gate_E"], m5.ports["multiplier_0_gate_E"]) + latch << c_route(pdk, m6.ports["multiplier_0_drain_E"], m8.ports["multiplier_0_gate_E"]) + latch << c_route(pdk, m8.ports["multiplier_0_drain_E"], m6.ports["multiplier_0_gate_E"]) + latch << straight_route(pdk, m6.ports["multiplier_0_source_E"], m5.ports["multiplier_0_drain_E"]) + latch << straight_route(pdk, m8.ports["multiplier_0_source_E"], m5.ports["multiplier_0_drain_E"]) + + # Ports for external connections + latch.add_port("D", port=m2.ports["multiplier_0_source_E"]) + latch.add_port("Dp", port=m7.ports["multiplier_0_source_E"]) + latch.add_port("CLK", port=m2.ports["multiplier_0_gate_E"]) + latch.add_port("CLKN", port=m7.ports["multiplier_0_gate_E"]) + latch.add_port("Q", port=m6.ports["multiplier_0_drain_E"]) + latch.add_port("Qp", port=m8.ports["multiplier_0_drain_E"]) + latch.add_port("VDD", port=m3.ports["multiplier_0_drain_E"]) + latch.add_port("VSS", port=m5.ports["multiplier_0_source_E"]) + + return latch