Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
b5a743922This file is pinned to the selected release. Download the original to open it in your design tool.
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## OpenLane Colab\n",
"\n",
"This Google Colab notebook will:\n",
"* Install OpenLane and its dependencies\n",
"* Run a simple design, namely a serial-parallel multiplier, through the flow\n",
" and targeting the [open source sky130 PDK](https://github.com/google/skywater-pdk/)\n",
" by Google and Skywater."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# @title Setup Nix {display-mode: \"form\"}\n",
"# @markdown <img src=\"https://raw.githubusercontent.com/NixOS/nixos-artwork/master/logo/nix-snowflake.svg\" width=\"32\"/>\n",
"# @markdown\n",
"# @markdown Nix is a package manager with an emphasis on reproducible builds,\n",
"# @markdown and it is the primary method for installing OpenLane 2.\n",
"# @markdown\n",
"# @markdown This step installs the Nix package manager and enables the\n",
"# @markdown experimental \"flakes\" feature.\n",
"# @markdown\n",
"# @markdown If you're not in a Colab, this just sets the environment variables.\n",
"# @markdown You will need to install Nix and enable flakes on your own following\n",
"# @markdown [this guide](https://openlane2.readthedocs.io/en/stable/getting_started/common/nix_installation/index.html).\n",
"import os\n",
"import sys\n",
"import shutil\n",
"\n",
"os.environ[\"LOCALE_ARCHIVE\"] = \"/usr/lib/locale/locale-archive\"\n",
"\n",
"if \"google.colab\" in sys.modules:\n",
" if shutil.which(\"nix-env\") is None:\n",
" !curl -L https://nixos.org/nix/install | bash -s -- --daemon --yes\n",
" !echo \"extra-experimental-features = nix-command flakes\" >> /etc/nix/nix.conf\n",
" !killall nix-daemon\n",
"else:\n",
" if shutil.which(\"nix-env\") is None:\n",
" raise RuntimeError(\"Nix is not installed!\")\n",
"\n",
"os.environ[\"PATH\"] = f\"/nix/var/nix/profiles/default/bin/:{os.getenv('PATH')}\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# @title Get OpenLane {display-mode: \"form\"}\n",
"# @markdown Click the ▷ button to download and install OpenLane.\n",
"# @markdown\n",
"# @markdown This will install OpenLane's tool dependencies using Nix,\n",
"# @markdown and OpenLane itself using PIP.\n",
"# @markdown\n",
"# @markdown Note that `python3-tk` may need to be installed using your OS's\n",
"# @markdown package manager.\n",
"import os\n",
"import subprocess\n",
"import IPython\n",
"\n",
"openlane_version = \"latest\" # @param {key:\"OpenLane Version\", type:\"string\"}\n",
"\n",
"if openlane_version == \"latest\":\n",
" openlane_version = \"main\"\n",
"\n",
"pdk_root = \"~/.volare\" # @param {key:\"PDK Root\", type:\"string\"}\n",
"\n",
"pdk_root = os.path.expanduser(pdk_root)\n",
"\n",
"pdk = \"sky130\" # @param {key:\"PDK (without the variant)\", type:\"string\"}\n",
"\n",
"openlane_ipynb_path = os.path.join(os.getcwd(), \"openlane_ipynb\")\n",
"\n",
"display(IPython.display.HTML(\"<h3>Downloading OpenLane…</a>\"))\n",
"\n",
"\n",
"TESTING_LOCALLY = False\n",
"!rm -rf {openlane_ipynb_path}\n",
"!mkdir -p {openlane_ipynb_path}\n",
"if TESTING_LOCALLY:\n",
" !ln -s {os.getcwd()} {openlane_ipynb_path}\n",
"else:\n",
" !curl -L \"https://github.com/efabless/openlane2/tarball/{openlane_version}\" | tar -xzC {openlane_ipynb_path} --strip-components 1\n",
"\n",
"try:\n",
" import tkinter\n",
"except ImportError:\n",
" if \"google.colab\" in sys.modules:\n",
" !sudo apt-get install python-tk\n",
"\n",
"try:\n",
" import tkinter\n",
"except ImportError as e:\n",
" display(\n",
" IPython.display.HTML(\n",
" '<h3 style=\"color: #800020\";>❌ Failed to import the <code>tkinter</code> library for Python, which is required to load PDK configuration values. Make sure <code>python3-tk</code> or equivalent is installed on your system.</a>'\n",
" )\n",
" )\n",
" raise e from None\n",
"\n",
"\n",
"display(IPython.display.HTML(\"<h3>Downloading OpenLane's dependencies…</a>\"))\n",
"try:\n",
" subprocess.check_call(\n",
" [\"nix\", \"profile\", \"install\", \".#colab-env\", \"--accept-flake-config\"],\n",
" cwd=openlane_ipynb_path,\n",
" )\n",
"except subprocess.CalledProcessError as e:\n",
" display(\n",
" IPython.display.HTML(\n",
" '<h3 style=\"color: #800020\";>❌ Failed to install binary dependencies using Nix…</h3>'\n",
" )\n",
" )\n",
"\n",
"display(IPython.display.HTML(\"<h3>Downloading Python dependencies using PIP…</a>\"))\n",
"try:\n",
" subprocess.check_call(\n",
" [\"pip3\", \"install\", \".\"],\n",
" cwd=openlane_ipynb_path,\n",
" )\n",
"except subprocess.CalledProcessError as e:\n",
" display(\n",
" IPython.display.HTML(\n",
" '<h3 style=\"color: #800020\";>❌ Failed to install Python dependencies using PIP…</h3>'\n",
" )\n",
" )\n",
" raise e from None\n",
"\n",
"display(IPython.display.HTML(\"<h3>Downloading PDK…</a>\"))\n",
"import volare\n",
"\n",
"volare.enable(\n",
" volare.get_volare_home(pdk_root),\n",
" pdk,\n",
" open(\n",
" os.path.join(openlane_ipynb_path, \"openlane\", \"open_pdks_rev\"),\n",
" encoding=\"utf8\",\n",
" )\n",
" .read()\n",
" .strip(),\n",
")\n",
"\n",
"sys.path.insert(0, openlane_ipynb_path)\n",
"display(IPython.display.HTML(\"<h3>⭕️ Done.</a>\"))\n",
"\n",
"import logging\n",
"\n",
"# Remove the stupid default colab logging handler\n",
"logging.getLogger().handlers.clear()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import openlane\n",
"\n",
"print(openlane.__version__)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Creating the design\n",
"\n",
"Now that OpenLane is set up, we can write a Verilog file as follows:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%writefile spm.v\n",
"module spm(clk, rst, x, y, p);\n",
" parameter size = 32;\n",
" input clk, rst;\n",
" input y;\n",
" input[size-1:0] x;\n",
" output p;\n",
"\n",
" wire[size-1:1] pp;\n",
" wire[size-1:0] xy;\n",
"\n",
" genvar i;\n",
"\n",
" CSADD csa0 (.clk(clk), .rst(rst), .x(x[0]&y), .y(pp[1]), .sum(p));\n",
" generate for(i=1; i<size-1; i=i+1) begin\n",
" CSADD csa (.clk(clk), .rst(rst), .x(x[i]&y), .y(pp[i+1]), .sum(pp[i]));\n",
" end endgenerate\n",
" TCMP tcmp (.clk(clk), .rst(rst), .a(x[size-1]&y), .s(pp[size-1]));\n",
"\n",
"endmodule\n",
"\n",
"module TCMP(clk, rst, a, s);\n",
" input clk, rst;\n",
" input a;\n",
" output reg s;\n",
"\n",
" reg z;\n",
"\n",
" always @(posedge clk or posedge rst) begin\n",
" if (rst) begin\n",
" //Reset logic goes here.\n",
" s <= 1'b0;\n",
" z <= 1'b0;\n",
" end\n",
" else begin\n",
" //Sequential logic goes here.\n",
" z <= a | z;\n",
" s <= a ^ z;\n",
" end\n",
" end\n",
"endmodule\n",
"\n",
"module CSADD(clk, rst, x, y, sum);\n",
" input clk, rst;\n",
" input x, y;\n",
" output reg sum;\n",
"\n",
" reg sc;\n",
"\n",
" // Half Adders logic\n",
" wire hsum1, hco1;\n",
" assign hsum1 = y ^ sc;\n",
" assign hco1 = y & sc;\n",
"\n",
" wire hsum2, hco2;\n",
" assign hsum2 = x ^ hsum1;\n",
" assign hco2 = x & hsum1;\n",
"\n",
" always @(posedge clk or posedge rst) begin\n",
" if (rst) begin\n",
" //Reset logic goes here.\n",
" sum <= 1'b0;\n",
" sc <= 1'b0;\n",
" end\n",
" else begin\n",
" //Sequential logic goes here.\n",
" sum <= hsum2;\n",
" sc <= hco1 ^ hco2;\n",
" end\n",
" end\n",
"endmodule"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Setting up the configuration\n",
"\n",
"OpenLane requries you to configure any Flow before using it. This is done using\n",
"the `config` module.\n",
"\n",
"For colaboratories, REPLs and other interactive environments where there is no\n",
"concrete Flow object, the Configuration may be initialized using `Config.interactive`,\n",
"which will automatically propagate the configuration to any future steps.\n",
"\n",
"You can find the documentation for `Config.interactive` [here](https://openlane2.readthedocs.io/en/latest/reference/api/config/index.html#openlane.config.Config.interactive).\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from openlane.config import Config\n",
"\n",
"Config.interactive(\n",
" \"spm\",\n",
" PDK=\"sky130A\",\n",
" CLOCK_PORT=\"clk\",\n",
" CLOCK_NET=\"clk\",\n",
" CLOCK_PERIOD=10,\n",
" PRIMARY_GDSII_STREAMOUT_TOOL=\"klayout\",\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Running implementation steps\n",
"\n",
"There are two ways to obtain OpenLane's built-in implementation steps:\n",
"\n",
"* via directly importing from the `steps` module using its category:\n",
" * `from openlane.steps import Yosys` then `Synthesis = Yosys.Synthesis`\n",
"* by using the step's id from the registry:\n",
" * `from openlane.steps import Step` then `Synthesis = Step.factory.get(\"Yosys.Synthesis\")`\n",
"\n",
"You can find a full list of included steps here: https://openlane2.readthedocs.io/en/latest/reference/step_config_vars.html"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from openlane.steps import Step"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"* First, get the step (and display its help)..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Synthesis = Step.factory.get(\"Yosys.Synthesis\")\n",
"\n",
"Synthesis.display_help()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"* Then run it. Note you can pass step-specific configs using Python keyword\n",
" arguments."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Synthesis\n",
"\n",
"We need to start by converting our high-level Verilog to one that just shows\n",
"the connections between small silicon patterns called \"standard cells\" in process\n",
"called Synthesis. We can do this by passing the Verilog files as a configuration\n",
"variable to `Yosys.Synthesis` as follows, then running it.\n",
"\n",
"As this is the first step, we need to create an empty state and pass it to it."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"from openlane.state import State\n",
"\n",
"synthesis = Synthesis(\n",
" VERILOG_FILES=[\"./spm.v\"],\n",
" state_in=State(),\n",
")\n",
"synthesis.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(synthesis)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Floorplanning\n",
"\n",
"Floorplanning does two things:\n",
"\n",
"* Determines the dimensions of the final chip. \n",
"* Creates the \"cell placement grid\" which placed cells must be aligned to.\n",
" * Each cell in the grid is called a \"site.\" Cells can occupy multiple\n",
" sites, with the overwhelming majority of cells occupying multiple sites\n",
" by width, and some standard cell libraries supporting varying heights as well.\n",
"\n",
"> Don't forget- you may call `display_help()` on any Step class to get a full\n",
"> list of configuration variables.\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Floorplan = Step.factory.get(\"OpenROAD.Floorplan\")\n",
"\n",
"floorplan = Floorplan(state_in=synthesis.state_out)\n",
"floorplan.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(floorplan)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Tap/Endcap Cell Insertion\n",
"\n",
"This places two kinds of cells on the floorplan:\n",
"\n",
"* End cap/boundary cells: Added at the beginning and end of each row. True to\n",
" their name, they \"cap off\" the core area of a design.\n",
"* Tap cells: Placed in a polka dot-ish fashion across the rows. Tap cells\n",
" connect VDD to the nwell and the psubstrate to VSS, which the majority of cells\n",
" do not do themselves to save area- but if you go long enough without one such\n",
" connection you end up with the cell \"latching-up\"; i.e.; refusing to switch\n",
" back to LO from HI.\n",
"\n",
" There is a maximum distance between tap cells enforced as part of every\n",
" foundry process."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"TapEndcapInsertion = Step.factory.get(\"OpenROAD.TapEndcapInsertion\")\n",
"\n",
"tdi = TapEndcapInsertion(state_in=floorplan.state_out)\n",
"tdi.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(tdi)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### I/O Placement\n",
"\n",
"This places metal pins at the edges of the design corresponding to the top level\n",
"inputs and outputs for your design. These pins act as the interface with other\n",
"designs when you integrate it with other designs."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"IOPlacement = Step.factory.get(\"OpenROAD.IOPlacement\")\n",
"\n",
"ioplace = IOPlacement(state_in=tdi.state_out)\n",
"ioplace.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(ioplace)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Generating the Power Distribution Network (PDN)\n",
"\n",
"This creates the power distribution network for your design, which is essentially\n",
"a plaid pattern of horizontal and vertical \"straps\" across the design that is\n",
"then connected to the rails' VDD and VSS (via the tap cells.)\n",
"\n",
"You can find an explanation of how the power distribution network works at this\n",
"link: https://openlane2.readthedocs.io/en/latest/usage/hardening_macros.html#pdn-generation\n",
"\n",
"While we typically don't need to mess with the PDN too much, the SPM is a small\n",
"design, so we're going to need to make the plaid pattern formed by the PDN a bit\n",
"smaller."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"GeneratePDN = Step.factory.get(\"OpenROAD.GeneratePDN\")\n",
"\n",
"pdn = GeneratePDN(\n",
" state_in=ioplace.state_out,\n",
" FP_PDN_VWIDTH=2,\n",
" FP_PDN_HWIDTH=2,\n",
" FP_PDN_VPITCH=30,\n",
" FP_PDN_HPITCH=30,\n",
")\n",
"pdn.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(pdn)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Global Placement\n",
"\n",
"Global Placement is deciding on a fuzzy, non-final location for each of the cells,\n",
"with the aim of minimizing the distance between cells that are connected\n",
"together (more specifically, the total length of the not-yet-created wires that\n",
"will connect them).\n",
"\n",
"As you will see in the `.display()` in the second cell below, the placement is\n",
"considered \"illegal\", i.e., not properly aligned with the cell placement grid.\n",
"This is addressed by \"Detailed Placement\", also referred to as \"placement\n",
"legalization\", which is the next step."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"GlobalPlacement = Step.factory.get(\"OpenROAD.GlobalPlacement\")\n",
"\n",
"gpl = GlobalPlacement(state_in=pdn.state_out)\n",
"gpl.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(gpl)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Detailed Placement\n",
"\n",
"This aligns the fuzzy placement from before with the grid, \"legalizing\" it."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"DetailedPlacement = Step.factory.get(\"OpenROAD.DetailedPlacement\")\n",
"\n",
"dpl = DetailedPlacement(state_in=gpl.state_out)\n",
"dpl.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(dpl)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Clock Tree Synthesis (CTS)\n",
"\n",
"With the cells now having a final placement, we can go ahead and create what\n",
"is known as the clock tree, i.e., the hierarchical set of buffers used\n",
"for clock signal to minimize what is known as \"clock skew\"- variable delay\n",
"of the clock cycle from register to register because of factors such as metal\n",
"wire length, clock load (number of gates connected to the same clock buffer,)\n",
"et cetera. \n",
"\n",
"The CTS step creates the cells and places the between the gaps in the detailed\n",
"placement above."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"CTS = Step.factory.get(\"OpenROAD.CTS\")\n",
"\n",
"cts = CTS(state_in=dpl.state_out)\n",
"cts.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(cts)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Global Routing\n",
"\n",
"Global routing \"plans\" the routes the wires between two gates (or gates and\n",
"I/O pins/the PDN) will take. The results of global routing (which are called\n",
"\"routing guides\") are stored in internal data structures and have no effect on\n",
"the actual design, so there is no `display()` statement."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"GlobalRouting = Step.factory.get(\"OpenROAD.GlobalRouting\")\n",
"\n",
"grt = GlobalRouting(state_in=cts.state_out)\n",
"grt.start()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Detailed Routing\n",
"\n",
"Detailed routing uses the guides from Global Routing to actually create wires\n",
"on the metal layers and connect the gates, making the connections finally physical.\n",
"\n",
"This is typically the longest step in the flow."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"DetailedRouting = Step.factory.get(\"OpenROAD.DetailedRouting\")\n",
"\n",
"drt = DetailedRouting(state_in=grt.state_out)\n",
"drt.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(drt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fill Insertion\n",
"\n",
"Finally, as we're done placing all the essential cells, the only thing left to\n",
"do is fill in the gaps.\n",
"\n",
"We prioritize the use of decap (decoupling capacitor) cells, which\n",
"further supports the power distribution network, but when there aren't any\n",
"small enough cells, we just use regular fill cells."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"FillInsertion = Step.factory.get(\"OpenROAD.FillInsertion\")\n",
"\n",
"fill = FillInsertion(state_in=drt.state_out)\n",
"fill.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(fill)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Parasitics Extraction a.k.a. Resistance/Capacitance Extraction (RCX)\n",
"\n",
"This step does not alter the design- rather, it computes the\n",
"[Parasitic elements](https://en.wikipedia.org/wiki/Parasitic_element_(electrical_networks))\n",
"of the circuit, which have an effect of timing, as we prepare to do the final\n",
"timing analysis.\n",
"\n",
"The parasitic elements are saved in the **Standard Parasitics Exchange Format**,\n",
"or SPEF. OpenLane creates a SPEF file for each interconnect corner as described in\n",
"the [Corners and STA](https://openlane2.readthedocs.io/en/latest/usage/corners_and_sta.html)\n",
"section of the documentation."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"RCX = Step.factory.get(\"OpenROAD.RCX\")\n",
"\n",
"rcx = RCX(state_in=fill.state_out)\n",
"rcx.start()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Static Timing Analysis (Post-PnR)\n",
"\n",
"STA is a process that verifies that a chip meets certain constraints on clock\n",
"and data timings to run at its rated clock speed. See [Corners and STA](https://openlane2.readthedocs.io/en/latest/usage/corners_and_sta.html)\n",
"in the documentation for more info.\n",
"\n",
"---\n",
"\n",
"This step generates two kinds of files:\n",
"* `.lib`: Liberty™-compatible Library files. Can be used to do static timing\n",
" analysis when creating a design with this design as a sub-macro.\n",
"* `.sdf`: Standard Delay Format. Can be used with certain simulation software\n",
" to do *dynamic* timing analysis.\n",
"\n",
"Unfortunately, the `.lib` files coming out of OpenLane right now are not super\n",
"reliable for timing purposes and are only provided for completeness.\n",
"When using OpenLane-created macros withing other designs, it is best to use the\n",
"macro's final netlist and extracted parasitics instead."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"STAPostPNR = Step.factory.get(\"OpenROAD.STAPostPNR\")\n",
"\n",
"sta_post_pnr = STAPostPNR(state_in=rcx.state_out)\n",
"sta_post_pnr.start()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Stream-out\n",
"\n",
"Stream-out is the process of converting the designs from the abstract formats\n",
"using during floorplanning, placement and routing into a concrete format called\n",
"GDSII (lit. Graphic Design System 2), which is the final file that is then sent\n",
"for fabrication."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"StreamOut = Step.factory.get(\"KLayout.StreamOut\")\n",
"\n",
"gds = StreamOut(state_in=sta_post_pnr.state_out)\n",
"gds.start()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(gds)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Design Rule Checks (DRC)\n",
"\n",
"DRC determines that the final layout does not violate any of the rules set by\n",
"the foundry to ensure the design is actually manufacturable- for example,\n",
"not enough space between two wires, *too much* space between tap cells, and so\n",
"on.\n",
"\n",
"A design not passing DRC will typically be rejected by the foundry, who\n",
"also run DRC on their side."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"DRC = Step.factory.get(\"Magic.DRC\")\n",
"\n",
"drc = DRC(state_in=gds.state_out)\n",
"drc.start()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### SPICE Extraction for Layout vs. Schematic Check\n",
"\n",
"This step tries to reconstruct a SPICE netlist from the GDSII file, so it can\n",
"later be used for the **Layout vs. Schematic** (LVS) check."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"SpiceExtraction = Step.factory.get(\"Magic.SpiceExtraction\")\n",
"\n",
"spx = SpiceExtraction(state_in=drc.state_out)\n",
"spx.start()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Layout vs. Schematic (LVS)\n",
"\n",
"A comparison between the final Verilog netlist (from PnR) and the final\n",
"SPICE netlist (extracted.)\n",
"\n",
"This check effectively compares the physically implemented circuit to the final\n",
"Verilog netlist output by OpenROAD.\n",
"\n",
"The idea is, if there are any disconnects, shorts or other mismatches in the\n",
"physical implementation that do not exist in the logical view of the design,\n",
"they would be caught at this step.\n",
"\n",
"Common issues that result in LVS violations include:\n",
"* Lack of fill cells or tap cells in the design\n",
"* Two unrelated signals to be shorted, or a wire to be disconnected (most\n",
" commonly seen with misconfigured PDN)\n",
"\n",
"Chips with LVS errors are typically dead on arrival."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"LVS = Step.factory.get(\"Netgen.LVS\")\n",
"\n",
"lvs = LVS(state_in=spx.state_out)\n",
"lvs.start()"
]
}
],
"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.11.6"
},
"vscode": {
"interpreter": {
"hash": "259a389b4057ccde35404af821e33afdf5883d9f2d987c370b23bfef9f2c3eb5"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}